more multiplayer stuff

multiplayer
LeRoyce Pearson 2024-04-06 22:18:21 -06:00
parent 390eaebd90
commit aa98b071eb
5 changed files with 344 additions and 250 deletions

View File

@ -1,5 +1,6 @@
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
discovered: *Discovered, discovered: *Discovered,
hovered: usize,
pub const Discovered = struct { pub const Discovered = struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
@ -14,6 +15,7 @@ pub fn create(allocator: std.mem.Allocator, discovered: *Discovered) !*@This() {
this.* = .{ this.* = .{
.allocator = allocator, .allocator = allocator,
.discovered = discovered, .discovered = discovered,
.hovered = 0,
}; };
return this; return this;
} }
@ -50,17 +52,45 @@ pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resou
this.discovered.mutex.lock(); this.discovered.mutex.lock();
defer this.discovered.mutex.unlock(); 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, .{}); 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]; pos[1] += text_size[1];
} }
} }
pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command { pub fn element_event(pointer: ?*anyopaque, event: Element.Event) ?Element.Command {
const this: *@This() = @ptrCast(@alignCast(pointer)); const this: *@This() = @ptrCast(@alignCast(pointer));
_ = this;
_ = event; 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; 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"); const Element = @import("../Element.zig");

View File

@ -1,27 +1,65 @@
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
reference: protocol.Reference, reference: protocol.Reference,
handler_pointer: ?*anyopaque, state_info: ReferenceStateInfo,
response: ?protocol.Response, response: ?protocol.Response,
const ReferenceStateInfo = union(protocol.Reference.Tag) {
handler: struct {
pointer: ?*anyopaque,
},
server: struct {
client: *c.ENetHost,
peer: *c.ENetPeer,
},
};
const View = @This(); const View = @This();
pub fn create(allocator: std.mem.Allocator, reference: protocol.Reference) !*@This() { pub fn create(allocator: std.mem.Allocator, reference: protocol.Reference) !*@This() {
const this = try allocator.create(@This()); const this = try allocator.create(@This());
errdefer allocator.destroy(this); errdefer allocator.destroy(this);
var handler_pointer: ?*anyopaque = null; const state_info = switch (reference) {
switch (reference) { .handler => |interface| ReferenceStateInfo{
.handler => |interface| if (interface.create) |handler_create| { .handler = .{
handler_pointer = try handler_create(allocator); .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.* = .{ this.* = .{
.allocator = allocator, .allocator = allocator,
.reference = reference, .reference = reference,
.response = null, .response = null,
.handler_pointer = handler_pointer, .state_info = state_info,
}; };
return this; return this;
} }
@ -33,17 +71,46 @@ pub fn setReference(this: *@This(), reference: protocol.Reference) void {
} }
switch (this.reference) { 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; this.reference = reference;
switch (this.reference) { this.state_info = switch (reference) {
.handler => |interface| if (interface.create) |handler_create| { .handler => |interface| ReferenceStateInfo{
this.handler_pointer = handler_create(this.allocator) catch unreachable; .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 { pub fn element(this: *@This()) Element {
@ -70,7 +137,9 @@ pub fn element_destroy(pointer: ?*anyopaque) void {
} }
switch (this.reference) { 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); 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)); const this: *@This() = @ptrCast(@alignCast(pointer));
if (this.response == null) { if (this.response == null) {
this.response = this.reference.handler.handle(this.handler_pointer, .{ switch (this.reference) {
.handler => |handler| {
const handler_pointer = this.state_info.handler.pointer;
this.response = handler.handle(handler_pointer, .{
.allocator = this.allocator, .allocator = this.allocator,
}) catch response_error: { }) catch response_error: {
std.log.warn("Could not get response", .{}); std.log.warn("Could not get response", .{});
break :response_error null; 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| { 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 Element = @import("../Element.zig");
const protocol = @import("../protocol.zig"); const protocol = @import("../protocol.zig");
const assets = @import("../assets.zig"); const assets = @import("../assets.zig");
const c = @import("../c.zig");
const seizer = @import("seizer"); const seizer = @import("seizer");
const gl = seizer.gl; const gl = seizer.gl;

View File

@ -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 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); 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.5, 0.5 }, .{ 0.5, 0.5 }, text.element());
@ -230,11 +230,7 @@ const JoinMultiplayerGame = struct {
const HostMultiplayerGame = struct { const HostMultiplayerGame = struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
mdns_addr4_string: []u8, mdns_sockets: []std.os.socket_t,
mdns_addr6_string: []u8,
mdns_socket4: std.os.socket_t,
mdns_socket6: std.os.socket_t,
service_string: []const u8, service_string: []const u8,
service_instance_string: []const u8, service_instance_string: []const u8,
@ -245,6 +241,8 @@ const HostMultiplayerGame = struct {
address: c.ENetAddress, address: c.ENetAddress,
server: *c.ENetHost, server: *c.ENetHost,
enet_thread: std.Thread,
enet_thread_should_stop: std.atomic.Value(bool),
pub const INTERFACE = &Handler.Interface{ pub const INTERFACE = &Handler.Interface{
.create = &create, .create = &create,
@ -270,127 +268,149 @@ const HostMultiplayerGame = struct {
// setup mDNS discovery service // setup mDNS discovery service
const mdns_socket = std.os.socket(std.os.AF.INET, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| { const addresses = @import("./multiplayer.zig").getIPAddresses(allocator) catch return error.OutOfMemory;
std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e}); defer this.allocator.free(addresses);
return error.OutOfMemory;
};
std.os.setsockopt( var mdns_sockets = std.ArrayList(std.os.socket_t).init(allocator);
mdns_socket, defer mdns_sockets.deinit();
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;
const ip_mreq_t = extern struct { try mdns_sockets.ensureTotalCapacity(addresses.len);
multicast_address: [4]u8, for (addresses) |ip_address| {
address: [4]u8, switch (ip_address) {
}; .ipv4 => |ip| {
std.os.setsockopt( const std_address = std.net.Ip4Address.init(ip, c.MDNS_PORT);
mdns_socket, const socket = c.mdns_socket_open_ipv4(@ptrCast(&std_address.sa));
std.os.IPPROTO.IP, mdns_sockets.appendAssumeCapacity(socket);
std.os.linux.IP.ADD_MEMBERSHIP, },
&std.mem.toBytes(ip_mreq_t{ .ipv6 => |ip| {
.multicast_address = .{ 224, 0, 0, 251 }, const std_address = std.net.Ip6Address.init(ip, c.MDNS_PORT, 0, 0);
.address = .{ 0, 0, 0, 0 }, const socket = c.mdns_socket_open_ipv6(@ptrCast(&std_address.sa));
}), mdns_sockets.appendAssumeCapacity(socket);
) catch unreachable; },
}
}
const mdns_sock_addr = std.net.Address.parseIp4("0.0.0.0", c.MDNS_PORT) catch unreachable; // const mdns_socket = std.os.socket(std.os.AF.INET, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| {
std.os.bind(mdns_socket, @ptrCast(&mdns_sock_addr), mdns_sock_addr.getOsSockLen()) catch |e| { // std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e});
std.log.scoped(.mdns).err("bind failed = {}", .{e}); // return error.OutOfMemory;
return error.OutOfMemory; // };
};
std.os.setsockopt( // std.os.setsockopt(
mdns_socket, // mdns_socket,
std.os.IPPROTO.IP, // std.os.SOL.SOCKET,
std.os.linux.IP.MULTICAST_IF, // std.os.SO.REUSEADDR,
&std.mem.toBytes(mdns_sock_addr.any), // &std.mem.toBytes(@as(c_int, 1)),
) catch unreachable; // ) 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 ip_mreq_t = extern struct {
const mdns_socket6 = std.os.socket(std.os.AF.INET6, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| { // multicast_address: [4]u8,
std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e}); // address: [4]u8,
return error.OutOfMemory; // };
}; // 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( // const mdns_sock_addr = std.net.Address.parseIp4("0.0.0.0", c.MDNS_PORT) catch unreachable;
mdns_socket6, // std.os.bind(mdns_socket, @ptrCast(&mdns_sock_addr), mdns_sock_addr.getOsSockLen()) catch |e| {
std.os.SOL.SOCKET, // std.log.scoped(.mdns).err("bind failed = {}", .{e});
std.os.SO.REUSEADDR, // return error.OutOfMemory;
&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 ipv6_mreq_t = extern struct { // std.os.setsockopt(
addr: [16]u8, // mdns_socket,
interface: c_int, // std.os.IPPROTO.IP,
}; // std.os.linux.IP.MULTICAST_IF,
// &std.mem.toBytes(mdns_sock_addr.any),
// ) catch unreachable;
std.os.setsockopt( // // ipv6 socket
mdns_socket6, // const mdns_socket6 = std.os.socket(std.os.AF.INET6, std.os.SOCK.DGRAM, std.os.IPPROTO.UDP) catch |e| {
std.os.IPPROTO.IPV6, // std.log.scoped(.mdns).err("failed to open mdns socket = {}", .{e});
std.os.linux.IPV6.ADD_MEMBERSHIP, // return error.OutOfMemory;
&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; // std.os.setsockopt(
var socklen = mdns_sock_addr6.getOsSockLen(); // mdns_socket6,
std.os.bind(mdns_socket6, &mdns_sock_addr6.any, socklen) catch unreachable; // 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; // const ipv6_mreq_t = extern struct {
std.os.getsockname(mdns_socket, &mdns_listen_addr.any, &socklen) catch unreachable; // addr: [16]u8,
const ipv4_sockaddr_string = try std.fmt.allocPrint(allocator, "{}", .{mdns_listen_addr}); // interface: c_int,
// };
socklen = mdns_sock_addr6.getOsSockLen(); // std.os.setsockopt(
var mdns_listen_addr6: std.net.Address = undefined; // mdns_socket6,
std.os.getsockname(mdns_socket6, &mdns_listen_addr6.any, &socklen) catch unreachable; // std.os.IPPROTO.IPV6,
const ipv6_sockaddr_string = try std.fmt.allocPrint(allocator, "{}", .{mdns_listen_addr6}); // 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); const service_string = try allocator.dupe(u8, SERVICE_NAME);
@ -436,10 +456,7 @@ const HostMultiplayerGame = struct {
.ttl = 0, .ttl = 0,
}); });
// get a list of addresses to populate A and AAAA DNS records // turn the list of IP addresses into A and AAAA DNS records
const addresses = @import("./multiplayer.zig").getIPAddresses(allocator) catch return error.OutOfMemory;
defer this.allocator.free(addresses);
for (addresses) |ip_address| { for (addresses) |ip_address| {
switch (ip_address) { switch (ip_address) {
.ipv4 => |ipv4| { .ipv4 => |ipv4| {
@ -472,10 +489,7 @@ const HostMultiplayerGame = struct {
this.* = .{ this.* = .{
.allocator = allocator, .allocator = allocator,
.mdns_addr4_string = ipv4_sockaddr_string, .mdns_sockets = try mdns_sockets.toOwnedSlice(),
.mdns_addr6_string = ipv6_sockaddr_string,
.mdns_socket4 = mdns_socket,
.mdns_socket6 = mdns_socket6,
.service_string = service_string, .service_string = service_string,
.service_instance_string = service_instance_string, .service_instance_string = service_instance_string,
.qualified_hostname = qualified_hostname, .qualified_hostname = qualified_hostname,
@ -485,9 +499,12 @@ const HostMultiplayerGame = struct {
.address = address, .address = address,
.server = server, .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.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; return this;
} }
@ -499,15 +516,15 @@ const HostMultiplayerGame = struct {
errdefer arena.deinit(); 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 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_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_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 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 qualified_hostname_text = try Element.Text.create(request.allocator, this.qualified_hostname);
var page = try Element.Page.create(request.allocator); 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.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.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.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.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()); 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.mdns_thread.join();
this.allocator.free(this.mdns_records); this.allocator.free(this.mdns_records);
this.allocator.free(this.mdns_addr4_string); this.allocator.free(this.mdns_sockets);
this.allocator.free(this.mdns_addr6_string); // 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_string);
this.allocator.free(this.service_instance_string); this.allocator.free(this.service_instance_string);
this.allocator.free(this.qualified_hostname); this.allocator.free(this.qualified_hostname);
@ -547,18 +565,9 @@ const HostMultiplayerGame = struct {
const log = std.log.scoped(.mdns); const log = std.log.scoped(.mdns);
log.info("mdns service started", .{}); log.info("mdns service started", .{});
for (this.mdns_sockets) |socket| {
_ = c.mdns_announce_multicast( _ = c.mdns_announce_multicast(
this.mdns_socket4, socket,
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.ptr,
buffer.len, buffer.len,
this.mdns_records[0], this.mdns_records[0],
@ -567,15 +576,21 @@ const HostMultiplayerGame = struct {
this.mdns_records[1..].ptr, this.mdns_records[1..].ptr,
this.mdns_records[1..].len, 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)) { while (!this.mdns_thread_should_stop.load(.Monotonic)) {
var pollfds = [_]std.os.pollfd{ for (pollfds) |*pollfd| {
.{ .fd = this.mdns_socket4, .events = std.os.POLL.IN, .revents = undefined }, pollfd.events = std.os.POLL.IN;
.{ .fd = this.mdns_socket6, .events = std.os.POLL.IN, .revents = undefined }, }
}; _ = std.os.poll(pollfds, 1000) catch break;
_ = std.os.poll(&pollfds, 1000) catch break;
// listen for mdns messages // listen for mdns messages
for (pollfds[0..2]) |pollfd| { for (pollfds) |pollfd| {
if (pollfd.revents & std.os.POLL.IN == 0) continue; if (pollfd.revents & std.os.POLL.IN == 0) continue;
_ = c.mdns_socket_listen(pollfd.fd, buffer.ptr, buffer.len, &mdns_thread_service_callback, this); _ = 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", .{}); 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( fn mdns_thread_service_callback(
sock: c_int, sock: c_int,
from: ?*const c.sockaddr, from: ?*const c.sockaddr,

View File

@ -586,75 +586,6 @@ fn endOfTurnConfirmHandler(arena: std.mem.Allocator, request: Request) Handler.E
return Response{ .page = page.element() }; 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 { fn isValidRummyMeld(cards: []const Card) bool {
std.debug.assert(std.sort.isSorted(Card, cards, {}, rummyHandSort)); std.debug.assert(std.sort.isSorted(Card, cards, {}, rummyHandSort));
if (cards.len < 3) { if (cards.len < 3) {

View File

@ -11,8 +11,14 @@ pub const Handler = struct {
}; };
}; };
pub const Reference = union(enum) { pub const Reference = union(Tag) {
handler: *const Handler.Interface, handler: *const Handler.Interface,
server: std.net.Address,
pub const Tag = enum {
handler,
server,
};
}; };
pub const Request = struct { pub const Request = struct {