feat: show list of discovered servers
parent
ff7e3c70bf
commit
390eaebd90
|
@ -3,6 +3,7 @@ pub const HBox = @import("./Element/HBox.zig");
|
||||||
pub const Link = @import("./Element/Link.zig");
|
pub const Link = @import("./Element/Link.zig");
|
||||||
pub const Page = @import("./Element/Page.zig");
|
pub const Page = @import("./Element/Page.zig");
|
||||||
pub const Pile = @import("./Element/Pile.zig");
|
pub const Pile = @import("./Element/Pile.zig");
|
||||||
|
pub const ServerList = @import("./Element/ServerList.zig");
|
||||||
pub const Text = @import("./Element/Text.zig");
|
pub const Text = @import("./Element/Text.zig");
|
||||||
pub const View = @import("./Element/View.zig");
|
pub const View = @import("./Element/View.zig");
|
||||||
|
|
||||||
|
|
|
@ -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");
|
|
@ -31,6 +31,11 @@ const SIOCGIFADDR = 0x8915;
|
||||||
const JoinMultiplayerGame = struct {
|
const JoinMultiplayerGame = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
discovery_thread: std.Thread,
|
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{
|
pub const INTERFACE = &Handler.Interface{
|
||||||
.create = &create,
|
.create = &create,
|
||||||
|
@ -42,11 +47,21 @@ const JoinMultiplayerGame = struct {
|
||||||
const this = try allocator.create(@This());
|
const this = try allocator.create(@This());
|
||||||
errdefer allocator.destroy(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.* = .{
|
this.* = .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.discovery_thread = discovery_thread,
|
.discovery_thread = discovery_thread,
|
||||||
|
.running = running,
|
||||||
|
.discovered = discovered,
|
||||||
};
|
};
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
|
@ -54,15 +69,17 @@ const JoinMultiplayerGame = struct {
|
||||||
|
|
||||||
fn handler(pointer: ?*anyopaque, request: Request) Handler.Error!Response {
|
fn handler(pointer: ?*anyopaque, request: Request) Handler.Error!Response {
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
const this: *@This() = @ptrCast(@alignCast(pointer));
|
||||||
_ = this;
|
|
||||||
|
|
||||||
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(), 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 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.5, 0.7 }, .{ 0.5, 0.5 }, server_list.element());
|
||||||
|
|
||||||
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
return Response{ .arena = arena, .body = .{ .element = page.element() } };
|
||||||
}
|
}
|
||||||
|
@ -70,12 +87,23 @@ const JoinMultiplayerGame = struct {
|
||||||
fn deinit(pointer: ?*anyopaque) void {
|
fn deinit(pointer: ?*anyopaque) void {
|
||||||
const this: *@This() = @ptrCast(@alignCast(pointer));
|
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);
|
this.allocator.destroy(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_local_games_thread(allocator: std.mem.Allocator) !void {
|
fn find_local_games_thread(running: *std.atomic.Value(bool), discovered: *Element.ServerList.Discovered) !void {
|
||||||
const buffer = try allocator.alloc(u8, 2048);
|
const buffer = try discovered.allocator.alloc(u8, 2048);
|
||||||
defer allocator.free(buffer);
|
defer discovered.allocator.free(buffer);
|
||||||
|
|
||||||
const log = std.log.scoped(.mdns);
|
const log = std.log.scoped(.mdns);
|
||||||
log.info("mdns discovery service started", .{});
|
log.info("mdns discovery service started", .{});
|
||||||
|
@ -95,7 +123,7 @@ const JoinMultiplayerGame = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (running.load(.Monotonic)) {
|
||||||
var pollfds = [_]std.os.pollfd{
|
var pollfds = [_]std.os.pollfd{
|
||||||
.{ .fd = mdns_ipv4_socket, .events = std.os.POLL.IN, .revents = undefined },
|
.{ .fd = mdns_ipv4_socket, .events = std.os.POLL.IN, .revents = undefined },
|
||||||
.{ .fd = mdns_ipv6_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;
|
_ = std.os.poll(&pollfds, 1000) catch break;
|
||||||
for (pollfds) |pollfd| {
|
for (pollfds) |pollfd| {
|
||||||
if (pollfd.revents & std.os.POLL.IN == 0) continue;
|
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(
|
fn find_local_games_thread_query_callback(
|
||||||
|
@ -129,8 +157,9 @@ const JoinMultiplayerGame = struct {
|
||||||
) callconv(.C) c_int {
|
) callconv(.C) c_int {
|
||||||
const log = std.log.scoped(.mdns);
|
const log = std.log.scoped(.mdns);
|
||||||
|
|
||||||
|
const discovered: *Element.ServerList.Discovered = @ptrCast(@alignCast(userdata));
|
||||||
|
|
||||||
_ = rclass;
|
_ = rclass;
|
||||||
_ = userdata;
|
|
||||||
_ = ttl;
|
_ = ttl;
|
||||||
_ = name_length;
|
_ = name_length;
|
||||||
_ = sock;
|
_ = sock;
|
||||||
|
@ -173,6 +202,15 @@ const JoinMultiplayerGame = struct {
|
||||||
var strbuffer: [128]u8 = undefined;
|
var strbuffer: [128]u8 = undefined;
|
||||||
const record = c.mdns_record_parse_srv(data_ptr_opaque, data_len, record_offset, record_length, &strbuffer, strbuffer.len);
|
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 });
|
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 => {
|
c.MDNS_RECORDTYPE_A => {
|
||||||
var std_address: std.net.Ip4Address = undefined;
|
var std_address: std.net.Ip4Address = undefined;
|
||||||
|
|
Loading…
Reference in New Issue