summaryrefslogtreecommitdiff
path: root/world.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'world.cpp')
-rw-r--r--world.cpp204
1 files changed, 204 insertions, 0 deletions
diff --git a/world.cpp b/world.cpp
new file mode 100644
index 0000000..5cfd8bf
--- /dev/null
+++ b/world.cpp
@@ -0,0 +1,204 @@
+#include "world.hpp"
+extern "C" {
+#include "memory.h"
+#include "plat.h"
+}
+
+#include <string.h>
+
+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)]++;
+}
+