more multiplayer stuff
parent
390eaebd90
commit
aa98b071eb
|
@ -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();
|
||||||
return null;
|
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");
|
const Element = @import("../Element.zig");
|
||||||
|
|
|
@ -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) {
|
||||||
.allocator = this.allocator,
|
.handler => |handler| {
|
||||||
}) catch response_error: {
|
const handler_pointer = this.state_info.handler.pointer;
|
||||||
std.log.warn("Could not get response", .{});
|
this.response = handler.handle(handler_pointer, .{
|
||||||
break :response_error null;
|
.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| {
|
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;
|
||||||
|
|
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 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,35 +565,32 @@ 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", .{});
|
||||||
|
|
||||||
_ = c.mdns_announce_multicast(
|
for (this.mdns_sockets) |socket| {
|
||||||
this.mdns_socket4,
|
_ = c.mdns_announce_multicast(
|
||||||
buffer.ptr,
|
socket,
|
||||||
buffer.len,
|
buffer.ptr,
|
||||||
this.mdns_records[0],
|
buffer.len,
|
||||||
null,
|
this.mdns_records[0],
|
||||||
0,
|
null,
|
||||||
this.mdns_records[1..].ptr,
|
0,
|
||||||
this.mdns_records[1..].len,
|
this.mdns_records[1..].ptr,
|
||||||
);
|
this.mdns_records[1..].len,
|
||||||
_ = c.mdns_announce_multicast(
|
);
|
||||||
this.mdns_socket6,
|
}
|
||||||
buffer.ptr,
|
|
||||||
buffer.len,
|
const pollfds = try this.allocator.alloc(std.os.pollfd, this.mdns_sockets.len);
|
||||||
this.mdns_records[0],
|
defer this.allocator.free(pollfds);
|
||||||
null,
|
for (pollfds, this.mdns_sockets) |*pollfd, socket| {
|
||||||
0,
|
pollfd.* = .{ .fd = socket, .events = std.os.POLL.IN, .revents = undefined };
|
||||||
this.mdns_records[1..].ptr,
|
}
|
||||||
this.mdns_records[1..].len,
|
|
||||||
);
|
|
||||||
|
|
||||||
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,
|
||||||
|
|
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() };
|
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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue