diff --git a/zig-ecs/src/ecs/groups.zig b/zig-ecs/src/ecs/groups.zig index 95c30f7..8a76b43 100644 --- a/zig-ecs/src/ecs/groups.zig +++ b/zig-ecs/src/ecs/groups.zig @@ -73,37 +73,27 @@ pub fn BasicGroup(comptime n_includes: usize, comptime n_excludes: usize) type { }; } -pub fn OwningGroup(comptime n_owned: usize, comptime n_includes: usize, comptime n_excludes: usize) type { - return struct { - const Self = @This(); - - current: *usize, - super: *usize, - registry: *Registry, - owned_type_ids: [n_owned]u32, - include_type_ids: [n_includes]u32, - exclude_type_ids: [n_excludes]u32, - - pub fn init(super: *usize, current: *usize, registry: *Registry, owned_type_ids: [n_owned]u32, include_type_ids: [n_includes]u32, exclude_type_ids: [n_excludes]u32) Self { - return Self{ - .super = super, - .current = current, - .registry = registry, - .owned_type_ids = owned_type_ids, - .include_type_ids = include_type_ids, - .exclude_type_ids = exclude_type_ids, - }; - } +pub const OwningGroup = struct { + registry: *Registry, + group_data: *Registry.GroupData, + super: *usize, + + pub fn init(registry: *Registry, group_data: *Registry.GroupData, super: *usize) OwningGroup { + return .{ + .registry = registry, + .group_data = group_data, + .super = super, + }; + } - pub fn len(self: Self) usize { - return self.current.*; - } + pub fn len(self: OwningGroup) usize { + return self.group_data.current; + } - pub fn sortable(self: *Registry, comptime T: type) bool { - return self.super.* == n_owned + n_includes + n_excludes; - } - }; -} + pub fn sortable(self: OwningGroup, comptime T: type) bool { + return self.group_data.super == self.group_data.size; + } +}; test "BasicGroup creation" { var reg = Registry.init(std.testing.allocator); @@ -133,7 +123,7 @@ test "BasicGroup excludes" { var reg = Registry.init(std.testing.allocator); defer reg.deinit(); - var group = reg.group(.{}, .{ i32 }, .{ u32 }); + var group = reg.group(.{}, .{i32}, .{u32}); std.testing.expectEqual(group.len(), 0); var e0 = reg.create(); @@ -168,7 +158,7 @@ test "OwningGroup" { var reg = Registry.init(std.testing.allocator); defer reg.deinit(); - var group = reg.group(.{i32, u32}, .{}, .{}); + var group = reg.group(.{ i32, u32 }, .{}, .{}); var e0 = reg.create(); reg.add(e0, @as(i32, 44)); @@ -180,7 +170,7 @@ test "OwningGroup add/remove" { var reg = Registry.init(std.testing.allocator); defer reg.deinit(); - var group = reg.group(.{i32, u32}, .{}, .{}); + var group = reg.group(.{ i32, u32 }, .{}, .{}); var e0 = reg.create(); reg.add(e0, @as(i32, 44)); @@ -203,9 +193,9 @@ test "multiple OwningGroups" { // var group1 = reg.group(.{u64, u32}, .{}, .{}); // var group2 = reg.group(.{u64, u32, u8}, .{}, .{}); - var group5 = reg.group(.{Sprite, Transform}, .{Renderable, Rotation}, .{}); + var group5 = reg.group(.{ Sprite, Transform }, .{ Renderable, Rotation }, .{}); var group3 = reg.group(.{Sprite}, .{Renderable}, .{}); - var group4 = reg.group(.{Sprite, Transform}, .{Renderable}, .{}); + var group4 = reg.group(.{ Sprite, Transform }, .{Renderable}, .{}); var last_size: u8 = 0; for (reg.groups.items) |grp| { diff --git a/zig-ecs/src/ecs/registry.zig b/zig-ecs/src/ecs/registry.zig index d79cc60..3411c00 100644 --- a/zig-ecs/src/ecs/registry.zig +++ b/zig-ecs/src/ecs/registry.zig @@ -37,7 +37,7 @@ pub const Registry = struct { allocator: *std.mem.Allocator, /// internal, persistant data structure to manage the entities in a group - const GroupData = struct { + pub const GroupData = struct { hash: u64, size: u8, /// optional. there will be an entity_set for non-owning groups and current for owning @@ -485,9 +485,7 @@ pub const Registry = struct { comptime const hash = comptime hashGroupTypes(owned, includes, excludes); for (self.groups.items) |grp| { - // TODO: these checks rely on owned/include/exclude to all be in the same order. fix that. - // TODO: prolly dont need the mem.eql since hash is the same damn thing - if (grp.hash == hash and std.mem.eql(u32, grp.owned, owned_arr[0..]) and std.mem.eql(u32, grp.include, includes_arr[0..]) and std.mem.eql(u32, grp.exclude, excludes_arr[0..])) { + if (grp.hash == hash) { maybe_group_data = grp; break; } @@ -500,7 +498,7 @@ pub const Registry = struct { return BasicGroup(includes.len, excludes.len).init(&group_data.entity_set, self, includes_arr, excludes_arr); } else { var first_owned = self.assure(owned[0]); - return OwningGroup(owned.len, includes.len, excludes.len).init(&first_owned.super, &group_data.current, self, owned_arr, includes_arr, excludes_arr); + return OwningGroup.init(self, group_data, &first_owned.super); } } @@ -577,7 +575,7 @@ pub const Registry = struct { new_group_data.entity_set.add(entity); } } else { - // ??we cannot iterate backwards because we want to leave behind valid entities in case of owned types + // ??? why not? we cannot iterate backwards because we want to leave behind valid entities in case of owned types var first_owned_storage = self.assure(owned[0]); for (first_owned_storage.data().*) |entity| { new_group_data.maybeValidIf(entity); @@ -591,17 +589,18 @@ pub const Registry = struct { return BasicGroup(includes.len, excludes.len).init(&new_group_data.entity_set, self, includes_arr, excludes_arr); } else { var first_owned_storage = self.assure(owned[0]); - return OwningGroup(owned.len, includes.len, excludes.len).init(&first_owned_storage.super, &new_group_data.current, self, owned_arr, includes_arr, excludes_arr); + return OwningGroup.init(self, new_group_data, &first_owned_storage.super); } } - /// returns the Type that a view will be based on the includes and excludes + /// returns the Type that a view will be, based on the includes and excludes fn GroupType(comptime owned: var, comptime includes: var, comptime excludes: var) type { if (owned.len == 0) return BasicGroup(includes.len, excludes.len); - return OwningGroup(owned.len, includes.len, excludes.len); + return OwningGroup; } - /// given the 3 group Types arrays, generates a (mostly) unique u64 hash. Simultaneously ensures there are no duped types. + /// given the 3 group Types arrays, generates a (mostly) unique u64 hash. Simultaneously ensures there are no duped types between + /// the 3 groups. inline fn hashGroupTypes(comptime owned: var, comptime includes: var, comptime excludes: var) u64 { comptime { for (owned) |t1| { @@ -623,10 +622,24 @@ pub const Registry = struct { } } + /// expects a tuple of types. Convertes them to type names, sorts them then concatenates and returns the string. inline fn concatTypes(comptime types: var) []const u8 { comptime { + const impl = struct { + fn asc(lhs: []const u8, rhs: []const u8) bool { + return std.mem.lessThan(u8, lhs, rhs); + } + }; + + var names: [types.len][]const u8 = undefined; + for (names) |*name, i| { + name.* = @typeName(types[i]); + } + + std.sort.sort([]const u8, &names, impl.asc); + comptime var res: []const u8 = ""; - inline for (types) |t| res = res ++ @typeName(t); + inline for (names) |name| res = res ++ name; return res; } } diff --git a/zig-ecs/tests/groups_test.zig b/zig-ecs/tests/groups_test.zig index ced5870..fda7110 100644 --- a/zig-ecs/tests/groups_test.zig +++ b/zig-ecs/tests/groups_test.zig @@ -72,5 +72,5 @@ test "nested OwningGroups entity order" { printStore(sprite_store, "Sprite"); printStore(transform_store, "Transform"); - warn("group2.current: {}\n", .{group2.current.*}); + warn("group2.current: {}\n", .{group2.group_data.current}); }