ecs: fix pointer invalidation in get/setComponent

The reference to the old archetype is invalidated by getOrPut() calls of
std.ArrayHashMap. The implementation of std.ArrayHashMap means that
pointers can be invalidated on getOrPut() calls even if the key exists
in the map. This means that the reference to the old archetype needs to
be refreshed unconditionally (i.e. not only if the new archetype didn't
exist previously).
This commit is contained in:
dweiller 2022-07-11 14:38:43 +10:00 committed by Stephen Gutekanst
parent 54780c9af8
commit 05b0df052d

View file

@ -523,11 +523,12 @@ pub fn Entities(all_components: anytype) type {
// new component was added), or the same archetype storage table (if just updating the // new component was added), or the same archetype storage table (if just updating the
// value of a component.) // value of a component.)
var archetype_entry = try entities.archetypes.getOrPut(entities.allocator, new_hash); var archetype_entry = try entities.archetypes.getOrPut(entities.allocator, new_hash);
if (!archetype_entry.found_existing) {
// getOrPut allocated, so the archetype we retrieved earlier may no longer be a valid
// pointer. Refresh it now:
archetype = entities.archetypeByID(entity);
// getOrPut allocated, so the archetype we retrieved earlier may no longer be a valid
// pointer. Refresh it now:
archetype = entities.archetypeByID(entity);
if (!archetype_entry.found_existing) {
const columns = entities.allocator.alloc(Column, archetype.columns.len + 1) catch |err| { const columns = entities.allocator.alloc(Column, archetype.columns.len + 1) catch |err| {
assert(entities.archetypes.swapRemove(new_hash)); assert(entities.archetypes.swapRemove(new_hash));
return err; return err;
@ -653,11 +654,12 @@ pub fn Entities(all_components: anytype) type {
// guarantee that archetype (A, C) exists - and so removing a component sometimes does // guarantee that archetype (A, C) exists - and so removing a component sometimes does
// require creating a new archetype table! // require creating a new archetype table!
var archetype_entry = try entities.archetypes.getOrPut(entities.allocator, new_hash); var archetype_entry = try entities.archetypes.getOrPut(entities.allocator, new_hash);
if (!archetype_entry.found_existing) {
// getOrPut allocated, so the archetype we retrieved earlier may no longer be a valid
// pointer. Refresh it now:
archetype = entities.archetypeByID(entity);
// getOrPut allocated, so the archetype we retrieved earlier may no longer be a valid
// pointer. Refresh it now:
archetype = entities.archetypeByID(entity);
if (!archetype_entry.found_existing) {
const columns = entities.allocator.alloc(Column, archetype.columns.len - 1) catch |err| { const columns = entities.allocator.alloc(Column, archetype.columns.len - 1) catch |err| {
assert(entities.archetypes.swapRemove(new_hash)); assert(entities.archetypes.swapRemove(new_hash));
return err; return err;