From 17db5498ee02bab1fdc8c27fff5b0d9e1ccde947 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Wed, 3 Apr 2024 08:13:22 -0700 Subject: [PATCH] module: write module events using a struct pattern Signed-off-by: Stephen Gutekanst --- examples/custom-renderer/Game.zig | 6 +- examples/custom-renderer/Renderer.zig | 8 +- examples/glyphs/Game.zig | 6 +- examples/glyphs/Text.zig | 11 +- examples/sprite/Game.zig | 6 +- examples/text/Game.zig | 8 +- src/ecs/entities.zig | 2 - src/ecs/main.zig | 8 +- src/ecs/query.zig | 3 - src/engine.zig | 25 +- src/gfx/Sprite.zig | 17 +- src/gfx/Text.zig | 17 +- src/module.zig | 331 ++++++++++++++------------ 13 files changed, 245 insertions(+), 203 deletions(-) diff --git a/examples/custom-renderer/Game.zig b/examples/custom-renderer/Game.zig index 1668193c..126c1957 100644 --- a/examples/custom-renderer/Game.zig +++ b/examples/custom-renderer/Game.zig @@ -20,9 +20,9 @@ pub const components = .{ .{ .name = .follower, .type = void }, }; -pub const events = .{ - .{ .global = .init, .handler = init }, - .{ .global = .tick, .handler = tick }, +pub const global_events = .{ + .init = .{ .handler = init }, + .tick = .{ .handler = tick }, }; // Each module must have a globally unique name declared, it is impossible to use two modules with diff --git a/examples/custom-renderer/Renderer.zig b/examples/custom-renderer/Renderer.zig index 3e3a1d59..3580b367 100644 --- a/examples/custom-renderer/Renderer.zig +++ b/examples/custom-renderer/Renderer.zig @@ -26,10 +26,10 @@ pub const components = .{ .{ .name = .scale, .type = f32 }, }; -pub const events = .{ - .{ .global = .init, .handler = init }, - .{ .global = .deinit, .handler = deinit }, - .{ .global = .tick, .handler = tick }, +pub const global_events = .{ + .init = .{ .handler = init }, + .deinit = .{ .handler = deinit }, + .tick = .{ .handler = tick }, }; // TODO: this shouldn't be a packed struct, it should be extern. diff --git a/examples/glyphs/Game.zig b/examples/glyphs/Game.zig index bc52caaa..83bea319 100644 --- a/examples/glyphs/Game.zig +++ b/examples/glyphs/Game.zig @@ -40,9 +40,9 @@ const d0 = 0.000001; pub const name = .game; pub const Mod = mach.Mod(@This()); -pub const events = .{ - .{ .global = .init, .handler = init }, - .{ .global = .tick, .handler = tick }, +pub const global_events = .{ + .init = .{ .handler = init }, + .tick = .{ .handler = tick }, }; pub const Pipeline = enum(u32) { diff --git a/examples/glyphs/Text.zig b/examples/glyphs/Text.zig index 0beffaf6..5a40c9f5 100644 --- a/examples/glyphs/Text.zig +++ b/examples/glyphs/Text.zig @@ -8,10 +8,13 @@ const assets = @import("assets"); pub const name = .game_text; pub const Mod = mach.Mod(@This()); -pub const events = .{ - .{ .global = .deinit, .handler = deinit }, - .{ .global = .init, .handler = init }, - .{ .local = .prepare, .handler = prepare }, +pub const global_events = .{ + .deinit = .{ .handler = deinit }, + .init = .{ .handler = init }, +}; + +pub const local_events = .{ + .prepare = .{ .handler = prepare }, }; const RegionMap = std.AutoArrayHashMapUnmanaged(u21, mach.gfx.Atlas.Region); diff --git a/examples/sprite/Game.zig b/examples/sprite/Game.zig index bb8e1697..4567c364 100644 --- a/examples/sprite/Game.zig +++ b/examples/sprite/Game.zig @@ -41,9 +41,9 @@ const d0 = 0.000001; pub const name = .game; pub const Mod = mach.Mod(@This()); -pub const events = .{ - .{ .global = .init, .handler = init }, - .{ .global = .tick, .handler = tick }, +pub const global_events = .{ + .init = .{ .handler = init }, + .tick = .{ .handler = tick }, }; pub const Pipeline = enum(u32) { diff --git a/examples/text/Game.zig b/examples/text/Game.zig index 8069bc8a..b83a7425 100644 --- a/examples/text/Game.zig +++ b/examples/text/Game.zig @@ -44,10 +44,10 @@ const d0 = 0.000001; pub const name = .game; pub const Mod = mach.Mod(@This()); -pub const events = .{ - .{ .global = .init, .handler = init }, - .{ .global = .deinit, .handler = deinit }, - .{ .global = .tick, .handler = tick }, +pub const global_events = .{ + .init = .{ .handler = init }, + .deinit = .{ .handler = deinit }, + .tick = .{ .handler = tick }, }; pub const Pipeline = enum(u32) { diff --git a/src/ecs/entities.zig b/src/ecs/entities.zig index 753caa32..df74f47c 100644 --- a/src/ecs/entities.zig +++ b/src/ecs/entities.zig @@ -753,7 +753,6 @@ test "example" { const all_components = ComponentTypesByName(.{ struct { pub const name = .game; - pub const events = .{}; pub const components = .{ .{ .name = .name, .type = []const u8 }, .{ .name = .location, .type = Location }, @@ -858,7 +857,6 @@ test "many entities" { const all_components = ComponentTypesByName(.{ struct { pub const name = .game; - pub const events = .{}; pub const components = .{ .{ .name = .name, .type = []const u8 }, .{ .name = .location, .type = Location }, diff --git a/src/ecs/main.zig b/src/ecs/main.zig index 612f45f3..c98dbf49 100644 --- a/src/ecs/main.zig +++ b/src/ecs/main.zig @@ -49,8 +49,8 @@ test "example" { pub const components = .{ .{ .name = .id, .type = u32 }, }; - pub const events = .{ - .{ .global = .tick, .handler = tick }, + pub const global_events = .{ + .tick = .{ .handler = tick }, }; fn tick(physics: *Modules(modules).Mod(Physics)) void { @@ -63,8 +63,8 @@ test "example" { pub const components = .{ .{ .name = .id, .type = u16 }, }; - pub const events = .{ - .{ .global = .tick, .handler = tick }, + pub const global_events = .{ + .tick = .{ .handler = tick }, }; fn tick( diff --git a/src/ecs/query.zig b/src/ecs/query.zig index 5c7e0f6c..9a8655b6 100644 --- a/src/ecs/query.zig +++ b/src/ecs/query.zig @@ -73,14 +73,12 @@ test "query" { const all_components = ComponentTypesByName(.{ struct { pub const name = .game; - pub const events = .{}; pub const components = .{ .{ .name = .name, .type = []const u8 }, }; }, struct { pub const name = .physics; - pub const events = .{}; pub const components = .{ .{ .name = .location, .type = Location }, .{ .name = .rotation, .type = Rotation }, @@ -88,7 +86,6 @@ test "query" { }, struct { pub const name = .renderer; - pub const events = .{}; }, }){}; diff --git a/src/engine.zig b/src/engine.zig index 90d547d9..de141d60 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -19,17 +19,20 @@ pub const Engine = struct { pub const name = .engine; pub const Mod = Modules.Mod(@This()); - pub const events = .{ - .{ .local = .init, .handler = init }, - .{ .local = .deinit, .handler = deinit }, - .{ .local = .exit, .handler = exit }, - .{ .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 }, + pub const global_events = .{ + .init = .{ .handler = fn () void }, + .deinit = .{ .handler = fn () void }, + .tick = .{ .handler = fn () void }, + .exit = .{ .handler = fn () void }, + }; + + pub const local_events = .{ + .init = .{ .handler = init }, + .deinit = .{ .handler = deinit }, + .exit = .{ .handler = exit }, + .begin_pass = .{ .handler = beginPass }, + .end_pass = .{ .handler = endPass }, + .present = .{ .handler = present }, }; fn init(engine: *Mod) !void { diff --git a/src/gfx/Sprite.zig b/src/gfx/Sprite.zig index 7f8923b2..bd12a1d8 100644 --- a/src/gfx/Sprite.zig +++ b/src/gfx/Sprite.zig @@ -43,13 +43,16 @@ pub const components = .{ }, }; -pub const events = .{ - .{ .global = .deinit, .handler = deinit }, - .{ .global = .init, .handler = init }, - .{ .local = .init_pipeline, .handler = initPipeline }, - .{ .local = .updated, .handler = updated }, - .{ .local = .pre_render, .handler = preRender }, - .{ .local = .render, .handler = render }, +pub const global_events = .{ + .deinit = .{ .handler = deinit }, + .init = .{ .handler = init }, +}; + +pub const local_events = .{ + .init_pipeline = .{ .handler = initPipeline }, + .updated = .{ .handler = updated }, + .pre_render = .{ .handler = preRender }, + .render = .{ .handler = render }, }; const Uniforms = extern struct { diff --git a/src/gfx/Text.zig b/src/gfx/Text.zig index 9825aaaf..d62b101d 100644 --- a/src/gfx/Text.zig +++ b/src/gfx/Text.zig @@ -78,13 +78,16 @@ pub const components = .{ }, }; -pub const events = .{ - .{ .global = .deinit, .handler = deinit }, - .{ .global = .init, .handler = init }, - .{ .local = .init_pipeline, .handler = initPipeline }, - .{ .local = .updated, .handler = updated }, - .{ .local = .pre_render, .handler = preRender }, - .{ .local = .render, .handler = render }, +pub const global_events = .{ + .deinit = .{ .handler = deinit }, + .init = .{ .handler = init }, +}; + +pub const local_events = .{ + .init_pipeline = .{ .handler = initPipeline }, + .updated = .{ .handler = updated }, + .pre_render = .{ .handler = preRender }, + .render = .{ .handler = render }, }; const Uniforms = extern struct { diff --git a/src/module.zig b/src/module.zig index f24e3df3..403d1cd0 100644 --- a/src/module.zig +++ b/src/module.zig @@ -11,10 +11,9 @@ fn ModuleInterface(comptime M: type) type { if (!@hasDecl(M, "name")) @compileError("mach: module must have `pub const name = .foobar;`"); if (@typeInfo(@TypeOf(M.name)) != .EnumLiteral) @compileError("mach: module must have `pub const name = .foobar;`, found type:" ++ @typeName(M.name)); - const prefix = "mach: module ." ++ @tagName(M.name) ++ " "; - if (!@hasDecl(M, "events")) @compileError(prefix ++ "must have `pub const events = .{};`"); - // TODO: re-enable this validation once module event handler arguments would not pose a type dependency loop - // validateEvents("mach: module ." ++ @tagName(M.name) ++ " ", M.events); + // TODO: enable once parameter dependency loop has been resolved + // if (@hasDecl(M, "global_events")) validateEvents("mach: module ." ++ @tagName(M.name) ++ " global_events ", M.global_events); + // if (@hasDecl(M, "local_events")) validateEvents("mach: module ." ++ @tagName(M.name) ++ " local_events ", M.global_events); _ = ComponentTypesM(M); return M; } @@ -231,20 +230,20 @@ pub fn Modules(comptime modules2: anytype) type { switch (event_name) { inline else => |ev_name| { inline for (modules) |M| { + // TODO: DRY with callLocal _ = ModuleInterface(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 != ev_name) continue; - switch (@typeInfo(@TypeOf(event.handler))) { - .Fn => try callHandler(event.handler, args, injectable), - .Type => switch (@typeInfo(event.handler)) { - .Fn => {}, // Pre-declaration of what args an event has, nothing to run. - else => unreachable, - }, - else => unreachable, - } - } + if (@hasDecl(M, "global_events")) inline for (@typeInfo(@TypeOf(M.global_events)).Struct.fields) |field| { + comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue; + const handler = @field(M.global_events, @tagName(ev_name)).handler; + if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do. + if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares global event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{ + @tagName(M.name), + @tagName(ev_name), + + @typeName(@TypeOf(handler)), + })); + try callHandler(handler, args, injectable); + }; } }, } @@ -257,23 +256,20 @@ pub fn Modules(comptime modules2: anytype) type { inline else => |ev_name| { switch (module_name) { inline else => |mod_name| { + // TODO: DRY with callGlobal const M = @field(NamespacedModules(@This().modules){}, @tagName(mod_name)); _ = ModuleInterface(M); // Validate the module - - inline for (M.events) |event| { - const Ev = @TypeOf(event); - const name_tag = if (@hasField(Ev, "local")) event.local else continue; - if (name_tag != ev_name) continue; - switch (@typeInfo(@TypeOf(event.handler))) { - .Fn => try callHandler(event.handler, args, injectable), - .Type => switch (@typeInfo(event.handler)) { - .Fn => {}, // Pre-declaration of what args an event has, nothing to run. - else => unreachable, - }, - else => unreachable, - } - break; - } + if (@hasDecl(M, "local_events")) inline for (@typeInfo(@TypeOf(M.local_events)).Struct.fields) |field| { + comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue; + const handler = @field(M.local_events, @tagName(ev_name)).handler; + if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do. + if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares local event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{ + @tagName(M.name), + @tagName(ev_name), + @typeName(@TypeOf(handler)), + })); + try callHandler(handler, args, injectable); + }; }, } }, @@ -487,18 +483,39 @@ fn UninjectedArgsTuple( return Tuple(std_args); } -pub fn LocalArgsM(comptime M: type, event_name: anytype) type { - _ = ModuleInterface(M); // Validate the module - inline for (M.events) |event| { - const Ev = @TypeOf(event); - const name_tag = if (@hasField(Ev, "local")) event.local else continue; - if (name_tag != event_name) continue; +// TODO: tests +fn LocalArgsM(comptime M: type, event_name: anytype) type { + return ArgsM(M, event_name, "local"); +} - const Handler = switch (@typeInfo(@TypeOf(event.handler))) { - .Fn => @TypeOf(event.handler), - .Type => switch (@typeInfo(event.handler)) { - .Fn => event.handler, - else => unreachable, +// TODO: tests +fn GlobalArgsM(comptime M: type, event_name: anytype) type { + return ArgsM(M, event_name, "global"); +} + +fn ArgsM(comptime M: type, event_name: anytype, comptime which: anytype) type { + _ = ModuleInterface(M); // Validate the module + if (!@hasDecl(M, which ++ "_events")) return @TypeOf(.{}); + + const m_events = @field(M, which ++ "_events"); // M.local_events or M.global_events + inline for (@typeInfo(@TypeOf(m_events)).Struct.fields) |field| { + comptime if (!std.mem.eql(u8, field.name, @tagName(event_name))) continue; + if (!@hasField(@TypeOf(m_events), @tagName(event_name))) @compileError(std.fmt.comptimePrint("mach: module .{s} declares no {s} event .{s}", .{ + @tagName(M.name), + which, + @tagName(event_name), + })); + const handler = @field(m_events, @tagName(event_name)).handler; + const Handler = switch (@typeInfo(@TypeOf(handler))) { + .Type => handler, // Pre-declaration of what args an event has + .Fn => blk: { + if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares {s} event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{ + @tagName(M.name), + which, + @tagName(event_name), + @typeName(@TypeOf(handler)), + })); + break :blk @TypeOf(handler); }, else => unreachable, }; @@ -508,52 +525,26 @@ pub fn LocalArgsM(comptime M: type, event_name: anytype) type { // `@Type(.{.Struct = .{ .is_tuple = false }})` instead of `.is_tuple = true`. return UninjectedArgsTuple(TupleHACK, Handler); } - @compileError("mach: module ." ++ @tagName(M.name) ++ " has no .local event handler for ." ++ @tagName(event_name)); -} - -pub fn GlobalArgsM(comptime M: type, event_name: anytype) type { - _ = ModuleInterface(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)); + @compileError("mach: module ." ++ @tagName(M.name) ++ " has no " ++ which ++ " event handler for ." ++ @tagName(event_name)); } +// TODO: DRY with GlobalEventEnum /// enum describing every possible comptime-known local event name fn LocalEventEnum(comptime modules: anytype) type { var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{}; var i: u32 = 0; for (modules) |M| { _ = ModuleInterface(M); // Validate the module - inline for (M.events) |event| { - const Event = @TypeOf(event); - const name_tag = if (@hasField(Event, "local")) event.local else continue; - + if (@hasDecl(M, "local_events")) inline for (@typeInfo(@TypeOf(M.local_events)).Struct.fields) |field| { const exists_already = blk: { - for (enum_fields) |existing| if (std.mem.eql(u8, existing.name, @tagName(name_tag))) break :blk true; + for (enum_fields) |existing| if (std.mem.eql(u8, existing.name, field.name)) break :blk true; break :blk false; }; if (!exists_already) { - enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = @tagName(name_tag), .value = i }}; + enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = field.name, .value = i }}; i += 1; } - } + }; } return @Type(.{ .Enum = .{ @@ -565,25 +556,49 @@ fn LocalEventEnum(comptime modules: anytype) type { }); } +// TODO: DRY with GlobalEventEnumM +/// enum describing every possible comptime-known local event name +fn LocalEventEnumM(comptime M: anytype) type { + var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{}; + var i: u32 = 0; + _ = ModuleInterface(M); // Validate the module + if (@hasDecl(M, "local_events")) inline for (@typeInfo(@TypeOf(M.local_events)).Struct.fields) |field| { + const exists_already = blk: { + for (enum_fields) |existing| if (std.mem.eql(u8, existing.name, field.name)) break :blk true; + break :blk false; + }; + if (!exists_already) { + enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = field.name, .value = i }}; + i += 1; + } + }; + return @Type(.{ + .Enum = .{ + .tag_type = if (enum_fields.len > 0) std.math.IntFittingRange(0, enum_fields.len - 1) else u0, + .fields = enum_fields, + .decls = &[_]std.builtin.Type.Declaration{}, + .is_exhaustive = true, + }, + }); +} + +// TODO: DRY with LocalEventEnum /// enum describing every possible comptime-known global event name fn GlobalEventEnum(comptime modules: anytype) type { var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{}; var i: u32 = 0; for (modules) |M| { _ = ModuleInterface(M); // Validate the module - inline for (M.events) |event| { - const Event = @TypeOf(event); - const name_tag = if (@hasField(Event, "global")) event.global else continue; - + if (@hasDecl(M, "global_events")) inline for (@typeInfo(@TypeOf(M.global_events)).Struct.fields) |field| { const exists_already = blk: { - for (enum_fields) |existing| if (std.mem.eql(u8, existing.name, @tagName(name_tag))) break :blk true; + for (enum_fields) |existing| if (std.mem.eql(u8, existing.name, field.name)) break :blk true; break :blk false; }; if (!exists_already) { - enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = @tagName(name_tag), .value = i }}; + enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = field.name, .value = i }}; i += 1; } - } + }; } return @Type(.{ .Enum = .{ @@ -595,6 +610,32 @@ fn GlobalEventEnum(comptime modules: anytype) type { }); } +// TODO: DRY with LocalEventEnumM +/// enum describing every possible comptime-known global event name +fn GlobalEventEnumM(comptime M: anytype) type { + var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{}; + var i: u32 = 0; + _ = ModuleInterface(M); // Validate the module + if (@hasDecl(M, "global_events")) inline for (@typeInfo(@TypeOf(M.global_events)).Struct.fields) |field| { + const exists_already = blk: { + for (enum_fields) |existing| if (std.mem.eql(u8, existing.name, field.name)) break :blk true; + break :blk false; + }; + if (!exists_already) { + enum_fields = enum_fields ++ [_]std.builtin.Type.EnumField{.{ .name = field.name, .value = i }}; + i += 1; + } + }; + return @Type(.{ + .Enum = .{ + .tag_type = if (enum_fields.len > 0) std.math.IntFittingRange(0, enum_fields.len - 1) else u0, + .fields = enum_fields, + .decls = &[_]std.builtin.Type.Declaration{}, + .is_exhaustive = true, + }, + }); +} + /// enum describing every possible comptime-known module name fn ModuleName(comptime modules: anytype) type { var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{}; @@ -636,31 +677,21 @@ fn NamespacedModules(comptime modules: anytype) type { // TODO: tests fn validateEvents(comptime error_prefix: anytype, comptime events: anytype) void { - if (@typeInfo(@TypeOf(events)) != .Struct or !@typeInfo(@TypeOf(events)).Struct.is_tuple) { - @compileError(error_prefix ++ "expected a tuple of structs, found: " ++ @typeName(@TypeOf(events))); + if (@typeInfo(@TypeOf(events)) != .Struct or @typeInfo(@TypeOf(events)).Struct.is_tuple) { + @compileError(error_prefix ++ "expected a struct .{}, found: " ++ @typeName(@TypeOf(events))); } - inline for (events, 0..) |event, i| { - const Event = @TypeOf(event); + inline for (@typeInfo(@TypeOf(events)).Struct.fields) |field| { + const Event = field.type; if (@typeInfo(Event) != .Struct) @compileError(std.fmt.comptimePrint( - error_prefix ++ "expected a tuple of structs, found tuple element ({}): {s}", - .{ i, @typeName(Event) }, + error_prefix ++ "expected .{s} = .{{}}, found type: {s}", + .{ field.name, @typeName(Event) }, )); + const event = @field(events, field.name); - // Verify .global = .foo, or .local = .foo, event handler name field - const name_tag = if (@hasField(Event, "global")) event.global else if (@hasField(Event, "local")) event.local else @compileError(std.fmt.comptimePrint( - error_prefix ++ "tuple element ({}) missing field `.global = .foo` or `.local = .foo` (event handler kind / name)", - .{i}, - )); - const is_global = if (@hasField(Event, "global")) true else false; - if (@typeInfo(@TypeOf(name_tag)) != .EnumLiteral) @compileError(std.fmt.comptimePrint( - error_prefix ++ "tuple element ({}) expected field `.{s} = .foo`, found: {s}", - .{ i, if (is_global) "global" else "local", @typeName(@TypeOf(name_tag)) }, - )); - - // Verify .handler = fn, field + // Verify .handler field if (!@hasField(Event, "handler")) @compileError(std.fmt.comptimePrint( - error_prefix ++ "tuple element ({}) missing field `.handler = fn`", - .{i}, + error_prefix ++ ".{s} missing field `.handler = fn` or `.handler = @TypeOf(fn)`", + .{field.name}, )); const valid_handler_type = switch (@typeInfo(@TypeOf(event.handler))) { .Fn => true, @@ -671,8 +702,8 @@ fn validateEvents(comptime error_prefix: anytype, comptime events: anytype) void else => false, }; if (!valid_handler_type) @compileError(std.fmt.comptimePrint( - error_prefix ++ "tuple element ({}) expected field `.handler = fn`, found: {s}", - .{ i, @typeName(@TypeOf(event.handler)) }, + error_prefix ++ ".{s} field .handler expected `.handler = fn` or `.handler = @TypeOf(fn)`, found found: {s}", + .{ field.name, @typeName(@TypeOf(event.handler)) }, )); } } @@ -860,8 +891,8 @@ test ModuleInterface { .{ .name = .location, .type = @Vector(3, f32), .description = "A location component" }, }; - pub const events = .{ - .{ .global = .tick, .handler = tick }, + pub const global_events = .{ + .tick = .{ .handler = tick }, }; fn tick() !void {} @@ -881,8 +912,8 @@ test Modules { .{ .name = .location, .type = @Vector(3, f32), .description = "A location component" }, }; - pub const events = .{ - .{ .global = .tick, .handler = tick }, + pub const global_events = .{ + .tick = .{ .handler = tick }, }; fn tick() !void {} @@ -890,8 +921,8 @@ test Modules { const Renderer = ModuleInterface(struct { pub const name = .engine_renderer; - pub const events = .{ - .{ .global = .tick, .handler = tick }, + pub const global_events = .{ + .tick = .{ .handler = tick }, }; /// Renderer module components @@ -902,7 +933,6 @@ test Modules { const Sprite2D = ModuleInterface(struct { pub const name = .engine_sprite2d; - pub const events = .{}; }); var modules: Modules(.{ @@ -921,11 +951,13 @@ test "event name" { const Physics = ModuleInterface(struct { pub const name = .engine_physics; pub const components = .{}; - pub const events = .{ - .{ .global = .foo, .handler = foo }, - .{ .global = .bar, .handler = bar }, - .{ .local = .baz, .handler = baz }, - .{ .local = .bam, .handler = bam }, + pub const global_events = .{ + .foo = .{ .handler = foo }, + .bar = .{ .handler = bar }, + }; + pub const local_events = .{ + .baz = .{ .handler = baz }, + .bam = .{ .handler = bam }, }; fn foo() !void {} @@ -937,12 +969,12 @@ test "event name" { const Renderer = ModuleInterface(struct { pub const name = .engine_renderer; pub const components = .{}; - pub const events = .{ - .{ .global = .foo_unused, .handler = fn (f32, i32) void }, - .{ .global = .bar_unused, .handler = fn (i32, f32) void }, - .{ .global = .tick, .handler = tick }, - .{ .global = .foo, .handler = foo }, - .{ .global = .bar, .handler = bar }, + pub const global_events = .{ + .foo_unused = .{ .handler = fn (f32, i32) void }, + .bar_unused = .{ .handler = fn (i32, f32) void }, + .tick = .{ .handler = tick }, + .foo = .{ .handler = foo }, + .bar = .{ .handler = bar }, }; fn tick() !void {} @@ -952,13 +984,13 @@ test "event name" { const Sprite2D = ModuleInterface(struct { pub const name = .engine_sprite2d; - pub const events = .{ - .{ .global = .tick, .handler = tick }, - .{ .global = .foobar, .handler = foobar }, + pub const global_events = .{ + .tick = .{ .handler = tick }, + .foobar = .{ .handler = fooBar }, }; fn tick() void {} // same .tick as .engine_renderer.tick - fn foobar() void {} + fn fooBar() void {} }); const Ms = Modules(.{ @@ -987,15 +1019,12 @@ test "event name" { test ModuleName { const Physics = ModuleInterface(struct { pub const name = .engine_physics; - pub const events = .{}; }); const Renderer = ModuleInterface(struct { pub const name = .engine_renderer; - pub const events = .{}; }); const Sprite2D = ModuleInterface(struct { pub const name = .engine_sprite2d; - pub const events = .{}; }); const Ms = Modules(.{ Physics, @@ -1134,10 +1163,12 @@ test "event name calling" { const Physics = ModuleInterface(struct { pub const name = .engine_physics; pub const components = .{}; - pub const events = .{ - .{ .global = .tick, .handler = tick }, - .{ .local = .update, .handler = update }, - .{ .local = .calc, .handler = calc }, + pub const global_events = .{ + .tick = .{ .handler = tick }, + }; + pub const local_events = .{ + .update = .{ .handler = update }, + .calc = .{ .handler = calc }, }; fn tick() void { @@ -1155,9 +1186,11 @@ test "event name calling" { const Renderer = ModuleInterface(struct { pub const name = .engine_renderer; pub const components = .{}; - pub const events = .{ - .{ .global = .tick, .handler = tick }, - .{ .local = .update, .handler = update }, + pub const global_events = .{ + .tick = .{ .handler = tick }, + }; + pub const local_events = .{ + .update = .{ .handler = update }, }; fn tick() void { @@ -1237,15 +1270,15 @@ test "dispatch" { }{}; const Minimal = ModuleInterface(struct { pub const name = .engine_minimal; - pub const events = .{}; }); const Physics = ModuleInterface(struct { pub const name = .engine_physics; - pub const components = .{}; - pub const events = .{ - .{ .global = .tick, .handler = tick }, - .{ .local = .update, .handler = update }, - .{ .local = .calc, .handler = calc }, + pub const global_events = .{ + .tick = .{ .handler = tick }, + }; + pub const local_events = .{ + .update = .{ .handler = update }, + .calc = .{ .handler = calc }, }; fn tick() void { @@ -1263,12 +1296,14 @@ test "dispatch" { const Renderer = ModuleInterface(struct { pub const name = .engine_renderer; pub const components = .{}; - pub const events = .{ - .{ .global = .tick, .handler = tick }, - .{ .global = .frame_done, .handler = fn (i32) void }, - .{ .local = .update, .handler = update }, - .{ .local = .basic_args, .handler = basicArgs }, - .{ .local = .injected_args, .handler = injectedArgs }, + pub const global_events = .{ + .tick = .{ .handler = tick }, + .frame_done = .{ .handler = fn (i32) void }, + }; + pub const local_events = .{ + .update = .{ .handler = update }, + .basic_args = .{ .handler = basicArgs }, + .injected_args = .{ .handler = injectedArgs }, }; pub const frameDone = fn (i32) void;