#include "world.hpp" extern "C" { #include "memory.h" #include "plat.h" } #include static int component_sizes[max_components]; void Slinky::init(Arena* a, int size) { data = arena_alloc(a, size * slinky_size); entities = (Entity_Id*)arena_alloc( a, sizeof *entities * slinky_size ); count = 0; } void* Slinky::add(Entity_Id eid, int size, int& mapping) { int idx = count++; assert(count <= slinky_size); entities[idx] = eid; mapping = idx; return &((char*)data)[idx * size]; } void* Slinky::get(Entity_Id eid, int mapping, int size) { char* e = &((char*)data)[mapping * size]; assert(eid == entities[mapping]); (void)eid; return e; } void Slinky::remove(Entity_Id eid, int mapping, int size) { int end = (count - 1) * size; assert(eid == entities[mapping]); (void)eid; memmove( (char*)data + mapping * size, (char*)data + end * size, size ); } void Pool::init(Arena* a, Component_Mask m) { int i, coff; mask = m; for (i = 0, coff = 0; i < max_components; i++) { if (m & ((Component_Mask)1 << i)) { offsets[i] = coff; coff += component_sizes[i]; } } size = coff; slinkies[0].init(a, size); slinky_count = 1; count = 0; } Slinky& Pool::get_slinky(Arena* a) { int idx = slinky_count - 1; if (slinkies[idx].count < slinky_size) { return slinkies[idx]; } assert(slinky_count < max_slinkies); Slinky& s = slinkies[slinky_count++]; s.init(a, size); return s; } void* Pool::add(Arena* a, Entity_Id eid) { Slinky& slinky = get_slinky(a); Mapping& m = mapping[entity_index(eid)]; m.slinky = &slinky - slinkies; count++; return slinky.add(eid, size, m.mapping); } void Pool::remove(Entity_Id eid) { Mapping m = mapping[entity_index(eid)]; Slinky& s = slinkies[m.slinky]; s.remove(eid, m.mapping, size); count--; } void* Pool::get(Entity_Id eid, int cid) { Mapping m = mapping[entity_index(eid)]; Slinky& s = slinkies[m.slinky]; char* ptr = (char*)s.get(eid, m.mapping, size); return ptr + offsets[cid]; } void* Pool::get(Entity_Id eid) { Mapping m = mapping[entity_index(eid)]; Slinky& s = slinkies[m.slinky]; char* ptr = (char*)s.get(eid, m.mapping, size); return ptr; } 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)); 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)]++; }