#include "world.hpp" extern "C" { #include "memory.h" #include "plat.h" } #include static int component_sizes[max_components]; void Pool::init(Arena* a, Component_Mask m) { int i, coff; next = 0; size = 0; mask = m; count = 0; for (i = 0, coff = 0; i < max_components; i++) { if (m & ((Component_Mask)1 << i)) { offsets[i] = coff; coff += component_sizes[i]; } } size = coff; data = arena_alloc(a, size * pool_cap); } void* Pool::add(Arena* a, Entity_Id eid) { int idx; if (next) return next->add(a, eid); if (count >= pool_cap) { next = (Pool*)arena_alloc(a, sizeof *next); next->init(a, mask); return next->add(a, eid); } idx = count++; entities[idx] = eid; mapping[entity_index(eid)] = idx; return &((char*)data)[idx * size]; } void Pool::remove(Entity_Id eid) { int eind = entity_index(eid); int end = count - 1; assert(eid == entities[mapping[eind]]); memmove( (char*)data + eind * size, (char*)data + end * size, size ); entities[eind] = entities[count]; mapping[end] = eind; count = end; } void* Pool::get(Entity_Id eid, int cid) { int eind = entity_index(eid); char* e = &((char*)data)[mapping[eind] * size]; assert(eid == entities[mapping[eind]]); return &e[offsets[cid]]; } void* Pool::get(Entity_Id eid) { int eind = entity_index(eid); void* e = &((char*)data)[mapping[eind] * size]; assert(eid == entities[mapping[eind]]); return e; } int get_new_component_id(int size) { static int id = 0; int i = id++; component_sizes[i] = size; return i; } int entity_version(Entity_Id e) { return e & 0xff; } int entity_index(Entity_Id e) { return (e >> 8) & 0xffffff; } Entity_Id entity_id(int index, int version) { return ((index & 0xffffff) << 8) | (version & 0xff); } void World::init(Arena* a) { int i; arena = a; count = 0; free_count = 0; for (i = 0; i < max_entities; i++) versions[i] = 1; for (i = 0; i < max_pools; i++) pools[i].mask = 0; } uint64_t World::hash_mask(Component_Mask m) { uint64_t h = (uint64_t)m; h = (h ^ (h >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); h = (h ^ (h >> 27)) * UINT64_C(0x94d049bb133111eb); h = (h ^ (h >> 31)); return h; } Pool& World::get_pool(Component_Mask m) { int i, idx; idx = (int)(hash_mask(m) % max_pools); for (i = 0; i < max_pools; i++) { Pool& p = pools[idx]; if (!p.mask) { p.init(arena, m); return p; } else if (p.mask == m) return p; idx++; idx %= max_pools; } assert(0); return pools[0]; } Entity_Id World::create_entity() { Entity_Id e; int ind; assert(count < max_entities); if (free_count) ind = entity_index(freelist[--free_count]); else ind = count++; e = entity_id(ind, versions[ind]); masks[ind] = 0; entities[ind] = e; return e; } void* World::add(Entity_Id eid, Component_Mask m) { int eind = entity_index(eid); int i; assert(entity_version(eid) == versions[eind]); Component_Mask om = masks[eind]; Component_Mask tm = om | m; assert(om != tm); Pool& p = get_pool(tm); p.add(arena, eid); if (om) { Pool& op = get_pool(om); for (i = 0; i < max_components; i++) { if (om & ((Component_Mask)1 << i)) { memcpy( p.get(eid, i), op.get(eid, i), component_sizes[i] ); } } op.remove(eid); } masks[eind] = tm; return p.get(eid); } void* World::get(Entity_Id eid, int cid) { int eind = entity_index(eid); Pool& p = get_pool(masks[eind]); assert(versions[eind] == entity_version(eid)); assert(p.entities[p.mapping[eind]] == eid); return p.get(eid, cid); } void World::remove(Entity_Id eid, int cid) { int eind = entity_index(eid), i; Component_Mask om = masks[eind]; Component_Mask nm = om & ~((Component_Mask)1 << cid); Pool& s = get_pool(om); if (nm) { Pool& d = get_pool(nm); for (i = 0; i < max_components; i++) { if (nm & ((uint64_t)1 << i)) { memcpy( d.get(eid, i), s.get(eid, i), component_sizes[i] ); } } s.remove(eid); } s.remove(eid); masks[eind] = nm; } void World::destroy(Entity_Id e) { int eind = entity_index(e); Component_Mask m = masks[eind]; Pool& p = get_pool(m); p.remove(e); assert(free_count < max_entities); freelist[free_count++] = e; versions[entity_index(e)]++; }