ecs: add support for module global values

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2022-06-25 13:43:42 -07:00 committed by Stephen Gutekanst
parent 3fddb687bc
commit f74faf90df
2 changed files with 94 additions and 0 deletions

View file

@ -53,6 +53,9 @@ test "example" {
.components = .{ .components = .{
.id = u16, .id = u16,
}, },
.globals = struct{
pointer: u8,
},
}), }),
.geometry = Module(.{ .geometry = Module(.{
.components = .{ .components = .{
@ -66,6 +69,10 @@ test "example" {
var world = try World(modules).init(allocator); var world = try World(modules).init(allocator);
defer world.deinit(); defer world.deinit();
// Initialize globals.
world.set(.physics, .pointer, 123);
_ = world.get(.physics, .pointer); // == 123
const player1 = try world.entities.new(); const player1 = try world.entities.new();
const player2 = try world.entities.new(); const player2 = try world.entities.new();
const player3 = try world.entities.new(); const player3 = try world.entities.new();

View file

@ -106,12 +106,71 @@ fn namespacedComponents(comptime modules: anytype) NamespacedComponents(modules)
return x; return x;
} }
/// Extracts namespaced globals from modules like this:
///
/// ```
/// .{
/// .renderer = .{
/// .globals = struct{
/// foo: *Bar,
/// baz: Bam,
/// },
/// ...
/// },
/// .physics2d = .{
/// .globals = struct{
/// foo: *Instance,
/// },
/// ...
/// },
/// }
/// ```
///
/// Into a namespaced global type like this:
///
/// ```
/// struct{
/// renderer: struct{
/// foo: *Bar,
/// baz: Bam,
/// },
/// physics2d: struct{
/// foo: *Instance,
/// },
/// }
/// ```
///
fn NamespacedGlobals(comptime modules: anytype) type {
var fields: []const StructField = &[0]StructField{};
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
const module = @field(modules, module_field.name);
if (@hasField(@TypeOf(module), "globals")) {
fields = fields ++ [_]std.builtin.Type.StructField{.{
.name = module_field.name,
.field_type = module.globals,
.default_value = null,
.is_comptime = false,
.alignment = @alignOf(module.globals),
}};
}
}
return @Type(.{
.Struct = .{
.layout = .Auto,
.is_tuple = false,
.fields = fields,
.decls = &[_]std.builtin.Type.Declaration{},
},
});
}
pub fn World(comptime modules: anytype) type { pub fn World(comptime modules: anytype) type {
const all_components = namespacedComponents(modules); const all_components = namespacedComponents(modules);
return struct { return struct {
allocator: Allocator, allocator: Allocator,
systems: std.StringArrayHashMapUnmanaged(System) = .{}, systems: std.StringArrayHashMapUnmanaged(System) = .{},
entities: Entities(all_components), entities: Entities(all_components),
globals: NamespacedGlobals(modules),
const Self = @This(); const Self = @This();
pub const System = fn (adapter: *Adapter(modules)) void; pub const System = fn (adapter: *Adapter(modules)) void;
@ -120,6 +179,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,
}; };
} }
@ -128,6 +188,33 @@ 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`
pub fn get(world: *Self, module_tag: anytype, global_tag: anytype) @TypeOf(@field(
@field(world.globals, @tagName(module_tag)),
@tagName(global_tag),
)) {
return comptime @field(
@field(world.globals, @tagName(module_tag)),
@tagName(global_tag),
);
}
/// Sets a global value called `.global_tag` in the module named `.module_tag`
pub fn set(
world: *Self,
comptime module_tag: anytype,
comptime global_tag: anytype,
value: @TypeOf(@field(
@field(world.globals, @tagName(module_tag)),
@tagName(global_tag),
)),
) void {
comptime @field(
@field(world.globals, @tagName(module_tag)),
@tagName(global_tag),
) = value;
}
pub fn register(world: *Self, name: []const u8, system: System) !void { pub fn register(world: *Self, name: []const u8, system: System) !void {
try world.systems.put(world.allocator, name, system); try world.systems.put(world.allocator, name, system);
} }