From 3661cb8721e13da39397121bea0753630ff43774 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Mon, 25 Mar 2024 11:06:54 -0700 Subject: [PATCH] module: sending global event requires sender module awareness Signed-off-by: Stephen Gutekanst --- src/ecs/main.zig | 2 +- src/ecs/systems.zig | 2 +- src/engine.zig | 4 ++- src/module.zig | 62 +++++++++++++++++++++++++-------------------- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/src/ecs/main.zig b/src/ecs/main.zig index 8ecc3ca2..401ecb24 100644 --- a/src/ecs/main.zig +++ b/src/ecs/main.zig @@ -122,6 +122,6 @@ test "example" { //------------------------------------------------------------------------- // Send events to modules - world.modules.send(.tick, .{}); + world.mod.renderer.sendGlobal(.tick, .{}); try world.dispatch(); } diff --git a/src/ecs/systems.zig b/src/ecs/systems.zig index 9d050c6f..92b056ba 100644 --- a/src/ecs/systems.zig +++ b/src/ecs/systems.zig @@ -77,7 +77,7 @@ pub fn World(comptime mods: anytype) type { pub inline fn sendGlobal(m: *@This(), comptime event_name: Modules.GlobalEvent, args: anytype) void { const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m)); const world = @fieldParentPtr(WorldT, "mod", mod_ptr); - world.modules.send(event_name, args); + world.modules.sendGlobal(module_tag, event_name, args); } pub fn dispatchNoError(m: *@This()) void { diff --git a/src/engine.zig b/src/engine.zig index 71f14598..8f840cc9 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -25,6 +25,8 @@ pub const Engine = struct { .{ .local = .begin_pass, .handler = beginPass }, .{ .local = .end_pass, .handler = endPass }, .{ .local = .present, .handler = present }, + .{ .global = .init, .handler = fn () void }, + .{ .global = .deinit, .handler = fn () void }, .{ .global = .tick, .handler = fn () void }, .{ .global = .exit, .handler = fn () void }, }; @@ -115,7 +117,7 @@ pub const App = struct { pub fn update(app: *@This()) !bool { // TODO: better dispatch implementation - app.world.modules.send(.tick, .{}); + app.world.mod.engine.sendGlobal(.tick, .{}); try app.world.dispatch(); // dispatch .tick try app.world.dispatch(); // dispatch any events produced by .tick return app.world.mod.engine.state.should_exit; diff --git a/src/module.zig b/src/module.zig index b49ba75e..1ab240e7 100644 --- a/src/module.zig +++ b/src/module.zig @@ -89,7 +89,7 @@ pub fn Modules(comptime mods: anytype) type { const Handler = switch (@typeInfo(@TypeOf(event.handler))) { .Fn => @TypeOf(event.handler), - .Type => |t| switch (@typeInfo(t)) { + .Type => switch (@typeInfo(event.handler)) { .Fn => event.handler, else => unreachable, }, @@ -106,39 +106,45 @@ pub fn Modules(comptime mods: anytype) type { /// Returns an args tuple representing the standard, uninjected, arguments which the given /// global event handler requires. - fn Args(event_name: GlobalEvent) type { + fn GlobalArgs(module_name: ModuleName(mods), event_name: GlobalEvent) type { inline for (modules) |M| { _ = Module(M); // Validate the module - - inline for (M.events) |event| { - const Ev = @TypeOf(event); - const name_tag = if (@hasField(Ev, "global")) event.global else continue; - if (name_tag != event_name) continue; - - const Handler = switch (@typeInfo(@TypeOf(event.handler))) { - .Fn => @TypeOf(event.handler), - .Type => switch (@typeInfo(event.handler)) { - .Fn => event.handler, - else => unreachable, - }, - else => unreachable, - }; - - // TODO: passing std.meta.Tuple here instead of TupleHACK results in a compiler - // segfault. The only difference is that TupleHACk does not produce a real tuple, - // `@Type(.{.Struct = .{ .is_tuple = false }})` instead of `.is_tuple = true`. - return UninjectedArgsTuple(TupleHACK, Handler); - } + if (M.name != module_name) continue; + return GlobalArgsM(M, event_name); } - @compileError("No global event handler ." ++ @tagName(event_name) ++ " is defined in any module."); } - /// Send a global event - pub fn send( + pub fn GlobalArgsM(comptime M: type, event_name: GlobalEvent) type { + _ = Module(M); // Validate the module + inline for (M.events) |event| { + const Ev = @TypeOf(event); + const name_tag = if (@hasField(Ev, "global")) event.global else continue; + if (name_tag != event_name) continue; + + const Handler = switch (@typeInfo(@TypeOf(event.handler))) { + .Fn => @TypeOf(event.handler), + .Type => switch (@typeInfo(event.handler)) { + .Fn => event.handler, + else => unreachable, + }, + else => unreachable, + }; + + // TODO: passing std.meta.Tuple here instead of TupleHACK results in a compiler + // segfault. The only difference is that TupleHACk does not produce a real tuple, + // `@Type(.{.Struct = .{ .is_tuple = false }})` instead of `.is_tuple = true`. + return UninjectedArgsTuple(TupleHACK, Handler); + } + @compileError("mach: module ." ++ @tagName(M.name) ++ " has no .global event handler for ." ++ @tagName(event_name)); + } + + /// Send a global event which the specified module defines + pub fn sendGlobal( m: *@This(), // TODO: is a variant of this function where event_name is not comptime known, but asserted to be a valid enum, useful? + comptime module_name: ModuleName(mods), comptime event_name: GlobalEvent, - args: Args(event_name), + args: GlobalArgs(module_name, event_name), ) void { // TODO: comptime safety/debugging m.sendInternal(null, @intFromEnum(event_name), args); @@ -986,7 +992,7 @@ test "dispatch" { // global event handler declaration within a module. It is required that all global event handlers // of the same name have the same standard arguments, although they can start with different // injected arguments. - modules.send(.tick, .{}); + modules.sendGlobal(.tick, .{}); try testing.expect(usize, 0).eql(global.ticks); try modules.dispatch(.{&foo}); try testing.expect(usize, 2).eql(global.ticks); @@ -998,7 +1004,7 @@ test "dispatch" { // Global events which are not handled by anyone yet can be written as `pub const fooBar = fn() void;` // within a module, which allows pre-declaring that `fooBar` is a valid global event, and enables // its arguments to be inferred still like this: - modules.send(.frame_done, .{ .@"0" = 1337 }); + modules.sendGlobal(.frame_done, .{ .@"0" = 1337 }); // Local events modules.sendToModule(.engine_renderer, .update, .{});