module: merge ecs/systems.zig -> module.zig
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
e847d46ff9
commit
0328598945
5 changed files with 261 additions and 294 deletions
|
|
@ -19,7 +19,7 @@ pub const EntityID = @import("entities.zig").EntityID;
|
|||
pub const Entities = @import("entities.zig").Entities;
|
||||
pub const Archetype = @import("Archetype.zig");
|
||||
|
||||
pub const World = @import("systems.zig").World;
|
||||
pub const Modules = @import("../module.zig").Modules;
|
||||
|
||||
// TODO:
|
||||
// * Iteration
|
||||
|
|
@ -28,13 +28,12 @@ pub const World = @import("systems.zig").World;
|
|||
// * Multiple entities having one value
|
||||
// * Sparse storage?
|
||||
|
||||
test "inclusion" {
|
||||
test {
|
||||
std.testing.refAllDeclsRecursive(@This());
|
||||
std.testing.refAllDeclsRecursive(@import("Archetype.zig"));
|
||||
std.testing.refAllDeclsRecursive(@import("entities.zig"));
|
||||
std.testing.refAllDeclsRecursive(@import("query.zig"));
|
||||
std.testing.refAllDeclsRecursive(@import("StringTable.zig"));
|
||||
std.testing.refAllDeclsRecursive(@import("systems.zig"));
|
||||
}
|
||||
|
||||
test "example" {
|
||||
|
|
@ -54,7 +53,7 @@ test "example" {
|
|||
.{ .global = .tick, .handler = tick },
|
||||
};
|
||||
|
||||
fn tick(physics: *World(modules).Mod(Physics)) void {
|
||||
fn tick(physics: *Modules(modules).Mod(Physics)) void {
|
||||
_ = physics;
|
||||
}
|
||||
};
|
||||
|
|
@ -69,8 +68,8 @@ test "example" {
|
|||
};
|
||||
|
||||
fn tick(
|
||||
physics: *World(modules).Mod(Physics),
|
||||
renderer: *World(modules).Mod(Renderer),
|
||||
physics: *Modules(modules).Mod(Physics),
|
||||
renderer: *Modules(modules).Mod(Renderer),
|
||||
) void {
|
||||
_ = renderer;
|
||||
_ = physics;
|
||||
|
|
@ -80,9 +79,15 @@ test "example" {
|
|||
|
||||
//-------------------------------------------------------------------------
|
||||
// Create a world.
|
||||
var world: World(root.modules) = undefined;
|
||||
var world: Modules(root.modules) = undefined;
|
||||
try world.init(allocator);
|
||||
defer world.deinit();
|
||||
defer world.deinit(allocator);
|
||||
|
||||
// TODO: better module initialization location
|
||||
world.mod.physics.entities = &world.entities;
|
||||
world.mod.physics.allocator = world.entities.allocator;
|
||||
world.mod.renderer.entities = &world.entities;
|
||||
world.mod.renderer.allocator = world.entities.allocator;
|
||||
|
||||
// Initialize module state.
|
||||
var physics = &world.mod.physics;
|
||||
|
|
|
|||
|
|
@ -1,212 +0,0 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const StructField = std.builtin.Type.StructField;
|
||||
|
||||
const mach = @import("../main.zig");
|
||||
const Entities = @import("entities.zig").Entities;
|
||||
const EntityID = @import("entities.zig").EntityID;
|
||||
const comp = @import("comptime.zig");
|
||||
const Module = @import("../module.zig").Module;
|
||||
const NamespacedComponents = @import("../module.zig").NamespacedComponents;
|
||||
const MComponents = @import("../module.zig").MComponents;
|
||||
|
||||
pub fn World(comptime mods: anytype) type {
|
||||
const StateT = NamespacedState(mods);
|
||||
return struct {
|
||||
allocator: mem.Allocator,
|
||||
entities: Entities(NamespacedComponents(mods){}),
|
||||
modules: Modules,
|
||||
mod: Mods,
|
||||
|
||||
const Modules = mach.Modules(mods);
|
||||
|
||||
const WorldT = @This();
|
||||
pub fn Mod(comptime M: anytype) type {
|
||||
const module_tag = M.name;
|
||||
const State = @TypeOf(@field(@as(StateT, undefined), @tagName(module_tag)));
|
||||
const components = MComponents(M){};
|
||||
return struct {
|
||||
state: State,
|
||||
entities: *Entities(NamespacedComponents(mods){}),
|
||||
allocator: mem.Allocator,
|
||||
|
||||
pub const IsInjectedArgument = void;
|
||||
|
||||
/// Sets the named component to the specified value for the given entity,
|
||||
/// moving the entity from it's current archetype table to the new archetype
|
||||
/// table if required.
|
||||
pub inline fn set(
|
||||
m: *@This(),
|
||||
entity: EntityID,
|
||||
// TODO: cleanup comptime
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(components)),
|
||||
component: @field(components, @tagName(component_name)).type,
|
||||
) !void {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
try world.entities.setComponent(entity, module_tag, component_name, component);
|
||||
}
|
||||
|
||||
/// gets the named component of the given type (which must be correct, otherwise undefined
|
||||
/// behavior will occur). Returns null if the component does not exist on the entity.
|
||||
pub inline fn get(
|
||||
m: *@This(),
|
||||
entity: EntityID,
|
||||
// TODO: cleanup comptime
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(components)),
|
||||
) ?@field(components, @tagName(component_name)).type {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
return world.entities.getComponent(entity, module_tag, component_name);
|
||||
}
|
||||
|
||||
/// Removes the named component from the entity, or noop if it doesn't have such a component.
|
||||
pub inline fn remove(
|
||||
m: *@This(),
|
||||
entity: EntityID,
|
||||
// TODO: cleanup comptime
|
||||
comptime component_name: std.meta.FieldEnum(@TypeOf(components)),
|
||||
) !void {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
try world.entities.removeComponent(entity, module_tag, component_name);
|
||||
}
|
||||
|
||||
pub inline fn send(m: *@This(), comptime event_name: Modules.LocalEvent, args: Modules.LocalArgsM(M, event_name)) void {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
world.modules.sendToModule(module_tag, event_name, args);
|
||||
}
|
||||
|
||||
pub inline fn sendGlobal(m: *@This(), comptime event_name: Modules.GlobalEvent, args: Modules.GlobalArgsM(M, event_name)) void {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
world.modules.sendGlobal(module_tag, event_name, args);
|
||||
}
|
||||
|
||||
// TODO: eliminate this
|
||||
pub fn dispatchNoError(m: *@This()) void {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
world.modules.dispatch(world.injectable()) catch |err| @panic(@errorName(err));
|
||||
}
|
||||
|
||||
/// Returns a new entity.
|
||||
pub fn newEntity(m: *@This()) !EntityID {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
return world.entities.new();
|
||||
}
|
||||
|
||||
/// Removes an entity.
|
||||
pub fn removeEntity(m: *@This(), entity: EntityID) !void {
|
||||
const mod_ptr: *Mods = @alignCast(@fieldParentPtr(Mods, @tagName(module_tag), m));
|
||||
const world = @fieldParentPtr(WorldT, "mod", mod_ptr);
|
||||
try world.entities.removeEntity(entity);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Mods = blk: {
|
||||
var fields: []const StructField = &[0]StructField{};
|
||||
for (mods) |M| {
|
||||
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||
.name = @tagName(M.name),
|
||||
.type = Mod(M),
|
||||
.default_value = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(Mod(M)),
|
||||
}};
|
||||
}
|
||||
break :blk @Type(.{
|
||||
.Struct = .{
|
||||
.layout = .Auto,
|
||||
.is_tuple = false,
|
||||
.fields = fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const Injectable = blk: {
|
||||
var types: []const type = &[0]type{};
|
||||
for (@typeInfo(Mods).Struct.fields) |field| {
|
||||
const ModPtr = @TypeOf(@as(*field.type, undefined));
|
||||
types = types ++ [_]type{ModPtr};
|
||||
}
|
||||
break :blk std.meta.Tuple(types);
|
||||
};
|
||||
fn injectable(world: *@This()) Injectable {
|
||||
var v: Injectable = undefined;
|
||||
outer: inline for (@typeInfo(Injectable).Struct.fields) |field| {
|
||||
inline for (@typeInfo(Mods).Struct.fields) |injectable_field| {
|
||||
if (*injectable_field.type == field.type) {
|
||||
@field(v, field.name) = &@field(world.mod, injectable_field.name);
|
||||
|
||||
// TODO: better module initialization location
|
||||
@field(v, field.name).entities = &world.entities;
|
||||
@field(v, field.name).allocator = world.allocator;
|
||||
continue :outer;
|
||||
}
|
||||
}
|
||||
@compileError("failed to initialize Injectable field (this is a bug): " ++ field.name ++ " " ++ @typeName(field.type));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
pub fn dispatch(world: *@This()) !void {
|
||||
try world.modules.dispatch(world.injectable());
|
||||
}
|
||||
|
||||
pub fn init(world: *@This(), allocator: mem.Allocator) !void {
|
||||
// TODO: switch Entities to stack allocation like Modules and World
|
||||
var entities = try Entities(NamespacedComponents(mods){}).init(allocator);
|
||||
errdefer entities.deinit();
|
||||
world.* = @This(){
|
||||
.allocator = allocator,
|
||||
.entities = entities,
|
||||
.modules = undefined,
|
||||
.mod = undefined,
|
||||
};
|
||||
try world.modules.init(allocator);
|
||||
}
|
||||
|
||||
pub fn deinit(world: *@This()) void {
|
||||
world.entities.deinit();
|
||||
world.modules.deinit(world.allocator);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: reconsider state concept
|
||||
fn NamespacedState(comptime modules: anytype) type {
|
||||
var fields: []const std.builtin.Type.StructField = &[0]std.builtin.Type.StructField{};
|
||||
inline for (modules) |M| {
|
||||
// TODO: can't verify module here because it would introduce a dependency loop
|
||||
// _ = Module(M);
|
||||
const state_fields = std.meta.fields(M);
|
||||
const State = if (state_fields.len > 0) @Type(.{
|
||||
.Struct = .{
|
||||
.layout = .Auto,
|
||||
.is_tuple = false,
|
||||
.fields = state_fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
},
|
||||
}) else struct {};
|
||||
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||
.name = @tagName(M.name),
|
||||
.type = State,
|
||||
.default_value = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(State),
|
||||
}};
|
||||
}
|
||||
return @Type(.{
|
||||
.Struct = .{
|
||||
.layout = .Auto,
|
||||
.is_tuple = false,
|
||||
.fields = fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
},
|
||||
});
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue