feat: show list of discovered servers

dev
LeRoyce Pearson 2024-03-03 23:42:59 -07:00
parent ff7e3c70bf
commit 390eaebd90
3 changed files with 119 additions and 9 deletions

View File

@ -3,6 +3,7 @@ pub const HBox = @import("./Element/HBox.zig");
pub const Link = @import("./Element/Link.zig");
pub const Page = @import("./Element/Page.zig");
pub const Pile = @import("./Element/Pile.zig");
pub const ServerList = @import("./Element/ServerList.zig");
pub const Text = @import("./Element/Text.zig");
pub const View = @import("./Element/View.zig");

View File

@ -0,0 +1,71 @@
allocator: std.mem.Allocator,
discovered: *Discovered,
pub const Discovered = struct {
allocator: std.mem.Allocator,
mutex: std.Thread.Mutex = .{},
servers: std.StringArrayHashMapUnmanaged(void) = .{},
};
pub fn create(allocator: std.mem.Allocator, discovered: *Discovered) !*@This() {
const this = try allocator.create(@This());
errdefer allocator.destroy(this);
this.* = .{
.allocator = allocator,
.discovered = discovered,
};
return this;
}
pub fn element(this: *@This()) Element {
return Element{
.pointer = this,
.interface = &Element.Interface{
.destroy = &element_destroy,
.minimum_size = &element_minimum_size,
.render = &element_render,
.event = &element_event,
},
};
}
pub fn element_destroy(pointer: ?*anyopaque) void {
const this: *@This() = @ptrCast(@alignCast(pointer));
this.allocator.destroy(this);
}
pub fn element_minimum_size(pointer: ?*anyopaque, render_resources: Element.RenderResources) [2]f32 {
const this: *@This() = @ptrCast(@alignCast(pointer));
_ = this;
return render_resources.font.textSize("example_server.xyz:5711", 1);
}
pub fn element_render(pointer: ?*anyopaque, canvas: *seizer.Canvas, render_resources: Element.RenderResources, min: [2]f32, max: [2]f32) void {
const this: *@This() = @ptrCast(@alignCast(pointer));
_ = max;
_ = render_resources;
var pos = min;
this.discovered.mutex.lock();
defer this.discovered.mutex.unlock();
for (this.discovered.servers.keys()) |url| {
const text_size = canvas.writeText(pos, url, .{});
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;
}
const Element = @import("../Element.zig");
const assets = @import("../assets.zig");
const seizer = @import("seizer");
const gl = seizer.gl;
const std = @import("std");

View File

@ -31,6 +31,11 @@ const SIOCGIFADDR = 0x8915;
const JoinMultiplayerGame = struct {
allocator: std.mem.Allocator,
discovery_thread: std.Thread,
running: *std.atomic.Value(bool),
discovered: *Element.ServerList.Discovered,
const Server = struct { url: []const u8, port: u16 };
pub const INTERFACE = &Handler.Interface{
.create = &create,
@ -42,11 +47,21 @@ const JoinMultiplayerGame = struct {
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;
const discovered = try allocator.create(Element.ServerList.Discovered);
errdefer allocator.destroy(discovered);
discovered.* = .{ .allocator = allocator };
const running = try allocator.create(std.atomic.Value(bool));
errdefer allocator.destroy(running);
running.store(true, .Monotonic);
const discovery_thread = std.Thread.spawn(.{}, find_local_games_thread, .{ running, discovered }) catch return error.OutOfMemory;
this.* = .{
.allocator = allocator,
.discovery_thread = discovery_thread,
.running = running,
.discovered = discovered,
};
return this;
@ -54,15 +69,17 @@ const JoinMultiplayerGame = struct {
fn handler(pointer: ?*anyopaque, request: Request) Handler.Error!Response {
const this: *@This() = @ptrCast(@alignCast(pointer));
_ = this;
var arena = std.heap.ArenaAllocator.init(request.allocator);
errdefer arena.deinit();
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 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.7 }, .{ 0.5, 0.5 }, server_list.element());
return Response{ .arena = arena, .body = .{ .element = page.element() } };
}
@ -70,12 +87,23 @@ const JoinMultiplayerGame = struct {
fn deinit(pointer: ?*anyopaque) void {
const this: *@This() = @ptrCast(@alignCast(pointer));
this.running.store(false, .Monotonic);
this.discovery_thread.join();
for (this.discovered.servers.keys()) |url| {
this.discovered.allocator.free(url);
}
this.discovered.servers.deinit(this.discovered.allocator);
this.allocator.destroy(this.discovered);
this.allocator.destroy(this.running);
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);
fn find_local_games_thread(running: *std.atomic.Value(bool), discovered: *Element.ServerList.Discovered) !void {
const buffer = try discovered.allocator.alloc(u8, 2048);
defer discovered.allocator.free(buffer);
const log = std.log.scoped(.mdns);
log.info("mdns discovery service started", .{});
@ -95,7 +123,7 @@ const JoinMultiplayerGame = struct {
}
}
while (true) {
while (running.load(.Monotonic)) {
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 },
@ -103,11 +131,11 @@ const JoinMultiplayerGame = struct {
_ = 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);
_ = c.mdns_query_recv(pollfd.fd, buffer.ptr, buffer.len, &find_local_games_thread_query_callback, discovered, 0);
}
}
log.info("mdns service stopping", .{});
log.info("mdns discovery service stopping", .{});
}
fn find_local_games_thread_query_callback(
@ -129,8 +157,9 @@ const JoinMultiplayerGame = struct {
) callconv(.C) c_int {
const log = std.log.scoped(.mdns);
const discovered: *Element.ServerList.Discovered = @ptrCast(@alignCast(userdata));
_ = rclass;
_ = userdata;
_ = ttl;
_ = name_length;
_ = sock;
@ -173,6 +202,15 @@ const JoinMultiplayerGame = struct {
var strbuffer: [128]u8 = undefined;
const record = c.mdns_record_parse_srv(data_ptr_opaque, data_len, record_offset, record_length, &strbuffer, strbuffer.len);
log.info("{?} : {s} {s} \"{}\":{}", .{ std_from, entrytype_str, recordtype_str, std.zig.fmtEscapes(record.name.str[0..record.name.length]), record.port });
const url = std.fmt.allocPrint(discovered.allocator, "{s}:{}", .{ record.name.str[0..record.name.length], record.port }) catch unreachable;
discovered.mutex.lock();
defer discovered.mutex.unlock();
const gop = discovered.servers.getOrPut(discovered.allocator, url) catch unreachable;
if (gop.found_existing) {
discovered.allocator.free(url);
}
},
c.MDNS_RECORDTYPE_A => {
var std_address: std.net.Ip4Address = undefined;