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 Registry = @import("registry.zig").Registry;
|
||||||
const Storage = @import("registry.zig").Storage;
|
const Storage = @import("registry.zig").Storage;
|
||||||
|
const SparseSet = @import("sparse_set.zig").SparseSet;
|
||||||
const Entity = @import("registry.zig").Entity;
|
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 {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
entity_set: *SparseSet(Entity, u16),
|
||||||
registry: *Registry,
|
registry: *Registry,
|
||||||
type_ids: [n_includes]u32,
|
type_ids: [n_includes]u32,
|
||||||
exclude_type_ids: [n_excludes]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{
|
return Self{
|
||||||
|
.entity_set = entity_set,
|
||||||
.registry = registry,
|
.registry = registry,
|
||||||
.type_ids = type_ids,
|
.type_ids = type_ids,
|
||||||
.exclude_type_ids = exclude_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);
|
var reg = Registry.init(std.testing.allocator);
|
||||||
defer reg.deinit();
|
defer reg.deinit();
|
||||||
|
|
||||||
var e0 = reg.create();
|
var group = reg.group(.{}, .{ i32, u32 }, .{});
|
||||||
reg.add(e0, @as(i32, -0));
|
std.testing.expectEqual(group.len(), 0);
|
||||||
reg.add(e0, @as(u32, 0));
|
|
||||||
|
|
||||||
var group = reg.group(.{}, .{i32}, .{});
|
var e0 = reg.create();
|
||||||
var group2 = reg.group(.{}, .{u32}, .{});
|
reg.add(e0, @as(i32, 44));
|
||||||
var group23 = reg.group(.{}, .{i32}, .{});
|
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 SparseSet = @import("sparse_set.zig").SparseSet;
|
||||||
const TypeMap = @import("type_map.zig").TypeMap;
|
const TypeMap = @import("type_map.zig").TypeMap;
|
||||||
const ComponentStorage = @import("component_storage.zig").ComponentStorage;
|
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);
|
// allow overriding EntityTraits by setting in root via: EntityTraits = EntityTraitsType(.medium);
|
||||||
const root = @import("root");
|
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 Entity = entity_traits.entity_type;
|
||||||
|
|
||||||
pub const BasicView = @import("views.zig").BasicView;
|
pub const BasicView = @import("views.zig").BasicView;
|
||||||
pub const BasicMultiView = @import("views.zig").BasicMultiView;
|
pub const MultiView = @import("views.zig").MultiView;
|
||||||
pub const NonOwningGroup = @import("groups.zig").NonOwningGroup;
|
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
|
/// 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 {
|
pub fn Storage(comptime CompT: type) type {
|
||||||
@ -32,28 +33,29 @@ pub const Registry = struct {
|
|||||||
handles: EntityHandles,
|
handles: EntityHandles,
|
||||||
components: std.AutoHashMap(u8, usize),
|
components: std.AutoHashMap(u8, usize),
|
||||||
contexts: std.AutoHashMap(u8, usize),
|
contexts: std.AutoHashMap(u8, usize),
|
||||||
groups: std.ArrayList(GroupData),
|
groups: std.ArrayList(*GroupData),
|
||||||
allocator: *std.mem.Allocator,
|
allocator: *std.mem.Allocator,
|
||||||
|
|
||||||
const GroupData = struct {
|
const GroupData = struct {
|
||||||
hash: u32,
|
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,
|
owned: []u32,
|
||||||
include: []u32,
|
include: []u32,
|
||||||
exclude: []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, include) == null);
|
||||||
std.debug.assert(std.mem.indexOfAny(u32, owned, exclude) == null);
|
std.debug.assert(std.mem.indexOfAny(u32, owned, exclude) == null);
|
||||||
std.debug.assert(std.mem.indexOfAny(u32, include, exclude) == null);
|
std.debug.assert(std.mem.indexOfAny(u32, include, exclude) == null);
|
||||||
|
|
||||||
const group_data = GroupData{
|
var group_data = allocator.create(GroupData) catch unreachable;
|
||||||
.hash = hash,
|
group_data.hash = hash;
|
||||||
.entity_set = SparseSet(Entity, u16).init(allocator),
|
group_data.entity_set = SparseSet(Entity, u16).init(allocator);
|
||||||
.owned = std.mem.dupe(allocator, u32, owned) catch unreachable,
|
group_data.owned = std.mem.dupe(allocator, u32, owned) catch unreachable;
|
||||||
.include = std.mem.dupe(allocator, u32, include) catch unreachable,
|
group_data.include = std.mem.dupe(allocator, u32, include) catch unreachable;
|
||||||
.exclude = std.mem.dupe(allocator, u32, exclude) catch unreachable,
|
group_data.exclude = std.mem.dupe(allocator, u32, exclude) catch unreachable;
|
||||||
};
|
group_data.registry = registry;
|
||||||
|
|
||||||
return group_data;
|
return group_data;
|
||||||
}
|
}
|
||||||
@ -63,10 +65,46 @@ pub const Registry = struct {
|
|||||||
allocator.free(self.owned);
|
allocator.free(self.owned);
|
||||||
allocator.free(self.include);
|
allocator.free(self.include);
|
||||||
allocator.free(self.exclude);
|
allocator.free(self.exclude);
|
||||||
|
allocator.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hasSameConstraints(self: *GroupData, owned: []u32, include: []u32, exclude: []u32) bool {
|
fn maybeValidIf(self: *GroupData, entity: Entity) void {
|
||||||
return std.mem.eql(u32, self.owned, owned) and std.mem.eql(u32, self.include, include) and std.mem.eql(u32, self.exclude, exclude);
|
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),
|
.handles = EntityHandles.init(allocator),
|
||||||
.components = std.AutoHashMap(u8, usize).init(allocator),
|
.components = std.AutoHashMap(u8, usize).init(allocator),
|
||||||
.contexts = 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,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -89,7 +127,7 @@ pub const Registry = struct {
|
|||||||
storage.deinit();
|
storage.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (self.groups.items) |*grp| {
|
for (self.groups.items) |grp| {
|
||||||
grp.deinit(self.allocator);
|
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
|
/// 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();
|
return self.assure(T).onConstruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a Sink object for the given component to add/remove listeners with
|
/// 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();
|
return self.assure(T).onUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a Sink object for the given component to add/remove listeners with
|
/// 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();
|
return self.assure(T).onDestruct();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,13 +357,13 @@ pub const Registry = struct {
|
|||||||
excludes_arr[i] = @as(u32, self.typemap.get(t));
|
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
|
/// returns the Type that a view will be based on the includes and excludes
|
||||||
fn ViewType(comptime includes: var, comptime excludes: var) type {
|
fn ViewType(comptime includes: var, comptime excludes: var) type {
|
||||||
if (includes.len == 1 and excludes.len == 0) return BasicView(includes[0]);
|
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) {
|
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)));
|
@compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(includes)));
|
||||||
if (@typeInfo(@TypeOf(excludes)) != .Struct)
|
if (@typeInfo(@TypeOf(excludes)) != .Struct)
|
||||||
@compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(excludes)));
|
@compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(excludes)));
|
||||||
std.debug.assert(includes.len + owned.len > 0);
|
std.debug.assert(owned.len + includes.len > 0);
|
||||||
std.debug.assert(includes.len + owned.len + excludes.len >= 1);
|
std.debug.assert(owned.len + includes.len + excludes.len > 1);
|
||||||
|
|
||||||
var owned_arr: [owned.len]u32 = undefined;
|
var owned_arr: [owned.len]u32 = undefined;
|
||||||
inline for (owned) |t, i| {
|
inline for (owned) |t, i| {
|
||||||
@ -357,35 +395,45 @@ pub const Registry = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a unique hash to identify the group
|
// 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);
|
comptime const hash = owned.len + (31 * includes.len) + (31 * 31 * excludes.len);
|
||||||
|
|
||||||
for (self.groups.items) |*grp| {
|
for (self.groups.items) |grp| {
|
||||||
if (grp.hash == hash and grp.hasSameConstraints(owned_arr[0..], includes_arr[0..], excludes_arr[0..])) {
|
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..])) {
|
||||||
group_data = grp;
|
maybe_group_data = grp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// do we already have the GroupData?
|
||||||
// non-owning groups
|
if (maybe_group_data) |group_data| {
|
||||||
if (owned.len == 0) {
|
// non-owning groups
|
||||||
if (group_data != null) {
|
if (owned.len == 0) {
|
||||||
return NonOwningGroup(includes.len, excludes.len).init(self, includes_arr, excludes_arr);
|
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
|
/// 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 {
|
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;
|
unreachable;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user