From 6f7f17c5e84f1fdad383877a09925de51138bdfa Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Mon, 27 Jun 2022 11:56:36 -0700 Subject: [PATCH] ecs: initial systems & message sending functionality Signed-off-by: Stephen Gutekanst --- ecs/src/main.zig | 17 +++++++++ ecs/src/systems.zig | 90 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/ecs/src/main.zig b/ecs/src/main.zig index dd967daa..bb984057 100644 --- a/ecs/src/main.zig +++ b/ecs/src/main.zig @@ -32,6 +32,8 @@ pub const Adapter = @import("systems.zig").Adapter; pub const System = @import("systems.zig").System; pub const Module = @import("systems.zig").Module; pub const Modules = @import("systems.zig").Modules; +pub const Messages = @import("systems.zig").Messages; +pub const MessagesTag = @import("systems.zig").MessagesTag; pub const World = @import("systems.zig").World; // TODO: @@ -48,6 +50,17 @@ test "inclusion" { test "example" { const allocator = testing.allocator; + const PhysicsMsg = Messages(.{ + .tick = void, + }); + const physicsUpdate = (struct { + pub fn physicsUpdate(msg: PhysicsMsg) void { + switch (msg) { + .tick => std.debug.print("\nphysics tick!\n", .{}), + } + } + }).physicsUpdate; + const modules = Modules(.{ .physics = Module(.{ .components = .{ @@ -56,6 +69,8 @@ test "example" { .globals = struct { pointer: u8, }, + .messages = PhysicsMsg, + .update = physicsUpdate, }), .renderer = Module(.{ .components = .{ @@ -81,4 +96,6 @@ test "example" { try world.entities.setComponent(player2, .physics, .id, 1234); try world.entities.setComponent(player3, .physics, .id, 1234); + + world.tick(); } diff --git a/ecs/src/systems.zig b/ecs/src/systems.zig index 071b1a73..eda6d088 100644 --- a/ecs/src/systems.zig +++ b/ecs/src/systems.zig @@ -2,7 +2,10 @@ const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; const testing = std.testing; +const math = std.math; const StructField = std.builtin.Type.StructField; +const EnumField = std.builtin.Type.EnumField; +const UnionField = std.builtin.Type.UnionField; const Entities = @import("entities.zig").Entities; @@ -18,6 +21,82 @@ pub fn Modules(modules: anytype) @TypeOf(modules) { return modules; } +/// Returns a tagged union representing the messages, turning this: +/// +/// ``` +/// .{ .tick = void, .foo = i32 } +/// ``` +/// +/// Into `T`: +/// +/// ``` +/// const T = union(MessagesTag(messages)) { +/// .tick = void, +/// .foo = i32, +/// }; +/// ``` +pub fn Messages(messages: anytype) type { + var fields: []const UnionField = &[0]UnionField{}; + const message_fields = std.meta.fields(@TypeOf(messages)); + inline for (message_fields) |message_field| { + const message_type = @field(messages, message_field.name); + fields = fields ++ [_]std.builtin.Type.UnionField{.{ + .name = message_field.name, + .field_type = message_type, + .alignment = if (message_type == void) 0 else @alignOf(message_type), + }}; + } + + // Hack to workaround stage1 compiler bug. https://github.com/ziglang/zig/issues/8114 + // + // return @Type(.{ + // .Union = .{ + // .layout = .Auto, + // .tag_type = MessagesTag(messages), + // .fields = fields, + // .decls = &[_]std.builtin.Type.Declaration{}, + // }, + // }); + // + const Ref = union(enum) { temp }; + var info = @typeInfo(Ref); + info.Union.tag_type = MessagesTag(messages); + info.Union.fields = fields; + return @Type(info); +} + +/// Returns the tag enum for a tagged union representing the messages, turning this: +/// +/// ``` +/// .{ .tick = void, .foo = i32 } +/// ``` +/// +/// Into this: +/// +/// ``` +/// enum { .tick, .foo }; +/// ``` +pub fn MessagesTag(messages: anytype) type { + var fields: []const EnumField = &[0]EnumField{}; + const message_fields = std.meta.fields(@TypeOf(messages)); + inline for (message_fields) |message_field, index| { + fields = fields ++ [_]std.builtin.Type.EnumField{.{ + .name = message_field.name, + .value = index, + }}; + } + + return @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = std.meta.Int(.unsigned, @floatToInt(u16, math.ceil(math.log2(@intToFloat(f64, message_fields.len))))), + .fields = fields, + .decls = &[_]std.builtin.Type.Declaration{}, + .is_exhaustive = true, + }, + }); +} + /// Returns the namespaced components struct **type**. // /// Consult `namespacedComponents` for how a value of this type looks. @@ -197,5 +276,16 @@ pub fn World(comptime modules: anytype) type { @tagName(global_tag), ) = value; } + + /// Tick sends the global 'tick' message to all modules that are subscribed to it. + pub fn tick(world: *Self) void { + _ = world; + inline for (std.meta.fields(@TypeOf(modules))) |module_field| { + const module = @field(modules, module_field.name); + if (@hasField(@TypeOf(module), "messages")) { + if (@hasField(module.messages, "tick")) module.update(.tick); + } + } + } }; }