2020-06-02 19:55:24 -07:00
|
|
|
const std = @import("std");
|
|
|
|
const utils = @import("utils.zig");
|
|
|
|
|
|
|
|
const Registry = @import("registry.zig").Registry;
|
|
|
|
const Storage = @import("registry.zig").Storage;
|
2020-06-03 15:01:30 -07:00
|
|
|
const SparseSet = @import("sparse_set.zig").SparseSet;
|
2020-06-02 19:55:24 -07:00
|
|
|
const Entity = @import("registry.zig").Entity;
|
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
/// BasicGroups do not own any components. Internally, they keep a SparseSet that is always kept up-to-date with the matching
|
|
|
|
/// entities.
|
2020-06-08 11:27:49 -07:00
|
|
|
pub const BasicGroup = struct {
|
|
|
|
registry: *Registry,
|
|
|
|
group_data: *Registry.GroupData,
|
2020-06-03 15:01:30 -07:00
|
|
|
|
2020-06-12 18:15:47 -07:00
|
|
|
pub fn init(registry: *Registry, group_data: *Registry.GroupData) BasicGroup {
|
|
|
|
return .{
|
2020-06-08 11:27:49 -07:00
|
|
|
.registry = registry,
|
|
|
|
.group_data = group_data,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-06-12 18:15:47 -07:00
|
|
|
pub fn len(self: BasicGroup) usize {
|
2020-06-08 11:27:49 -07:00
|
|
|
return self.group_data.entity_set.len();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Direct access to the array of entities
|
2020-06-12 18:15:47 -07:00
|
|
|
pub fn data(self: BasicGroup) []const Entity {
|
2020-06-08 11:27:49 -07:00
|
|
|
return self.group_data.entity_set.data();
|
|
|
|
}
|
|
|
|
|
2020-06-12 20:05:55 -07:00
|
|
|
pub fn get(self: BasicGroup, comptime T: type, entity: Entity) *T {
|
2020-06-08 11:27:49 -07:00
|
|
|
return self.registry.assure(T).get(entity);
|
|
|
|
}
|
|
|
|
|
2020-06-12 20:05:55 -07:00
|
|
|
pub fn getConst(self: BasicGroup, comptime T: type, entity: Entity) T {
|
2020-06-08 11:27:49 -07:00
|
|
|
return self.registry.assure(T).getConst(entity);
|
|
|
|
}
|
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
/// iterates the matched entities backwards, so the current entity can always be removed safely
|
|
|
|
/// and newly added entities wont affect it.
|
2020-06-12 18:15:47 -07:00
|
|
|
pub fn iterator(self: BasicGroup) utils.ReverseSliceIterator(Entity) {
|
2020-06-10 13:10:56 -07:00
|
|
|
return self.group_data.entity_set.reverseIterator();
|
2020-06-08 11:27:49 -07:00
|
|
|
}
|
2020-06-12 18:15:47 -07:00
|
|
|
|
2020-06-12 20:05:55 -07:00
|
|
|
pub fn sort(self: BasicGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
|
2020-06-12 18:15:47 -07:00
|
|
|
if (T == Entity) {
|
|
|
|
self.group_data.entity_set.sort(context, lessThan);
|
|
|
|
} else {
|
|
|
|
// TODO: in debug mode, validate that T is present in the group
|
2020-06-12 20:05:55 -07:00
|
|
|
const SortContext = struct {
|
|
|
|
group: BasicGroup,
|
2020-06-12 18:15:47 -07:00
|
|
|
wrapped_context: @TypeOf(context),
|
|
|
|
lessThan: fn (@TypeOf(context), T, T) bool,
|
|
|
|
|
|
|
|
fn sort(this: @This(), a: Entity, b: Entity) bool {
|
|
|
|
const real_a = this.group.getConst(T, a);
|
|
|
|
const real_b = this.group.getConst(T, b);
|
|
|
|
return this.lessThan(this.wrapped_context, real_a, real_b);
|
|
|
|
}
|
|
|
|
};
|
2020-06-12 20:05:55 -07:00
|
|
|
var wrapper = SortContext{ .group = self, .wrapped_context = context, .lessThan = lessThan };
|
2020-06-12 18:15:47 -07:00
|
|
|
self.group_data.entity_set.sort(wrapper, SortContext.sort);
|
|
|
|
}
|
|
|
|
}
|
2020-06-08 11:27:49 -07:00
|
|
|
};
|
2020-06-02 19:55:24 -07:00
|
|
|
|
2020-06-08 11:06:15 -07:00
|
|
|
pub const OwningGroup = struct {
|
|
|
|
registry: *Registry,
|
|
|
|
group_data: *Registry.GroupData,
|
|
|
|
super: *usize,
|
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
/// iterator the provides the data from all the requested owned components in a single struct. Access to the current Entity
|
|
|
|
/// being iterated is available via the entity() method, useful for accessing non-owned component data. The get() method can
|
|
|
|
/// also be used to fetch non-owned component data for the currently iterated Entity.
|
2020-06-12 20:05:55 -07:00
|
|
|
/// TODO: support const types in the Components struct in addition to the current ptrs
|
2020-06-09 14:35:36 -07:00
|
|
|
fn Iterator(comptime Components: var) type {
|
|
|
|
return struct {
|
|
|
|
group: OwningGroup,
|
2020-06-10 13:10:56 -07:00
|
|
|
index: usize,
|
2020-06-09 14:35:36 -07:00
|
|
|
storage: *Storage(u1),
|
|
|
|
component_ptrs: [@typeInfo(Components).Struct.fields.len][*]u8,
|
|
|
|
|
|
|
|
pub fn init(group: OwningGroup) @This() {
|
|
|
|
const component_info = @typeInfo(Components).Struct;
|
|
|
|
|
|
|
|
var component_ptrs: [component_info.fields.len][*]u8 = undefined;
|
|
|
|
inline for (component_info.fields) |field, i| {
|
|
|
|
const storage = group.registry.assure(field.field_type.Child);
|
|
|
|
component_ptrs[i] = @ptrCast([*]u8, storage.instances.items.ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return .{
|
|
|
|
.group = group,
|
2020-06-10 13:10:56 -07:00
|
|
|
.index = group.group_data.current,
|
2020-06-09 14:35:36 -07:00
|
|
|
.storage = group.firstOwnedStorage(),
|
|
|
|
.component_ptrs = component_ptrs,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next(it: *@This()) ?Components {
|
2020-06-10 13:10:56 -07:00
|
|
|
if (it.index == 0) return null;
|
|
|
|
it.index -= 1;
|
2020-06-09 14:35:36 -07:00
|
|
|
|
|
|
|
const ent = it.storage.set.dense.items[it.index];
|
|
|
|
const entity_index = it.storage.set.index(ent);
|
|
|
|
|
|
|
|
// fill and return the struct
|
|
|
|
var comps: Components = undefined;
|
|
|
|
inline for (@typeInfo(Components).Struct.fields) |field, i| {
|
|
|
|
const typed_ptr = @ptrCast([*]field.field_type.Child, @alignCast(@alignOf(field.field_type.Child), it.component_ptrs[i]));
|
|
|
|
@field(comps, field.name) = &typed_ptr[entity_index];
|
|
|
|
}
|
|
|
|
return comps;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn entity(it: @This()) Entity {
|
2020-06-10 13:10:56 -07:00
|
|
|
std.debug.assert(it.index >= 0 and it.index < it.group.group_data.current);
|
|
|
|
return it.storage.set.dense.items[it.index];
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get(it: @This(), comptime T: type) *T {
|
|
|
|
return it.group.registry.get(T, it.entity());
|
2020-06-09 14:35:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the iterator to the initial index
|
|
|
|
pub fn reset(it: *@This()) void {
|
2020-06-10 13:10:56 -07:00
|
|
|
it.index = it.group.group_data.current;
|
2020-06-09 14:35:36 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-06-08 11:06:15 -07:00
|
|
|
pub fn init(registry: *Registry, group_data: *Registry.GroupData, super: *usize) OwningGroup {
|
|
|
|
return .{
|
|
|
|
.registry = registry,
|
|
|
|
.group_data = group_data,
|
|
|
|
.super = super,
|
|
|
|
};
|
|
|
|
}
|
2020-06-03 23:33:59 -07:00
|
|
|
|
2020-06-09 19:22:24 -07:00
|
|
|
/// grabs an untyped (u1) reference to the first Storage(T) in the owned array
|
2020-06-09 14:35:36 -07:00
|
|
|
fn firstOwnedStorage(self: OwningGroup) *Storage(u1) {
|
|
|
|
const ptr = self.registry.components.getValue(self.group_data.owned[0]).?;
|
|
|
|
return @intToPtr(*Storage(u1), ptr);
|
|
|
|
}
|
|
|
|
|
2020-06-09 19:22:24 -07:00
|
|
|
/// total number of entities in the group
|
2020-06-08 11:06:15 -07:00
|
|
|
pub fn len(self: OwningGroup) usize {
|
|
|
|
return self.group_data.current;
|
|
|
|
}
|
2020-06-07 17:28:42 -07:00
|
|
|
|
2020-06-09 14:35:36 -07:00
|
|
|
/// direct access to the array of entities of the first owning group
|
|
|
|
pub fn data(self: OwningGroup) []const Entity {
|
|
|
|
return self.firstOwnedStorage().data();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn contains(self: OwningGroup, entity: Entity) bool {
|
|
|
|
var storage = self.firstOwnedStorage();
|
|
|
|
return storage.contains(entity) and storage.set.index(entity) < self.len();
|
|
|
|
}
|
|
|
|
|
2020-06-09 19:22:24 -07:00
|
|
|
fn validate(self: OwningGroup, comptime Components: var) void {
|
2020-06-10 16:01:09 -07:00
|
|
|
if (std.builtin.mode == .Debug and self.group_data.owned.len > 0) {
|
|
|
|
std.debug.assert(@typeInfo(Components) == .Struct);
|
2020-06-09 19:22:24 -07:00
|
|
|
|
2020-06-10 16:01:09 -07:00
|
|
|
inline for (@typeInfo(Components).Struct.fields) |field| {
|
|
|
|
std.debug.assert(@typeInfo(field.field_type) == .Pointer);
|
|
|
|
const found = std.mem.indexOfScalar(u32, self.group_data.owned, utils.typeId(std.meta.Child(field.field_type)));
|
|
|
|
std.debug.assert(found != null);
|
|
|
|
}
|
2020-06-09 19:22:24 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 14:35:36 -07:00
|
|
|
pub fn getOwned(self: OwningGroup, entity: Entity, comptime Components: var) Components {
|
2020-06-09 19:22:24 -07:00
|
|
|
self.validate(Components);
|
2020-06-09 14:35:36 -07:00
|
|
|
const component_info = @typeInfo(Components).Struct;
|
|
|
|
|
|
|
|
var component_ptrs: [component_info.fields.len][*]u8 = undefined;
|
|
|
|
inline for (component_info.fields) |field, i| {
|
|
|
|
const storage = self.registry.assure(field.field_type.Child);
|
|
|
|
component_ptrs[i] = @ptrCast([*]u8, storage.instances.items.ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// fill the struct
|
|
|
|
const index = self.firstOwnedStorage().set.index(entity);
|
|
|
|
var comps: Components = undefined;
|
|
|
|
inline for (component_info.fields) |field, i| {
|
|
|
|
const typed_ptr = @ptrCast([*]field.field_type.Child, @alignCast(@alignOf(field.field_type.Child), component_ptrs[i]));
|
|
|
|
@field(comps, field.name) = &typed_ptr[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
return comps;
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:21:26 -07:00
|
|
|
pub fn each(self: OwningGroup, comptime func: var) void {
|
|
|
|
const Components = switch (@typeInfo(@TypeOf(func))) {
|
|
|
|
.BoundFn => |func_info| func_info.args[1].arg_type.?,
|
|
|
|
.Fn => |func_info| func_info.args[0].arg_type.?,
|
|
|
|
else => std.debug.assert("invalid func"),
|
|
|
|
};
|
2020-06-09 19:22:24 -07:00
|
|
|
self.validate(Components);
|
2020-06-09 16:21:26 -07:00
|
|
|
|
2020-06-09 19:22:24 -07:00
|
|
|
// optionally we could just use an Iterator here and pay for some slight indirection for code sharing
|
2020-06-09 16:37:53 -07:00
|
|
|
// var iter = self.iterator(Components);
|
|
|
|
// while (iter.next()) |comps| {
|
|
|
|
// @call(.{ .modifier = .always_inline }, func, .{comps});
|
|
|
|
// }
|
|
|
|
|
|
|
|
const component_info = @typeInfo(Components).Struct;
|
|
|
|
|
2020-06-09 19:22:24 -07:00
|
|
|
// get the data pointers for the requested component types
|
2020-06-09 16:37:53 -07:00
|
|
|
var component_ptrs: [component_info.fields.len][*]u8 = undefined;
|
|
|
|
inline for (component_info.fields) |field, i| {
|
|
|
|
const storage = self.registry.assure(field.field_type.Child);
|
|
|
|
component_ptrs[i] = @ptrCast([*]u8, storage.instances.items.ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
var storage = self.firstOwnedStorage();
|
2020-06-10 13:10:56 -07:00
|
|
|
var index: usize = self.group_data.current;
|
|
|
|
while (true) {
|
|
|
|
if (index == 0) return;
|
|
|
|
index -= 1;
|
|
|
|
|
2020-06-09 16:37:53 -07:00
|
|
|
const ent = storage.set.dense.items[index];
|
|
|
|
const entity_index = storage.set.index(ent);
|
|
|
|
|
|
|
|
var comps: Components = undefined;
|
|
|
|
inline for (component_info.fields) |field, i| {
|
|
|
|
const typed_ptr = @ptrCast([*]field.field_type.Child, @alignCast(@alignOf(field.field_type.Child), component_ptrs[i]));
|
|
|
|
@field(comps, field.name) = &typed_ptr[entity_index];
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:21:26 -07:00
|
|
|
@call(.{ .modifier = .always_inline }, func, .{comps});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 14:35:36 -07:00
|
|
|
/// returns the component storage for the given type for direct access
|
2020-06-12 20:05:55 -07:00
|
|
|
pub fn getStorage(self: OwningGroup, comptime T: type) *Storage(T) {
|
2020-06-09 14:35:36 -07:00
|
|
|
return self.registry.assure(T);
|
|
|
|
}
|
|
|
|
|
2020-06-12 20:05:55 -07:00
|
|
|
pub fn get(self: OwningGroup, comptime T: type, entity: Entity) *T {
|
2020-06-09 14:35:36 -07:00
|
|
|
return self.registry.assure(T).get(entity);
|
|
|
|
}
|
|
|
|
|
2020-06-12 20:05:55 -07:00
|
|
|
pub fn getConst(self: OwningGroup, comptime T: type, entity: Entity) T {
|
2020-06-09 14:35:36 -07:00
|
|
|
return self.registry.assure(T).getConst(entity);
|
|
|
|
}
|
|
|
|
|
2020-06-09 19:22:24 -07:00
|
|
|
pub fn sortable(self: OwningGroup) bool {
|
2020-06-08 11:06:15 -07:00
|
|
|
return self.group_data.super == self.group_data.size;
|
|
|
|
}
|
2020-06-09 14:35:36 -07:00
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
/// returns an iterator with optimized access to the owend Components. Note that Components should be a struct with
|
2020-06-12 20:05:55 -07:00
|
|
|
/// fields that are pointers to the component types that you want to fetch. Only types that are owned are valid! Non-owned
|
|
|
|
/// types should be fetched via Iterator.get.
|
2020-06-09 14:35:36 -07:00
|
|
|
pub fn iterator(self: OwningGroup, comptime Components: var) Iterator(Components) {
|
2020-06-09 19:22:24 -07:00
|
|
|
self.validate(Components);
|
2020-06-09 14:35:36 -07:00
|
|
|
return Iterator(Components).init(self);
|
|
|
|
}
|
2020-06-10 13:10:56 -07:00
|
|
|
|
|
|
|
pub fn entityIterator(self: OwningGroup) utils.ReverseSliceIterator(Entity) {
|
|
|
|
return utils.ReverseSliceIterator(Entity).init(self.firstOwnedStorage().set.dense.items[0..self.group_data.current]);
|
|
|
|
}
|
2020-06-12 20:05:55 -07:00
|
|
|
|
|
|
|
pub fn sort(self: OwningGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
|
|
|
|
var first_storage = self.firstOwnedStorage();
|
|
|
|
|
|
|
|
if (T == Entity) {
|
|
|
|
// only sort up to self.group_data.current
|
|
|
|
first_storage.sort(Entity, context, lessThan);
|
|
|
|
} else {
|
|
|
|
// TODO: in debug mode, validate that T is present in the group
|
|
|
|
const SortContext = struct {
|
|
|
|
group: OwningGroup,
|
|
|
|
wrapped_context: @TypeOf(context),
|
|
|
|
lessThan: fn (@TypeOf(context), T, T) bool,
|
|
|
|
|
|
|
|
fn sort(this: @This(), a: Entity, b: Entity) bool {
|
|
|
|
const real_a = this.group.getConst(T, a);
|
|
|
|
const real_b = this.group.getConst(T, b);
|
|
|
|
return this.lessThan(this.wrapped_context, real_a, real_b);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const wrapper = SortContext{ .group = self, .wrapped_context = context, .lessThan = lessThan };
|
|
|
|
first_storage.sort(Entity, wrapper, SortContext.sort);
|
|
|
|
}
|
|
|
|
|
|
|
|
// sync up the rest of the owned components. First get our Storages in
|
|
|
|
// var tmp_storages: [20]*Storage(u1) = undefined;
|
|
|
|
// for (self.group_data.owned[1..]) |type_id, i| {
|
|
|
|
// var other_ptr = self.registry.components.getValue(type_id).?;
|
|
|
|
// tmp_storages[i] = @intToPtr(*Storage(u1), other_ptr);
|
|
|
|
// }
|
|
|
|
// var storages = tmp_storages[0 .. self.group_data.owned.len - 1];
|
|
|
|
|
|
|
|
var next: usize = self.group_data.current;
|
|
|
|
while (true) : (next -= 1) {
|
|
|
|
if (next == 0) break;
|
|
|
|
const pos = next - 1;
|
|
|
|
const entity = first_storage.data()[pos];
|
|
|
|
|
|
|
|
// skip the first one since its what we are using to sort with
|
|
|
|
for (self.group_data.owned[1..]) |type_id| {
|
|
|
|
var other_ptr = self.registry.components.getValue(type_id).?;
|
|
|
|
var storage = @intToPtr(*Storage(u1), other_ptr);
|
|
|
|
storage.swap(storage.data()[pos], entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// for (self.group_data.owned[1..]) |type_id| {
|
|
|
|
// var other_ptr = self.registry.components.getValue(type_id).?;
|
|
|
|
// var other = @intToPtr(*Storage(u1), other_ptr);
|
|
|
|
|
|
|
|
// var i: usize = self.group_data.current - 1;
|
|
|
|
// while (true) : (i -= 1) {
|
|
|
|
// if (i == 0) break;
|
|
|
|
// const pos = i - 1;
|
|
|
|
// const entity =
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
}
|
2020-06-08 11:06:15 -07:00
|
|
|
};
|
2020-06-03 23:33:59 -07:00
|
|
|
|
2020-06-09 10:21:42 -07:00
|
|
|
test "BasicGroup creation/iteration" {
|
2020-06-02 19:55:24 -07:00
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
2020-06-03 15:01:30 -07:00
|
|
|
var group = reg.group(.{}, .{ i32, u32 }, .{});
|
|
|
|
std.testing.expectEqual(group.len(), 0);
|
|
|
|
|
2020-06-02 19:55:24 -07:00
|
|
|
var e0 = reg.create();
|
2020-06-03 15:01:30 -07:00
|
|
|
reg.add(e0, @as(i32, 44));
|
|
|
|
reg.add(e0, @as(u32, 55));
|
|
|
|
|
|
|
|
std.debug.assert(group.len() == 1);
|
2020-06-02 19:55:24 -07:00
|
|
|
|
2020-06-03 15:01:30 -07:00
|
|
|
var iterated_entities: usize = 0;
|
|
|
|
var iter = group.iterator();
|
|
|
|
while (iter.next()) |entity| {
|
|
|
|
iterated_entities += 1;
|
|
|
|
}
|
|
|
|
std.testing.expectEqual(iterated_entities, 1);
|
|
|
|
|
2020-06-09 10:21:42 -07:00
|
|
|
iterated_entities = 0;
|
|
|
|
for (group.data()) |entity| {
|
|
|
|
iterated_entities += 1;
|
|
|
|
}
|
|
|
|
std.testing.expectEqual(iterated_entities, 1);
|
|
|
|
|
2020-06-03 15:01:30 -07:00
|
|
|
reg.remove(i32, e0);
|
|
|
|
std.debug.assert(group.len() == 0);
|
|
|
|
}
|
2020-06-03 15:09:25 -07:00
|
|
|
|
2020-06-03 15:25:27 -07:00
|
|
|
test "BasicGroup excludes" {
|
2020-06-03 15:09:25 -07:00
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
2020-06-08 11:06:15 -07:00
|
|
|
var group = reg.group(.{}, .{i32}, .{u32});
|
2020-06-03 15:09:25 -07:00
|
|
|
std.testing.expectEqual(group.len(), 0);
|
|
|
|
|
|
|
|
var e0 = reg.create();
|
|
|
|
reg.add(e0, @as(i32, 44));
|
|
|
|
|
|
|
|
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.add(e0, @as(u32, 55));
|
|
|
|
std.debug.assert(group.len() == 0);
|
2020-06-03 15:25:27 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
test "BasicGroup create late" {
|
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
|
|
|
var e0 = reg.create();
|
|
|
|
reg.add(e0, @as(i32, 44));
|
|
|
|
reg.add(e0, @as(u32, 55));
|
|
|
|
|
|
|
|
var group = reg.group(.{}, .{ i32, u32 }, .{});
|
|
|
|
std.testing.expectEqual(group.len(), 1);
|
2020-06-03 23:33:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
test "OwningGroup" {
|
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
2020-06-08 11:06:15 -07:00
|
|
|
var group = reg.group(.{ i32, u32 }, .{}, .{});
|
2020-06-03 23:33:59 -07:00
|
|
|
|
|
|
|
var e0 = reg.create();
|
|
|
|
reg.add(e0, @as(i32, 44));
|
|
|
|
reg.add(e0, @as(u32, 55));
|
|
|
|
std.testing.expectEqual(group.len(), 1);
|
2020-06-09 14:35:36 -07:00
|
|
|
std.testing.expect(group.contains(e0));
|
|
|
|
|
|
|
|
std.testing.expectEqual(group.get(i32, e0).*, 44);
|
|
|
|
std.testing.expectEqual(group.getConst(u32, e0), 55);
|
|
|
|
|
|
|
|
var vals = group.getOwned(e0, struct { int: *i32, uint: *u32 });
|
|
|
|
std.testing.expectEqual(vals.int.*, 44);
|
|
|
|
std.testing.expectEqual(vals.uint.*, 55);
|
|
|
|
|
|
|
|
vals.int.* = 666;
|
|
|
|
var vals2 = group.getOwned(e0, struct { int: *i32, uint: *u32 });
|
|
|
|
std.testing.expectEqual(vals2.int.*, 666);
|
2020-06-03 23:33:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
test "OwningGroup add/remove" {
|
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
2020-06-08 11:06:15 -07:00
|
|
|
var group = reg.group(.{ i32, u32 }, .{}, .{});
|
2020-06-03 23:33:59 -07:00
|
|
|
|
|
|
|
var e0 = reg.create();
|
|
|
|
reg.add(e0, @as(i32, 44));
|
|
|
|
reg.add(e0, @as(u32, 55));
|
|
|
|
std.testing.expectEqual(group.len(), 1);
|
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
reg.remove(u32, e0);
|
2020-06-03 23:33:59 -07:00
|
|
|
std.testing.expectEqual(group.len(), 0);
|
2020-06-07 17:28:42 -07:00
|
|
|
}
|
|
|
|
|
2020-06-09 14:35:36 -07:00
|
|
|
test "OwningGroup iterate" {
|
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
|
|
|
var e0 = reg.create();
|
|
|
|
reg.add(e0, @as(i32, 44));
|
|
|
|
reg.add(e0, @as(u32, 55));
|
2020-06-10 13:10:56 -07:00
|
|
|
reg.add(e0, @as(u8, 11));
|
2020-06-09 14:35:36 -07:00
|
|
|
|
|
|
|
var e1 = reg.create();
|
|
|
|
reg.add(e1, @as(i32, 666));
|
|
|
|
reg.add(e1, @as(u32, 999));
|
2020-06-10 13:10:56 -07:00
|
|
|
reg.add(e1, @as(f32, 55.5));
|
2020-06-09 14:35:36 -07:00
|
|
|
|
|
|
|
var group = reg.group(.{ i32, u32 }, .{}, .{});
|
|
|
|
var iter = group.iterator(struct { int: *i32, uint: *u32 });
|
|
|
|
while (iter.next()) |item| {
|
2020-06-10 13:10:56 -07:00
|
|
|
if (iter.entity() == e0) {
|
2020-06-09 14:35:36 -07:00
|
|
|
std.testing.expectEqual(item.int.*, 44);
|
|
|
|
std.testing.expectEqual(item.uint.*, 55);
|
2020-06-10 13:10:56 -07:00
|
|
|
std.testing.expectEqual(iter.get(u8).*, 11);
|
2020-06-09 14:35:36 -07:00
|
|
|
} else {
|
|
|
|
std.testing.expectEqual(item.int.*, 666);
|
|
|
|
std.testing.expectEqual(item.uint.*, 999);
|
2020-06-10 13:10:56 -07:00
|
|
|
std.testing.expectEqual(iter.get(f32).*, 55.5);
|
2020-06-09 14:35:36 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 16:21:26 -07:00
|
|
|
fn each(components: struct {
|
|
|
|
int: *i32,
|
|
|
|
uint: *u32,
|
|
|
|
}) void {
|
|
|
|
std.testing.expectEqual(components.int.*, 44);
|
|
|
|
std.testing.expectEqual(components.uint.*, 55);
|
|
|
|
}
|
|
|
|
|
|
|
|
test "OwningGroup each" {
|
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
|
|
|
var e0 = reg.create();
|
|
|
|
reg.add(e0, @as(i32, 44));
|
|
|
|
reg.add(e0, @as(u32, 55));
|
|
|
|
|
|
|
|
const Thing = struct {
|
|
|
|
fn each(self: @This(), components: struct {
|
|
|
|
int: *i32,
|
|
|
|
uint: *u32,
|
|
|
|
}) void {
|
|
|
|
std.testing.expectEqual(components.int.*, 44);
|
|
|
|
std.testing.expectEqual(components.uint.*, 55);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var thing = Thing{};
|
|
|
|
|
|
|
|
var group = reg.group(.{ i32, u32 }, .{}, .{});
|
|
|
|
group.each(thing.each);
|
|
|
|
group.each(each);
|
|
|
|
}
|
|
|
|
|
2020-06-07 17:28:42 -07:00
|
|
|
test "multiple OwningGroups" {
|
|
|
|
const Sprite = struct { x: f32 };
|
|
|
|
const Transform = struct { x: f32 };
|
|
|
|
const Renderable = struct { x: f32 };
|
|
|
|
const Rotation = struct { x: f32 };
|
|
|
|
|
|
|
|
var reg = Registry.init(std.testing.allocator);
|
|
|
|
defer reg.deinit();
|
|
|
|
|
|
|
|
// var group1 = reg.group(.{u64, u32}, .{}, .{});
|
|
|
|
// var group2 = reg.group(.{u64, u32, u8}, .{}, .{});
|
|
|
|
|
2020-06-08 11:06:15 -07:00
|
|
|
var group5 = reg.group(.{ Sprite, Transform }, .{ Renderable, Rotation }, .{});
|
2020-06-07 17:28:42 -07:00
|
|
|
var group3 = reg.group(.{Sprite}, .{Renderable}, .{});
|
2020-06-08 11:06:15 -07:00
|
|
|
var group4 = reg.group(.{ Sprite, Transform }, .{Renderable}, .{});
|
2020-06-07 17:28:42 -07:00
|
|
|
|
2020-06-10 13:10:56 -07:00
|
|
|
// ensure groups are ordered correctly internally
|
2020-06-07 17:28:42 -07:00
|
|
|
var last_size: u8 = 0;
|
|
|
|
for (reg.groups.items) |grp| {
|
|
|
|
std.testing.expect(last_size <= grp.size);
|
|
|
|
last_size = grp.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
std.testing.expect(!reg.sortable(Sprite));
|
|
|
|
|
|
|
|
// this will break the group
|
|
|
|
// var group6 = reg.group(.{Sprite, Rotation}, .{}, .{});
|
2020-06-07 22:10:52 -07:00
|
|
|
}
|