ecs: rename module globals -> state

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2023-03-26 20:31:21 -07:00 committed by Stephen Gutekanst
parent e6cfbbe82a
commit fcf9943d0f
2 changed files with 30 additions and 29 deletions

View file

@ -2,8 +2,9 @@
//! //!
//! ## Design principles: //! ## Design principles:
//! //!
//! * Clean-room implementation (author has not read any other ECS implementation code, just working //! * Initially a 100% clean-room implementation, working from first-principles. Later informed by
//! from first-principles) //! research into how other ECS work, with advice from e.g. Bevy and Flecs authors at different
//! points (thank you!)
//! * Solve the problems ECS solves, in a way that is natural to Zig and leverages Zig comptime. //! * Solve the problems ECS solves, in a way that is natural to Zig and leverages Zig comptime.
//! * Fast. Optimal for CPU caches, multi-threaded, leverage comptime as much as is reasonable. //! * Fast. Optimal for CPU caches, multi-threaded, leverage comptime as much as is reasonable.
//! * Simple. Small API footprint, should be natural and fun - not like you're writing boilerplate. //! * Simple. Small API footprint, should be natural and fun - not like you're writing boilerplate.
@ -71,7 +72,7 @@ test "example" {
var world = try World(modules).init(allocator); var world = try World(modules).init(allocator);
defer world.deinit(); defer world.deinit();
// Initialize globals. // Initialize module state.
world.set(.physics, .pointer, 123); world.set(.physics, .pointer, 123);
_ = world.get(.physics, .pointer); // == 123 _ = world.get(.physics, .pointer); // == 123

View file

@ -11,7 +11,7 @@ const Entities = @import("entities.zig").Entities;
/// Validates that a module matches the expected type layout. /// Validates that a module matches the expected type layout.
/// ///
/// An ECS module has components, systems, global values & more. /// An ECS module has components, systems, state & more.
pub fn Module(comptime M: anytype) type { pub fn Module(comptime M: anytype) type {
if (@hasDecl(M, "name")) { if (@hasDecl(M, "name")) {
_ = @tagName(M.name); _ = @tagName(M.name);
@ -25,7 +25,7 @@ pub fn Module(comptime M: anytype) type {
/// Validates that a list of module matches the expected type layout. /// Validates that a list of module matches the expected type layout.
/// ///
/// ECS modules have components, systems, global values & more. /// ECS modules have components, systems, state & more.
pub fn Modules(comptime modules: anytype) @TypeOf(modules) { pub fn Modules(comptime modules: anytype) @TypeOf(modules) {
inline for (modules) |m| _ = Module(m); inline for (modules) |m| _ = Module(m);
return modules; return modules;
@ -99,7 +99,7 @@ pub fn MessagesTag(comptime messages: anytype) type {
} }
const NoComponents = @TypeOf(.{}); const NoComponents = @TypeOf(.{});
const NoGlobals = @TypeOf(.{}); const NoState = @TypeOf(.{});
/// Returns the namespaced components struct **type**. /// Returns the namespaced components struct **type**.
// //
@ -168,7 +168,7 @@ fn namespacedComponents(comptime modules: anytype) NamespacedComponents(modules)
return x; return x;
} }
/// Extracts namespaced globals from modules (a module is said to have globals if the struct has /// Extracts namespaced state from modules (a module is said to have state if the struct has
/// any fields), returning a type like e.g.: /// any fields), returning a type like e.g.:
/// ///
/// ``` /// ```
@ -183,26 +183,26 @@ fn namespacedComponents(comptime modules: anytype) NamespacedComponents(modules)
/// } /// }
/// ``` /// ```
/// ///
fn NamespacedGlobals(comptime modules: anytype) type { fn NamespacedState(comptime modules: anytype) type {
var fields: []const StructField = &[0]StructField{}; var fields: []const StructField = &[0]StructField{};
inline for (std.meta.fields(@TypeOf(modules))) |module_field| { inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
const module = @field(modules, module_field.name); const module = @field(modules, module_field.name);
const module_name = @tagName(@field(module, "name")); const module_name = @tagName(@field(module, "name"));
const global_fields = std.meta.fields(module); const state_fields = std.meta.fields(module);
const Globals = if (global_fields.len > 0) @Type(.{ const State = if (state_fields.len > 0) @Type(.{
.Struct = .{ .Struct = .{
.layout = .Auto, .layout = .Auto,
.is_tuple = false, .is_tuple = false,
.fields = global_fields, .fields = state_fields,
.decls = &[_]std.builtin.Type.Declaration{}, .decls = &[_]std.builtin.Type.Declaration{},
}, },
}) else NoGlobals; }) else NoState;
fields = fields ++ [_]std.builtin.Type.StructField{.{ fields = fields ++ [_]std.builtin.Type.StructField{.{
.name = module_name, .name = module_name,
.type = Globals, .type = State,
.default_value = null, .default_value = null,
.is_comptime = false, .is_comptime = false,
.alignment = @alignOf(Globals), .alignment = @alignOf(State),
}}; }};
} }
return @Type(.{ return @Type(.{
@ -220,7 +220,7 @@ pub fn World(comptime modules: anytype) type {
return struct { return struct {
allocator: Allocator, allocator: Allocator,
entities: Entities(all_components), entities: Entities(all_components),
globals: NamespacedGlobals(modules), state: NamespacedState(modules),
const Self = @This(); const Self = @This();
@ -228,7 +228,7 @@ pub fn World(comptime modules: anytype) type {
return Self{ return Self{
.allocator = allocator, .allocator = allocator,
.entities = try Entities(all_components).init(allocator), .entities = try Entities(all_components).init(allocator),
.globals = undefined, .state = undefined,
}; };
} }
@ -236,34 +236,34 @@ pub fn World(comptime modules: anytype) type {
world.entities.deinit(); world.entities.deinit();
} }
/// Gets a global value called `.global_tag` from the module named `.module_tag` /// Gets a state value called `.state_tag` from the module named `.module_tag`
pub fn get(world: *Self, comptime module_tag: anytype, comptime global_tag: anytype) @TypeOf(@field( pub fn get(world: *Self, comptime module_tag: anytype, comptime state_tag: anytype) @TypeOf(@field(
@field(world.globals, @tagName(module_tag)), @field(world.state, @tagName(module_tag)),
@tagName(global_tag), @tagName(state_tag),
)) { )) {
return comptime @field( return comptime @field(
@field(world.globals, @tagName(module_tag)), @field(world.state, @tagName(module_tag)),
@tagName(global_tag), @tagName(state_tag),
); );
} }
/// Sets a global value called `.global_tag` in the module named `.module_tag` /// Sets a state value called `.state_tag` in the module named `.module_tag`
pub fn set( pub fn set(
world: *Self, world: *Self,
comptime module_tag: anytype, comptime module_tag: anytype,
comptime global_tag: anytype, comptime state_tag: anytype,
value: @TypeOf(@field( value: @TypeOf(@field(
@field(world.globals, @tagName(module_tag)), @field(world.state, @tagName(module_tag)),
@tagName(global_tag), @tagName(state_tag),
)), )),
) void { ) void {
comptime @field( comptime @field(
@field(world.globals, @tagName(module_tag)), @field(world.state, @tagName(module_tag)),
@tagName(global_tag), @tagName(state_tag),
) = value; ) = value;
} }
/// Broadcasts a global message to all modules that are subscribed to it. /// Broadcasts a state message to all modules that are subscribed to it.
pub fn send(world: *Self, comptime msg_tag: anytype) !void { pub fn send(world: *Self, comptime msg_tag: anytype) !void {
inline for (std.meta.fields(@TypeOf(modules))) |module_field| { inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
const module = @field(modules, module_field.name); const module = @field(modules, module_field.name);