BasicGroup mostly working
This commit is contained in:
parent
db332e82d9
commit
3dc2e33461
@ -3,35 +3,96 @@ const utils = @import("utils.zig");
|
||||
|
||||
const Registry = @import("registry.zig").Registry;
|
||||
const Storage = @import("registry.zig").Storage;
|
||||
const SparseSet = @import("sparse_set.zig").SparseSet;
|
||||
const Entity = @import("registry.zig").Entity;
|
||||
|
||||
pub fn NonOwningGroup(comptime n_includes: usize, comptime n_excludes: usize) type {
|
||||
/// BasicGroups do not own any components
|
||||
pub fn BasicGroup(comptime n_includes: usize, comptime n_excludes: usize) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
entity_set: *SparseSet(Entity, u16),
|
||||
registry: *Registry,
|
||||
type_ids: [n_includes]u32,
|
||||
exclude_type_ids: [n_excludes]u32,
|
||||
|
||||
pub fn init(registry: *Registry, type_ids: [n_includes]u32, exclude_type_ids: [n_excludes]u32) Self {
|
||||
pub const Iterator = struct {
|
||||
group: *Self,
|
||||
index: usize = 0,
|
||||
entities: *const []Entity,
|
||||
|
||||
pub fn init(group: *Self) Iterator {
|
||||
return .{
|
||||
.group = group,
|
||||
.entities = group.entity_set.data(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn next(it: *Iterator) ?Entity {
|
||||
if (it.index >= it.entities.len) return null;
|
||||
|
||||
it.index += 1;
|
||||
return it.entities.*[it.index - 1];
|
||||
}
|
||||
|
||||
// Reset the iterator to the initial index
|
||||
pub fn reset(it: *Iterator) void {
|
||||
it.index = 0;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(entity_set: *SparseSet(Entity, u16), registry: *Registry, type_ids: [n_includes]u32, exclude_type_ids: [n_excludes]u32) Self {
|
||||
return Self{
|
||||
.entity_set = entity_set,
|
||||
.registry = registry,
|
||||
.type_ids = type_ids,
|
||||
.exclude_type_ids = exclude_type_ids,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn len(self: Self) usize {
|
||||
return self.entity_set.len();
|
||||
}
|
||||
|
||||
/// Direct access to the array of entities
|
||||
pub fn data(self: Self) *const []Entity {
|
||||
return self.entity_set.data();
|
||||
}
|
||||
|
||||
pub fn get(self: *Self, comptime T: type, entity: Entity) *T {
|
||||
return self.registry.assure(T).get(entity);
|
||||
}
|
||||
|
||||
pub fn getConst(self: *Self, comptime T: type, entity: Entity) T {
|
||||
return self.registry.assure(T).getConst(entity);
|
||||
}
|
||||
|
||||
pub fn iterator(self: *Self) Iterator {
|
||||
return Iterator.init(self);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "group creation" {
|
||||
test "BasicGroup creation" {
|
||||
var reg = Registry.init(std.testing.allocator);
|
||||
defer reg.deinit();
|
||||
|
||||
var e0 = reg.create();
|
||||
reg.add(e0, @as(i32, -0));
|
||||
reg.add(e0, @as(u32, 0));
|
||||
var group = reg.group(.{}, .{ i32, u32 }, .{});
|
||||
std.testing.expectEqual(group.len(), 0);
|
||||
|
||||
var group = reg.group(.{}, .{i32}, .{});
|
||||
var group2 = reg.group(.{}, .{u32}, .{});
|
||||
var group23 = reg.group(.{}, .{i32}, .{});
|
||||
}
|
||||
var e0 = reg.create();
|
||||
reg.add(e0, @as(i32, 44));
|
||||
reg.add(e0, @as(u32, 55));
|
||||
|
||||
std.debug.assert(group.len() == 1);
|
||||
|
||||
var iterated_entities: usize = 0;
|
||||
var iter = group.iterator();
|
||||
while (iter.next()) |entity| {
|
||||
iterated_entities += 1;
|
||||
}
|
||||
std.testing.expectEqual(iterated_entities, 1);
|
||||
|
||||
reg.remove(i32, e0);
|
||||
std.debug.assert(group.len() == 0);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ const Handles = @import("handles.zig").Handles;
|
||||
const SparseSet = @import("sparse_set.zig").SparseSet;
|
||||
const TypeMap = @import("type_map.zig").TypeMap;
|
||||
const ComponentStorage = @import("component_storage.zig").ComponentStorage;
|
||||
const Sink = @import("../signals/sink.zig").Sink;
|
||||
|
||||
// allow overriding EntityTraits by setting in root via: EntityTraits = EntityTraitsType(.medium);
|
||||
const root = @import("root");
|
||||
@ -16,8 +17,8 @@ const EntityHandles = Handles(entity_traits.entity_type, entity_traits.index_typ
|
||||
pub const Entity = entity_traits.entity_type;
|
||||
|
||||
pub const BasicView = @import("views.zig").BasicView;
|
||||
pub const BasicMultiView = @import("views.zig").BasicMultiView;
|
||||
pub const NonOwningGroup = @import("groups.zig").NonOwningGroup;
|
||||
pub const MultiView = @import("views.zig").MultiView;
|
||||
pub const BasicGroup = @import("groups.zig").BasicGroup;
|
||||
|
||||
/// 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 {
|
||||
@ -32,28 +33,29 @@ pub const Registry = struct {
|
||||
handles: EntityHandles,
|
||||
components: std.AutoHashMap(u8, usize),
|
||||
contexts: std.AutoHashMap(u8, usize),
|
||||
groups: std.ArrayList(GroupData),
|
||||
groups: std.ArrayList(*GroupData),
|
||||
allocator: *std.mem.Allocator,
|
||||
|
||||
const GroupData = struct {
|
||||
hash: u32,
|
||||
entity_set: SparseSet(Entity, u16) = undefined,
|
||||
entity_set: SparseSet(Entity, u16), // TODO: dont hardcode this. put it in EntityTraits maybe. All SparseSets would need to use the value.
|
||||
owned: []u32,
|
||||
include: []u32,
|
||||
exclude: []u32,
|
||||
registry: *Registry,
|
||||
|
||||
pub fn init(allocator: *std.mem.Allocator, registry: *Registry, hash: u32, owned: []u32, include: []u32, exclude: []u32) GroupData {
|
||||
pub fn initPtr(allocator: *std.mem.Allocator, registry: *Registry, hash: u32, owned: []u32, include: []u32, exclude: []u32) *GroupData {
|
||||
std.debug.assert(std.mem.indexOfAny(u32, owned, include) == null);
|
||||
std.debug.assert(std.mem.indexOfAny(u32, owned, exclude) == null);
|
||||
std.debug.assert(std.mem.indexOfAny(u32, include, exclude) == null);
|
||||
|
||||
const group_data = GroupData{
|
||||
.hash = hash,
|
||||
.entity_set = SparseSet(Entity, u16).init(allocator),
|
||||
.owned = std.mem.dupe(allocator, u32, owned) catch unreachable,
|
||||
.include = std.mem.dupe(allocator, u32, include) catch unreachable,
|
||||
.exclude = std.mem.dupe(allocator, u32, exclude) catch unreachable,
|
||||
};
|
||||
var group_data = allocator.create(GroupData) catch unreachable;
|
||||
group_data.hash = hash;
|
||||
group_data.entity_set = SparseSet(Entity, u16).init(allocator);
|
||||
group_data.owned = std.mem.dupe(allocator, u32, owned) catch unreachable;
|
||||
group_data.include = std.mem.dupe(allocator, u32, include) catch unreachable;
|
||||
group_data.exclude = std.mem.dupe(allocator, u32, exclude) catch unreachable;
|
||||
group_data.registry = registry;
|
||||
|
||||
return group_data;
|
||||
}
|
||||
@ -63,10 +65,46 @@ pub const Registry = struct {
|
||||
allocator.free(self.owned);
|
||||
allocator.free(self.include);
|
||||
allocator.free(self.exclude);
|
||||
allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn hasSameConstraints(self: *GroupData, owned: []u32, include: []u32, exclude: []u32) bool {
|
||||
return std.mem.eql(u32, self.owned, owned) and std.mem.eql(u32, self.include, include) and std.mem.eql(u32, self.exclude, exclude);
|
||||
fn maybeValidIf(self: *GroupData, entity: Entity) void {
|
||||
const isValid: bool = blk: {
|
||||
for (self.owned) |tid| {
|
||||
const ptr = self.registry.components.getValue(@intCast(u8, tid)).?;
|
||||
if (!@intToPtr(*Storage(u1), ptr).contains(entity))
|
||||
break :blk false;
|
||||
}
|
||||
|
||||
for (self.include) |tid| {
|
||||
const ptr = self.registry.components.getValue(@intCast(u8, tid)).?;
|
||||
if (!@intToPtr(*Storage(u1), ptr).contains(entity))
|
||||
break :blk false;
|
||||
}
|
||||
|
||||
for (self.exclude) |tid| {
|
||||
const ptr = self.registry.components.getValue(@intCast(u8, tid)).?;
|
||||
if (@intToPtr(*Storage(u1), ptr).contains(entity))
|
||||
break :blk false;
|
||||
}
|
||||
break :blk true;
|
||||
};
|
||||
|
||||
if (self.owned.len == 0) {
|
||||
if (isValid and !self.entity_set.contains(entity))
|
||||
self.entity_set.add(entity);
|
||||
} else {
|
||||
std.debug.assert(self.owned.len >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn discardIf(self: *GroupData, entity: Entity) void {
|
||||
if (self.owned.len == 0) {
|
||||
if (self.entity_set.contains(entity))
|
||||
self.entity_set.remove(entity);
|
||||
} else {
|
||||
std.debug.assert(self.owned.len == 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -76,7 +114,7 @@ pub const Registry = struct {
|
||||
.handles = EntityHandles.init(allocator),
|
||||
.components = std.AutoHashMap(u8, usize).init(allocator),
|
||||
.contexts = std.AutoHashMap(u8, usize).init(allocator),
|
||||
.groups = std.ArrayList(GroupData).init(allocator),
|
||||
.groups = std.ArrayList(*GroupData).init(allocator),
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
@ -89,7 +127,7 @@ pub const Registry = struct {
|
||||
storage.deinit();
|
||||
}
|
||||
|
||||
for (self.groups.items) |*grp| {
|
||||
for (self.groups.items) |grp| {
|
||||
grp.deinit(self.allocator);
|
||||
}
|
||||
|
||||
@ -253,17 +291,17 @@ pub const Registry = struct {
|
||||
}
|
||||
|
||||
/// Returns a Sink object for the given component to add/remove listeners with
|
||||
pub fn onConstruct(self: *Self, comptime T: type) Sink(Entity) {
|
||||
pub fn onConstruct(self: *Registry, comptime T: type) Sink(Entity) {
|
||||
return self.assure(T).onConstruct();
|
||||
}
|
||||
|
||||
/// Returns a Sink object for the given component to add/remove listeners with
|
||||
pub fn onUpdate(self: *Self, comptime T: type) Sink(Entity) {
|
||||
pub fn onUpdate(self: *Registry, comptime T: type) Sink(Entity) {
|
||||
return self.assure(T).onUpdate();
|
||||
}
|
||||
|
||||
/// Returns a Sink object for the given component to add/remove listeners with
|
||||
pub fn onDestruct(self: *Self, comptime T: type) Sink(Entity) {
|
||||
pub fn onDestruct(self: *Registry, comptime T: type) Sink(Entity) {
|
||||
return self.assure(T).onDestruct();
|
||||
}
|
||||
|
||||
@ -319,13 +357,13 @@ pub const Registry = struct {
|
||||
excludes_arr[i] = @as(u32, self.typemap.get(t));
|
||||
}
|
||||
|
||||
return BasicMultiView(includes.len, excludes.len).init(self, includes_arr, excludes_arr);
|
||||
return MultiView(includes.len, excludes.len).init(self, includes_arr, excludes_arr);
|
||||
}
|
||||
|
||||
/// returns the Type that a view will be based on the includes and excludes
|
||||
fn ViewType(comptime includes: var, comptime excludes: var) type {
|
||||
if (includes.len == 1 and excludes.len == 0) return BasicView(includes[0]);
|
||||
return BasicMultiView(includes.len, excludes.len);
|
||||
return MultiView(includes.len, excludes.len);
|
||||
}
|
||||
|
||||
pub fn group(self: *Registry, comptime owned: var, comptime includes: var, comptime excludes: var) GroupType(owned, includes, excludes) {
|
||||
@ -335,8 +373,8 @@ pub const Registry = struct {
|
||||
@compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(includes)));
|
||||
if (@typeInfo(@TypeOf(excludes)) != .Struct)
|
||||
@compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(excludes)));
|
||||
std.debug.assert(includes.len + owned.len > 0);
|
||||
std.debug.assert(includes.len + owned.len + excludes.len >= 1);
|
||||
std.debug.assert(owned.len + includes.len > 0);
|
||||
std.debug.assert(owned.len + includes.len + excludes.len > 1);
|
||||
|
||||
var owned_arr: [owned.len]u32 = undefined;
|
||||
inline for (owned) |t, i| {
|
||||
@ -357,35 +395,45 @@ pub const Registry = struct {
|
||||
}
|
||||
|
||||
// create a unique hash to identify the group
|
||||
var group_data: ?*GroupData = null;
|
||||
var maybe_group_data: ?*GroupData = null;
|
||||
comptime const hash = owned.len + (31 * includes.len) + (31 * 31 * excludes.len);
|
||||
|
||||
for (self.groups.items) |*grp| {
|
||||
if (grp.hash == hash and grp.hasSameConstraints(owned_arr[0..], includes_arr[0..], excludes_arr[0..])) {
|
||||
group_data = grp;
|
||||
for (self.groups.items) |grp| {
|
||||
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..])) {
|
||||
maybe_group_data = grp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// non-owning groups
|
||||
if (owned.len == 0) {
|
||||
if (group_data != null) {
|
||||
return NonOwningGroup(includes.len, excludes.len).init(self, includes_arr, excludes_arr);
|
||||
// do we already have the GroupData?
|
||||
if (maybe_group_data) |group_data| {
|
||||
// non-owning groups
|
||||
if (owned.len == 0) {
|
||||
return BasicGroup(includes.len, excludes.len).init(&group_data.entity_set, self, includes_arr, excludes_arr);
|
||||
} else {
|
||||
@compileLog("owned groups not implemented");
|
||||
}
|
||||
|
||||
var new_group_data = GroupData.init(self.allocator, self, hash, &[_]u32{}, includes_arr[0..], excludes_arr[0..]);
|
||||
new_group_data.entity_set.reserve(5);
|
||||
self.groups.append(new_group_data) catch unreachable;
|
||||
return NonOwningGroup(includes.len, excludes.len).init(self, includes_arr, excludes_arr);
|
||||
}
|
||||
|
||||
@compileLog("owned groups not implemented");
|
||||
// we need to create a new GroupData
|
||||
var new_group_data = GroupData.initPtr(self.allocator, self, hash, &[_]u32{}, includes_arr[0..], excludes_arr[0..]);
|
||||
|
||||
// wire up our listeners
|
||||
inline for (owned) |t| self.onConstruct(t).connectBound(new_group_data, "maybeValidIf");
|
||||
inline for (includes) |t| self.onConstruct(t).connectBound(new_group_data, "maybeValidIf");
|
||||
inline for (excludes) |t| self.onDestruct(t).connectBound(new_group_data, "maybeValidIf");
|
||||
|
||||
inline for (owned) |t| self.onDestruct(t).connectBound(new_group_data, "discardIf");
|
||||
inline for (includes) |t| self.onDestruct(t).connectBound(new_group_data, "discardIf");
|
||||
inline for (excludes) |t| self.onConstruct(t).connectBound(new_group_data, "discardIf");
|
||||
|
||||
self.groups.append(new_group_data) catch unreachable;
|
||||
return BasicGroup(includes.len, excludes.len).init(&new_group_data.entity_set, self, includes_arr, excludes_arr);
|
||||
}
|
||||
|
||||
/// 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 NonOwningGroup(includes.len, excludes.len);
|
||||
if (owned.len == 0) return BasicGroup(includes.len, excludes.len);
|
||||
unreachable;
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user