start implementing mDNS discovery on client side
parent
a6fe88b65e
commit
cb62ac2f63
153
src/LocalUI.zig
153
src/LocalUI.zig
|
@ -9,7 +9,7 @@ fn main_menu_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
errdefer arena.deinit();
|
errdefer arena.deinit();
|
||||||
|
|
||||||
var join_multiplayer_text = try Element.Text.create(arena.allocator(), "Join Multiplayer Game");
|
var join_multiplayer_text = try Element.Text.create(arena.allocator(), "Join Multiplayer Game");
|
||||||
var join_multiplayer_link = try Element.Link.create(arena.allocator(), join_multiplayer_text.element(), .{ .handler = join_multiplayer_game });
|
var join_multiplayer_link = try Element.Link.create(arena.allocator(), join_multiplayer_text.element(), .{ .handler = JoinMultiplayerGame.INTERFACE });
|
||||||
|
|
||||||
var host_multiplayer_text = try Element.Text.create(arena.allocator(), "Host Multiplayer Game");
|
var host_multiplayer_text = try Element.Text.create(arena.allocator(), "Host Multiplayer Game");
|
||||||
var host_multiplayer_link = try Element.Link.create(arena.allocator(), host_multiplayer_text.element(), .{ .handler = HostMultiplayerGame.INTERFACE });
|
var host_multiplayer_link = try Element.Link.create(arena.allocator(), host_multiplayer_text.element(), .{ .handler = HostMultiplayerGame.INTERFACE });
|
||||||
|
@ -24,23 +24,156 @@ fn main_menu_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const join_multiplayer_game = &Handler.Interface{
|
const JoinMultiplayerGame = struct {
|
||||||
.create = null,
|
allocator: std.mem.Allocator,
|
||||||
.handle = &join_multiplayer_game_handler,
|
discovery_thread: std.Thread,
|
||||||
.deinit = null,
|
|
||||||
};
|
pub const INTERFACE = &Handler.Interface{
|
||||||
|
.create = &create,
|
||||||
|
.handle = &handler,
|
||||||
|
.deinit = &deinit,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn create(allocator: std.mem.Allocator) Handler.Error!?*anyopaque {
|
||||||
|
const this = try allocator.create(@This());
|
||||||
|
errdefer allocator.destroy(this);
|
||||||
|
|
||||||
|
const discovery_thread = std.Thread.spawn(.{}, find_local_games_thread, .{allocator}) catch return error.OutOfMemory;
|
||||||
|
|
||||||
|
this.* = .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.discovery_thread = discovery_thread,
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handler(pointer: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
_ = this;
|
||||||
|
|
||||||
fn join_multiplayer_game_handler(_: ?*anyopaque, request: Request) Handler.Error!Response {
|
|
||||||
var arena = std.heap.ArenaAllocator.init(request.allocator);
|
var arena = std.heap.ArenaAllocator.init(request.allocator);
|
||||||
errdefer arena.deinit();
|
errdefer arena.deinit();
|
||||||
|
|
||||||
var text = try Element.Text.create(arena.allocator(), "Joining Multiplayer Game");
|
var text = try Element.Text.create(arena.allocator(), try std.fmt.allocPrint(arena.allocator(), "Looking for multiplayer games", .{}));
|
||||||
|
|
||||||
var page = try Element.Page.create(arena.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());
|
||||||
|
|
||||||
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deinit(pointer: ?*anyopaque) void {
|
||||||
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
|
|
||||||
|
this.allocator.destroy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_local_games_thread(allocator: std.mem.Allocator) !void {
|
||||||
|
const buffer = try allocator.alloc(u8, 2048);
|
||||||
|
defer allocator.free(buffer);
|
||||||
|
|
||||||
|
const log = std.log.scoped(.mdns);
|
||||||
|
log.info("mdns discovery service started", .{});
|
||||||
|
|
||||||
|
// IPv4
|
||||||
|
const mdns_ipv4_socket = c.mdns_socket_open_ipv4(null);
|
||||||
|
defer c.mdns_socket_close(mdns_ipv4_socket);
|
||||||
|
|
||||||
|
// IPv6
|
||||||
|
const mdns_ipv6_socket = c.mdns_socket_open_ipv6(null);
|
||||||
|
defer c.mdns_socket_close(mdns_ipv6_socket);
|
||||||
|
|
||||||
|
const service_name = "rummy._udp.local.";
|
||||||
|
for (&[_]c_int{ mdns_ipv4_socket, mdns_ipv6_socket }) |socket| {
|
||||||
|
switch (c.mdns_query_send(socket, c.MDNS_RECORDTYPE_PTR, service_name.ptr, service_name.len, buffer.ptr, buffer.len, 0)) {
|
||||||
|
0 => {},
|
||||||
|
else => |err_code| log.warn("Failed to send question; error code = {}", .{err_code}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var pollfds = [_]std.os.pollfd{
|
||||||
|
.{ .fd = mdns_ipv4_socket, .events = std.os.POLL.IN, .revents = undefined },
|
||||||
|
.{ .fd = mdns_ipv6_socket, .events = std.os.POLL.IN, .revents = undefined },
|
||||||
|
};
|
||||||
|
_ = std.os.poll(&pollfds, 1000) catch break;
|
||||||
|
for (pollfds) |pollfd| {
|
||||||
|
if (pollfd.revents & std.os.POLL.IN == 0) continue;
|
||||||
|
_ = c.mdns_query_recv(pollfd.fd, buffer.ptr, buffer.len, &find_local_games_thread_query_callback, null, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("mdns service stopping", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_local_games_thread_query_callback(
|
||||||
|
sock: c_int,
|
||||||
|
from: ?*const c.sockaddr,
|
||||||
|
addrlen: usize,
|
||||||
|
entry: c.mdns_entry_type_t,
|
||||||
|
query_id: u16,
|
||||||
|
rtype: u16,
|
||||||
|
rclass: u16,
|
||||||
|
ttl: u32,
|
||||||
|
data_ptr_opaque: ?*const anyopaque,
|
||||||
|
data_len: usize,
|
||||||
|
name_offset: usize,
|
||||||
|
name_length: usize,
|
||||||
|
record_offset: usize,
|
||||||
|
record_length: usize,
|
||||||
|
userdata: ?*anyopaque,
|
||||||
|
) callconv(.C) c_int {
|
||||||
|
const log = std.log.scoped(.mdns);
|
||||||
|
|
||||||
|
_ = rclass;
|
||||||
|
_ = userdata;
|
||||||
|
_ = ttl;
|
||||||
|
_ = name_length;
|
||||||
|
_ = sock;
|
||||||
|
_ = addrlen;
|
||||||
|
_ = query_id;
|
||||||
|
|
||||||
|
// const data_ptr: [*]const u8 = @ptrCast(data_ptr_opaque.?);
|
||||||
|
// const data = data_ptr[0..data_len];
|
||||||
|
|
||||||
|
var name_buffer: [128]u8 = undefined;
|
||||||
|
var offset = name_offset;
|
||||||
|
const name_mdns_str = c.mdns_string_extract(data_ptr_opaque, data_len, &offset, &name_buffer, name_buffer.len);
|
||||||
|
const name = name_mdns_str.str[0..name_mdns_str.length];
|
||||||
|
|
||||||
|
const std_from: ?std.net.Address = if (from) |f| std.net.Address{ .any = @bitCast(f.*) } else null;
|
||||||
|
|
||||||
|
const entrytype_str = switch (entry) {
|
||||||
|
c.MDNS_ENTRYTYPE_QUESTION => "question",
|
||||||
|
c.MDNS_ENTRYTYPE_ANSWER => "answer",
|
||||||
|
c.MDNS_ENTRYTYPE_ADDITIONAL => "additional",
|
||||||
|
c.MDNS_ENTRYTYPE_AUTHORITY => "authority",
|
||||||
|
else => "???",
|
||||||
|
};
|
||||||
|
const recordtype_str = switch (rtype) {
|
||||||
|
c.MDNS_RECORDTYPE_PTR => "PTR",
|
||||||
|
c.MDNS_RECORDTYPE_SRV => "SRV",
|
||||||
|
c.MDNS_RECORDTYPE_A => "A",
|
||||||
|
c.MDNS_RECORDTYPE_AAAA => "AAAA",
|
||||||
|
c.MDNS_RECORDTYPE_TXT => "TXT",
|
||||||
|
else => "???",
|
||||||
|
};
|
||||||
|
if (rtype == c.MDNS_RECORDTYPE_PTR) {
|
||||||
|
log.info("{?} : {s} {s} \"{}\"", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(name) });
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rtype) {
|
||||||
|
c.MDNS_RECORDTYPE_PTR => {
|
||||||
|
var namebuffer: [128]u8 = undefined;
|
||||||
|
const namestr = c.mdns_record_parse_ptr(data_ptr_opaque, data_len, record_offset, record_length, &namebuffer, namebuffer.len);
|
||||||
|
log.info("{?} : {s} {s} \"{}\"", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(namestr.str[0..namestr.length]) });
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const HostMultiplayerGame = struct {
|
const HostMultiplayerGame = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
|
Loading…
Reference in New Issue