218 lines
5.3 KiB
Zig
218 lines
5.3 KiB
Zig
|
|
// --- LIBS ---
|
||
|
|
const std = @import("std");
|
||
|
|
const Archetype = @import("archetype.zig");
|
||
|
|
const Signature = @import("signature.zig");
|
||
|
|
const EntityRecord = @import("entity_record.zig");
|
||
|
|
const QueryFilters = @import("query_filters.zig");
|
||
|
|
const Query = @import("query.zig").Query;
|
||
|
|
const TypeID = @import("../template/type_id.zig").of;
|
||
|
|
const Self = @This();
|
||
|
|
|
||
|
|
//
|
||
|
|
// FIELDS
|
||
|
|
//
|
||
|
|
|
||
|
|
archetypes: std.ArrayList(Archetype),
|
||
|
|
entity_map: std.AutoHashMap(u64, EntityRecord),
|
||
|
|
signature_map: std.AutoHashMap(u64, usize),
|
||
|
|
next_entity: u64,
|
||
|
|
alloc: std.mem.Allocator,
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||
|
|
return .{
|
||
|
|
.archetypes = .empty,
|
||
|
|
.entity_map = .init(alloc),
|
||
|
|
.signature_map = .init(alloc),
|
||
|
|
.next_entity = 1,
|
||
|
|
.alloc = alloc,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
pub fn deinit(self: *Self) void {
|
||
|
|
// --- ARCHETYPES ---
|
||
|
|
for (self.archetypes.items) |*arch| {
|
||
|
|
arch.deinit();
|
||
|
|
}
|
||
|
|
self.entity_map.deinit();
|
||
|
|
self.archetypes.deinit(self.alloc);
|
||
|
|
self.signature_map.deinit();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
pub fn spawn(
|
||
|
|
self: *Self,
|
||
|
|
components: anytype,
|
||
|
|
) !void {
|
||
|
|
_ = try self.spawnEntity(components);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
pub fn despawn(
|
||
|
|
self: *Self,
|
||
|
|
entity: u64,
|
||
|
|
) void {
|
||
|
|
const record = self.entity_map.get(entity) orelse return;
|
||
|
|
if (self.archetypes.items[record.archetype_idx].remove(record.row)) |moved| {
|
||
|
|
self.entity_map.getPtr(moved).?.row = record.row;
|
||
|
|
}
|
||
|
|
_ = self.entity_map.remove(entity);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
pub fn spawnEntity(
|
||
|
|
self: *Self,
|
||
|
|
components: anytype,
|
||
|
|
) !u64 {
|
||
|
|
const Components = @TypeOf(components);
|
||
|
|
|
||
|
|
const fields = std.meta.fields(Components);
|
||
|
|
|
||
|
|
// --- SIGNATURE ---
|
||
|
|
var ids: [fields.len]u64 = undefined;
|
||
|
|
inline for (fields, 0..) |field, i| {
|
||
|
|
const component = @field(
|
||
|
|
components,
|
||
|
|
field.name,
|
||
|
|
);
|
||
|
|
ids[i] = TypeID(@TypeOf(component));
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- SORT TYPE ID'S ---
|
||
|
|
std.mem.sort(
|
||
|
|
u64,
|
||
|
|
&ids,
|
||
|
|
{},
|
||
|
|
std.sort.asc(u64),
|
||
|
|
);
|
||
|
|
|
||
|
|
const signature_hash = std.hash.Wyhash.hash(0, std.mem.sliceAsBytes(&ids));
|
||
|
|
|
||
|
|
// --- FIND OR CREATE ARCHETYPE ---
|
||
|
|
const archetype_idx =
|
||
|
|
self.signature_map.get(signature_hash) orelse blk: {
|
||
|
|
const sig_ids = try self.alloc.dupe(u64, &ids);
|
||
|
|
|
||
|
|
var arch: Archetype = .init(.{ .ids = sig_ids }, self.alloc);
|
||
|
|
|
||
|
|
// --- NEW COLUMNS ---
|
||
|
|
inline for (fields) |field| {
|
||
|
|
const component = @field(components, field.name);
|
||
|
|
try arch.addColumn(@TypeOf(component));
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- ADD ARCHETYPE ---
|
||
|
|
try self.archetypes.append(
|
||
|
|
self.alloc,
|
||
|
|
arch,
|
||
|
|
);
|
||
|
|
|
||
|
|
// --- ADD SIGNATURE ---
|
||
|
|
const idx = self.archetypes.items.len - 1;
|
||
|
|
try self.signature_map.put(
|
||
|
|
signature_hash,
|
||
|
|
idx,
|
||
|
|
);
|
||
|
|
|
||
|
|
break :blk idx;
|
||
|
|
};
|
||
|
|
|
||
|
|
// --- NEW ENTITY ---
|
||
|
|
const entity = self.next_entity;
|
||
|
|
self.next_entity += 1;
|
||
|
|
|
||
|
|
// --- ADD ROW ---
|
||
|
|
var arch = &self.archetypes.items[archetype_idx];
|
||
|
|
|
||
|
|
const row = arch.entities.items.len;
|
||
|
|
|
||
|
|
try arch.add(
|
||
|
|
entity,
|
||
|
|
components,
|
||
|
|
);
|
||
|
|
|
||
|
|
// --- STORE ENTITY LOCATION ---
|
||
|
|
try self.entity_map.put(entity, .{
|
||
|
|
.archetype_idx = archetype_idx,
|
||
|
|
.row = row,
|
||
|
|
});
|
||
|
|
|
||
|
|
return entity;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
fn createEntity(self: *Self) u64 {
|
||
|
|
const entity = self.next_entity;
|
||
|
|
self.next_entity += 1;
|
||
|
|
return entity;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
fn createArchetype(
|
||
|
|
self: *Self,
|
||
|
|
comptime Components: type,
|
||
|
|
) !usize {
|
||
|
|
const fields =
|
||
|
|
std.meta.fields(Components);
|
||
|
|
|
||
|
|
var ids = try self.alloc.alloc(
|
||
|
|
u64,
|
||
|
|
fields.len,
|
||
|
|
);
|
||
|
|
|
||
|
|
inline for (fields, 0..) |field, i| {
|
||
|
|
ids[i] = TypeID(field.type);
|
||
|
|
}
|
||
|
|
|
||
|
|
const sig: Signature = .{
|
||
|
|
.ids = ids,
|
||
|
|
};
|
||
|
|
|
||
|
|
var arch: Archetype = .init(
|
||
|
|
sig,
|
||
|
|
self.alloc,
|
||
|
|
);
|
||
|
|
|
||
|
|
inline for (fields) |field| {
|
||
|
|
try arch.addColumn(
|
||
|
|
field.type,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
try self.archetypes.append(
|
||
|
|
self.alloc,
|
||
|
|
arch,
|
||
|
|
);
|
||
|
|
|
||
|
|
return self.archetypes.items.len - 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
/// Example:
|
||
|
|
/// ```
|
||
|
|
/// var query = world.query(struct {
|
||
|
|
/// player: ?*Player,
|
||
|
|
/// pos: *Position,
|
||
|
|
/// vel: *Velocity,
|
||
|
|
/// }, .{});
|
||
|
|
/// while (query.next()) |entity| {
|
||
|
|
/// if (entity.player) |player| {}
|
||
|
|
/// }
|
||
|
|
/// ```
|
||
|
|
/// ----------------------------------------------------
|
||
|
|
pub fn query(
|
||
|
|
self: *Self,
|
||
|
|
comptime Result: type,
|
||
|
|
comptime Filters: QueryFilters,
|
||
|
|
) Query(Result, Filters) {
|
||
|
|
return .init(self);
|
||
|
|
}
|