ecs: initial systems & message sending functionality

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2022-06-27 11:56:36 -07:00 committed by Stephen Gutekanst
parent 98ec7f5190
commit 6f7f17c5e8
2 changed files with 107 additions and 0 deletions

View file

@ -32,6 +32,8 @@ pub const Adapter = @import("systems.zig").Adapter;
pub const System = @import("systems.zig").System; pub const System = @import("systems.zig").System;
pub const Module = @import("systems.zig").Module; pub const Module = @import("systems.zig").Module;
pub const Modules = @import("systems.zig").Modules; 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; pub const World = @import("systems.zig").World;
// TODO: // TODO:
@ -48,6 +50,17 @@ test "inclusion" {
test "example" { test "example" {
const allocator = testing.allocator; 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(.{ const modules = Modules(.{
.physics = Module(.{ .physics = Module(.{
.components = .{ .components = .{
@ -56,6 +69,8 @@ test "example" {
.globals = struct { .globals = struct {
pointer: u8, pointer: u8,
}, },
.messages = PhysicsMsg,
.update = physicsUpdate,
}), }),
.renderer = Module(.{ .renderer = Module(.{
.components = .{ .components = .{
@ -81,4 +96,6 @@ test "example" {
try world.entities.setComponent(player2, .physics, .id, 1234); try world.entities.setComponent(player2, .physics, .id, 1234);
try world.entities.setComponent(player3, .physics, .id, 1234); try world.entities.setComponent(player3, .physics, .id, 1234);
world.tick();
} }

View file

@ -2,7 +2,10 @@ const std = @import("std");
const mem = std.mem; const mem = std.mem;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
const testing = std.testing; const testing = std.testing;
const math = std.math;
const StructField = std.builtin.Type.StructField; const StructField = std.builtin.Type.StructField;
const EnumField = std.builtin.Type.EnumField;
const UnionField = std.builtin.Type.UnionField;
const Entities = @import("entities.zig").Entities; const Entities = @import("entities.zig").Entities;
@ -18,6 +21,82 @@ pub fn Modules(modules: anytype) @TypeOf(modules) {
return 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**. /// Returns the namespaced components struct **type**.
// //
/// Consult `namespacedComponents` for how a value of this type looks. /// Consult `namespacedComponents` for how a value of this type looks.
@ -197,5 +276,16 @@ pub fn World(comptime modules: anytype) type {
@tagName(global_tag), @tagName(global_tag),
) = value; ) = 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);
}
}
}
}; };
} }