Compare commits
1 Commits
dev
...
multiplaye
Author | SHA1 | Date |
---|---|---|
LeRoyce Pearson | aa98b071eb |
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
352
src/LocalUI.zig
352
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,
|
||||
|
|
69
src/main.zig
69
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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue