update to latest zig version

master
Mike 4 years ago
parent 76e322a49c
commit 9ca3e90b7b

@ -0,0 +1,46 @@
# Zig ECS
This is a zigification of the fantasic [Entt](https://github.com/skypjack/entt). Entt is _highly_ templated C++ code which depending on your opinion is either a good thing or satan itself in code form. Zig doesn't have the same concept as C++ templates (thank goodness!) so the templated code was changed over to use Zig's generics and compile time metaprogramming.
## What does a zigified Entt look like?
Below are examples of a View and a Group, the two main ways to work with entities in the ecs along with the scaffolding code.
Declare some structs to work with:
```
pub const Velocity = struct { x: f32, y: f32 };
pub const Position = struct { x: f32, y: f32 };
```
Setup the Registry, which holds the entity data and is where we run our queries:
```
var reg = ecs.Registry.init(std.testing.allocator);
```
Create a couple entities and add some components to them
```
var entity = reg.create();
reg.add(entity, Position{ .x = 0, .y = 0 });
reg.add(entity, Velocity{ .x = 5, .y = 7 });
...
```
Create and iterate a View that matches all entities with a `Velocity` and `Position` component:
```
var view = reg.view(.{ Velocity, Position }, .{});
var iter = view.iterator();
while (iter.next()) |entity| {
const pos = view.getConst(Position, entity); // readonly copy
var vel = view.get(Velocity, entity); // mutable
}
```
The same example using a non-owning Group:
```
var group = reg.group(.{}, .{ Velocity, Position }, .{});
group.each(each);
fn each(e: struct { vel: *Velocity, pos: *Position }) void {
e.pos.*.x += e.vel.x;
e.pos.*.y += e.vel.y;
}
```

@ -32,7 +32,7 @@ fn createEntities(reg: *ecs.Registry) void {
}
var end = timer.lap();
std.debug.warn("create entities: {d}\n", .{@intToFloat(f64, end) / 1000000000});
std.debug.warn("create {d} entities: {d}\n", .{total_entities, @intToFloat(f64, end) / 1000000000});
}
fn owningGroup(reg: *ecs.Registry) void {

@ -13,4 +13,4 @@ pub const OwningGroup = @import("ecs/groups.zig").OwningGroup;
// signals
pub const Signal = @import("signals/signal.zig").Signal;
pub const Dispatcher = @import("signals/dispatcher.zig").Dispatcher;
pub const Dispatcher = @import("signals/dispatcher.zig").Dispatcher;

@ -18,7 +18,7 @@ pub const Actor = struct {
self.registry.destroy(self.entity);
}
pub fn add(self: *Actor, value: var) void {
pub fn add(self: *Actor, value: anytype) void {
self.registry.add(self.entity, value);
}

@ -188,7 +188,7 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
struct {
/// Sort Entities according to the given comparison function. Only T == Entity is allowed. The constraint param only exists for
/// parity with non-empty Components
pub fn sort(self: Self, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
pub fn sort(self: Self, comptime T: type, context: anytype, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
std.debug.assert(T == Entity);
self.set.sort(context, lessThan);
}
@ -226,7 +226,7 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
}
/// Sort Entities or Components according to the given comparison function. Valid types for T are Entity or Component.
pub fn sort(self: *Self, comptime T: type, length: usize, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
pub fn sort(self: *Self, comptime T: type, length: usize, context: anytype, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
std.debug.assert(T == Entity or T == Component);
// we have to perform a swap after the sort for all moved entities so we make a helper struct for that. In the

@ -42,7 +42,7 @@ pub const BasicGroup = struct {
return self.group_data.entity_set.reverseIterator();
}
pub fn sort(self: BasicGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
pub fn sort(self: BasicGroup, comptime T: type, context: anytype, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
if (T == Entity) {
self.group_data.entity_set.sort(context, lessThan);
} else {
@ -73,7 +73,7 @@ pub const OwningGroup = struct {
/// 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.
/// TODO: support const types in the Components struct in addition to the current ptrs
fn Iterator(comptime Components: var) type {
fn Iterator(comptime Components: anytype) type {
return struct {
group: OwningGroup,
index: usize,
@ -136,7 +136,7 @@ pub const OwningGroup = struct {
/// grabs an untyped (u1) reference to the first Storage(T) in the owned array
fn firstOwnedStorage(self: OwningGroup) *Storage(u1) {
const ptr = self.registry.components.getValue(self.group_data.owned[0]).?;
const ptr = self.registry.components.get(self.group_data.owned[0]).?;
return @intToPtr(*Storage(u1), ptr);
}
@ -155,7 +155,7 @@ pub const OwningGroup = struct {
return storage.contains(entity) and storage.set.index(entity) < self.len();
}
fn validate(self: OwningGroup, comptime Components: var) void {
fn validate(self: OwningGroup, comptime Components: anytype) void {
if (std.builtin.mode == .Debug and self.group_data.owned.len > 0) {
std.debug.assert(@typeInfo(Components) == .Struct);
@ -167,7 +167,7 @@ pub const OwningGroup = struct {
}
}
pub fn getOwned(self: OwningGroup, entity: Entity, comptime Components: var) Components {
pub fn getOwned(self: OwningGroup, entity: Entity, comptime Components: anytype) Components {
self.validate(Components);
const component_info = @typeInfo(Components).Struct;
@ -188,7 +188,7 @@ pub const OwningGroup = struct {
return comps;
}
pub fn each(self: OwningGroup, comptime func: var) void {
pub fn each(self: OwningGroup, comptime func: anytype) 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.?,
@ -223,7 +223,7 @@ pub const OwningGroup = struct {
/// returns an iterator with optimized access to the owend Components. Note that Components should be a struct with
/// 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.
pub fn iterator(self: OwningGroup, comptime Components: var) Iterator(Components) {
pub fn iterator(self: OwningGroup, comptime Components: anytype) Iterator(Components) {
self.validate(Components);
return Iterator(Components).init(self);
}
@ -232,7 +232,7 @@ pub const OwningGroup = struct {
return utils.ReverseSliceIterator(Entity).init(self.firstOwnedStorage().set.dense.items[0..self.group_data.current]);
}
pub fn sort(self: OwningGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
pub fn sort(self: OwningGroup, comptime T: type, context: anytype, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
var first_storage = self.firstOwnedStorage();
if (T == Entity) {
@ -264,7 +264,7 @@ pub const OwningGroup = struct {
// 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 other_ptr = self.registry.components.get(type_id).?;
var storage = @intToPtr(*Storage(u1), other_ptr);
storage.swap(storage.data()[pos], entity);
}

@ -81,19 +81,19 @@ pub const Registry = struct {
pub fn maybeValidIf(self: *GroupData, entity: Entity) void {
const isValid: bool = blk: {
for (self.owned) |tid| {
const ptr = self.registry.components.getValue(tid).?;
const ptr = self.registry.components.get(tid).?;
if (!@intToPtr(*Storage(u1), ptr).contains(entity))
break :blk false;
}
for (self.include) |tid| {
const ptr = self.registry.components.getValue(tid).?;
const ptr = self.registry.components.get(tid).?;
if (!@intToPtr(*Storage(u1), ptr).contains(entity))
break :blk false;
}
for (self.exclude) |tid| {
const ptr = self.registry.components.getValue(tid).?;
const ptr = self.registry.components.get(tid).?;
if (@intToPtr(*Storage(u1), ptr).contains(entity))
break :blk false;
}
@ -106,11 +106,11 @@ pub const Registry = struct {
}
} else {
if (isValid) {
const ptr = self.registry.components.getValue(self.owned[0]).?;
const ptr = self.registry.components.get(self.owned[0]).?;
if (!(@intToPtr(*Storage(u1), ptr).set.index(entity) < self.current)) {
for (self.owned) |tid| {
// store.swap hides a safe version that types it correctly
const store_ptr = self.registry.components.getValue(tid).?;
const store_ptr = self.registry.components.get(tid).?;
var store = @intToPtr(*Storage(u1), store_ptr);
store.swap(store.data()[self.current], entity);
}
@ -127,12 +127,12 @@ pub const Registry = struct {
self.entity_set.remove(entity);
}
} else {
const ptr = self.registry.components.getValue(self.owned[0]).?;
const ptr = self.registry.components.get(self.owned[0]).?;
var store = @intToPtr(*Storage(u1), ptr);
if (store.contains(entity) and store.set.index(entity) < self.current) {
self.current -= 1;
for (self.owned) |tid| {
const store_ptr = self.registry.components.getValue(tid).?;
const store_ptr = self.registry.components.get(tid).?;
store = @intToPtr(*Storage(u1), store_ptr);
store.swap(store.data()[self.current], entity);
}
@ -215,7 +215,7 @@ pub const Registry = struct {
pub fn assure(self: *Registry, comptime T: type) *Storage(T) {
var type_id = utils.typeId(T);
if (self.components.get(type_id)) |kv| {
if (self.components.getEntry(type_id)) |kv| {
return @intToPtr(*Storage(T), kv.value);
}
@ -281,7 +281,7 @@ pub const Registry = struct {
return self.handles.iterator();
}
pub fn add(self: *Registry, entity: Entity, value: var) void {
pub fn add(self: *Registry, entity: Entity, value: anytype) void {
assert(self.valid(entity));
self.assure(@TypeOf(value)).add(entity, value);
}
@ -292,14 +292,14 @@ pub const Registry = struct {
}
/// adds all the component types passed in as zero-initialized values
pub fn addTypes(self: *Registry, entity: Entity, comptime types: var) void {
pub fn addTypes(self: *Registry, entity: Entity, comptime types: anytype) void {
inline for (types) |t| {
self.assure(t).add(entity, std.mem.zeroes(t));
}
}
/// Replaces the given component for an entity
pub fn replace(self: *Registry, entity: Entity, value: var) void {
pub fn replace(self: *Registry, entity: Entity, value: anytype) void {
assert(self.valid(entity));
self.assure(@TypeOf(value)).replace(entity, value);
}
@ -309,7 +309,7 @@ pub const Registry = struct {
self.replace(entity, value);
}
pub fn addOrReplace(self: *Registry, entity: Entity, value: var) void {
pub fn addOrReplace(self: *Registry, entity: Entity, value: anytype) void {
assert(self.valid(entity));
const store = self.assure(@TypeOf(value));
@ -393,7 +393,7 @@ pub const Registry = struct {
}
/// Binds an object to the context of the registry
pub fn setContext(self: *Registry, context: var) void {
pub fn setContext(self: *Registry, context: anytype) void {
std.debug.assert(@typeInfo(@TypeOf(context)) == .Pointer);
var type_id = utils.typeId(@typeInfo(@TypeOf(context)).Pointer.child);
@ -432,7 +432,7 @@ pub const Registry = struct {
return self.assure(T).super == 0;
}
pub fn view(self: *Registry, comptime includes: var, comptime excludes: var) ViewType(includes, excludes) {
pub fn view(self: *Registry, comptime includes: anytype, comptime excludes: anytype) ViewType(includes, excludes) {
std.debug.assert(@typeInfo(@TypeOf(includes)) == .Struct);
std.debug.assert(@typeInfo(@TypeOf(excludes)) == .Struct);
std.debug.assert(includes.len > 0);
@ -457,13 +457,13 @@ pub const Registry = struct {
}
/// 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: anytype, comptime excludes: anytype) type {
if (includes.len == 1 and excludes.len == 0) return BasicView(includes[0]);
return MultiView(includes.len, excludes.len);
}
/// creates an optimized group for iterating components
pub fn group(self: *Registry, comptime owned: var, comptime includes: var, comptime excludes: var) (if (owned.len == 0) BasicGroup else OwningGroup) {
pub fn group(self: *Registry, comptime owned: anytype, comptime includes: anytype, comptime excludes: anytype) (if (owned.len == 0) BasicGroup else OwningGroup) {
std.debug.assert(@typeInfo(@TypeOf(owned)) == .Struct);
std.debug.assert(@typeInfo(@TypeOf(includes)) == .Struct);
std.debug.assert(@typeInfo(@TypeOf(excludes)) == .Struct);
@ -593,7 +593,7 @@ pub const Registry = struct {
/// 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 {
inline fn hashGroupTypes(comptime owned: anytype, comptime includes: anytype, comptime excludes: anytype) u64 {
comptime {
for (owned) |t1| {
for (includes) |t2| {
@ -615,7 +615,7 @@ 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 {
inline fn concatTypes(comptime types: anytype) []const u8 {
comptime {
if (types.len == 0) return "_";

@ -148,7 +148,7 @@ pub fn SparseSet(comptime SparseT: type) type {
}
/// Sort elements according to the given comparison function
pub fn sort(self: *Self, context: var, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool) void {
pub fn sort(self: *Self, context: anytype, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool) void {
std.sort.insertionSort(SparseT, self.dense.items, context, lessThan);
for (self.dense.items) |sparse, i| {
@ -159,7 +159,7 @@ pub fn SparseSet(comptime SparseT: type) type {
/// Sort elements according to the given comparison function. Use this when a data array needs to stay in sync with the SparseSet
/// by passing in a "swap_context" that contains a "swap" method with a sig of fn(ctx,SparseT,SparseT)void
pub fn arrange(self: *Self, length: usize, context: var, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool, swap_context: var) void {
pub fn arrange(self: *Self, length: usize, context: anytype, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool, swap_context: anytype) void {
std.sort.insertionSort(SparseT, self.dense.items[0..length], context, lessThan);
for (self.dense.items[0..length]) |sparse, pos| {

@ -13,7 +13,7 @@ pub const TypeStore = struct {
};
}
pub fn deinit(self: TypeStore) void {
pub fn deinit(self: *TypeStore) void {
var iter = self.map.iterator();
while (iter.next()) |kv| {
self.allocator.free(kv.value);
@ -22,7 +22,7 @@ pub const TypeStore = struct {
}
/// adds instance, returning a pointer to the item as it lives in the store
pub fn add(self: *TypeStore, instance: var) void {
pub fn add(self: *TypeStore, instance: anytype) void {
var bytes = self.allocator.alloc(u8, @sizeOf(@TypeOf(instance))) catch unreachable;
std.mem.copy(u8, bytes, std.mem.asBytes(&instance));
_ = self.map.put(utils.typeId(@TypeOf(instance)), bytes) catch unreachable;

@ -3,7 +3,7 @@ const std = @import("std");
pub const ErasedPtr = struct {
ptr: usize,
pub fn init(ptr: var) ErasedPtr {
pub fn init(ptr: anytype) ErasedPtr {
if (@sizeOf(@TypeOf(ptr)) == 0) {
return .{ .ptr = undefined };
}
@ -64,7 +64,7 @@ pub fn sortSub(comptime T1: type, comptime T2: type, items: []T1, sub_items: []T
}
}
pub fn sortSubSub(comptime T1: type, comptime T2: type, items: []T1, sub_items: []T2, context: var, comptime lessThan: fn (@TypeOf(context), lhs: T1, rhs: T1) bool) void {
pub fn sortSubSub(comptime T1: type, comptime T2: type, items: []T1, sub_items: []T2, context: anytype, comptime lessThan: fn (@TypeOf(context), lhs: T1, rhs: T1) bool) void {
var i: usize = 1;
while (i < items.len) : (i += 1) {
const x = items[i];

@ -15,7 +15,7 @@ pub const Scheduler = struct {
allocator: *std.mem.Allocator,
/// helper to create and prepare a process
fn createProcessHandler(comptime T: type, data: var, allocator: *std.mem.Allocator) *Process {
fn createProcessHandler(comptime T: type, data: anytype, allocator: *std.mem.Allocator) *Process {
var proc = allocator.create(T) catch unreachable;
proc.initialize(data);
@ -41,7 +41,7 @@ pub const Scheduler = struct {
return .{ .process = process, .allocator = allocator };
}
pub fn next(self: *@This(), comptime T: type, data: var) *@This() {
pub fn next(self: *@This(), comptime T: type, data: anytype) *@This() {
self.process.next = createProcessHandler(T, data, self.allocator);
self.process = self.process.next.?;
return self;
@ -61,7 +61,7 @@ pub const Scheduler = struct {
}
/// Schedules a process for the next tick
pub fn attach(self: *Scheduler, comptime T: type, data: var) Continuation {
pub fn attach(self: *Scheduler, comptime T: type, data: anytype) Continuation {
std.debug.assert(@hasDecl(T, "initialize"));
std.debug.assert(@hasField(T, "process"));
@ -137,7 +137,7 @@ test "" {
process: Process,
fart: usize,
pub fn initialize(self: *@This(), data: var) void {
pub fn initialize(self: *@This(), data: anytype) void {
self.process = .{
.startFn = start,
.updateFn = update,
@ -190,7 +190,7 @@ test "scheduler.clear" {
const Tester = struct {
process: Process,
pub fn initialize(self: *@This(), data: var) void {
pub fn initialize(self: *@This(), data: anytype) void {
self.process = .{ .updateFn = update };
}
@ -212,7 +212,7 @@ test "scheduler.attach.next" {
process: Process,
counter: *usize,
pub fn initialize(self: *@This(), data: var) void {
pub fn initialize(self: *@This(), data: anytype) void {
self.process = .{ .updateFn = update };
self.counter = data;
}

@ -33,11 +33,11 @@ pub const Assets = struct {
return cache;
}
pub fn load(self: *Assets, id: u16, comptime loader: var) ReturnType(loader, false) {
pub fn load(self: *Assets, id: u16, comptime loader: anytype) ReturnType(loader, false) {
return self.get(ReturnType(loader, true)).load(id, loader);
}
fn ReturnType(comptime loader: var, strip_ptr: bool) type {
fn ReturnType(comptime loader: anytype, strip_ptr: bool) type {
var ret = @typeInfo(@TypeOf(@field(loader, "load"))).BoundFn.return_type.?;
if (strip_ptr) {
return ret.Child;

@ -42,7 +42,7 @@ pub fn Cache(comptime T: type) type {
self.safe_deinit(self);
}
pub fn load(self: *@This(), id: u32, comptime loader: var) @typeInfo(@TypeOf(@field(loader, "load"))).BoundFn.return_type.? {
pub fn load(self: *@This(), id: u32, comptime loader: anytype) @typeInfo(@TypeOf(@field(loader, "load"))).BoundFn.return_type.? {
if (self.resources.getValue(id)) |resource| {
return resource;
}

@ -12,7 +12,7 @@ pub fn Delegate(comptime Event: type) type {
},
/// sets a bound function as the Delegate callback
pub fn initBound(ctx: var, comptime fn_name: []const u8) Self {
pub fn initBound(ctx: anytype, comptime fn_name: []const u8) Self {
std.debug.assert(@typeInfo(@TypeOf(ctx)) == .Pointer);
std.debug.assert(@ptrToInt(ctx) != 0);
@ -50,7 +50,7 @@ pub fn Delegate(comptime Event: type) type {
};
}
pub fn containsBound(self: Self, ctx: var) bool {
pub fn containsBound(self: Self, ctx: anytype) bool {
std.debug.assert(@ptrToInt(ctx) != 0);
std.debug.assert(@typeInfo(@TypeOf(ctx)) == .Pointer);

@ -27,7 +27,7 @@ pub fn Sink(comptime Event: type) type {
return self;
}
pub fn beforeBound(self: Self, ctx: var) Self {
pub fn beforeBound(self: Self, ctx: anytype) Self {
if (@typeInfo(@TypeOf(ctx)) == .Pointer) {
if (self.indexOfBound(ctx)) |index| {
return Self{ .insert_index = index };
@ -41,7 +41,7 @@ pub fn Sink(comptime Event: type) type {
_ = owning_signal.calls.insert(self.insert_index, Delegate(Event).initFree(callback)) catch unreachable;
}
pub fn connectBound(self: Self, ctx: var, comptime fn_name: []const u8) void {
pub fn connectBound(self: Self, ctx: anytype, comptime fn_name: []const u8) void {
std.debug.assert(self.indexOfBound(ctx) == null);
_ = owning_signal.calls.insert(self.insert_index, Delegate(Event).initBound(ctx, fn_name)) catch unreachable;
}
@ -52,7 +52,7 @@ pub fn Sink(comptime Event: type) type {
}
}
pub fn disconnectBound(self: Self, ctx: var) void {
pub fn disconnectBound(self: Self, ctx: anytype) void {
if (self.indexOfBound(ctx)) |index| {
_ = owning_signal.calls.swapRemove(index);
}
@ -67,7 +67,7 @@ pub fn Sink(comptime Event: type) type {
return null;
}
fn indexOfBound(self: Self, ctx: var) ?usize {
fn indexOfBound(self: Self, ctx: anytype) ?usize {
for (owning_signal.calls.items) |call, i| {
if (call.containsBound(ctx)) {
return i;
@ -112,4 +112,4 @@ test "Sink Before bound" {
signal.sink().beforeBound(&thing).connect(tester);
std.testing.expectEqual(signal.sink().indexOf(tester).?, 0);
}
}

Loading…
Cancel
Save