NYXA/src/world/world.zig

218 lines
5.3 KiB
Zig
Raw Normal View History

2026-07-09 22:30:04 +01:00
// --- 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);
}