module: write module events using a struct pattern
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
582a3c07f6
commit
17db5498ee
13 changed files with 245 additions and 203 deletions
|
|
@ -20,9 +20,9 @@ pub const components = .{
|
||||||
.{ .name = .follower, .type = void },
|
.{ .name = .follower, .type = void },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
// Each module must have a globally unique name declared, it is impossible to use two modules with
|
// Each module must have a globally unique name declared, it is impossible to use two modules with
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ pub const components = .{
|
||||||
.{ .name = .scale, .type = f32 },
|
.{ .name = .scale, .type = f32 },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .global = .deinit, .handler = deinit },
|
.deinit = .{ .handler = deinit },
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this shouldn't be a packed struct, it should be extern.
|
// TODO: this shouldn't be a packed struct, it should be extern.
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,9 @@ const d0 = 0.000001;
|
||||||
pub const name = .game;
|
pub const name = .game;
|
||||||
pub const Mod = mach.Mod(@This());
|
pub const Mod = mach.Mod(@This());
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Pipeline = enum(u32) {
|
pub const Pipeline = enum(u32) {
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,13 @@ const assets = @import("assets");
|
||||||
pub const name = .game_text;
|
pub const name = .game_text;
|
||||||
pub const Mod = mach.Mod(@This());
|
pub const Mod = mach.Mod(@This());
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .deinit, .handler = deinit },
|
.deinit = .{ .handler = deinit },
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .local = .prepare, .handler = prepare },
|
};
|
||||||
|
|
||||||
|
pub const local_events = .{
|
||||||
|
.prepare = .{ .handler = prepare },
|
||||||
};
|
};
|
||||||
|
|
||||||
const RegionMap = std.AutoArrayHashMapUnmanaged(u21, mach.gfx.Atlas.Region);
|
const RegionMap = std.AutoArrayHashMapUnmanaged(u21, mach.gfx.Atlas.Region);
|
||||||
|
|
|
||||||
|
|
@ -41,9 +41,9 @@ const d0 = 0.000001;
|
||||||
pub const name = .game;
|
pub const name = .game;
|
||||||
pub const Mod = mach.Mod(@This());
|
pub const Mod = mach.Mod(@This());
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Pipeline = enum(u32) {
|
pub const Pipeline = enum(u32) {
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,10 @@ const d0 = 0.000001;
|
||||||
pub const name = .game;
|
pub const name = .game;
|
||||||
pub const Mod = mach.Mod(@This());
|
pub const Mod = mach.Mod(@This());
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .global = .deinit, .handler = deinit },
|
.deinit = .{ .handler = deinit },
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Pipeline = enum(u32) {
|
pub const Pipeline = enum(u32) {
|
||||||
|
|
|
||||||
|
|
@ -753,7 +753,6 @@ test "example" {
|
||||||
const all_components = ComponentTypesByName(.{
|
const all_components = ComponentTypesByName(.{
|
||||||
struct {
|
struct {
|
||||||
pub const name = .game;
|
pub const name = .game;
|
||||||
pub const events = .{};
|
|
||||||
pub const components = .{
|
pub const components = .{
|
||||||
.{ .name = .name, .type = []const u8 },
|
.{ .name = .name, .type = []const u8 },
|
||||||
.{ .name = .location, .type = Location },
|
.{ .name = .location, .type = Location },
|
||||||
|
|
@ -858,7 +857,6 @@ test "many entities" {
|
||||||
const all_components = ComponentTypesByName(.{
|
const all_components = ComponentTypesByName(.{
|
||||||
struct {
|
struct {
|
||||||
pub const name = .game;
|
pub const name = .game;
|
||||||
pub const events = .{};
|
|
||||||
pub const components = .{
|
pub const components = .{
|
||||||
.{ .name = .name, .type = []const u8 },
|
.{ .name = .name, .type = []const u8 },
|
||||||
.{ .name = .location, .type = Location },
|
.{ .name = .location, .type = Location },
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,8 @@ test "example" {
|
||||||
pub const components = .{
|
pub const components = .{
|
||||||
.{ .name = .id, .type = u32 },
|
.{ .name = .id, .type = u32 },
|
||||||
};
|
};
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick(physics: *Modules(modules).Mod(Physics)) void {
|
fn tick(physics: *Modules(modules).Mod(Physics)) void {
|
||||||
|
|
@ -63,8 +63,8 @@ test "example" {
|
||||||
pub const components = .{
|
pub const components = .{
|
||||||
.{ .name = .id, .type = u16 },
|
.{ .name = .id, .type = u16 },
|
||||||
};
|
};
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick(
|
fn tick(
|
||||||
|
|
|
||||||
|
|
@ -73,14 +73,12 @@ test "query" {
|
||||||
const all_components = ComponentTypesByName(.{
|
const all_components = ComponentTypesByName(.{
|
||||||
struct {
|
struct {
|
||||||
pub const name = .game;
|
pub const name = .game;
|
||||||
pub const events = .{};
|
|
||||||
pub const components = .{
|
pub const components = .{
|
||||||
.{ .name = .name, .type = []const u8 },
|
.{ .name = .name, .type = []const u8 },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
struct {
|
struct {
|
||||||
pub const name = .physics;
|
pub const name = .physics;
|
||||||
pub const events = .{};
|
|
||||||
pub const components = .{
|
pub const components = .{
|
||||||
.{ .name = .location, .type = Location },
|
.{ .name = .location, .type = Location },
|
||||||
.{ .name = .rotation, .type = Rotation },
|
.{ .name = .rotation, .type = Rotation },
|
||||||
|
|
@ -88,7 +86,6 @@ test "query" {
|
||||||
},
|
},
|
||||||
struct {
|
struct {
|
||||||
pub const name = .renderer;
|
pub const name = .renderer;
|
||||||
pub const events = .{};
|
|
||||||
},
|
},
|
||||||
}){};
|
}){};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,20 @@ pub const Engine = struct {
|
||||||
pub const name = .engine;
|
pub const name = .engine;
|
||||||
pub const Mod = Modules.Mod(@This());
|
pub const Mod = Modules.Mod(@This());
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .local = .init, .handler = init },
|
.init = .{ .handler = fn () void },
|
||||||
.{ .local = .deinit, .handler = deinit },
|
.deinit = .{ .handler = fn () void },
|
||||||
.{ .local = .exit, .handler = exit },
|
.tick = .{ .handler = fn () void },
|
||||||
.{ .local = .begin_pass, .handler = beginPass },
|
.exit = .{ .handler = fn () void },
|
||||||
.{ .local = .end_pass, .handler = endPass },
|
};
|
||||||
.{ .local = .present, .handler = present },
|
|
||||||
.{ .global = .init, .handler = fn () void },
|
pub const local_events = .{
|
||||||
.{ .global = .deinit, .handler = fn () void },
|
.init = .{ .handler = init },
|
||||||
.{ .global = .tick, .handler = fn () void },
|
.deinit = .{ .handler = deinit },
|
||||||
.{ .global = .exit, .handler = fn () void },
|
.exit = .{ .handler = exit },
|
||||||
|
.begin_pass = .{ .handler = beginPass },
|
||||||
|
.end_pass = .{ .handler = endPass },
|
||||||
|
.present = .{ .handler = present },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn init(engine: *Mod) !void {
|
fn init(engine: *Mod) !void {
|
||||||
|
|
|
||||||
|
|
@ -43,13 +43,16 @@ pub const components = .{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .deinit, .handler = deinit },
|
.deinit = .{ .handler = deinit },
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .local = .init_pipeline, .handler = initPipeline },
|
};
|
||||||
.{ .local = .updated, .handler = updated },
|
|
||||||
.{ .local = .pre_render, .handler = preRender },
|
pub const local_events = .{
|
||||||
.{ .local = .render, .handler = render },
|
.init_pipeline = .{ .handler = initPipeline },
|
||||||
|
.updated = .{ .handler = updated },
|
||||||
|
.pre_render = .{ .handler = preRender },
|
||||||
|
.render = .{ .handler = render },
|
||||||
};
|
};
|
||||||
|
|
||||||
const Uniforms = extern struct {
|
const Uniforms = extern struct {
|
||||||
|
|
|
||||||
|
|
@ -78,13 +78,16 @@ pub const components = .{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .deinit, .handler = deinit },
|
.deinit = .{ .handler = deinit },
|
||||||
.{ .global = .init, .handler = init },
|
.init = .{ .handler = init },
|
||||||
.{ .local = .init_pipeline, .handler = initPipeline },
|
};
|
||||||
.{ .local = .updated, .handler = updated },
|
|
||||||
.{ .local = .pre_render, .handler = preRender },
|
pub const local_events = .{
|
||||||
.{ .local = .render, .handler = render },
|
.init_pipeline = .{ .handler = initPipeline },
|
||||||
|
.updated = .{ .handler = updated },
|
||||||
|
.pre_render = .{ .handler = preRender },
|
||||||
|
.render = .{ .handler = render },
|
||||||
};
|
};
|
||||||
|
|
||||||
const Uniforms = extern struct {
|
const Uniforms = extern struct {
|
||||||
|
|
|
||||||
331
src/module.zig
331
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 (!@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));
|
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) ++ " ";
|
// TODO: enable once parameter dependency loop has been resolved
|
||||||
if (!@hasDecl(M, "events")) @compileError(prefix ++ "must have `pub const events = .{};`");
|
// if (@hasDecl(M, "global_events")) validateEvents("mach: module ." ++ @tagName(M.name) ++ " global_events ", M.global_events);
|
||||||
// TODO: re-enable this validation once module event handler arguments would not pose a type dependency loop
|
// if (@hasDecl(M, "local_events")) validateEvents("mach: module ." ++ @tagName(M.name) ++ " local_events ", M.global_events);
|
||||||
// validateEvents("mach: module ." ++ @tagName(M.name) ++ " ", M.events);
|
|
||||||
_ = ComponentTypesM(M);
|
_ = ComponentTypesM(M);
|
||||||
return M;
|
return M;
|
||||||
}
|
}
|
||||||
|
|
@ -231,20 +230,20 @@ pub fn Modules(comptime modules2: anytype) type {
|
||||||
switch (event_name) {
|
switch (event_name) {
|
||||||
inline else => |ev_name| {
|
inline else => |ev_name| {
|
||||||
inline for (modules) |M| {
|
inline for (modules) |M| {
|
||||||
|
// TODO: DRY with callLocal
|
||||||
_ = ModuleInterface(M); // Validate the module
|
_ = ModuleInterface(M); // Validate the module
|
||||||
inline for (M.events) |event| {
|
if (@hasDecl(M, "global_events")) inline for (@typeInfo(@TypeOf(M.global_events)).Struct.fields) |field| {
|
||||||
const Ev = @TypeOf(event);
|
comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue;
|
||||||
const name_tag = if (@hasField(Ev, "global")) event.global else continue;
|
const handler = @field(M.global_events, @tagName(ev_name)).handler;
|
||||||
if (name_tag != ev_name) continue;
|
if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do.
|
||||||
switch (@typeInfo(@TypeOf(event.handler))) {
|
if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares global event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{
|
||||||
.Fn => try callHandler(event.handler, args, injectable),
|
@tagName(M.name),
|
||||||
.Type => switch (@typeInfo(event.handler)) {
|
@tagName(ev_name),
|
||||||
.Fn => {}, // Pre-declaration of what args an event has, nothing to run.
|
|
||||||
else => unreachable,
|
@typeName(@TypeOf(handler)),
|
||||||
},
|
}));
|
||||||
else => unreachable,
|
try callHandler(handler, args, injectable);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -257,23 +256,20 @@ pub fn Modules(comptime modules2: anytype) type {
|
||||||
inline else => |ev_name| {
|
inline else => |ev_name| {
|
||||||
switch (module_name) {
|
switch (module_name) {
|
||||||
inline else => |mod_name| {
|
inline else => |mod_name| {
|
||||||
|
// TODO: DRY with callGlobal
|
||||||
const M = @field(NamespacedModules(@This().modules){}, @tagName(mod_name));
|
const M = @field(NamespacedModules(@This().modules){}, @tagName(mod_name));
|
||||||
_ = ModuleInterface(M); // Validate the module
|
_ = ModuleInterface(M); // Validate the module
|
||||||
|
if (@hasDecl(M, "local_events")) inline for (@typeInfo(@TypeOf(M.local_events)).Struct.fields) |field| {
|
||||||
inline for (M.events) |event| {
|
comptime if (!std.mem.eql(u8, @tagName(ev_name), field.name)) continue;
|
||||||
const Ev = @TypeOf(event);
|
const handler = @field(M.local_events, @tagName(ev_name)).handler;
|
||||||
const name_tag = if (@hasField(Ev, "local")) event.local else continue;
|
if (@typeInfo(@TypeOf(handler)) == .Type) continue; // Pre-declaration of what args an event has, nothing to do.
|
||||||
if (name_tag != ev_name) continue;
|
if (@typeInfo(@TypeOf(handler)) != .Fn) @compileError(std.fmt.comptimePrint("mach: module .{s} declares local event .{s} = .{{ .handler = T }}, expected fn but found: {s}", .{
|
||||||
switch (@typeInfo(@TypeOf(event.handler))) {
|
@tagName(M.name),
|
||||||
.Fn => try callHandler(event.handler, args, injectable),
|
@tagName(ev_name),
|
||||||
.Type => switch (@typeInfo(event.handler)) {
|
@typeName(@TypeOf(handler)),
|
||||||
.Fn => {}, // Pre-declaration of what args an event has, nothing to run.
|
}));
|
||||||
else => unreachable,
|
try callHandler(handler, args, injectable);
|
||||||
},
|
};
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -487,18 +483,39 @@ fn UninjectedArgsTuple(
|
||||||
return Tuple(std_args);
|
return Tuple(std_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn LocalArgsM(comptime M: type, event_name: anytype) type {
|
// TODO: tests
|
||||||
_ = ModuleInterface(M); // Validate the module
|
fn LocalArgsM(comptime M: type, event_name: anytype) type {
|
||||||
inline for (M.events) |event| {
|
return ArgsM(M, event_name, "local");
|
||||||
const Ev = @TypeOf(event);
|
}
|
||||||
const name_tag = if (@hasField(Ev, "local")) event.local else continue;
|
|
||||||
if (name_tag != event_name) continue;
|
|
||||||
|
|
||||||
const Handler = switch (@typeInfo(@TypeOf(event.handler))) {
|
// TODO: tests
|
||||||
.Fn => @TypeOf(event.handler),
|
fn GlobalArgsM(comptime M: type, event_name: anytype) type {
|
||||||
.Type => switch (@typeInfo(event.handler)) {
|
return ArgsM(M, event_name, "global");
|
||||||
.Fn => event.handler,
|
}
|
||||||
else => unreachable,
|
|
||||||
|
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,
|
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`.
|
// `@Type(.{.Struct = .{ .is_tuple = false }})` instead of `.is_tuple = true`.
|
||||||
return UninjectedArgsTuple(TupleHACK, Handler);
|
return UninjectedArgsTuple(TupleHACK, Handler);
|
||||||
}
|
}
|
||||||
@compileError("mach: module ." ++ @tagName(M.name) ++ " has no .local event handler for ." ++ @tagName(event_name));
|
@compileError("mach: module ." ++ @tagName(M.name) ++ " has no " ++ which ++ " 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: DRY with GlobalEventEnum
|
||||||
/// enum describing every possible comptime-known local event name
|
/// enum describing every possible comptime-known local event name
|
||||||
fn LocalEventEnum(comptime modules: anytype) type {
|
fn LocalEventEnum(comptime modules: anytype) type {
|
||||||
var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{};
|
var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{};
|
||||||
var i: u32 = 0;
|
var i: u32 = 0;
|
||||||
for (modules) |M| {
|
for (modules) |M| {
|
||||||
_ = ModuleInterface(M); // Validate the module
|
_ = ModuleInterface(M); // Validate the module
|
||||||
inline for (M.events) |event| {
|
if (@hasDecl(M, "local_events")) inline for (@typeInfo(@TypeOf(M.local_events)).Struct.fields) |field| {
|
||||||
const Event = @TypeOf(event);
|
|
||||||
const name_tag = if (@hasField(Event, "local")) event.local else continue;
|
|
||||||
|
|
||||||
const exists_already = blk: {
|
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;
|
break :blk false;
|
||||||
};
|
};
|
||||||
if (!exists_already) {
|
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;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return @Type(.{
|
return @Type(.{
|
||||||
.Enum = .{
|
.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
|
/// enum describing every possible comptime-known global event name
|
||||||
fn GlobalEventEnum(comptime modules: anytype) type {
|
fn GlobalEventEnum(comptime modules: anytype) type {
|
||||||
var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{};
|
var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{};
|
||||||
var i: u32 = 0;
|
var i: u32 = 0;
|
||||||
for (modules) |M| {
|
for (modules) |M| {
|
||||||
_ = ModuleInterface(M); // Validate the module
|
_ = ModuleInterface(M); // Validate the module
|
||||||
inline for (M.events) |event| {
|
if (@hasDecl(M, "global_events")) inline for (@typeInfo(@TypeOf(M.global_events)).Struct.fields) |field| {
|
||||||
const Event = @TypeOf(event);
|
|
||||||
const name_tag = if (@hasField(Event, "global")) event.global else continue;
|
|
||||||
|
|
||||||
const exists_already = blk: {
|
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;
|
break :blk false;
|
||||||
};
|
};
|
||||||
if (!exists_already) {
|
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;
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
return @Type(.{
|
return @Type(.{
|
||||||
.Enum = .{
|
.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
|
/// enum describing every possible comptime-known module name
|
||||||
fn ModuleName(comptime modules: anytype) type {
|
fn ModuleName(comptime modules: anytype) type {
|
||||||
var enum_fields: []const std.builtin.Type.EnumField = &[0]std.builtin.Type.EnumField{};
|
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
|
// TODO: tests
|
||||||
fn validateEvents(comptime error_prefix: anytype, comptime events: anytype) void {
|
fn validateEvents(comptime error_prefix: anytype, comptime events: anytype) void {
|
||||||
if (@typeInfo(@TypeOf(events)) != .Struct or !@typeInfo(@TypeOf(events)).Struct.is_tuple) {
|
if (@typeInfo(@TypeOf(events)) != .Struct or @typeInfo(@TypeOf(events)).Struct.is_tuple) {
|
||||||
@compileError(error_prefix ++ "expected a tuple of structs, found: " ++ @typeName(@TypeOf(events)));
|
@compileError(error_prefix ++ "expected a struct .{}, found: " ++ @typeName(@TypeOf(events)));
|
||||||
}
|
}
|
||||||
inline for (events, 0..) |event, i| {
|
inline for (@typeInfo(@TypeOf(events)).Struct.fields) |field| {
|
||||||
const Event = @TypeOf(event);
|
const Event = field.type;
|
||||||
if (@typeInfo(Event) != .Struct) @compileError(std.fmt.comptimePrint(
|
if (@typeInfo(Event) != .Struct) @compileError(std.fmt.comptimePrint(
|
||||||
error_prefix ++ "expected a tuple of structs, found tuple element ({}): {s}",
|
error_prefix ++ "expected .{s} = .{{}}, found type: {s}",
|
||||||
.{ i, @typeName(Event) },
|
.{ field.name, @typeName(Event) },
|
||||||
));
|
));
|
||||||
|
const event = @field(events, field.name);
|
||||||
|
|
||||||
// Verify .global = .foo, or .local = .foo, event handler name field
|
// Verify .handler 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
|
|
||||||
if (!@hasField(Event, "handler")) @compileError(std.fmt.comptimePrint(
|
if (!@hasField(Event, "handler")) @compileError(std.fmt.comptimePrint(
|
||||||
error_prefix ++ "tuple element ({}) missing field `.handler = fn`",
|
error_prefix ++ ".{s} missing field `.handler = fn` or `.handler = @TypeOf(fn)`",
|
||||||
.{i},
|
.{field.name},
|
||||||
));
|
));
|
||||||
const valid_handler_type = switch (@typeInfo(@TypeOf(event.handler))) {
|
const valid_handler_type = switch (@typeInfo(@TypeOf(event.handler))) {
|
||||||
.Fn => true,
|
.Fn => true,
|
||||||
|
|
@ -671,8 +702,8 @@ fn validateEvents(comptime error_prefix: anytype, comptime events: anytype) void
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
if (!valid_handler_type) @compileError(std.fmt.comptimePrint(
|
if (!valid_handler_type) @compileError(std.fmt.comptimePrint(
|
||||||
error_prefix ++ "tuple element ({}) expected field `.handler = fn`, found: {s}",
|
error_prefix ++ ".{s} field .handler expected `.handler = fn` or `.handler = @TypeOf(fn)`, found found: {s}",
|
||||||
.{ i, @typeName(@TypeOf(event.handler)) },
|
.{ field.name, @typeName(@TypeOf(event.handler)) },
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -860,8 +891,8 @@ test ModuleInterface {
|
||||||
.{ .name = .location, .type = @Vector(3, f32), .description = "A location component" },
|
.{ .name = .location, .type = @Vector(3, f32), .description = "A location component" },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick() !void {}
|
fn tick() !void {}
|
||||||
|
|
@ -881,8 +912,8 @@ test Modules {
|
||||||
.{ .name = .location, .type = @Vector(3, f32), .description = "A location component" },
|
.{ .name = .location, .type = @Vector(3, f32), .description = "A location component" },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick() !void {}
|
fn tick() !void {}
|
||||||
|
|
@ -890,8 +921,8 @@ test Modules {
|
||||||
|
|
||||||
const Renderer = ModuleInterface(struct {
|
const Renderer = ModuleInterface(struct {
|
||||||
pub const name = .engine_renderer;
|
pub const name = .engine_renderer;
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Renderer module components
|
/// Renderer module components
|
||||||
|
|
@ -902,7 +933,6 @@ test Modules {
|
||||||
|
|
||||||
const Sprite2D = ModuleInterface(struct {
|
const Sprite2D = ModuleInterface(struct {
|
||||||
pub const name = .engine_sprite2d;
|
pub const name = .engine_sprite2d;
|
||||||
pub const events = .{};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var modules: Modules(.{
|
var modules: Modules(.{
|
||||||
|
|
@ -921,11 +951,13 @@ test "event name" {
|
||||||
const Physics = ModuleInterface(struct {
|
const Physics = ModuleInterface(struct {
|
||||||
pub const name = .engine_physics;
|
pub const name = .engine_physics;
|
||||||
pub const components = .{};
|
pub const components = .{};
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .foo, .handler = foo },
|
.foo = .{ .handler = foo },
|
||||||
.{ .global = .bar, .handler = bar },
|
.bar = .{ .handler = bar },
|
||||||
.{ .local = .baz, .handler = baz },
|
};
|
||||||
.{ .local = .bam, .handler = bam },
|
pub const local_events = .{
|
||||||
|
.baz = .{ .handler = baz },
|
||||||
|
.bam = .{ .handler = bam },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn foo() !void {}
|
fn foo() !void {}
|
||||||
|
|
@ -937,12 +969,12 @@ test "event name" {
|
||||||
const Renderer = ModuleInterface(struct {
|
const Renderer = ModuleInterface(struct {
|
||||||
pub const name = .engine_renderer;
|
pub const name = .engine_renderer;
|
||||||
pub const components = .{};
|
pub const components = .{};
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .foo_unused, .handler = fn (f32, i32) void },
|
.foo_unused = .{ .handler = fn (f32, i32) void },
|
||||||
.{ .global = .bar_unused, .handler = fn (i32, f32) void },
|
.bar_unused = .{ .handler = fn (i32, f32) void },
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
.{ .global = .foo, .handler = foo },
|
.foo = .{ .handler = foo },
|
||||||
.{ .global = .bar, .handler = bar },
|
.bar = .{ .handler = bar },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick() !void {}
|
fn tick() !void {}
|
||||||
|
|
@ -952,13 +984,13 @@ test "event name" {
|
||||||
|
|
||||||
const Sprite2D = ModuleInterface(struct {
|
const Sprite2D = ModuleInterface(struct {
|
||||||
pub const name = .engine_sprite2d;
|
pub const name = .engine_sprite2d;
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
.{ .global = .foobar, .handler = foobar },
|
.foobar = .{ .handler = fooBar },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick() void {} // same .tick as .engine_renderer.tick
|
fn tick() void {} // same .tick as .engine_renderer.tick
|
||||||
fn foobar() void {}
|
fn fooBar() void {}
|
||||||
});
|
});
|
||||||
|
|
||||||
const Ms = Modules(.{
|
const Ms = Modules(.{
|
||||||
|
|
@ -987,15 +1019,12 @@ test "event name" {
|
||||||
test ModuleName {
|
test ModuleName {
|
||||||
const Physics = ModuleInterface(struct {
|
const Physics = ModuleInterface(struct {
|
||||||
pub const name = .engine_physics;
|
pub const name = .engine_physics;
|
||||||
pub const events = .{};
|
|
||||||
});
|
});
|
||||||
const Renderer = ModuleInterface(struct {
|
const Renderer = ModuleInterface(struct {
|
||||||
pub const name = .engine_renderer;
|
pub const name = .engine_renderer;
|
||||||
pub const events = .{};
|
|
||||||
});
|
});
|
||||||
const Sprite2D = ModuleInterface(struct {
|
const Sprite2D = ModuleInterface(struct {
|
||||||
pub const name = .engine_sprite2d;
|
pub const name = .engine_sprite2d;
|
||||||
pub const events = .{};
|
|
||||||
});
|
});
|
||||||
const Ms = Modules(.{
|
const Ms = Modules(.{
|
||||||
Physics,
|
Physics,
|
||||||
|
|
@ -1134,10 +1163,12 @@ test "event name calling" {
|
||||||
const Physics = ModuleInterface(struct {
|
const Physics = ModuleInterface(struct {
|
||||||
pub const name = .engine_physics;
|
pub const name = .engine_physics;
|
||||||
pub const components = .{};
|
pub const components = .{};
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
.{ .local = .update, .handler = update },
|
};
|
||||||
.{ .local = .calc, .handler = calc },
|
pub const local_events = .{
|
||||||
|
.update = .{ .handler = update },
|
||||||
|
.calc = .{ .handler = calc },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick() void {
|
fn tick() void {
|
||||||
|
|
@ -1155,9 +1186,11 @@ test "event name calling" {
|
||||||
const Renderer = ModuleInterface(struct {
|
const Renderer = ModuleInterface(struct {
|
||||||
pub const name = .engine_renderer;
|
pub const name = .engine_renderer;
|
||||||
pub const components = .{};
|
pub const components = .{};
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
.{ .local = .update, .handler = update },
|
};
|
||||||
|
pub const local_events = .{
|
||||||
|
.update = .{ .handler = update },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick() void {
|
fn tick() void {
|
||||||
|
|
@ -1237,15 +1270,15 @@ test "dispatch" {
|
||||||
}{};
|
}{};
|
||||||
const Minimal = ModuleInterface(struct {
|
const Minimal = ModuleInterface(struct {
|
||||||
pub const name = .engine_minimal;
|
pub const name = .engine_minimal;
|
||||||
pub const events = .{};
|
|
||||||
});
|
});
|
||||||
const Physics = ModuleInterface(struct {
|
const Physics = ModuleInterface(struct {
|
||||||
pub const name = .engine_physics;
|
pub const name = .engine_physics;
|
||||||
pub const components = .{};
|
pub const global_events = .{
|
||||||
pub const events = .{
|
.tick = .{ .handler = tick },
|
||||||
.{ .global = .tick, .handler = tick },
|
};
|
||||||
.{ .local = .update, .handler = update },
|
pub const local_events = .{
|
||||||
.{ .local = .calc, .handler = calc },
|
.update = .{ .handler = update },
|
||||||
|
.calc = .{ .handler = calc },
|
||||||
};
|
};
|
||||||
|
|
||||||
fn tick() void {
|
fn tick() void {
|
||||||
|
|
@ -1263,12 +1296,14 @@ test "dispatch" {
|
||||||
const Renderer = ModuleInterface(struct {
|
const Renderer = ModuleInterface(struct {
|
||||||
pub const name = .engine_renderer;
|
pub const name = .engine_renderer;
|
||||||
pub const components = .{};
|
pub const components = .{};
|
||||||
pub const events = .{
|
pub const global_events = .{
|
||||||
.{ .global = .tick, .handler = tick },
|
.tick = .{ .handler = tick },
|
||||||
.{ .global = .frame_done, .handler = fn (i32) void },
|
.frame_done = .{ .handler = fn (i32) void },
|
||||||
.{ .local = .update, .handler = update },
|
};
|
||||||
.{ .local = .basic_args, .handler = basicArgs },
|
pub const local_events = .{
|
||||||
.{ .local = .injected_args, .handler = injectedArgs },
|
.update = .{ .handler = update },
|
||||||
|
.basic_args = .{ .handler = basicArgs },
|
||||||
|
.injected_args = .{ .handler = injectedArgs },
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const frameDone = fn (i32) void;
|
pub const frameDone = fn (i32) void;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue