diff --git a/build.zig b/build.zig index 824c584c..72a125c9 100644 --- a/build.zig +++ b/build.zig @@ -39,7 +39,7 @@ pub fn build(b: *std.build.Builder) !void { std.log.err("Unsupported target: {}\n", .{ target.getOsTag() }); } } - exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c", "lib/cross_platform_udp_socket.c"}, &[_][]const u8{"-g"}); + exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c"}, &[_][]const u8{"-g"}); exe.addPackage(freetype.pkg); exe.addPackage(freetype.harfbuzz_pkg); freetype.link(b, exe, .{ .harfbuzz = .{} }); diff --git a/include/cross_platform_udp_socket.h b/include/cross_platform_udp_socket.h deleted file mode 100644 index 30eb7905..00000000 --- a/include/cross_platform_udp_socket.h +++ /dev/null @@ -1,11 +0,0 @@ -#include - -void startup(); - -int init(unsigned short localPort); -int deinit(int socketID); -int sendTo(int socketID, const char* data, uintptr_t size, uint32_t ip, uint16_t port); -intptr_t receiveFrom(int socketID, char* buffer, uintptr_t size, int timeout, uint32_t* resultIP, uint16_t* resultPort); -uint32_t resolveIP(const char* ip); - -int getError(); \ No newline at end of file diff --git a/lib/cross_platform_udp_socket.c b/lib/cross_platform_udp_socket.c deleted file mode 100644 index 53d29ab1..00000000 --- a/lib/cross_platform_udp_socket.c +++ /dev/null @@ -1,110 +0,0 @@ -#ifdef _WIN32 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#endif -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#include - -#include - -int checkError(int in) { - // TODO: Print the error here. - return in; -} - -void startup() { -#ifdef _WIN32 - WSADATA d; - if (WSAStartup(MAKEWORD(2, 2), &d)) { - fprintf(stderr, "Failed to initialize.\n"); - } -#endif -} - -int init(unsigned short localPort) { - int socketID = checkError(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)); - if(socketID == -1) return -1; - struct sockaddr_in bindingAddr; - bindingAddr.sin_family = AF_INET; - bindingAddr.sin_port = htons(localPort); - bindingAddr.sin_addr.s_addr = INADDR_ANY; - memset(&bindingAddr.sin_zero, 0, 8); - if(checkError(bind(socketID, (const struct sockaddr*)&bindingAddr, sizeof(bindingAddr))) == -1) { -#ifdef _WIN32 - closesocket(socketID); -#else - close(socketID); -#endif - return -1; - }; - return socketID; -} - -int deinit(int socketID) { -#ifdef _WIN32 - return checkError(closesocket(socketID)); -#else - return checkError(close(socketID)); -#endif -} - -int sendTo(int socketID, const char* data, uintptr_t size, uint32_t ip, uint16_t port) { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = ip; - memset(&addr.sin_zero, 0, 8); - return checkError(sendto(socketID, data, size, 0, (const struct sockaddr*)&addr, sizeof(addr))); -} - -intptr_t receiveFrom(int socketID, char* buffer, uintptr_t size, int timeout, uint32_t* resultIP, uint16_t* resultPort) { - struct pollfd pfd = {.fd = socketID, .events = POLLIN}; -#ifdef _WIN32 - intptr_t result = checkError(WSAPoll(&pfd, 1, timeout)); -#else - intptr_t result = checkError(poll(&pfd, 1, timeout)); -#endif - if(result <= 0) return result; - struct sockaddr_in address; - uint32_t addrLen = sizeof(address); - result = checkError(recvfrom(socketID, buffer, size, 0, (struct sockaddr *)&address, &addrLen)); - - *resultIP = address.sin_addr.s_addr; - *resultPort = ntohs(address.sin_port); - - return result; -} -uint32_t resolveIP(const char* address) { - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - struct addrinfo* res; - int result = getaddrinfo(address, NULL, &hints, &res); - if(result != 0) { - errno = result; - return 0xffffffff; - } - - uint32_t ip = ((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr; - freeaddrinfo(res); - return ip; -} - -int getError() { - return errno; -} \ No newline at end of file diff --git a/src/network.zig b/src/network.zig index 144ed725..a04816d4 100644 --- a/src/network.zig +++ b/src/network.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const std = @import("std"); const Allocator = std.mem.Allocator; @@ -22,46 +23,65 @@ const Vec3f = vec.Vec3f; //TODO: Might want to use SSL or something similar to encode the message const Socket = struct { - const c = @cImport({@cInclude("cross_platform_udp_socket.h");}); - socketID: u31, + const os = std.os; + socketID: os.socket_t, - fn checkError(comptime msg: []const u8, comptime T: type, result: T) !std.meta.Int(.unsigned, @bitSizeOf(T) - 1) { - if(result == -1) { - std.log.warn(msg, .{c.getError()}); - return error.SocketError; + fn startup() void { + if(builtin.os.tag == .windows) { + _ = os.windows.WSAStartup(2, 2) catch |err| { // TODO: Return the error (this triggers a false depency loop error right now). + std.log.err("Error trying to startup WSA: {}", .{@errorName(err)}); + }; } - return @intCast(std.meta.Int(.unsigned, @bitSizeOf(T) - 1), result); } fn init(localPort: u16) !Socket { - return Socket{.socketID = try checkError("Socket creation failed with error: {}", c_int, c.init(localPort))}; + var self = Socket { + .socketID = try os.socket(os.AF.INET, os.SOCK.DGRAM, os.IPPROTO.UDP), + }; + errdefer self.deinit(); + const bindingAddr = os.sockaddr.in { + .port = @byteSwap(localPort), + .addr = 0, + }; + try os.bind(self.socketID, @ptrCast(*const os.sockaddr, &bindingAddr), @sizeOf(os.sockaddr.in)); + return self; } fn deinit(self: Socket) void { - _ = checkError("Error while closing socket: {}", c_int, c.deinit(self.socketID)) catch 0; + os.closeSocket(self.socketID); } fn send(self: Socket, data: []const u8, destination: Address) !void { - _ = try checkError("Error sending data: {}", isize, c.sendTo(self.socketID, data.ptr, data.len, destination.ip, destination.port)); + const addr = os.sockaddr.in { + .port = @byteSwap(destination.port), + .addr = destination.ip, + }; + std.debug.assert(data.len == try os.sendto(self.socketID, data, 0, @ptrCast(*const os.sockaddr, &addr), @sizeOf(os.sockaddr.in))); } - fn receive(self: Socket, buffer: []u8, timeout: c_int, resultAddress: *Address) ![]u8 { - var length = try checkError("Receive failed: {}", isize, c.receiveFrom(self.socketID, buffer.ptr, buffer.len, timeout, &resultAddress.ip, &resultAddress.port)); + fn receive(self: Socket, buffer: []u8, timeout: i32, resultAddress: *Address) ![]u8 { + var pfd = [1]os.pollfd { + .{.fd = self.socketID, .events = os.POLL.IN, .revents = undefined}, + }; + var length = try os.poll(&pfd, timeout); if(length == 0) return error.Timeout; + var addr: os.sockaddr.in = undefined; + var addrLen: os.socklen_t = @sizeOf(os.sockaddr.in); + length = try os.recvfrom(self.socketID, buffer, 0, @ptrCast(*os.sockaddr, &addr), &addrLen); + resultAddress.ip = addr.addr; + resultAddress.port = @byteSwap(addr.port); return buffer[0..length]; } - fn resolveIP(ip: [:0]const u8) u32 { - const result: u32 = c.resolveIP(ip.ptr); - if(result == 0xffffffff) { - std.log.warn("Could not resolve address: {s} error: {}", .{ip, c.getError()}); - } - return result; + fn resolveIP(addr: []const u8) !u32 { + const list = try std.net.getAddressList(main.threadAllocator, addr, settings.defaultPort); + defer list.deinit(); + return list.addrs[0].in.sa.addr; } }; pub fn init() void { - Socket.c.startup(); + Socket.startup(); inline for(@typeInfo(@TypeOf(Protocols)).Struct.fields) |field| { if(field.type == type) { const id = @field(Protocols, field.name).id; @@ -239,9 +259,14 @@ const STUN = struct { random.fill(data[8..]); // Fill the transaction ID. var splitter = std.mem.split(u8, server, ":"); - var nullTerminatedIP = main.threadAllocator.dupeZ(u8, splitter.first()) catch continue; - defer main.threadAllocator.free(nullTerminatedIP); - var serverAddress = Address{.ip=Socket.resolveIP(nullTerminatedIP), .port=std.fmt.parseUnsigned(u16, splitter.rest(), 10) catch 3478}; + const ip = splitter.first(); + var serverAddress = Address { + .ip=Socket.resolveIP(ip) catch |err| { + std.log.err("Cannot resolve stun server address: {s}, error: {s}", .{ip, @errorName(err)}); + continue; + }, + .port=std.fmt.parseUnsigned(u16, splitter.rest(), 10) catch 3478 + }; if(connection.sendRequest(connection.allocator, &data, serverAddress, 500*1000000) catch |err| { std.log.warn("Encountered error: {s} while connecting to STUN server: {s}", .{@errorName(err), server}); continue; @@ -270,7 +295,7 @@ const STUN = struct { std.log.warn("Couldn't reach STUN server: {s}", .{server}); } } - return Address{.ip=Socket.resolveIP("127.0.0.1"), .port=settings.defaultPort}; // TODO: Return ip address in LAN. + return Address{.ip=Socket.resolveIP("127.0.0.1") catch unreachable, .port=settings.defaultPort}; // TODO: Return ip address in LAN. } fn findIPPort(_data: []const u8) !Address { @@ -330,7 +355,7 @@ pub const ConnectionManager = struct { thread: std.Thread = undefined, threadId: std.Thread.Id = undefined, externalAddress: Address = undefined, - online: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(true), + online: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(false), running: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(true), connections: std.ArrayList(*Connection) = undefined, @@ -1248,9 +1273,8 @@ pub const Connection = struct { std.ArrayList(u32).init(result.allocator), }; var splitter = std.mem.split(u8, ipPort, ":"); - var nullTerminatedIP = try main.threadAllocator.dupeZ(u8, splitter.first()); - defer main.threadAllocator.free(nullTerminatedIP); - result.remoteAddress.ip = Socket.resolveIP(nullTerminatedIP); + const ip = splitter.first(); + result.remoteAddress.ip = try Socket.resolveIP(ip); var port = splitter.rest(); if(port.len != 0 and port[0] == '?') { result.remoteAddress.isSymmetricNAT = true;