// --- 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); }