From ca0a37cabbf435bbd012eab48b16ec30d1f761d7 Mon Sep 17 00:00:00 2001 From: IntegratedQuantum Date: Sun, 16 Feb 2025 16:23:39 +0100 Subject: [PATCH] Add an option to allow people to join without being invited (requires a public IP address). Due to the non-obvious technical requirements, I decided to hide it a bit. fixes #618 --- src/gui/windows/invite.zig | 18 +++++++++++++++++- src/network.zig | 31 +++++++++++++++++++------------ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/gui/windows/invite.zig b/src/gui/windows/invite.zig index d21d865f..3a70cdf4 100644 --- a/src/gui/windows/invite.zig +++ b/src/gui/windows/invite.zig @@ -9,6 +9,7 @@ const gui = @import("../gui.zig"); const GuiComponent = gui.GuiComponent; const GuiWindow = gui.GuiWindow; const Button = @import("../components/Button.zig"); +const CheckBox = @import("../components/CheckBox.zig"); const Label = @import("../components/Label.zig"); const TextInput = @import("../components/TextInput.zig"); const VerticalList = @import("../components/VerticalList.zig"); @@ -59,8 +60,22 @@ fn copyIp(_: usize) void { main.Window.setClipboardString(ipAddress); } +fn inviteFromExternal(address: main.network.Address) void { + const ip = std.fmt.allocPrint(main.stackAllocator.allocator, "{}", .{address}) catch unreachable; + defer main.stackAllocator.free(ip); + const user = main.server.User.initAndIncreaseRefCount(main.server.connectionManager, ip) catch |err| { + std.log.err("Cannot connect user from external IP {}: {s}", .{address, @errorName(err)}); + return; + }; + user.decreaseRefCount(); +} + +fn makePublic(public: bool) void { + main.server.connectionManager.newConnectionCallback.store(if(public) &inviteFromExternal else null, .monotonic); +} + pub fn onOpen() void { - const list = VerticalList.init(.{padding, 16 + padding}, 300, 16); + const list = VerticalList.init(.{padding, 16 + padding}, 260, 16); list.add(Label.init(.{0, 0}, width, "Please send your IP to the player who wants to join and enter their IP below.", .center)); // 255.255.255.255:?65536 (longest possible ip address) ipAddressLabel = Label.init(.{0, 0}, width, " ", .center); @@ -70,6 +85,7 @@ pub fn onOpen() void { list.add(ipAddressEntry); list.add(Button.initText(.{0, 0}, 100, "Invite", .{.callback = &invite})); list.add(Button.initText(.{0, 0}, 100, "Manage Players", gui.openWindowCallback("manage_players"))); + list.add(CheckBox.init(.{0, 0}, width, "Allow anyone to join (requires a publicly visible IP address+port which may need some configuration in your router)", main.server.connectionManager.newConnectionCallback.load(.monotonic) != null, &makePublic)); list.finish(.center); window.rootComponent = list.toComponent(); window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding)); diff --git a/src/network.zig b/src/network.zig index 3002a884..e4eac3f4 100644 --- a/src/network.zig +++ b/src/network.zig @@ -126,7 +126,7 @@ pub fn init() void { } } -const Address = struct { +pub const Address = struct { ip: u32, port: u16, isSymmetricNAT: bool = false, @@ -368,6 +368,7 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager mutex: std.Thread.Mutex = .{}, waitingToFinishReceive: std.Thread.Condition = std.Thread.Condition{}, + newConnectionCallback: Atomic(?*const fn(Address) void) = .init(null), receiveBuffer: [Connection.maxPacketSize]u8 = undefined, @@ -533,19 +534,25 @@ pub const ConnectionManager = struct { // MARK: ConnectionManager } } } - defer self.mutex.unlock(); - // Check if it's part of an active request: - for(self.requests.items) |request| { - if(request.address.ip == source.ip and request.address.port == source.port) { - request.data = main.globalAllocator.dupe(u8, data); - request.requestNotifier.signal(); - return; + { + defer self.mutex.unlock(); + // Check if it's part of an active request: + for(self.requests.items) |request| { + if(request.address.ip == source.ip and request.address.port == source.port) { + request.data = main.globalAllocator.dupe(u8, data); + request.requestNotifier.signal(); + return; + } } + if(self.online.load(.acquire) and source.ip == self.externalAddress.ip and source.port == self.externalAddress.port) return; + } + if(self.newConnectionCallback.load(.monotonic)) |callback| { + callback(source); + } else { + // TODO: Reduce the number of false alarms in the short period after a disconnect. + std.log.warn("Unknown connection from address: {}", .{source}); + std.log.debug("Message: {any}", .{data}); } - if(self.online.load(.acquire) and source.ip == self.externalAddress.ip and source.port == self.externalAddress.port) return; - // TODO: Reduce the number of false alarms in the short period after a disconnect. - std.log.warn("Unknown connection from address: {}", .{source}); - std.log.debug("Message: {any}", .{data}); } pub fn run(self: *ConnectionManager) void {