#define LUA_LIB #include #include #include #include #include #include #include "luaecs.h" #define MAX_COMPONENT 256 #define ENTITY_REMOVED 0 #define DEFAULT_SIZE 128 #define STRIDE_TAG 0 #define STRIDE_LUA -1 #define STRIDE_ORDER -2 #define DUMMY_PTR (void *)(uintptr_t)(~0) #define REARRANGE_THRESHOLD 0x80000000 struct component_pool { int cap; int n; int stride; // -1 means lua object int last_lookup; unsigned int *id; void *buffer; }; struct entity_world { unsigned int max_id; struct component_pool c[MAX_COMPONENT]; }; static void init_component_pool(struct entity_world *w, int index, int stride, int opt_size) { struct component_pool *c = &w->c[index]; c->cap = opt_size; c->n = 0; c->stride = stride; c->id = NULL; c->last_lookup = 0; if (stride > 0) { c->buffer = NULL; } else { c->buffer = DUMMY_PTR; } } static void entity_new_type(lua_State *L, struct entity_world *w, int cid, int stride, int opt_size) { if (opt_size <= 0) { opt_size = DEFAULT_SIZE; } if (cid < 0 || cid >=MAX_COMPONENT || w->c[cid].cap != 0) { luaL_error(L, "Can't new type %d", cid); } init_component_pool(w, cid, stride, opt_size); } static inline struct entity_world * getW(lua_State *L) { return (struct entity_world *)luaL_checkudata(L, 1, "ENTITY_WORLD"); } static int lnew_type(lua_State *L) { struct entity_world *w = getW(L); int cid = luaL_checkinteger(L, 2); int stride = luaL_checkinteger(L, 3); int size = luaL_optinteger(L, 4, 0); entity_new_type(L, w, cid, stride, size); return 0; } static int lcount_memory(lua_State *L) { struct entity_world *w = getW(L); size_t sz = sizeof(*w); int i; size_t msz = sz; for (i=0;ic[i]; if (c->id) { sz += c->cap * sizeof(unsigned int); msz += c->n * sizeof(unsigned int); } if (c->buffer != DUMMY_PTR) { sz += c->cap * c->stride; msz += c->cap * c->stride; } } lua_pushinteger(L, sz); lua_pushinteger(L, msz); return 2; } static void shrink_component_pool(lua_State *L, struct component_pool *c, int id) { if (c->id == NULL) return; if (c->n == 0) { c->id = NULL; if (c->stride > 0) c->buffer = NULL; lua_pushnil(L); lua_setiuservalue(L, 1, id * 2 + 1); lua_pushnil(L); lua_setiuservalue(L, 1, id * 2 + 2); } else if (c->stride > 0 && c->n < c->cap) { c->cap = c->n; c->id = (unsigned int *)lua_newuserdatauv(L, c->n * sizeof(unsigned int), 0); lua_setiuservalue(L, 1, id * 2 + 1); c->buffer = lua_newuserdatauv(L, c->n * c->stride, 0); lua_setiuservalue(L, 1, id * 2 + 2); } } static int lcollect_memory(lua_State *L) { struct entity_world *w = getW(L); int i; for (i=0;ic[i], i); } return 0; } static int add_component_id_(lua_State *L, int world_index, struct entity_world *w, int cid, unsigned int eid) { struct component_pool *pool = &w->c[cid]; int cap = pool->cap; int index = pool->n; assert(pool->stride != STRIDE_ORDER); if (pool->n == 0) { if (pool->id == NULL) { pool->id = (unsigned int *)lua_newuserdatauv(L, cap * sizeof(unsigned int), 0); lua_setiuservalue(L, world_index, cid * 2 + 1); } if (pool->buffer == NULL) { pool->buffer = lua_newuserdatauv(L, cap * pool->stride, 0); lua_setiuservalue(L, world_index, cid * 2 + 2); } else if (pool->stride == STRIDE_LUA) { lua_newtable(L); lua_setiuservalue(L, world_index, cid * 2 + 2); } } else if (pool->n >= pool->cap) { // expand pool int newcap = cap * 3 / 2; unsigned int *newid = (unsigned int *)lua_newuserdatauv(L, newcap * sizeof(unsigned int), 0); lua_setiuservalue(L, world_index, cid * 2 + 1); memcpy(newid, pool->id, cap * sizeof(unsigned int)); pool->id = newid; int stride = pool->stride; if (stride > 0) { void *newbuffer = lua_newuserdatauv(L, newcap * stride, 0); lua_setiuservalue(L, world_index, cid * 2 + 2); memcpy(newbuffer, pool->buffer, cap * stride); pool->buffer = newbuffer; } pool->cap = newcap; } ++pool->n; pool->id[index] = eid; if (index > 0 && eid < pool->id[index-1]) { luaL_error(L, "Add component %d fail", cid); } return index; } static inline void * get_ptr(struct component_pool *c, int index) { if (c->stride > 0) return (void *)((char *)c->buffer + c->stride * index); else return DUMMY_PTR; } static void * add_component_(lua_State *L, int world_index, struct entity_world *w, int cid, unsigned int eid, const void *buffer) { int index = add_component_id_(L, world_index, w, cid, eid); struct component_pool *pool = &w->c[cid]; assert(pool->stride >= 0); void *ret = get_ptr(pool, index); if (buffer) memcpy(ret, buffer, pool->stride); return ret; } static inline int check_cid(lua_State *L, struct entity_world *w, int index) { int cid = luaL_checkinteger(L, index); struct component_pool *c = &w->c[cid]; if (cid < 0 || cid >=MAX_COMPONENT || c->cap == 0) { luaL_error(L, "Invalid type %d", cid); } return cid; } static int ladd_component(lua_State *L) { struct entity_world *w = getW(L); unsigned int eid = luaL_checkinteger(L, 2); int cid = check_cid(L, w, 3); int index = add_component_id_(L, 1, w, cid, eid); lua_pushinteger(L, index + 1); return 1; } static int lnew_entity(lua_State *L) { struct entity_world *w = getW(L); unsigned int eid = ++w->max_id; assert(eid != 0); lua_pushinteger(L, eid); return 1; } static void insert_id(lua_State *L, int world_index, struct entity_world *w, int cid, unsigned int eid) { struct component_pool *c = &w->c[cid]; assert(c->stride == STRIDE_TAG); int from = 0; int to = c->n; while(from < to) { int mid = (from + to)/2; int aa = c->id[mid]; if (aa == eid) return; else if (aa < eid) { from = mid + 1; } else { to = mid; } } // insert eid at [from] if (from < c->n - 1) { int i; // Any dup id ? for (i=from;in-1;i++) { if (c->id[i] == c->id[i+1]) { memmove(c->id + from + 1, c->id + from, sizeof(unsigned int) * (i - from)); c->id[from] = eid; return; } } } // 0xffffffff max uint avoid check add_component_id_(L, world_index, w, cid, 0xffffffff); memmove(c->id + from + 1, c->id + from, sizeof(unsigned int) * (c->n - from - 1)); c->id[from] = eid; } static void entity_enable_tag_(struct entity_world *w, int cid, int index, int tag_id, void *L, int world_index) { struct component_pool *c = &w->c[cid]; assert(index >=0 && index < c->n); unsigned int eid = c->id[index]; insert_id((lua_State *)L, world_index, w, tag_id, eid); } static int binary_search(unsigned int *a, int from, int to, unsigned int v) { while(from < to) { int mid = (from + to)/2; int aa = a[mid]; if (aa == v) return mid; else if (aa < v) { from = mid + 1; } else { to = mid; } } return -1; } #define GUESS_RANGE 64 static inline int lookup_component(struct component_pool *pool, unsigned int eid, int guess_index) { int n = pool->n; if (n == 0) return -1; if (guess_index < 0 || guess_index >= pool->n) return binary_search(pool->id, 0, pool->n, eid); unsigned int *a = pool->id; int lower = a[guess_index]; if (eid <= lower) { if (eid == lower) return guess_index; return binary_search(a, 0, guess_index, eid); } if (guess_index + GUESS_RANGE*2 >= pool->n) { return binary_search(a, guess_index + 1, pool->n, eid); } int higher = a[guess_index + GUESS_RANGE]; if (eid > higher) { return binary_search(a, guess_index + GUESS_RANGE + 1, pool->n, eid); } return binary_search(a, guess_index + 1, guess_index + GUESS_RANGE + 1, eid); } static inline void replace_id(struct component_pool *c, int from, int to, unsigned int eid) { int i; for (i=from;iid[i] = eid; } } static void entity_disable_tag_(struct entity_world *w, int cid, int index, int tag_id) { struct component_pool *c = &w->c[cid]; assert(index >=0 && index < c->n); unsigned int eid = c->id[index]; if (cid != tag_id) { c = &w->c[tag_id]; index = lookup_component(c, eid, c->last_lookup); if (index < 0) return; } int from,to; // find next tag. You may disable subsquent tags in iteration. // For example, The sequence is 1 3 5 7 9 . We are now on 5 , and disable 7 . // We should change 7 to 9 ( 1 3 5 9 9 ) rather than 7 to 5 ( 1 3 5 5 9 ) // iterator -> ^ ^ for (to = index+1; ton; to++) { if (c->id[to] != eid) { for (from = index-1; from>=0; from--) { if (c->id[from] != eid) break; } replace_id(c, from+1, to, c->id[to]); return; } } for (from = index-1; from>=0; from--) { if (c->id[from] != eid) break; } c->n = from + 1; } static void entity_remove_(struct entity_world *w, int cid, int index, void *L, int world_index) { entity_enable_tag_(w, cid, index, ENTITY_REMOVED, L, world_index); } struct rearrange_context { struct entity_world *w; unsigned int ptr[MAX_COMPONENT-1]; }; static int find_min(struct rearrange_context *ctx) { unsigned int m = ~0; int i; int r = -1; struct entity_world *w = ctx->w; for (i=1;iptr[i-1]; if (index < w->c[i].n) { if (w->c[i].id[index] <= m) { m = w->c[i].id[index]; r = i; } } } return r; } static void rearrange(struct entity_world *w) { struct rearrange_context ctx; memset(&ctx, 0, sizeof(ctx)); ctx.w = w; int cid; unsigned int new_id = 1; unsigned int last_id = 0; while ((cid = find_min(&ctx)) >= 0) { int index = ctx.ptr[cid-1]; unsigned int current_id = w->c[cid].id[index]; // printf("arrange %d <- %d\n", new_id, w->c[cid].id[index]); w->c[cid].id[index] = new_id; if (current_id != last_id) { ++new_id; last_id = current_id; } ++ctx.ptr[cid-1]; } w->max_id = new_id; } static inline void move_tag(struct component_pool *pool, int from, int to) { if (from != to) { pool->id[to] = pool->id[from]; } } static inline void move_item(struct component_pool *pool, int from, int to) { if (from != to) { pool->id[to] = pool->id[from]; int stride = pool->stride; memcpy((char *)pool->buffer + to * stride, (char *)pool->buffer + from * stride, stride); } } static void move_object(lua_State *L, struct component_pool *pool, int from, int to) { if (from != to) { pool->id[to] = pool->id[from]; lua_rawgeti(L, -1, from+1); lua_rawseti(L, -2, to+1); } } static void remove_all(lua_State *L, struct component_pool *pool, struct component_pool *removed, int cid) { int index = 0; int count = 0; int i; if (pool->stride != STRIDE_ORDER) { unsigned int *id = removed->id; unsigned int last_id = 0; for (i=0;in;i++) { if (id[i] != last_id) { // todo : order int r = lookup_component(pool, id[i], index); if (r >= 0) { index = r; pool->id[r] = 0; ++count; } } } } else { unsigned int *id = pool->id; for (i=0;in;i++) { int r = lookup_component(removed, id[i], 0); if (r >= 0) { id[i] = 0; ++count; } } } if (count > 0) { index = 0; switch (pool->stride) { case STRIDE_LUA: if (lua_getiuservalue(L, 1, cid * 2 + 2) != LUA_TTABLE) { luaL_error(L, "Missing lua object table for type %d", cid); } for (i=0;in;i++) { if (pool->id[i] != 0) { move_object(L, pool, i, index); ++index; } } lua_pop(L, 1); // pop lua object table break; case STRIDE_TAG: case STRIDE_ORDER: for (i=0;in;i++) { if (pool->id[i] != 0) { move_tag(pool, i, index); ++index; } } break; default: for (i=0;in;i++) { if (pool->id[i] != 0) { move_item(pool, i, index); ++index; } } break; } pool->n -= count; } } static int lupdate(lua_State *L) { struct entity_world *w = getW(L); struct component_pool *removed = &w->c[ENTITY_REMOVED]; int i; if (removed->n > 0) { // mark removed assert(ENTITY_REMOVED == 0); for (i=1;ic[i]; if (pool->n > 0) remove_all(L, pool, removed, i); } removed->n = 0; } if (w->max_id > REARRANGE_THRESHOLD) { rearrange(w); } return 0; } static void remove_dup(struct component_pool *c, int index) { int i; unsigned int eid = c->id[index]; int to = index; for (i=index+1;in;i++) { if (c->id[i] != eid) { eid = c->id[i]; c->id[to] = eid; ++to; } } c->n = to; } static void * entity_iter_(struct entity_world *w, int cid, int index) { struct component_pool *c = &w->c[cid]; assert(index >= 0); if (index >= c->n) return NULL; if (c->stride == STRIDE_TAG) { // it's a tag unsigned int eid = c->id[index]; if (index < c->n - 1 && eid == c->id[index+1]) { remove_dup(c, index+1); } return DUMMY_PTR; } return get_ptr(c, index); } static void * entity_iter_lua_(struct entity_world *w, int cid, int index, void *L, int world_index) { void * ret = entity_iter_(w, cid, index); if (ret != DUMMY_PTR) return ret; if (lua_getiuservalue(L, world_index, cid * 2 + 2) != LUA_TTABLE) { lua_pop(L, 1); return NULL; } int t = lua_rawgeti(L, -1, index+1); switch(t) { case LUA_TSTRING: ret = (void *)lua_tostring(L, -1); break; case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA: ret = (void *)lua_touserdata(L, -1); break; default: ret = NULL; break; } lua_pop(L, 2); return ret; } static int entity_assign_lua_(struct entity_world *w, int cid, int index, void *L, int world_index) { struct component_pool *c = &w->c[cid]; ++index; assert(lua_gettop(L) > 1); if (c->stride != STRIDE_LUA || index <=0 || index > c->n) { lua_pop(L, 1); return 0; } if (lua_getiuservalue(L, world_index, cid * 2 + 2) != LUA_TTABLE) { lua_pop(L, 2); return 0; } lua_insert(L, -2); // table value lua_rawseti(L, -2, index); lua_pop(L, 1); return 1; } static void entity_clear_type_(struct entity_world *w, int cid) { struct component_pool *c = &w->c[cid]; c->n = 0; } static int lclear_type(lua_State *L) { struct entity_world *w = getW(L); int cid = check_cid(L,w, 2); entity_clear_type_(w, cid); return 0; } static int entity_sibling_index_(struct entity_world *w, int cid, int index, int silbling_id) { struct component_pool *c = &w->c[cid]; if (index < 0 || index >= c->n) return 0; unsigned int eid = c->id[index]; c = &w->c[silbling_id]; assert(c->stride != STRIDE_ORDER); int result_index = lookup_component(c, eid, c->last_lookup); if (result_index >= 0) { c->last_lookup = result_index; return result_index + 1; } return 0; } static void * entity_add_sibling_(struct entity_world *w, int cid, int index, int silbling_id, const void *buffer, void *L, int world_index) { struct component_pool *c = &w->c[cid]; assert(index >=0 && index < c->n); unsigned int eid = c->id[index]; // todo: pcall add_component_ return add_component_((lua_State *)L, world_index, w, silbling_id, eid, buffer); } static int entity_new_(struct entity_world *w, int cid, const void *buffer, void *L, int world_index) { unsigned int eid = ++w->max_id; assert(eid != 0); struct component_pool *c = &w->c[cid]; assert(c->cap > 0); if (buffer == NULL) { return add_component_id_(L, world_index, w, cid, eid); } else { assert(c->stride >= 0); int index = add_component_id_(L, world_index, w, cid, eid); void *ret = get_ptr(c, index); memcpy(ret, buffer, c->stride); return index; } } static int entity_add_sibling_index_(lua_State *L, int world_index, struct entity_world *w, int cid, int index, int slibling_id) { struct component_pool *c = &w->c[cid]; assert(index >=0 && index < c->n); unsigned int eid = c->id[index]; // todo: pcall add_component_ int ret = add_component_id_(L, world_index, w, slibling_id, eid); return ret; } static inline int comp_index(const void *a, const void *b, void *v) { const unsigned int *aa = (const unsigned int *)a; const unsigned int *bb = (const unsigned int *)b; int * vv = (int *)v; return vv[*aa] - vv[*bb]; } static inline int comp_index_s(void *v, const void *a, const void *b) { return comp_index(a,b,v); } static inline void reserve_(struct entity_world *w, int orderid, int cap, void *L, int world_index) { struct component_pool *c = &w->c[orderid]; if (c->id == NULL || c->cap < cap) { c->cap = cap; c->id = (unsigned int *)lua_newuserdatauv(L, cap * sizeof(unsigned int), 0); lua_setiuservalue(L, world_index, orderid * 2 + 1); } } static void entity_sort_key_(struct entity_world *w, int orderid, int cid, void *L, int world_index) { struct component_pool *c = &w->c[cid]; assert(c->stride == sizeof(int)); struct component_pool *order = &w->c[orderid]; assert(order->stride == STRIDE_ORDER); reserve_(w, orderid, c->cap, L, world_index); int i; for (i = 0; i < c->n ; i++) { order->id[i] = i; } #ifdef _GNU_SOURCE qsort_r(order->id, c->n, sizeof(unsigned int), comp_index, c->buffer); #elif defined(__APPLE__) qsort_r(order->id, c->n, sizeof(unsigned int), c->buffer, comp_index_s); #elif defined(_WIN32) qsort_s(order->id, c->n, sizeof(unsigned int), comp_index_s, c->buffer); #else # error Unknown operating system #endif for (i = 0; i < c->n ; i++) { order->id[i] = c->id[order->id[i]]; } order->n = c->n; } static int lcontext(lua_State *L) { struct entity_world *w = getW(L); luaL_checktype(L, 2, LUA_TTABLE); lua_len(L, 2); int n = lua_tointeger(L, -1); lua_pop(L, 1); if (n <= 0) { return luaL_error(L, "Invalid length %d of table", n); } size_t sz = sizeof(struct ecs_context) + sizeof(int) * n; struct ecs_context *ctx = (struct ecs_context *)lua_newuserdatauv(L, sz, 1); ctx->L = (void *)lua_newthread(L); lua_pushvalue(L, 1); lua_xmove(L, ctx->L, 1); // put world in the index 1 of newthread lua_setiuservalue(L, -2, 1); ctx->max_id = n; ctx->world = w; static struct ecs_capi c_api = { entity_iter_, entity_clear_type_, entity_sibling_index_, entity_add_sibling_, entity_new_, entity_remove_, entity_enable_tag_, entity_disable_tag_, entity_sort_key_, entity_iter_lua_, entity_assign_lua_, }; ctx->api = &c_api; ctx->cid[0] = ENTITY_REMOVED; int i; for (i=1;i<=n;i++) { if (lua_geti(L, 2, i) != LUA_TNUMBER) { return luaL_error(L, "Invalid id at index %d", i); } ctx->cid[i] = lua_tointeger(L, -1); lua_pop(L, 1); int cid = ctx->cid[i]; if (cid == ENTITY_REMOVED || cid < 0 || cid >= MAX_COMPONENT) return luaL_error(L, "Invalid id (%d) at index %d", cid, i); } return 1; } static int lnew_world(lua_State *L) { size_t sz = sizeof(struct entity_world); struct entity_world *w = (struct entity_world *)lua_newuserdatauv(L, sz, MAX_COMPONENT * 2); memset(w, 0, sz); // removed set entity_new_type(L, w, ENTITY_REMOVED, 0, 0); luaL_getmetatable(L, "ENTITY_WORLD"); lua_setmetatable(L, -2); return 1; } #define TYPE_INT 0 #define TYPE_FLOAT 1 #define TYPE_BOOL 2 #define TYPE_INT64 3 #define TYPE_DWORD 4 #define TYPE_WORD 5 #define TYPE_BYTE 6 #define TYPE_DOUBLE 7 #define TYPE_USERDATA 8 #define TYPE_COUNT 9 struct field { const char *key; int offset; int type; }; static int check_type(lua_State *L) { int type = lua_tointeger(L, -1); if (type < 0 || type >= TYPE_COUNT) { luaL_error(L, "Invalid field type(%d)", type); } lua_pop(L, 1); return type; } static void get_field(lua_State *L, int i, struct field *f) { if (lua_geti(L, -1, 1) != LUA_TNUMBER) { luaL_error(L, "Invalid field %d [1] type", i); } f->type = check_type(L); if (lua_geti(L, -1, 2) != LUA_TSTRING) { luaL_error(L, "Invalid field %d [2] key", i); } f->key = lua_tostring(L, -1); lua_pop(L, 1); if (lua_geti(L, -1, 3) != LUA_TNUMBER) { luaL_error(L, "Invalid field %d [3] offset", i); } f->offset = lua_tointeger(L, -1); lua_pop(L, 1); lua_pop(L, 1); } static void write_value(lua_State *L, struct field *f, char *buffer) { int luat = lua_type(L, -1); char *ptr = buffer + f->offset; switch (f->type) { case TYPE_INT: if (!lua_isinteger(L, -1)) luaL_error(L, "Invalid .%s type %s (int)", f->key ? f->key : "*", lua_typename(L, luat)); *(int *)ptr = lua_tointeger(L, -1); break; case TYPE_FLOAT: if (luat != LUA_TNUMBER) luaL_error(L, "Invalid .%s type %s (float)", f->key ? f->key : "*", lua_typename(L, luat)); *(float *)ptr = lua_tonumber(L, -1); break; case TYPE_BOOL: if (luat != LUA_TBOOLEAN) luaL_error(L, "Invalid .%s type %s (bool)", f->key ? f->key : "*", lua_typename(L, luat)); *(unsigned char *)ptr = lua_toboolean(L, -1); break; case TYPE_INT64: if (!lua_isinteger(L, -1)) luaL_error(L, "Invalid .%s type %s (int64)", f->key ? f->key : "*", lua_typename(L, luat)); *(int64_t *)ptr = lua_tointeger(L, -1); break; case TYPE_DWORD: if (!lua_isinteger(L, -1)) luaL_error(L, "Invalid .%s type %s (uint32)", f->key ? f->key : "*", lua_typename(L, luat)); else { int64_t v = lua_tointeger(L, -1); if (v < 0 || v > 0xffffffff) { luaL_error(L, "Invalid DWORD %d", (int)v); } *(uint32_t *)ptr = v; } break; case TYPE_WORD: if (!lua_isinteger(L, -1)) luaL_error(L, "Invalid .%s type %s (uint16)", f->key ? f->key : "*", lua_typename(L, luat)); else { int v = lua_tointeger(L, -1); if (v < 0 || v > 0xffff) { luaL_error(L, "Invalid WORD %d", v); } *(uint16_t *)ptr = v; } break; case TYPE_BYTE: if (!lua_isinteger(L, -1)) luaL_error(L, "Invalid .%s type %s (uint8)", f->key ? f->key : "*", lua_typename(L, luat)); else { int v = lua_tointeger(L, -1); if (v < 0 || v > 255) { luaL_error(L, "Invalid BYTE %d", v); } *(uint16_t *)ptr = v; } break; case TYPE_DOUBLE: if (luat != LUA_TNUMBER) luaL_error(L, "Invalid .%s type %s (double)", f->key ? f->key : "*", lua_typename(L, luat)); *(double *)ptr = lua_tonumber(L, -1); break; case TYPE_USERDATA: if (luat != LUA_TLIGHTUSERDATA) luaL_error(L, "Invalid .%s type %s (pointer)", f->key ? f->key : "*", lua_typename(L, luat)); *(void **)ptr = lua_touserdata(L, -1); break; } lua_pop(L, 1); } static inline void write_component(lua_State *L, int field_n, struct field *f, int index, char *buffer) { int i; for (i=0; i < field_n; i++) { lua_getfield(L, index, f[i].key); write_value(L, &f[i], buffer); } } static void read_value(lua_State *L, struct field *f, const char *buffer) { const char * ptr = buffer + f->offset; switch (f->type) { case TYPE_INT: lua_pushinteger(L, *(const int *)ptr); break; case TYPE_FLOAT: lua_pushnumber(L, *(const float *)ptr); break; case TYPE_BOOL: lua_pushboolean(L, *ptr); break; case TYPE_INT64: lua_pushinteger(L, *(const int64_t *)ptr); break; case TYPE_DWORD: lua_pushinteger(L, *(const uint32_t *)ptr); break; case TYPE_WORD: lua_pushinteger(L, *(const uint16_t *)ptr); break; case TYPE_BYTE: lua_pushinteger(L, *(const uint8_t *)ptr); break; case TYPE_DOUBLE: lua_pushnumber(L, *(const double *)ptr); break; case TYPE_USERDATA: lua_pushlightuserdata(L, *(void **)ptr); break; default: // never here luaL_error(L, "Invalid field type %d", f->type); break; } } static void read_component(lua_State *L, int field_n, struct field *f, int index, const char * buffer) { int i; for (i=0; i < field_n; i++) { read_value(L, &f[i], buffer); lua_setfield(L, index, f[i].key); } } static int get_len(lua_State *L, int index) { lua_len(L, index); if (lua_type(L, -1) != LUA_TNUMBER) { return luaL_error(L, "Invalid table length"); } int n = lua_tointeger(L, -1); if (n < 0) { return luaL_error(L, "Invalid table length %d", n); } lua_pop(L, 1); return n; } #define COMPONENT_IN 1 #define COMPONENT_OUT 2 #define COMPONENT_OPTIONAL 4 #define COMPONENT_OBJECT 8 #define COMPONENT_EXIST 0x10 #define COMPONENT_ABSENT 0x20 #define COMPONENT_FILTER (COMPONENT_EXIST | COMPONENT_ABSENT) #define COMPONENT_REFINDEX 0x40 #define COMPONENT_REFOBJECT 0x80 struct group_key { const char *name; int id; int field_n; int attrib; }; static inline int is_temporary(int attrib) { if (attrib & COMPONENT_FILTER) return 0; return (attrib & COMPONENT_IN) == 0 && (attrib & COMPONENT_OUT) == 0; } struct group_iter { struct entity_world *world; struct field *f; int nkey; int readonly; struct group_key k[1]; }; static int get_write_component(lua_State *L, int lua_index, const char *name, struct field *f, struct component_pool *c) { switch (lua_getfield(L, lua_index, name)) { case LUA_TNIL: lua_pop(L, 1); // restore cache (metatable can be absent during sync) if (lua_getmetatable(L, lua_index)) { lua_getfield(L, -1, name); lua_setfield(L, lua_index, name); lua_pop(L, 1); // pop metatable } return 0; case LUA_TTABLE: return 1; default: if (c->stride == STRIDE_LUA) { // lua object return 1; } if (f->key == NULL) { // value type return 1; } return luaL_error(L, "Invalid iterator type %s", lua_typename(L, lua_type(L, -1))); } } static void write_component_object(lua_State *L, int n, struct field *f, void *buffer) { if (f->key == NULL) { write_value(L, f, buffer); } else { write_component(L, n, f, -1, (char *)buffer); lua_pop(L, 1); } } static int remove_tag(lua_State *L, int lua_index, const char *name) { int r = 0; switch (lua_getfield(L, lua_index, name)) { case LUA_TNIL: r = 1; break; case LUA_TBOOLEAN: r = !lua_toboolean(L, -1); break; default: return luaL_error(L, "Invalid tag type %s", lua_typename(L, lua_type(L, -1))); } lua_pop(L, 1); return r; } static void update_iter(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx, int mainkey, int skip) { struct field *f = iter->f; int i; for (i=0;ik[i].field_n; } for (i=skip;inkey;i++) { struct group_key *k = &iter->k[i]; if (!(k->attrib & (COMPONENT_FILTER | COMPONENT_REFINDEX))) { struct component_pool *c = &iter->world->c[k->id]; if (c->stride == STRIDE_TAG) { // It's a tag if ((k->attrib & COMPONENT_OUT)) { switch (lua_getfield(L, lua_index, k->name)) { case LUA_TNIL: break; case LUA_TBOOLEAN: if (lua_toboolean(L, -1)) { entity_enable_tag_(iter->world, mainkey, idx, k->id, L, world_index); } else { entity_disable_tag_(iter->world, mainkey, idx, k->id); } if (!(k->attrib & COMPONENT_IN)) { // reset tag lua_pushnil(L); lua_setfield(L, lua_index, k->name); } break; default: luaL_error(L, ".%s is a tag , should be a boolean or nil. It's %s", k->name, lua_typename(L, lua_type(L, -1))); } lua_pop(L, 1); } } else if ((k->attrib & COMPONENT_OUT) && get_write_component(L, lua_index, k->name, f, c)) { int index; if (k->attrib & COMPONENT_REFOBJECT) { struct group_key *index_k = &iter->k[i-1]; index = entity_sibling_index_(iter->world, mainkey, idx, index_k->id); if (index) { int *ref = entity_iter_(iter->world, index_k->id, index-1); index = *ref; } } else { index = entity_sibling_index_(iter->world, mainkey, idx, k->id); } if (index == 0) { luaL_error(L, "Can't find sibling %s of %s", k->name, iter->k[0].name); } if (c->stride == STRIDE_LUA) { if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) { luaL_error(L, "Missing lua table for %d", k->id); } lua_insert(L, -2); lua_rawseti(L, -2, index); } else { void *buffer = get_ptr(c, index - 1); write_component_object(L, k->field_n, f, buffer); } } else if (is_temporary(k->attrib) && get_write_component(L, lua_index, k->name, f, c)) { if (k->attrib & COMPONENT_REFOBJECT) { // new ref object struct group_key *index_k = &iter->k[i-1]; int dead_tag = k->id + 1; int id; if (entity_iter_(iter->world, dead_tag, 0)) { // reuse id = entity_sibling_index_(iter->world, dead_tag, 0, k->id); entity_disable_tag_(iter->world, dead_tag, 0, dead_tag); } else { id = entity_new_(iter->world, k->id, NULL, L, world_index) + 1; } if (c->stride == STRIDE_LUA) { if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) { luaL_error(L, "Missing lua table for %d", k->id); } lua_insert(L, -2); lua_rawseti(L, -2, id); } else { void *buffer = entity_iter_(iter->world, k->id, id - 1); write_component_object(L, k->field_n, f, buffer); } // write ref id entity_add_sibling_(iter->world, mainkey, idx, index_k->id, &id, L, world_index); } else if (c->stride == STRIDE_LUA) { int index = entity_add_sibling_index_(L, world_index, iter->world, mainkey, idx, k->id); if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) { luaL_error(L, "Missing lua table for %d", k->id); } lua_insert(L, -2); lua_rawseti(L, -2, index + 1); } else { void *buffer = entity_add_sibling_(iter->world, mainkey, idx, k->id, NULL, L, world_index); write_component_object(L, k->field_n, f, buffer); } } } f += k->field_n; } } static void update_last_index(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx) { int mainkey = iter->k[0].id; struct component_pool *c = &iter->world->c[mainkey]; int disable_mainkey = 0; if (!(iter->k[0].attrib & COMPONENT_FILTER)) { if (c->stride == STRIDE_TAG) { // The mainkey is a tag, delay disable disable_mainkey = ((iter->k[0].attrib & COMPONENT_OUT) && remove_tag(L, lua_index, iter->k[0].name)); } else if ((iter->k[0].attrib & COMPONENT_OUT) && get_write_component(L, lua_index, iter->k[0].name, iter->f, c)) { struct component_pool *c = &iter->world->c[mainkey]; if (c->n <= idx) { luaL_error(L, "Can't find component %s for index %d", iter->k[0].name, idx); } if (c->stride == STRIDE_LUA) { if (lua_getiuservalue(L, world_index, mainkey * 2 + 2) != LUA_TTABLE) { luaL_error(L, "Missing lua table for %d", mainkey); } lua_insert(L, -2); lua_rawseti(L, -2, idx+1); } else { void * buffer = get_ptr(c, idx); write_component_object(L, iter->k[0].field_n, iter->f, buffer); } } } update_iter(L, world_index, lua_index, iter, idx, mainkey, 1); if (disable_mainkey) { entity_disable_tag_(iter->world, mainkey, idx, mainkey); } } static void read_component_in_field(lua_State *L, int lua_index, const char *name, int n, struct field *f, void *buffer) { if (n == 0) { // It's tag lua_pushboolean(L, buffer ? 1 : 0); lua_setfield(L, lua_index, name); return; } if (f->key == NULL) { // value type read_value(L, f, buffer); lua_setfield(L, lua_index, name); return; } if (lua_getfield(L, lua_index, name) != LUA_TTABLE) { lua_pop(L, 1); lua_newtable(L); lua_pushvalue(L, -1); lua_setfield(L, lua_index, name); } read_component(L, n , f, lua_gettop(L), buffer); lua_pop(L, 1); } // -1 : end ; 0 : next ; 1 : succ static int query_index(struct group_iter *iter, int skip, int mainkey, int idx, unsigned int index[MAX_COMPONENT]) { if (entity_iter_(iter->world, mainkey, idx) == NULL) { return -1; } int j; for (j=skip;jnkey;j++) { struct group_key *k = &iter->k[j]; if (k->attrib & COMPONENT_ABSENT) { if (entity_sibling_index_(iter->world, mainkey, idx, k->id)) { // exist. try next return 0; } index[j] = 0; } else if (k->attrib & COMPONENT_REFOBJECT) { if (index[j-1]) { struct group_key *index_k = &iter->k[j-1]; int *ref = entity_iter_(iter->world, index_k->id, index[j-1]-1); index[j] = *ref; index[j-1] = 0; } } else if (!is_temporary(k->attrib)) { index[j] = entity_sibling_index_(iter->world, mainkey, idx, k->id); if (index[j] == 0) { if (!(k->attrib & COMPONENT_OPTIONAL)) { // required. try next return 0; } } } else { index[j] = 0; } } return 1; } static void read_iter(lua_State *L, int world_index, int obj_index, struct group_iter *iter, unsigned int index[MAX_COMPONENT]) { struct field *f = iter->f; int i; for (i=0;inkey;i++) { struct group_key *k = &iter->k[i]; if (!(k->attrib & COMPONENT_FILTER)) { struct component_pool *c = &iter->world->c[k->id]; if (c->stride == STRIDE_LUA) { // lua object component if (index[i]) { if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) { luaL_error(L, "Missing lua table for %d", k->id); } lua_rawgeti(L, -1, index[i]); lua_setfield(L, obj_index, k->name); lua_pop(L, 1); } else { lua_pushnil(L); lua_setfield(L, obj_index, k->name); } } else if (c->stride != STRIDE_ORDER) { if (k->attrib & COMPONENT_IN) { if (index[i]) { void *ptr = get_ptr(c, index[i]-1); read_component_in_field(L, obj_index, k->name, k->field_n, f, ptr); } else { lua_pushnil(L); lua_setfield(L, obj_index, k->name); } } else if (index[i] == 0 && !is_temporary(k->attrib)) { lua_pushnil(L); lua_setfield(L, obj_index, k->name); } } } f += k->field_n; } } static int get_integer(lua_State *L, int index, int i, const char *key) { if (lua_rawgeti(L, index, i) != LUA_TNUMBER) { return luaL_error(L, "Can't find %s in iterator", key); } int r = lua_tointeger(L, -1); lua_pop(L, 1); if (r <= 0) return luaL_error(L, "Invalid %s (%d)", key, r); return r; } static int lsync(lua_State *L) { struct group_iter *iter = luaL_checkudata(L, 2, "ENTITY_GROUPITER"); luaL_checktype(L, 3, LUA_TTABLE); int idx = get_integer(L, 3, 1, "index") - 1; int mainkey = get_integer(L, 3, 2, "mainkey"); unsigned int index[MAX_COMPONENT]; if (query_index(iter, 0, mainkey, idx, index) <=0) { return luaL_error(L, "Read pattern fail"); } if (!iter->readonly) { update_iter(L, 1, 3, iter, idx, mainkey, 0); } read_iter(L, 1, 3, iter, index); return 0; } static int leach_group(lua_State *L) { struct group_iter *iter = lua_touserdata(L, 1); if (lua_rawgeti(L, 2, 1) != LUA_TNUMBER) { return luaL_error(L, "Invalid group iterator"); } int i = lua_tointeger(L, -1); if (i < 0) return luaL_error(L, "Invalid iterator index %d", i); lua_pop(L, 1); if (lua_getiuservalue(L, 1, 1) != LUA_TUSERDATA) { return luaL_error(L, "Missing world object for iterator"); } int world_index = lua_gettop(L); if (i > 0 && !iter->readonly) { update_last_index(L, world_index, 2, iter, i-1); } int mainkey = iter->k[0].id; unsigned int index[MAX_COMPONENT]; for (;;) { int idx = i++; index[0] = idx + 1; int ret = query_index(iter, 1, mainkey, idx, index); if (ret < 0) return 0; if (ret > 0) break; } lua_pushinteger(L, i); lua_rawseti(L, 2, 1); read_iter(L, world_index, 2, iter, index); lua_settop(L, 2); return 1; } static void create_key_cache(lua_State *L, struct group_key *k, struct field *f) { if (k->field_n == 0 // is tag or object? || (k->attrib & COMPONENT_FILTER)) { // existence or ref return; } if (k->field_n == 1 && f[0].key == NULL) { // value type switch (f[0].type) { case TYPE_INT: case TYPE_INT64: case TYPE_DWORD: case TYPE_WORD: case TYPE_BYTE: lua_pushinteger(L, 0); break; case TYPE_FLOAT: case TYPE_DOUBLE: lua_pushnumber(L, 0); break; case TYPE_BOOL: lua_pushboolean(L, 0); break; case TYPE_USERDATA: lua_pushlightuserdata(L, NULL); break; default: lua_pushnil(L); break; } } else { lua_createtable(L, 0, k->field_n); } lua_setfield(L, -2, k->name); } static int lpairs_group(lua_State *L) { struct group_iter *iter = lua_touserdata(L, 1); lua_pushcfunction(L, leach_group); lua_pushvalue(L, 1); lua_createtable(L, 2, iter->nkey); int i; int opt = 0; struct field *f = iter->f; for (i=0;inkey;i++) { struct group_key *k = &iter->k[i]; create_key_cache(L, k, f); f += k->field_n; if (k->attrib & COMPONENT_OPTIONAL) ++opt; } if (opt) { // create backup table in metatable lua_createtable(L, 0, opt); for (i=0;inkey;i++) { struct group_key *k = &iter->k[i]; if (k->attrib & COMPONENT_OPTIONAL) { lua_getfield(L, -2, k->name); lua_setfield(L, -2, k->name); } } lua_setmetatable(L, -2); } lua_pushinteger(L, 0); lua_rawseti(L, -2, 1); lua_pushinteger(L, iter->k[0].id); // mainkey lua_rawseti(L, -2, 2); return 3; } static int check_boolean(lua_State *L, const char * key) { int r = 0; switch (lua_getfield(L, -1, key)) { case LUA_TNIL: break; case LUA_TBOOLEAN: r = lua_toboolean(L, -1); break; default: return luaL_error(L, "Invalid boolean type %s", lua_typename(L, lua_type(L, -1))); } lua_pop(L, 1); return r; } static int is_value(lua_State *L, struct field *f) { switch (lua_getfield(L, -1, "type")) { case LUA_TNIL: lua_pop(L, 1); return 0; case LUA_TNUMBER: f->key = NULL; f->offset = 0; f->type = check_type(L); return 1; default: return luaL_error(L, "Invalid value type %s", lua_typename(L, lua_type(L, -1))); } } static int get_key(struct entity_world *w, lua_State *L, struct group_key *key, struct field *f) { if (lua_getfield(L, -1, "id") != LUA_TNUMBER) { return luaL_error(L, "Invalid id"); } key->id = lua_tointeger(L, -1); lua_pop(L, 1); if (key->id < 0 || key->id >= MAX_COMPONENT || w->c[key->id].cap == 0) { return luaL_error(L, "Invalid id %d", key->id); } if (lua_getfield(L, -1, "name") != LUA_TSTRING) { return luaL_error(L, "Invalid component name"); } key->name = lua_tostring(L, -1); lua_pop(L, 1); int attrib = 0; if (check_boolean(L, "r")) { attrib |= COMPONENT_IN; } if (check_boolean(L, "w")) { attrib |= COMPONENT_OUT; } if (check_boolean(L, "opt")) { attrib |= COMPONENT_OPTIONAL; } if (check_boolean(L, "exist")) { attrib |= COMPONENT_EXIST; } if (check_boolean(L, "absent")) { attrib |= COMPONENT_ABSENT; } if (check_boolean(L, "ref")) { attrib |= COMPONENT_REFINDEX; } key->attrib = attrib; if (is_value(L, f)) { key->field_n = 1; return 1; } else { int i = 0; int ttype; while ((ttype = lua_geti(L, -1, i+1)) != LUA_TNIL) { if (ttype != LUA_TTABLE) { return luaL_error(L, "Invalid field %d", i+1); } get_field(L, i+1, &f[i]); ++i; } key->field_n = i; lua_pop(L, 1); return i; } } static int lgroupiter(lua_State *L) { struct entity_world *w = getW(L); luaL_checktype(L, 2, LUA_TTABLE); int nkey = get_len(L, 2); int field_n = 0; int i; if (nkey == 0) { return luaL_error(L, "At least one key"); } if (nkey > MAX_COMPONENT) { return luaL_error(L, "Too many keys"); } for (i=0;inkey = nkey; iter->world = w; iter->readonly = 1; struct field *f = (struct field *)((char *)iter + header_size); iter->f = f; for (i=0; i< nkey; i++) { lua_geti(L, 2, i+1); int n = get_key(w, L, &iter->k[i], f); if (i>0 && (iter->k[i-1].attrib & COMPONENT_REFINDEX)) { iter->k[i].attrib |= COMPONENT_REFOBJECT; } struct component_pool *c = &w->c[iter->k[i].id]; if (c->stride == STRIDE_TAG && is_temporary(iter->k[i].attrib)) { return luaL_error(L, "%s is a tag, use %s?out instead", iter->k[i].name, iter->k[i].name); } f += n; lua_pop(L, 1); if (c->stride == STRIDE_LUA) { if (n != 0) return luaL_error(L, ".%s is object component, no fields needed", iter->k[i].name); iter->k[i].attrib |= COMPONENT_OBJECT; } else if (c->stride == STRIDE_ORDER) { if (i != 0) { return luaL_error(L, ".%s is an order key, must be main key", iter->k[i].name); } else if (iter->k[0].attrib & COMPONENT_OUT) { return luaL_error(L, ".%s is an order key, it should be readonly", iter->k[0].name); } } int attrib = iter->k[i].attrib; int readonly; if (attrib & COMPONENT_FILTER) readonly = 0; else readonly = (attrib & COMPONENT_IN) && !(attrib & COMPONENT_OUT); if (!readonly) iter->readonly = 0; } int mainkey_attrib = iter->k[0].attrib; if (mainkey_attrib & COMPONENT_ABSENT) { return luaL_error(L, "The main key can't be absent"); } if (luaL_newmetatable(L, "ENTITY_GROUPITER")) { lua_pushcfunction(L, lpairs_group); lua_setfield(L, -2, "__call"); } lua_setmetatable(L, -2); return 1; } static int lremove(lua_State *L) { struct entity_world *w = getW(L); luaL_checktype(L, 2, LUA_TTABLE); int iter = get_integer(L, 2, 1, "index") - 1; int mainkey = get_integer(L, 2, 2, "mainkey"); entity_remove_(w, mainkey, iter, L, 1); return 0; } static int lsortkey(lua_State *L) { struct entity_world *w = getW(L); int oid = check_cid(L, w, 2); int cid = check_cid(L, w, 3); entity_sort_key_(w, oid, cid, L, 1); return 0; } static int lorderkey(lua_State *L) { struct entity_world *w = getW(L); int oid = check_cid(L, w, 2); int cid = check_cid(L, w, 3); struct component_pool *c = &w->c[oid]; if (c->stride != STRIDE_ORDER) return luaL_error(L, "Need order component"); int n = get_len(L, 4); reserve_(w, oid, n, L, 1); struct component_pool *ref = &w->c[cid]; if (n > ref->n) { return luaL_error(L, "Invalid length of order array (%d/%d)", n, ref->n); } int i; for (i=0;i ref->n) { return luaL_error(L, "Invalid refid %d", refid); } c->id[i] = ref->id[refid - 1]; } c->n = n; return 0; } static int lobject(lua_State *L) { struct group_iter *iter = luaL_checkudata(L, 1, "ENTITY_GROUPITER"); int index = luaL_checkinteger(L, 3) - 1; int cid = iter->k[0].id; struct entity_world * w = iter->world; if (cid < 0 || cid >=MAX_COMPONENT) { return luaL_error(L, "Invalid object %d", cid); } lua_settop(L, 2); struct component_pool *c = &w->c[cid]; if (c->n <= index) { return luaL_error(L, "No object %d", cid); } if (c->stride == STRIDE_LUA) { // lua object if (lua_getiuservalue(L, 1, 1) != LUA_TUSERDATA) { return luaL_error(L, "No world"); } if (lua_getiuservalue(L, -1, cid * 2 + 2) != LUA_TTABLE) { return luaL_error(L, "Missing lua table for %d", cid); } if (lua_isnil(L, 2)) { lua_rawgeti(L, -1, index + 1); } else { lua_pushvalue(L, 2); lua_rawseti(L, -2, index + 1); lua_settop(L, 2); } return 1; } else if (c->stride == 0) { if (lua_type(L, 2) != LUA_TBOOLEAN) return luaL_error(L, "%s is a tag, need boolean", iter->k[0].name); if (!lua_toboolean(L, 2)) { entity_disable_tag_(w, cid, index, cid); } return 1; } else if (c->stride < 0) { return luaL_error(L, "Invalid object %d", cid); } struct field *f = iter->f; void * buffer = get_ptr(c, index); if (lua_isnoneornil(L, 2)) { // read object if (f->key == NULL) { // value type read_value(L, f, buffer); } else { lua_createtable(L, 0, iter->k[0].field_n); int lua_index = lua_gettop(L); read_component(L, iter->k[0].field_n, f, lua_index, buffer); } } else { // write object lua_pushvalue(L, 2); write_component_object(L, iter->k[0].field_n, f, buffer); } return 1; } static int lrelease(lua_State *L) { struct entity_world *w = getW(L); int cid = check_cid(L, w, 2); int refid = luaL_checkinteger(L, 3) - 1; int dead_tag = cid + 1; entity_enable_tag_(w, cid, refid, dead_tag, L, 1); return 0; } static int lreuse(lua_State *L) { struct entity_world *w = getW(L); int cid = check_cid(L, w, 2); int dead_tagid = cid + 1; struct component_pool *c = &w->c[dead_tagid]; if (c->stride != STRIDE_TAG) { return luaL_error(L, "%d is not a tag", dead_tagid); } if (c->n == 0) return 0; int id = entity_sibling_index_(w, dead_tagid, 0, cid); if (id == 0) return luaL_error(L, "Invalid ref component %d", cid); entity_disable_tag_(w, dead_tagid, 0, dead_tagid); lua_pushinteger(L, id); return 1; } static int find_boundary(int from, int to, unsigned int *a, unsigned int eid) { while(from < to) { int mid = (from + to)/2; unsigned int aa = a[mid]; if (aa == eid) return mid; else if (aa < eid) { from = mid + 1; } else { to = mid; } } return to; } static inline int next_removed_index(int removed_index, struct component_pool *removed, unsigned int *removed_eid) { for (;;) { ++removed_index; if (removed_index >= removed->n) { *removed_eid = 0; break; } unsigned int last_eid = *removed_eid; *removed_eid = removed->id[removed_index]; if (*removed_eid != last_eid) break; } return removed_index; } // remove reference object where id == 0 static void remove_unused_reference(lua_State *L, struct component_pool *c, int from) { int i; int to = from; for (i=from; i < c->n; i++) { if (c->id[i]) { c->id[to] = c->id[i]; lua_geti(L, -1, i+1); lua_seti(L, -2, to+1); ++to; } } for (i=to;i<=c->n;i++) { lua_pushnil(L); lua_seti(L, -2, i+1); } c->n = to; } static int lupdate_reference(lua_State *L) { struct entity_world *w = getW(L); struct component_pool *removed = &w->c[ENTITY_REMOVED]; if (removed->n == 0) // no removed entity return 0; int cid = check_cid(L, w, 2); struct component_pool *reference = &w->c[cid]; if (reference->n == 0) return 0; // no reference if (lua_getiuservalue(L, 1, cid * 2 + 2) != LUA_TTABLE) { return luaL_error(L, "Invalid reference component %d", cid); } int i; int removed_index = 0; unsigned int removed_eid = removed->id[removed_index]; int index = find_boundary(0, reference->n, reference->id, removed_eid); int reference_index = index + 1; int removed_reference = 0; for (i=index; i< reference->n; i++) { int rtype = lua_geti(L, -1, i+1); if (rtype != LUA_TBOOLEAN && rtype != LUA_TTABLE) { // false means removed reference return luaL_error(L, "Invalid reference object"); } if (removed_eid) { while (removed_eid < reference->id[i]) { removed_index = next_removed_index(removed_index, removed, &removed_eid); } } if (removed_eid == reference->id[i]) { // removed reference, clear reference id lua_pushnil(L); removed_index = next_removed_index(removed_index, removed, &removed_eid); } else { // update reference id lua_pushinteger(L, reference_index); ++reference_index; } if (rtype == LUA_TBOOLEAN) { // set id = 0, so removed_reference() can remove them --reference_index; reference->id[i] = 0; if (removed_reference == 0) { removed_reference = i + 1; } lua_pop(L, 2); } else { lua_seti(L, -2, 1); lua_pop(L, 1); } } if (removed_reference) { remove_unused_reference(L, reference, removed_reference - 1); } return 0; } static int ldumpid(lua_State *L) { struct entity_world *w = getW(L); int cid = check_cid(L, w, 2); struct component_pool *c = &w->c[cid]; lua_createtable(L, c->n, 0); int i; for (i=0;in;i++) { lua_pushinteger(L, c->id[i]); lua_rawseti(L, -2, i+1); } return 1; } LUAMOD_API int luaopen_ecs_core(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { { "_world", lnew_world }, { NULL, NULL }, }; luaL_newlib(L,l); lua_pushinteger(L, MAX_COMPONENT-1); lua_setfield(L, -2, "_MAXTYPE"); if (luaL_newmetatable(L, "ENTITY_WORLD")) { luaL_Reg l[] = { { "__index", NULL }, { "memory", lcount_memory }, { "collect", lcollect_memory }, { "_newtype",lnew_type }, { "_newentity", lnew_entity }, { "_addcomponent", ladd_component }, { "_update", lupdate }, { "_clear", lclear_type }, { "_context", lcontext }, { "_groupiter", lgroupiter }, { "remove", lremove }, { "_sortkey", lsortkey }, { "_orderkey", lorderkey }, { "_object", lobject }, { "_sync", lsync }, { "_release", lrelease }, { "_reuse", lreuse }, { "_update_reference", lupdate_reference }, { "_dumpid", ldumpid }, { NULL, NULL }, }; luaL_setfuncs(L,l,0); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); } else { return luaL_error(L, "ENTITY_WORLD exist"); } lua_setfield(L, -2, "_METHODS"); lua_pushinteger(L, TYPE_INT); lua_setfield(L, -2, "_TYPEINT"); lua_pushinteger(L, TYPE_FLOAT); lua_setfield(L, -2, "_TYPEFLOAT"); lua_pushinteger(L, TYPE_BOOL); lua_setfield(L, -2, "_TYPEBOOL"); lua_pushinteger(L, TYPE_INT64); lua_setfield(L, -2, "_TYPEINT64"); lua_pushinteger(L, TYPE_DWORD); lua_setfield(L, -2, "_TYPEDWORD"); lua_pushinteger(L, TYPE_WORD); lua_setfield(L, -2, "_TYPEWORD"); lua_pushinteger(L, TYPE_BYTE); lua_setfield(L, -2, "_TYPEBYTE"); lua_pushinteger(L, TYPE_DOUBLE); lua_setfield(L, -2, "_TYPEDOUBLE"); lua_pushinteger(L, TYPE_USERDATA); lua_setfield(L, -2, "_TYPEUSERDATA"); lua_pushinteger(L, STRIDE_LUA); lua_setfield(L, -2, "_LUAOBJECT"); lua_pushinteger(L, ENTITY_REMOVED); lua_setfield(L, -2, "_REMOVED"); lua_pushinteger(L, STRIDE_ORDER); lua_setfield(L, -2, "_ORDERKEY"); lua_pushlightuserdata(L, NULL); lua_setfield(L, -2, "NULL"); return 1; } #ifdef TEST_LUAECS #include #define COMPONENT_VECTOR2 1 #define TAG_MARK 2 #define COMPONENT_ID 3 #define SINGLETON_STRING 4 struct vector2 { float x; float y; }; struct id { int v; }; static int ltest(lua_State *L) { struct ecs_context *ctx = lua_touserdata(L, 1); struct vector2 *v; int i; for (i=0;(v=(struct vector2 *)entity_iter(ctx, COMPONENT_VECTOR2, i));i++) { printf("vector2 %d: x=%f y=%f\n", i, v->x, v->y); struct id * id = (struct id *)entity_sibling(ctx, COMPONENT_VECTOR2, i, COMPONENT_ID); if (id) { printf("\tid = %d\n", id->v); } void * mark = entity_sibling(ctx, COMPONENT_VECTOR2, i, TAG_MARK); if (mark) { printf("\tMARK\n"); } } return 0; } static int lsum(lua_State *L) { struct ecs_context *ctx = lua_touserdata(L, 1); struct vector2 *v; int i; float s = 0; for (i=0;(v=(struct vector2 *)entity_iter(ctx, COMPONENT_VECTOR2, i));i++) { s += v->x + v->y; } lua_pushnumber(L, s); return 1; } static int lget(lua_State *L) { struct ecs_context *ctx = lua_touserdata(L, 1); const char *ret = (const char *)entity_ref_object(ctx, SINGLETON_STRING, 1); if (ret) { lua_pushfstring(L, "[string:%s]", ret); } return 1; } struct userdata_t { unsigned char a; void *b; }; static int ltestuserdata(lua_State *L) { struct ecs_context *ctx = lua_touserdata(L, 1); struct userdata_t *ud = entity_iter(ctx, 1, 0); ud->a = 1 - ud->a; ud->b = ctx; return 0; } LUAMOD_API int luaopen_ecs_ctest(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { { "test", ltest }, { "sum", lsum }, { "get", lget }, { "testuserdata", ltestuserdata }, { NULL, NULL }, }; luaL_newlib(L, l); return 1; } #endif