From aa98b071ebf993b9d960894177f16a115b7cc4c2 Mon Sep 17 00:00:00 2001 From: geemili Date: Sat, 6 Apr 2024 22:18:21 -0600 Subject: [PATCH] more multiplayer stuff --- src/Element/ServerList.zig | 38 +++- src/Element/View.zig | 127 ++++++++++--- src/LocalUI.zig | 352 +++++++++++++++++++++---------------- src/main.zig | 69 -------- src/protocol.zig | 8 +- 5 files changed, 344 insertions(+), 250 deletions(-) diff --git a/src/Element/ServerList.zig b/src/Element/ServerList.zig index ed5fc7f..ee2049d 100644 --- a/src/Element/ServerList.zig +++ b/src/Element/ServerList.zig @@ -1,5 +1,6 @@ allocator: std.mem.Allocator, discovered: *Discovered, +hovered: usize, pub const Discovered = struct { allocator: std.mem.Allocator, @@ -14,6 +15,7 @@ pub fn create(allocator: std.mem.Allocator, discovered: *Discovered) !*@This() { this.* = .{ .allocator = allocator, .discovered = discovered, + .hovered = 0, }; return this; } @@ -50,17 +52,45 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou this.discovered.mutex.lock(); defer this.discovered.mutex.unlock(); - for (this.discovered.servers.keys()) |url| { + for (this.discovered.servers.keys(), 0..) |url, i| { const text_size = canvas.writeText(pos, url, .{}); + if (i == this.hovered) { + canvas.rect(pos, text_size, .{ .color = .{ 0xAA, 0xFF, 0xAA, 0x60 } }); + } pos[1] += text_size[1]; } } pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command { const this: *@This() = @ptrCast(@alignCast(pointer)); - _ = this; - _ = event; - return null; + + this.discovered.mutex.lock(); + defer this.discovered.mutex.unlock(); + std.log.debug("server list event = {}", .{event}); + const new_hovered: usize = switch (event) { + .activate => if (this.hovered < this.discovered.servers.count()) { + var iter = std.mem.splitScalar(u8, this.discovered.servers.keys()[this.hovered], ':'); + const domain_name = iter.next().?; + const port_str = iter.next().?; + std.debug.assert(std.mem.eql(u8, iter.rest(), "")); + + const port = std.fmt.parseInt(u16, port_str, 10) catch return null; + + const ip_addresses = std.net.getAddressList(this.allocator, domain_name, port) catch return null; + defer ip_addresses.deinit(); + + if (ip_addresses.addrs.len <= 0) return null; + + return Element.Command{ .goto_reference = .{ .server = ip_addresses.addrs[0] } }; + } else { + return null; + }, + .down => if (this.hovered < this.discovered.servers.count() - 1) this.hovered + 1 else return null, + .up => if (this.hovered > 0) this.hovered - 1 else return null, + else => return null, + }; + this.hovered = new_hovered; + return Element.Command.none; } const Element = @import("../Element.zig"); diff --git a/src/Element/View.zig b/src/Element/View.zig index ac96afb..97bd963 100644 --- a/src/Element/View.zig +++ b/src/Element/View.zig @@ -1,27 +1,65 @@ allocator: std.mem.Allocator, reference: protocol.Reference, -handler_pointer: ?*anyopaque, +state_info: ReferenceStateInfo, + response: ?protocol.Response, +const ReferenceStateInfo = union(protocol.Reference.Tag) { + handler: struct { + pointer: ?*anyopaque, + }, + server: struct { + client: *c.ENetHost, + peer: *c.ENetPeer, + }, +}; + const View = @This(); pub fn create(allocator: std.mem.Allocator, reference: protocol.Reference) !*@This() { const this = try allocator.create(@This()); errdefer allocator.destroy(this); - var handler_pointer: ?*anyopaque = null; - switch (reference) { - .handler => |interface| if (interface.create) |handler_create| { - handler_pointer = try handler_create(allocator); + const state_info = switch (reference) { + .handler => |interface| ReferenceStateInfo{ + .handler = .{ + .pointer = if (interface.create) |handler_create| + try handler_create(allocator) + else + null, + }, }, - } + .server => |address| create_enet_host: { + const client = c.enet_host_create(null, 1, 2, 0, 0); + if (client == null) { + std.log.warn("Failed to create ENetClient host", .{}); + return error.OutOfMemory; + } + + var enet_address: c.ENetAddress = .{ + .host = address.in.sa.addr, + .port = address.in.getPort(), + }; + const peer = c.enet_host_connect(client, &enet_address, 2, 0); + if (peer == null) { + std.log.warn("Failed to create ENetClient host", .{}); + return error.OutOfMemory; + } + break :create_enet_host ReferenceStateInfo{ + .server = .{ + .client = client, + .peer = peer, + }, + }; + }, + }; this.* = .{ .allocator = allocator, .reference = reference, .response = null, - .handler_pointer = handler_pointer, + .state_info = state_info, }; return this; } @@ -33,17 +71,46 @@ pub fn setReference(this: *@This(), reference: protocol.Reference) void { } switch (this.reference) { - .handler => |interface| if (interface.deinit) |deinit| deinit(this.handler_pointer), + .handler => |interface| if (interface.deinit) |deinit| deinit(this.state_info.handler.pointer), + // TODO + .server => {}, } - this.handler_pointer = null; this.reference = reference; - switch (this.reference) { - .handler => |interface| if (interface.create) |handler_create| { - this.handler_pointer = handler_create(this.allocator) catch unreachable; + this.state_info = switch (reference) { + .handler => |interface| ReferenceStateInfo{ + .handler = .{ + .pointer = if (interface.create) |handler_create| + handler_create(this.allocator) catch unreachable + else + null, + }, }, - } + .server => |address| create_enet_host: { + std.log.info("Connecting to {}", .{address}); + + const client = c.enet_host_create(null, 1, 2, 0, 0); + if (client == null) { + std.debug.panic("Failed to create ENetClient host", .{}); + } + + var enet_address: c.ENetAddress = .{ + .host = address.in.sa.addr, + .port = address.in.getPort(), + }; + const peer = c.enet_host_connect(client, &enet_address, 2, 0); + if (peer == null) { + std.debug.panic("Failed to connect to ENet peer", .{}); + } + break :create_enet_host ReferenceStateInfo{ + .server = .{ + .client = client, + .peer = peer, + }, + }; + }, + }; } pub fn element(this: *@This()) Element { @@ -70,7 +137,9 @@ pub fn element_destroy(pointer: ?*anyopaque) void { } switch (this.reference) { - .handler => |interface| if (interface.deinit) |deinit| deinit(this.handler_pointer), + .handler => |interface| if (interface.deinit) |deinit| deinit(this.state_info.handler.pointer), + // TODO + .server => {}, } this.allocator.destroy(this); @@ -91,12 +160,29 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou const this: *@This() = @ptrCast(@alignCast(pointer)); if (this.response == null) { - this.response = this.reference.handler.handle(this.handler_pointer, .{ - .allocator = this.allocator, - }) catch response_error: { - std.log.warn("Could not get response", .{}); - break :response_error null; - }; + switch (this.reference) { + .handler => |handler| { + const handler_pointer = this.state_info.handler.pointer; + this.response = handler.handle(handler_pointer, .{ + .allocator = this.allocator, + }) catch response_error: { + std.log.warn("Could not get response", .{}); + break :response_error null; + }; + }, + .server => |address| { + const server = this.state_info.server; + var event: c.ENetEvent = undefined; + while (c.enet_host_service(server.client, &event, 0) > 0) { + switch (event.type) { + c.ENET_EVENT_TYPE_CONNECT => { + std.log.info("Connection to {} succeeded", .{address}); + }, + else => {}, + } + } + }, + } } if (this.response) |response| { @@ -128,6 +214,7 @@ pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Comman const Element = @import("../Element.zig"); const protocol = @import("../protocol.zig"); const assets = @import("../assets.zig"); +const c = @import("../c.zig"); const seizer = @import("seizer"); const gl = seizer.gl; diff --git a/src/LocalUI.zig b/src/LocalUI.zig index fc904d3..7f8ced6 100644 --- a/src/LocalUI.zig +++ b/src/LocalUI.zig @@ -75,7 +75,7 @@ const JoinMultiplayerGame = struct { var text = try Element.Text.create(arena.allocator(), try std.fmt.allocPrint(arena.allocator(), "Looking for multiplayer games", .{})); - var server_list = try Element.ServerList.create(arena.allocator(), this.discovered); + var server_list = try Element.ServerList.create(this.allocator, this.discovered); var page = try Element.Page.create(request.allocator); try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, text.element()); @@ -230,11 +230,7 @@ const JoinMultiplayerGame = struct { const HostMultiplayerGame = struct { allocator: std.mem.Allocator, - mdns_addr4_string: []u8, - mdns_addr6_string: []u8, - - mdns_socket4: std.os.socket_t, - mdns_socket6: std.os.socket_t, + mdns_sockets: []std.os.socket_t, service_string: []const u8, service_instance_string: []const u8, @@ -245,6 +241,8 @@ const HostMultiplayerGame = struct { address: c.ENetAddress, server: *c.ENetHost, + enet_thread: std.Thread, + enet_thread_should_stop: std.atomic.Value(bool), pub const INTERFACE = &Handler.Interface{ .create = &create, @@ -270,127 +268,149 @@ const HostMultiplayerGame = struct { // setup mDNS discovery service - const mdns_socket = std.os.socket(std.os.AF.INET, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| { - std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e}); - return error.OutOfMemory; - }; + const addresses = @import("./multiplayer.zig").getIPAddresses(allocator) catch return error.OutOfMemory; + defer this.allocator.free(addresses); - std.os.setsockopt( - mdns_socket, - std.os.SOL.SOCKET, - std.os.SO.REUSEADDR, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; - std.os.setsockopt( - mdns_socket, - std.os.SOL.SOCKET, - std.os.SO.REUSEPORT, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; - std.os.setsockopt( - mdns_socket, - std.os.IPPROTO.IP, - std.os.linux.IP.MULTICAST_TTL, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; - std.os.setsockopt( - mdns_socket, - std.os.IPPROTO.IP, - std.os.linux.IP.MULTICAST_LOOP, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; + var mdns_sockets = std.ArrayList(std.os.socket_t).init(allocator); + defer mdns_sockets.deinit(); - const ip_mreq_t = extern struct { - multicast_address: [4]u8, - address: [4]u8, - }; - std.os.setsockopt( - mdns_socket, - std.os.IPPROTO.IP, - std.os.linux.IP.ADD_MEMBERSHIP, - &std.mem.toBytes(ip_mreq_t{ - .multicast_address = .{ 224, 0, 0, 251 }, - .address = .{ 0, 0, 0, 0 }, - }), - ) catch unreachable; + try mdns_sockets.ensureTotalCapacity(addresses.len); + for (addresses) |ip_address| { + switch (ip_address) { + .ipv4 => |ip| { + const std_address = std.net.Ip4Address.init(ip, c.MDNS_PORT); + const socket = c.mdns_socket_open_ipv4(@ptrCast(&std_address.sa)); + mdns_sockets.appendAssumeCapacity(socket); + }, + .ipv6 => |ip| { + const std_address = std.net.Ip6Address.init(ip, c.MDNS_PORT, 0, 0); + const socket = c.mdns_socket_open_ipv6(@ptrCast(&std_address.sa)); + mdns_sockets.appendAssumeCapacity(socket); + }, + } + } - const mdns_sock_addr = std.net.Address.parseIp4("0.0.0.0", c.MDNS_PORT) catch unreachable; - std.os.bind(mdns_socket, @ptrCast(&mdns_sock_addr), mdns_sock_addr.getOsSockLen()) catch |e| { - std.log.scoped(.mdns).err("bind failed = {}", .{e}); - return error.OutOfMemory; - }; + // const mdns_socket = std.os.socket(std.os.AF.INET, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| { + // std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e}); + // return error.OutOfMemory; + // }; - std.os.setsockopt( - mdns_socket, - std.os.IPPROTO.IP, - std.os.linux.IP.MULTICAST_IF, - &std.mem.toBytes(mdns_sock_addr.any), - ) catch unreachable; + // std.os.setsockopt( + // mdns_socket, + // std.os.SOL.SOCKET, + // std.os.SO.REUSEADDR, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; + // std.os.setsockopt( + // mdns_socket, + // std.os.SOL.SOCKET, + // std.os.SO.REUSEPORT, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; + // std.os.setsockopt( + // mdns_socket, + // std.os.IPPROTO.IP, + // std.os.linux.IP.MULTICAST_TTL, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; + // std.os.setsockopt( + // mdns_socket, + // std.os.IPPROTO.IP, + // std.os.linux.IP.MULTICAST_LOOP, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; - // ipv6 socket - const mdns_socket6 = std.os.socket(std.os.AF.INET6, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| { - std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e}); - return error.OutOfMemory; - }; + // const ip_mreq_t = extern struct { + // multicast_address: [4]u8, + // address: [4]u8, + // }; + // std.os.setsockopt( + // mdns_socket, + // std.os.IPPROTO.IP, + // std.os.linux.IP.ADD_MEMBERSHIP, + // &std.mem.toBytes(ip_mreq_t{ + // .multicast_address = .{ 224, 0, 0, 251 }, + // .address = .{ 0, 0, 0, 0 }, + // }), + // ) catch unreachable; - std.os.setsockopt( - mdns_socket6, - std.os.SOL.SOCKET, - std.os.SO.REUSEADDR, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; - std.os.setsockopt( - mdns_socket6, - std.os.SOL.SOCKET, - std.os.SO.REUSEPORT, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; - std.os.setsockopt( - mdns_socket6, - std.os.IPPROTO.IPV6, - std.os.linux.IPV6.MULTICAST_HOPS, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; - std.os.setsockopt( - mdns_socket6, - std.os.IPPROTO.IPV6, - std.os.linux.IPV6.MULTICAST_LOOP, - &std.mem.toBytes(@as(c_int, 1)), - ) catch unreachable; - std.os.setsockopt( - mdns_socket6, - std.os.IPPROTO.IPV6, - std.os.linux.IPV6.MULTICAST_IF, - &std.mem.toBytes(@as(c_int, 0)), - ) catch unreachable; + // const mdns_sock_addr = std.net.Address.parseIp4("0.0.0.0", c.MDNS_PORT) catch unreachable; + // std.os.bind(mdns_socket, @ptrCast(&mdns_sock_addr), mdns_sock_addr.getOsSockLen()) catch |e| { + // std.log.scoped(.mdns).err("bind failed = {}", .{e}); + // return error.OutOfMemory; + // }; - const ipv6_mreq_t = extern struct { - addr: [16]u8, - interface: c_int, - }; + // std.os.setsockopt( + // mdns_socket, + // std.os.IPPROTO.IP, + // std.os.linux.IP.MULTICAST_IF, + // &std.mem.toBytes(mdns_sock_addr.any), + // ) catch unreachable; - std.os.setsockopt( - mdns_socket6, - std.os.IPPROTO.IPV6, - std.os.linux.IPV6.ADD_MEMBERSHIP, - &std.mem.toBytes(ipv6_mreq_t{ - .addr = .{ 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFB }, - .interface = 0, - }), - ) catch unreachable; + // // ipv6 socket + // const mdns_socket6 = std.os.socket(std.os.AF.INET6, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| { + // std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e}); + // return error.OutOfMemory; + // }; - const mdns_sock_addr6 = std.net.Address.parseIp6("::", c.MDNS_PORT) catch unreachable; - var socklen = mdns_sock_addr6.getOsSockLen(); - std.os.bind(mdns_socket6, &mdns_sock_addr6.any, socklen) catch unreachable; + // std.os.setsockopt( + // mdns_socket6, + // std.os.SOL.SOCKET, + // std.os.SO.REUSEADDR, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; + // std.os.setsockopt( + // mdns_socket6, + // std.os.SOL.SOCKET, + // std.os.SO.REUSEPORT, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; + // std.os.setsockopt( + // mdns_socket6, + // std.os.IPPROTO.IPV6, + // std.os.linux.IPV6.MULTICAST_HOPS, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; + // std.os.setsockopt( + // mdns_socket6, + // std.os.IPPROTO.IPV6, + // std.os.linux.IPV6.MULTICAST_LOOP, + // &std.mem.toBytes(@as(c_int, 1)), + // ) catch unreachable; + // std.os.setsockopt( + // mdns_socket6, + // std.os.IPPROTO.IPV6, + // std.os.linux.IPV6.MULTICAST_IF, + // &std.mem.toBytes(@as(c_int, 0)), + // ) catch unreachable; - var mdns_listen_addr: std.net.Address = undefined; - std.os.getsockname(mdns_socket, &mdns_listen_addr.any, &socklen) catch unreachable; - const ipv4_sockaddr_string = try std.fmt.allocPrint(allocator, "{}", .{mdns_listen_addr}); + // const ipv6_mreq_t = extern struct { + // addr: [16]u8, + // interface: c_int, + // }; - socklen = mdns_sock_addr6.getOsSockLen(); - var mdns_listen_addr6: std.net.Address = undefined; - std.os.getsockname(mdns_socket6, &mdns_listen_addr6.any, &socklen) catch unreachable; - const ipv6_sockaddr_string = try std.fmt.allocPrint(allocator, "{}", .{mdns_listen_addr6}); + // std.os.setsockopt( + // mdns_socket6, + // std.os.IPPROTO.IPV6, + // std.os.linux.IPV6.ADD_MEMBERSHIP, + // &std.mem.toBytes(ipv6_mreq_t{ + // .addr = .{ 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFB }, + // .interface = 0, + // }), + // ) catch unreachable; + + // const mdns_sock_addr6 = std.net.Address.parseIp6("::", c.MDNS_PORT) catch unreachable; + // var socklen = mdns_sock_addr6.getOsSockLen(); + // std.os.bind(mdns_socket6, &mdns_sock_addr6.any, socklen) catch unreachable; + + // var mdns_listen_addr: std.net.Address = undefined; + // std.os.getsockname(mdns_socket, &mdns_listen_addr.any, &socklen) catch unreachable; + // const ipv4_sockaddr_string = try std.fmt.allocPrint(allocator, "{}", .{mdns_listen_addr}); + + // socklen = mdns_sock_addr6.getOsSockLen(); + // var mdns_listen_addr6: std.net.Address = undefined; + // std.os.getsockname(mdns_socket6, &mdns_listen_addr6.any, &socklen) catch unreachable; + // const ipv6_sockaddr_string = try std.fmt.allocPrint(allocator, "{}", .{mdns_listen_addr6}); const service_string = try allocator.dupe(u8, SERVICE_NAME); @@ -436,10 +456,7 @@ const HostMultiplayerGame = struct { .ttl = 0, }); - // get a list of addresses to populate A and AAAA DNS records - const addresses = @import("./multiplayer.zig").getIPAddresses(allocator) catch return error.OutOfMemory; - defer this.allocator.free(addresses); - + // turn the list of IP addresses into A and AAAA DNS records for (addresses) |ip_address| { switch (ip_address) { .ipv4 => |ipv4| { @@ -472,10 +489,7 @@ const HostMultiplayerGame = struct { this.* = .{ .allocator = allocator, - .mdns_addr4_string = ipv4_sockaddr_string, - .mdns_addr6_string = ipv6_sockaddr_string, - .mdns_socket4 = mdns_socket, - .mdns_socket6 = mdns_socket6, + .mdns_sockets = try mdns_sockets.toOwnedSlice(), .service_string = service_string, .service_instance_string = service_instance_string, .qualified_hostname = qualified_hostname, @@ -485,9 +499,12 @@ const HostMultiplayerGame = struct { .address = address, .server = server, + .enet_thread = undefined, + .enet_thread_should_stop = std.atomic.Value(bool).init(false), }; this.mdns_thread = std.Thread.spawn(.{}, mdns_thread_main, .{this}) catch return error.OutOfMemory; + this.enet_thread = std.Thread.spawn(.{}, enet_thread_main, .{this}) catch return error.OutOfMemory; return this; } @@ -499,15 +516,15 @@ const HostMultiplayerGame = struct { errdefer arena.deinit(); // var text = try Element.Text.create(arena.allocator(), try std.fmt.allocPrint(arena.allocator(), "Hosting Multiplayer Game at {}:{}", .{ this.address.host, this.address.port })); - var mdns_addr4_string_text = try Element.Text.create(request.allocator, this.mdns_addr4_string); - var mdns_addr6_string_text = try Element.Text.create(request.allocator, this.mdns_addr6_string); + // var mdns_addr4_string_text = try Element.Text.create(request.allocator, this.mdns_addr4_string); + // var mdns_addr6_string_text = try Element.Text.create(request.allocator, this.mdns_addr6_string); var service_instance_string_text = try Element.Text.create(request.allocator, this.service_instance_string); var qualified_hostname_text = try Element.Text.create(request.allocator, this.qualified_hostname); var page = try Element.Page.create(request.allocator); // try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, text.element()); - try page.addElement(.{ 0.3, 0.6 }, .{ 0.5, 0.5 }, mdns_addr4_string_text.element()); - try page.addElement(.{ 0.7, 0.6 }, .{ 0.5, 0.5 }, mdns_addr6_string_text.element()); + // try page.addElement(.{ 0.3, 0.6 }, .{ 0.5, 0.5 }, mdns_addr4_string_text.element()); + // try page.addElement(.{ 0.7, 0.6 }, .{ 0.5, 0.5 }, mdns_addr6_string_text.element()); try page.addElement(.{ 0.5, 0.7 }, .{ 0.5, 0.5 }, service_instance_string_text.element()); try page.addElement(.{ 0.5, 0.9 }, .{ 0.5, 0.5 }, qualified_hostname_text.element()); @@ -523,8 +540,9 @@ const HostMultiplayerGame = struct { this.mdns_thread.join(); this.allocator.free(this.mdns_records); - this.allocator.free(this.mdns_addr4_string); - this.allocator.free(this.mdns_addr6_string); + this.allocator.free(this.mdns_sockets); + // this.allocator.free(this.mdns_addr4_string); + // this.allocator.free(this.mdns_addr6_string); this.allocator.free(this.service_string); this.allocator.free(this.service_instance_string); this.allocator.free(this.qualified_hostname); @@ -547,35 +565,32 @@ const HostMultiplayerGame = struct { const log = std.log.scoped(.mdns); log.info("mdns service started", .{}); - _ = c.mdns_announce_multicast( - this.mdns_socket4, - buffer.ptr, - buffer.len, - this.mdns_records[0], - null, - 0, - this.mdns_records[1..].ptr, - this.mdns_records[1..].len, - ); - _ = c.mdns_announce_multicast( - this.mdns_socket6, - buffer.ptr, - buffer.len, - this.mdns_records[0], - null, - 0, - this.mdns_records[1..].ptr, - this.mdns_records[1..].len, - ); + for (this.mdns_sockets) |socket| { + _ = c.mdns_announce_multicast( + socket, + buffer.ptr, + buffer.len, + this.mdns_records[0], + null, + 0, + this.mdns_records[1..].ptr, + this.mdns_records[1..].len, + ); + } + + const pollfds = try this.allocator.alloc(std.os.pollfd, this.mdns_sockets.len); + defer this.allocator.free(pollfds); + for (pollfds, this.mdns_sockets) |*pollfd, socket| { + pollfd.* = .{ .fd = socket, .events = std.os.POLL.IN, .revents = undefined }; + } while (!this.mdns_thread_should_stop.load(.Monotonic)) { - var pollfds = [_]std.os.pollfd{ - .{ .fd = this.mdns_socket4, .events = std.os.POLL.IN, .revents = undefined }, - .{ .fd = this.mdns_socket6, .events = std.os.POLL.IN, .revents = undefined }, - }; - _ = std.os.poll(&pollfds, 1000) catch break; + for (pollfds) |*pollfd| { + pollfd.events = std.os.POLL.IN; + } + _ = std.os.poll(pollfds, 1000) catch break; // listen for mdns messages - for (pollfds[0..2]) |pollfd| { + for (pollfds) |pollfd| { if (pollfd.revents & std.os.POLL.IN == 0) continue; _ = c.mdns_socket_listen(pollfd.fd, buffer.ptr, buffer.len, &mdns_thread_service_callback, this); } @@ -584,6 +599,31 @@ const HostMultiplayerGame = struct { log.info("mdns service stopping", .{}); } + fn enet_thread_main(this: *@This()) !void { + const log = std.log.scoped(.enet); + log.info("thread started", .{}); + + while (!this.mdns_thread_should_stop.load(.Monotonic)) { + var event: c.ENetEvent = undefined; + while (c.enet_host_service(this.server, &event, 1000) > 0) { + switch (event.type) { + c.ENET_EVENT_TYPE_CONNECT => { + std.log.info("New connection from {}:{}", .{ event.peer.*.address.host, event.peer.*.address.port }); + }, + c.ENET_EVENT_TYPE_RECEIVE => { + c.enet_packet_destroy(event.packet); + }, + c.ENET_EVENT_TYPE_DISCONNECT => { + std.log.info("{}:{} disconnected", .{ event.peer.*.address.host, event.peer.*.address.port }); + }, + else => {}, + } + } + } + + log.info("thread stopped", .{}); + } + fn mdns_thread_service_callback( sock: c_int, from: ?*const c.sockaddr, diff --git a/src/main.zig b/src/main.zig index 217581b..1f0200b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -586,75 +586,6 @@ fn endOfTurnConfirmHandler(arena: std.mem.Allocator, request: Request) Handler.E return Response{ .page = page.element() }; } -fn mainMenuHandler(arena: std.mem.Allocator, request: Request) Handler.Error!Response { - if (request.command) |command| { - if (std.mem.eql(u8, command, "join-multiplayer-game")) { - var new_game_state = try request.game_state.clone(arena); - new_game_state.handler = &joinMultiplayerGameHandler; - - return Response{ .transition = .{ - .game_state = new_game_state, - .history_type = .start, - } }; - } else if (std.mem.eql(u8, command, "host-multiplayer-game")) { - var new_game_state = try request.game_state.clone(arena); - new_game_state.handler = &hostMultiplayerGameHandler; - - return Response{ .transition = .{ - .game_state = new_game_state, - .history_type = .start, - } }; - } - } - - var join_multiplayer_text = try Element.Textcreate(arena, .{ - .text = "Join Multiplayer Game", - .command = "join-multiplayer-game", - }); - - var host_multiplayer_text = try Element.Text.create(arena, .{ - .text = "Host Multiplayer Game", - .command = "host-multiplayer-game", - }); - - var play_game_hbox = try Element.HBox.create(arena); - try play_game_hbox.addElement(join_multiplayer_text.element()); - try play_game_hbox.addElement(host_multiplayer_text.element()); - - var page = try Element.Page.create(arena); - try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, play_game_hbox.element()); - - return Response{ .page = page.element() }; -} - -fn hostMultiplayerGameHandler(arena: std.mem.Allocator, request: Request) Handler.Error!Response { - _ = request; - - var text = try Element.Text.create(arena, .{ - .text = "Hosting Multiplayer Game", - .command = null, - }); - - var page = try Element.Page.create(arena); - try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, text.element()); - - return Response{ .page = page.element() }; -} - -fn joinMultiplayerGameHandler(arena: std.mem.Allocator, request: Request) Handler.Error!Response { - _ = request; - - var text = try Element.Text.create(arena, .{ - .text = "Joining Multiplayer Game", - .command = null, - }); - - var page = try Element.Page.create(arena); - try page.addElement(.{ 0.5, 0.5 }, .{ 0.5, 0.5 }, text.element()); - - return Response{ .page = page.element() }; -} - fn isValidRummyMeld(cards: []const Card) bool { std.debug.assert(std.sort.isSorted(Card, cards, {}, rummyHandSort)); if (cards.len < 3) { diff --git a/src/protocol.zig b/src/protocol.zig index ad0f2ac..c2eb1d0 100644 --- a/src/protocol.zig +++ b/src/protocol.zig @@ -11,8 +11,14 @@ pub const Handler = struct { }; }; -pub const Reference = union(enum) { +pub const Reference = union(Tag) { handler: *const Handler.Interface, + server: std.net.Address, + + pub const Tag = enum { + handler, + server, + }; }; pub const Request = struct {