World: Added get and add components
This commit is contained in:
parent
4113f66731
commit
7b8b2833a9
2 changed files with 205 additions and 0 deletions
|
|
@ -37,6 +37,10 @@ deinit_fn: *const fn (
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
) void,
|
) void,
|
||||||
|
|
||||||
|
init_fn: *const fn (
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) anyerror!Self,
|
||||||
|
|
||||||
/// ------------------------------------------
|
/// ------------------------------------------
|
||||||
/// ------------------------------------------
|
/// ------------------------------------------
|
||||||
pub fn init(
|
pub fn init(
|
||||||
|
|
@ -101,6 +105,12 @@ pub fn init(
|
||||||
allocator.destroy(col);
|
allocator.destroy(col);
|
||||||
}
|
}
|
||||||
}.f,
|
}.f,
|
||||||
|
|
||||||
|
.init_fn = struct {
|
||||||
|
fn f(a: std.mem.Allocator) !Self {
|
||||||
|
return init(T, a);
|
||||||
|
}
|
||||||
|
}.f,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +123,27 @@ pub fn deinit(self: *Self) void {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// Create a new empty Column of the same type
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn cloneEmpty(self: *Self) !Self {
|
||||||
|
return self.init_fn(self.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// Copy a single element from another column
|
||||||
|
/// Both columns must store the same concrete type
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn copyElement(
|
||||||
|
self: *Self,
|
||||||
|
other: *Self,
|
||||||
|
idx: usize,
|
||||||
|
) !void {
|
||||||
|
if (other.getPtr(idx)) |ptr| {
|
||||||
|
try self.append(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// ------------------------------------------
|
/// ------------------------------------------
|
||||||
/// ------------------------------------------
|
/// ------------------------------------------
|
||||||
pub fn append(
|
pub fn append(
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,180 @@ pub fn spawn(
|
||||||
_ = try self.spawnEntity(components);
|
_ = try self.spawnEntity(components);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn addComponents(
|
||||||
|
self: *Self,
|
||||||
|
entity: u64,
|
||||||
|
components: anytype,
|
||||||
|
) !void {
|
||||||
|
const record = self.entity_map.get(entity) orelse return;
|
||||||
|
const new_fields = std.meta.fields(@TypeOf(components));
|
||||||
|
const old_count = self.archetypes.items[record.archetype_idx].signature.ids.len;
|
||||||
|
|
||||||
|
// --- BUILD COMBINED SIGNATURE (dedup against existing) ---
|
||||||
|
var ids = try self.alloc.alloc(u64, old_count + new_fields.len);
|
||||||
|
defer self.alloc.free(ids);
|
||||||
|
|
||||||
|
for (self.archetypes.items[record.archetype_idx].signature.ids, 0..) |id, i| {
|
||||||
|
ids[i] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
var total: usize = old_count;
|
||||||
|
inline for (new_fields) |field| {
|
||||||
|
const component = @field(components, field.name);
|
||||||
|
const id = TypeID(@TypeOf(component));
|
||||||
|
var dominated = false;
|
||||||
|
|
||||||
|
for (ids[0..total]) |existing| {
|
||||||
|
if (existing == id) {
|
||||||
|
dominated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dominated) {
|
||||||
|
ids[total] = id;
|
||||||
|
total += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total == old_count) return;
|
||||||
|
|
||||||
|
std.mem.sort(u64, ids[0..total], {}, std.sort.asc(u64));
|
||||||
|
const signature_hash = std.hash.Wyhash.hash(0, std.mem.sliceAsBytes(ids[0..total]));
|
||||||
|
|
||||||
|
// --- FIND OR CREATE NEW ARCHETYPE ---
|
||||||
|
const new_arch_idx =
|
||||||
|
self.signature_map.get(signature_hash) orelse blk: {
|
||||||
|
const old = &self.archetypes.items[record.archetype_idx];
|
||||||
|
const sig_ids = try self.alloc.dupe(u64, ids[0..total]);
|
||||||
|
var arch: Archetype = .init(.{ .ids = sig_ids }, self.alloc);
|
||||||
|
|
||||||
|
// --- CLONE OLD COLUMNS ---
|
||||||
|
for (old.columns.items) |*col| {
|
||||||
|
try arch.columns.append(self.alloc, try col.cloneEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- COPY OLD TYPE MAPPINGS ---
|
||||||
|
var old_iter = old.type_map.iterator();
|
||||||
|
while (old_iter.next()) |entry| {
|
||||||
|
try arch.type_map.put(entry.key_ptr.*, entry.value_ptr.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ADD NEW COLUMNS ---
|
||||||
|
inline for (new_fields) |field| {
|
||||||
|
const component = @field(components, field.name);
|
||||||
|
const T = @TypeOf(component);
|
||||||
|
var already = false;
|
||||||
|
for (old.signature.ids) |old_id| {
|
||||||
|
if (old_id == TypeID(T)) {
|
||||||
|
already = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!already) try arch.addColumn(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.archetypes.append(self.alloc, arch);
|
||||||
|
const idx = self.archetypes.items.len - 1;
|
||||||
|
try self.signature_map.put(signature_hash, idx);
|
||||||
|
break :blk idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- RE-FETCH ---
|
||||||
|
const old_arch = &self.archetypes.items[record.archetype_idx];
|
||||||
|
const new_arch = &self.archetypes.items[new_arch_idx];
|
||||||
|
|
||||||
|
// --- COPY OLD COMPONENT VALUES ---
|
||||||
|
try new_arch.entities.append(
|
||||||
|
self.alloc,
|
||||||
|
entity,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (old_arch.columns.items, 0..) |*old_col, i| {
|
||||||
|
if (old_col.getPtr(record.row)) |ptr| {
|
||||||
|
try new_arch.columns.items[i].append(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ADD NEW COMPONENT VALUES ---
|
||||||
|
inline for (new_fields) |field| {
|
||||||
|
const component = @field(components, field.name);
|
||||||
|
const T = @TypeOf(component);
|
||||||
|
|
||||||
|
var already = false;
|
||||||
|
for (old_arch.signature.ids) |old_id| {
|
||||||
|
if (old_id == TypeID(T)) {
|
||||||
|
already = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!already) {
|
||||||
|
const idx = new_arch.type_map.get(TypeID(T)) orelse return error.ComponentNotFound;
|
||||||
|
try new_arch.columns.items[idx].append(&component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- REMOVE FROM OLD ARCHETYPE ---
|
||||||
|
if (old_arch.remove(record.row)) |moved| {
|
||||||
|
self.entity_map.getPtr(moved).?.row = record.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- UPDATE ENTITY MAP ---
|
||||||
|
self.entity_map.getPtr(entity).?.archetype_idx = new_arch_idx;
|
||||||
|
self.entity_map.getPtr(entity).?.row = new_arch.entities.items.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getComponent(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
entity: u64,
|
||||||
|
) ?*T {
|
||||||
|
const record = self.entity_map.get(entity) orelse return null;
|
||||||
|
const arch = &self.archetypes.items[record.archetype_idx];
|
||||||
|
return arch.get(T, record.row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getComponents(
|
||||||
|
self: *Self,
|
||||||
|
comptime Result: type,
|
||||||
|
entity: u64,
|
||||||
|
) error{ComponentNotRegistered}!Result {
|
||||||
|
// --- RESULT ---
|
||||||
|
var result: Result = undefined;
|
||||||
|
|
||||||
|
// --- GET FIELDS ---
|
||||||
|
inline for (std.meta.fields(Result)) |field| {
|
||||||
|
// --- FIELD INFO ---
|
||||||
|
const FieldType = field.type;
|
||||||
|
|
||||||
|
switch (@typeInfo(FieldType)) {
|
||||||
|
.optional => |opt| {
|
||||||
|
const ptr_info = @typeInfo(opt.child).pointer;
|
||||||
|
@field(result, field.name) = self.getComponent(ptr_info.child, entity);
|
||||||
|
},
|
||||||
|
|
||||||
|
.pointer => |ptr| {
|
||||||
|
@field(result, field.name) =
|
||||||
|
self.getComponent(ptr.child, entity) orelse
|
||||||
|
return error.ComponentNotRegistered;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => @compileError(
|
||||||
|
"getComponents fields must be *T or ?*T; field '" ++ field.name ++ "' has type '" ++
|
||||||
|
@typeName(FieldType) ++ "'",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn despawn(
|
pub fn despawn(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue