diff --git a/zig-ecs/src/ecs/registry.zig b/zig-ecs/src/ecs/registry.zig index 2212c33..0b16b44 100644 --- a/zig-ecs/src/ecs/registry.zig +++ b/zig-ecs/src/ecs/registry.zig @@ -6,6 +6,7 @@ const Handles = @import("handles.zig").Handles; const SparseSet = @import("sparse_set.zig").SparseSet; const ComponentStorage = @import("component_storage.zig").ComponentStorage; const Sink = @import("../signals/sink.zig").Sink; +const TypeStore = @import("type_store.zig").TypeStore; // allow overriding EntityTraits by setting in root via: EntityTraits = EntityTraitsType(.medium); const root = @import("root"); @@ -15,10 +16,10 @@ const entity_traits = if (@hasDecl(root, "EntityTraits")) root.EntityTraits.init const EntityHandles = Handles(entity_traits.entity_type, entity_traits.index_type, entity_traits.version_type); pub const Entity = entity_traits.entity_type; -pub const BasicView = @import("views.zig").BasicView; -pub const MultiView = @import("views.zig").MultiView; -pub const BasicGroup = @import("groups.zig").BasicGroup; -pub const OwningGroup = @import("groups.zig").OwningGroup; +const BasicView = @import("views.zig").BasicView; +const MultiView = @import("views.zig").MultiView; +const BasicGroup = @import("groups.zig").BasicGroup; +const OwningGroup = @import("groups.zig").OwningGroup; /// Stores an ArrayList of components. The max amount that can be stored is based on the type below pub fn Storage(comptime CompT: type) type { @@ -33,6 +34,7 @@ pub const Registry = struct { components: std.AutoHashMap(u32, usize), contexts: std.AutoHashMap(u32, usize), groups: std.ArrayList(*GroupData), + singletons: TypeStore, allocator: *std.mem.Allocator, /// internal, persistant data structure to manage the entities in a group @@ -141,6 +143,7 @@ pub const Registry = struct { .components = std.AutoHashMap(u32, usize).init(allocator), .contexts = std.AutoHashMap(u32, usize).init(allocator), .groups = std.ArrayList(*GroupData).init(allocator), + .singletons = TypeStore.init(allocator), .allocator = allocator, }; } @@ -160,6 +163,7 @@ pub const Registry = struct { self.components.deinit(); self.contexts.deinit(); self.groups.deinit(); + self.singletons.deinit(); self.handles.deinit(); } @@ -303,7 +307,7 @@ pub const Registry = struct { return self.assure(T).getConst(entity); } - /// Returns a reference to the given component for an entity + /// Returns a reference to the given component for an entity creating it if necessary pub fn getOrAdd(self: *Registry, comptime T: type, entity: Entity) *T { if (self.has(T, entity)) return self.get(T, entity); self.add(T, entity, std.mem.zeros(T)); @@ -353,6 +357,11 @@ pub const Registry = struct { null; } + /// provides access to a TypeStore letting you add singleton components to the registry + pub fn singletons(self: Registry) TypeStore { + return self.singletons; + } + /// Checks whether the given component belongs to any group pub fn sortable(self: Registry, comptime T: type) bool { return true; diff --git a/zig-ecs/src/ecs/type_store.zig b/zig-ecs/src/ecs/type_store.zig new file mode 100644 index 0000000..9a52341 --- /dev/null +++ b/zig-ecs/src/ecs/type_store.zig @@ -0,0 +1,89 @@ +const std = @import("std"); +const utils = @import("utils.zig"); + +/// stores a single object of type T for each T added +pub const TypeStore = struct { + map: std.AutoHashMap(u32, []u8), + allocator: *std.mem.Allocator, + + pub fn init(allocator: *std.mem.Allocator) TypeStore { + return TypeStore{ + .map = std.AutoHashMap(u32, []u8).init(allocator), + .allocator = allocator, + }; + } + + pub fn deinit(self: TypeStore) void { + var iter = self.map.iterator(); + while (iter.next()) |kv| { + self.allocator.free(kv.value); + } + self.map.deinit(); + } + + /// adds instance, returning a pointer to the item as it lives in the store + pub fn add(self: *TypeStore, instance: var) *@TypeOf(instance) { + var bytes = self.allocator.alloc(u8, @sizeOf(@TypeOf(instance))) catch unreachable; + var ptr = @ptrCast(*@TypeOf(instance), @alignCast(@alignOf(@TypeOf(instance)), bytes)); + ptr.* = instance; + _ = self.map.put(utils.typeId(@TypeOf(instance)), bytes) catch unreachable; + return ptr; + } + + pub fn get(self: *TypeStore, comptime T: type) *T { + if (self.map.getValue(utils.typeId(T))) |bytes| { + return @ptrCast(*T, @alignCast(@alignOf(T), bytes)); + } + unreachable; + } + + pub fn getConst(self: *TypeStore, comptime T: type) T { + return self.get(T).*; + } + + pub fn getOrAdd(self: *TypeStore, comptime T: type) *T { + if (self.has(T)) return self.get(T); + var instance = std.mem.zeroes(T); + return self.add(instance); + } + + pub fn remove(self: *TypeStore, comptime T: type) void { + if (self.map.getValue(utils.typeId(T))) |bytes| { + self.allocator.free(bytes); + _ = self.map.remove(utils.typeId(T)); + } + } + + pub fn has(self: *TypeStore, comptime T: type) bool { + return self.map.contains(utils.typeId(T)); + } +}; + +test "TypeStore" { + const Vector = struct { x: f32 = 0, y: f32 = 0, z: f32 = 0}; + + var store = TypeStore.init(std.testing.allocator); + defer store.deinit(); + + var orig = Vector{.x = 5, .y = 6, .z = 8}; + var inserted = store.add(orig); + std.testing.expect(store.has(Vector)); + std.testing.expectEqual(inserted.*, Vector{.x = 5, .y = 6, .z = 8}); + + var v = store.get(Vector); + std.testing.expectEqual(v.*, Vector{.x = 5, .y = 6, .z = 8}); + v.*.x = 666; + + var v2 = store.get(Vector); + std.testing.expectEqual(v2.*, Vector{.x = 666, .y = 6, .z = 8}); + + store.remove(Vector); + std.testing.expect(!store.has(Vector)); + + var v3 = store.getOrAdd(u32); + std.testing.expectEqual(v3.*, 0); + v3.* = 777; + + var v4 = store.get(u32); + std.testing.expectEqual(v3.*, 777); +} \ No newline at end of file diff --git a/zig-ecs/src/tests.zig b/zig-ecs/src/tests.zig index a6d9de4..5d91e6d 100644 --- a/zig-ecs/src/tests.zig +++ b/zig-ecs/src/tests.zig @@ -8,6 +8,7 @@ comptime { _ = @import("ecs/sparse_set.zig"); _ = @import("ecs/views.zig"); _ = @import("ecs/groups.zig"); + _ = @import("ecs/type_store.zig"); // signals _ = @import("signals/delegate.zig"); diff --git a/zig-ecs/tests/registry_test.zig b/zig-ecs/tests/registry_test.zig index 2488772..bec5c88 100644 --- a/zig-ecs/tests/registry_test.zig +++ b/zig-ecs/tests/registry_test.zig @@ -53,7 +53,7 @@ test "context not pointer" { // reg.setContext(pos); } -test "component context get/set/unset" { +test "context get/set/unset" { const SomeType = struct { dummy: u1 }; var reg = Registry.init(std.testing.allocator); @@ -72,6 +72,19 @@ test "component context get/set/unset" { std.testing.expectEqual(ctx, null); } +test "singletons" { + var reg = Registry.init(std.testing.allocator); + defer reg.deinit(); + + var pos = Position{ .x = 5, .y = 5 }; + var inserted = reg.singletons.add(pos); + std.testing.expect(reg.singletons.has(Position)); + std.testing.expectEqual(inserted.*, pos); + + reg.singletons.remove(Position); + std.testing.expect(!reg.singletons.has(Position)); +} + test "destroy" { var reg = Registry.init(std.testing.allocator); defer reg.deinit();