diff --git a/framework/Makefile b/framework/Makefile index 68904bb..c9faf4e 100644 --- a/framework/Makefile +++ b/framework/Makefile @@ -28,5 +28,9 @@ cleanall: testluaclib: $(SKYNET_ROOT)3rd/lua/lua $(SKYNET_DEPS)testluaclib.lua + +simulation: + $(SKYNET_ROOT)3rd/lua/lua simulation/simulation.lua + #-------------------------------------------------------------# #-------------------------------------------------------------# diff --git a/framework/lualib-src/lua-ecs/luaecs.c b/framework/lualib-src/lua-ecs/luaecs.c old mode 100755 new mode 100644 index 90c76c9..fd9bbe9 --- a/framework/lualib-src/lua-ecs/luaecs.c +++ b/framework/lualib-src/lua-ecs/luaecs.c @@ -12,6 +12,7 @@ #define MAX_COMPONENT 256 #define ENTITY_REMOVED 0 #define DEFAULT_SIZE 128 +#define STRIDE_LUA -1 #define DUMMY_PTR (void *)(uintptr_t)(~0) struct component_pool @@ -19,7 +20,7 @@ struct component_pool int cap; int n; int count; - int stride; + int stride; // -1 means lua object int last_lookup; unsigned int *id; void *buffer; @@ -96,13 +97,13 @@ lcount_memory(lua_State *L) if (c->id) { sz += c->cap * sizeof(unsigned int); + msz += c->n * sizeof(unsigned int); } - if (c->buffer) + if (c->buffer != DUMMY_PTR) { sz += c->cap * c->stride; + msz += c->cap * c->stride; } - - msz += c->n * (sizeof(unsigned int) + c->stride); } lua_pushinteger(L, sz); lua_pushinteger(L, msz); @@ -124,16 +125,13 @@ shrink_component_pool(lua_State *L, struct component_pool *c, int id) lua_pushnil(L); lua_setiuservalue(L, 1, id * 2 + 2); } - if (c->n < c->cap) + 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); - if (c->stride > 0) - { - c->buffer = lua_newuserdatauv(L, c->n * c->stride, 0); - lua_setiuservalue(L, 1, id * 2 + 2); - } + c->buffer = lua_newuserdatauv(L, c->n * c->stride, 0); + lua_setiuservalue(L, 1, id * 2 + 2); } } @@ -149,13 +147,12 @@ lcollect_memory(lua_State *L) return 0; } -static void -add_component_(lua_State *L, struct entity_world *w, int cid, unsigned int eid, const void *buffer) +static int +add_component_id_(lua_State *L, struct entity_world *w, int cid, unsigned int eid) { struct component_pool *pool = &w->c[cid]; int cap = pool->cap; int index = pool->n; - int stride = pool->stride; if (pool->n == 0) { if (pool->id == NULL) @@ -165,33 +162,57 @@ add_component_(lua_State *L, struct entity_world *w, int cid, unsigned int eid, } if (pool->buffer == NULL) { - pool->buffer = lua_newuserdatauv(L, cap * stride, 0); + pool->buffer = lua_newuserdatauv(L, cap * pool->stride, 0); + lua_setiuservalue(L, 1, cid * 2 + 2); + } + else if (pool->stride == STRIDE_LUA) + { + lua_newtable(L); lua_setiuservalue(L, 1, cid * 2 + 2); } } - else + else if (pool->n >= pool->cap) { - 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, 1, cid * 2 + 1); + memcpy(newid, pool->id, cap * sizeof(unsigned int)); + pool->id = newid; + int stride = pool->stride; + if (stride > 0) { - // expand pool - int newcap = cap * 3 / 2; - unsigned int *newid = (unsigned int *)lua_newuserdatauv(L, newcap * sizeof(unsigned int), 0); - lua_setiuservalue(L, 1, cid * 2 + 1); - memcpy(newid, pool->id, cap * sizeof(unsigned int)); - pool->id = newid; - if (stride > 0) - { - void *newbuffer = lua_newuserdatauv(L, newcap * stride, 0); - lua_setiuservalue(L, 1, cid * 2 + 2); - memcpy(newbuffer, pool->buffer, cap * stride); - pool->buffer = newbuffer; - } - pool->cap = newcap; + void *newbuffer = lua_newuserdatauv(L, newcap * stride, 0); + lua_setiuservalue(L, 1, cid * 2 + 2); + memcpy(newbuffer, pool->buffer, cap * stride); + pool->buffer = newbuffer; } + pool->cap = newcap; } ++pool->n; pool->id[index] = eid; - memcpy((char *)pool->buffer + index * stride, buffer, stride); + 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 c->buffer; +} + +static void * +add_component_(lua_State *L, struct entity_world *w, int cid, unsigned int eid, const void *buffer) +{ + int index = add_component_id_(L, w, cid, eid); + struct component_pool *pool = &w->c[cid]; + assert(pool->stride != STRIDE_LUA); + void *ret = get_ptr(pool, index); + if (buffer) + memcpy(ret, buffer, pool->stride); + return ret; } static inline int @@ -212,14 +233,33 @@ 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); - size_t sz; - const char *buffer = lua_tolstring(L, 4, &sz); int stride = w->c[cid].stride; - if ((buffer == NULL && stride > 0) || (sz != stride)) + if (stride <= 0) + { + int index = add_component_id_(L, w, cid, eid); + if (stride == STRIDE_LUA) + { + // lua object + lua_settop(L, 4); + if (lua_getiuservalue(L, 1, cid * 2 + 2) != LUA_TTABLE) + { + luaL_error(L, "Missing lua table for %d", cid); + } + lua_insert(L, -2); + lua_rawseti(L, -2, index + 1); + } + } + else { - return luaL_error(L, "Invalid data (size=%d/%d) for type %d", (int)sz, stride, cid); + size_t sz; + const char *buffer = lua_tolstring(L, 4, &sz); + int stride = w->c[cid].stride; + if (buffer == NULL || sz != stride) + { + return luaL_error(L, "Invalid data (size=%d/%d) for type %d", (int)sz, stride, cid); + } + add_component_(L, w, cid, eid, buffer); } - add_component_(L, w, cid, eid, buffer); return 0; } @@ -244,7 +284,7 @@ remove_entity_(struct entity_world *w, int cid, int index, void *L) if (index < 0 || index >= c->count) luaL_error((lua_State *)L, "Invalid index %d/%d", index, c->count); unsigned int eid = c->id[index]; - add_component_((lua_State *)L, w, ENTITY_REMOVED, eid, NULL); + add_component_id_((lua_State *)L, w, ENTITY_REMOVED, eid); } static int @@ -392,18 +432,39 @@ rearrange(struct entity_world *w) w->max_id = new_id + w->max_id - w->wrap_begin - 1; } +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]; - memcpy((char *)pool->buffer + to * pool->stride, (char *)pool->buffer + from * pool->stride, pool->stride); + 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(struct component_pool *pool, struct component_pool *removed) +remove_all(lua_State *L, struct component_pool *pool, struct component_pool *removed, int cid) { int index = 0; int i; @@ -427,12 +488,42 @@ remove_all(struct component_pool *pool, struct component_pool *removed) if (count > 0) { index = 0; - for (i = 0; i < pool->n; i++) + if (pool->stride == STRIDE_LUA) + { + if (lua_getiuservalue(L, 1, cid * 2 + 2) != LUA_TTABLE) + { + luaL_error(L, "Missing lua object table for type %d", id); + } + for (i = 0; i < pool->n; i++) + { + if (pool->id[i] != 0) + { + move_object(L, pool, i, index); + ++index; + } + } + lua_pop(L, 1); // pop lua object table + } + else if (pool->stride == 0) { - if (pool->id[i] != 0) + for (i = 0; i < pool->n; i++) { - move_item(pool, i, index); - ++index; + if (pool->id[i] != 0) + { + move_tag(pool, i, index); + ++index; + } + } + } + else + { + for (i = 0; i < pool->n; i++) + { + if (pool->id[i] != 0) + { + move_item(pool, i, index); + ++index; + } } } pool->n -= count; @@ -454,7 +545,7 @@ lupdate(lua_State *L) { struct component_pool *pool = &w->c[i]; if (pool->n > 0) - remove_all(pool, removed); + remove_all(L, pool, removed, i); } removed->n = 0; removed->count = 0; @@ -482,7 +573,7 @@ entity_iter_(struct entity_world *w, int cid, int index) assert(index >= 0); if (index >= c->count) return NULL; - return (char *)c->buffer + c->stride * index; + return get_ptr(c, index); } static void @@ -514,20 +605,52 @@ entity_sibling_(struct entity_world *w, int cid, int index, int slibling_id) if (result_index >= 0) { c->last_lookup = result_index; - return (char *)c->buffer + c->stride * result_index; + return get_ptr(c, result_index); } return NULL; } -static void +static int +entity_sibling_index_(struct entity_world *w, int cid, int index, int slibling_id) +{ + struct component_pool *c = &w->c[cid]; + if (index < 0 || index >= c->count) + return 0; + unsigned int eid = c->id[index]; + c = &w->c[slibling_id]; + 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 slibling_id, const void *buffer, void *L) { struct component_pool *c = &w->c[cid]; assert(index >= 0 && index < c->count); unsigned int eid = c->id[index]; // todo: pcall add_component_ - add_component_((lua_State *)L, w, slibling_id, eid, buffer); + assert(c->stride != STRIDE_LUA); + void *ret = add_component_((lua_State *)L, w, slibling_id, eid, buffer); + c->count = c->n; + return ret; +} + +static int +entity_add_sibling_index_(lua_State *L, struct entity_world *w, int cid, int index, int slibling_id) +{ + struct component_pool *c = &w->c[cid]; + assert(index >= 0 && index < c->count); + unsigned int eid = c->id[index]; + // todo: pcall add_component_ + assert(c->stride == STRIDE_LUA); + int ret = add_component_id_(L, w, slibling_id, eid); c->count = c->n; + return ret; } static int @@ -568,6 +691,9 @@ lcontext(lua_State *L) 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); + struct component_pool *c = &w->c[cid]; + if (c->stride == STRIDE_LUA) + return luaL_error(L, "Can't iterate lua component in C (%d)", cid); } return 1; } @@ -596,34 +722,28 @@ struct field int type; }; -struct simple_iter -{ - int id; - int field_n; - struct entity_world *world; - struct field f[1]; -}; - -static void -get_field(lua_State *L, int index, int i, struct field *f) +static int +check_type(lua_State *L) { - if (lua_geti(L, index, i) != LUA_TTABLE) + int type = lua_tointeger(L, -1); + if (type != TYPE_INT && + type != TYPE_FLOAT && + type != TYPE_BOOL) { - luaL_error(L, "Invalid field %d", i); + 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 = lua_tointeger(L, -1); - if (f->type != TYPE_INT && - f->type != TYPE_FLOAT && - f->type != TYPE_BOOL) - { - luaL_error(L, "Invalid field %d [1] type(%d)", i, f->type); - } - lua_pop(L, 1); + f->type = check_type(L); if (lua_geti(L, -1, 2) != LUA_TSTRING) { @@ -643,142 +763,609 @@ get_field(lua_State *L, int index, int i, struct field *f) } static void -write_component(lua_State *L, struct simple_iter *iter, int index, char *buffer) +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; + } + 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; + 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 < iter->field_n; i++) + for (i = 0; i < field_n; i++) { - int luat = lua_getfield(L, index, iter->f[i].key); - char *ptr = buffer + iter->f[i].offset; - switch (iter->f[i].type) + 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 + +struct group_key +{ + const char *name; + int id; + int field_n; + int attrib; +}; + +static inline int +is_temporary(int attrib) +{ + 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 + 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) { - case TYPE_INT: - if (!lua_isinteger(L, -1)) - luaL_error(L, "Invalid .%s type %s (int)", iter->f[i].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)", iter->f[i].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)", iter->f[i].key, lua_typename(L, luat)); - *(unsigned char *)ptr = lua_toboolean(L, -1); + // 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_in_field(lua_State *L, int lua_index, const char *name, 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 void -read_component(lua_State *L, struct simple_iter *iter, int index, const char *buffer) +update_last_index(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx) { int i; - for (i = 0; i < iter->field_n; i++) + int mainkey = iter->k[0].id; + struct component_pool *c = &iter->world->c[mainkey]; + if ((iter->k[0].attrib & COMPONENT_OUT) && get_write_component(L, lua_index, iter->k[0].name, iter->f, c)) { - const char *ptr = buffer + iter->f[i].offset; - switch (iter->f[i].type) + struct component_pool *c = &iter->world->c[mainkey]; + if (c->count <= idx) { - 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; - default: - // never here - lua_pushnil(L); - break; + 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_in_field(L, lua_index, iter->k[0].name, iter->k[0].field_n, iter->f, buffer); + } + } + + struct field *f = iter->f + iter->k[0].field_n; + + for (i = 1; i < iter->nkey; i++) + { + struct group_key *k = &iter->k[i]; + struct component_pool *c = &iter->world->c[k->id]; + if ((k->attrib & COMPONENT_OUT) && get_write_component(L, lua_index, k->name, f, c)) + { + int 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_in_field(L, lua_index, k->name, k->field_n, f, buffer); + } } - lua_setfield(L, index, iter->f[i].key); + else if (is_temporary(k->attrib) && get_write_component(L, lua_index, k->name, f, c)) + { + if (c->stride == STRIDE_LUA) + { + int index = entity_add_sibling_index_(L, 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); + } + else + { + void *buffer = entity_add_sibling_(iter->world, mainkey, idx, k->id, NULL, L); + write_component_in_field(L, lua_index, k->name, k->field_n, f, buffer); + } + } + f += k->field_n; } } +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) + { + luaL_error(L, ".%s is missing", name); + } + read_component(L, n, f, lua_gettop(L), buffer); + lua_pop(L, 1); +} + static int -leach_simple(lua_State *L) +leach_group(lua_State *L) { - struct simple_iter *iter = lua_touserdata(L, 1); + struct group_iter *iter = lua_touserdata(L, 1); if (lua_rawgeti(L, 2, 1) != LUA_TNUMBER) { - return luaL_error(L, "Invalid simple iterator"); + 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 (i > 0) + + if (lua_getiuservalue(L, 1, 1) != LUA_TUSERDATA) { - void *write_buffer = entity_iter_(iter->world, iter->id, i - 1); - if (write_buffer == NULL) - return luaL_error(L, "Can't write to index %d", i); - write_component(L, iter, 2, (char *)write_buffer); + return luaL_error(L, "Missing world object for iterator"); } - void *read_buffer = entity_iter_(iter->world, iter->id, i++); - if (read_buffer == NULL) + int world_index = lua_gettop(L); + + if (i > 0 && !iter->readonly) { - return 0; + update_last_index(L, world_index, 2, iter, i - 1); } + int mainkey = iter->k[0].id; + unsigned int index[MAX_COMPONENT]; + int j; + do + { + if (iter->world->c[mainkey].count <= i) + { + return 0; + } + index[0] = i + 1; + for (j = 1; j < iter->nkey; j++) + { + struct group_key *k = &iter->k[j]; + if (!is_temporary(k->attrib)) + { + index[j] = entity_sibling_index_(iter->world, mainkey, i, k->id); + if (index[j] == 0) + { + if (!(k->attrib & COMPONENT_OPTIONAL)) + { + // required. try next + break; + } + } + } + else + { + index[j] = 0; + } + } + ++i; + } while (j < iter->nkey); + lua_pushinteger(L, i); lua_rawseti(L, 2, 1); - read_component(L, iter, 2, (const char *)read_buffer); + + struct field *f = iter->f; + + for (i = 0; i < iter->nkey; i++) + { + struct group_key *k = &iter->k[i]; + 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) + { + return luaL_error(L, "Missing lua table for %d", k->id); + } + + lua_rawgeti(L, -1, index[i]); + lua_setfield(L, 2, k->name); + lua_pop(L, 1); + } + else + { + lua_pushnil(L); + lua_setfield(L, 2, k->name); + } + } + else if (k->attrib & COMPONENT_IN) + { + if (index[i]) + { + void *ptr = get_ptr(c, index[i] - 1); + read_component_in_field(L, 2, k->name, k->field_n, f, ptr); + } + else + { + lua_pushnil(L); + lua_setfield(L, 2, k->name); + } + } + else if (index[i] == 0 && !is_temporary(k->attrib)) + { + lua_pushnil(L); + lua_setfield(L, 2, k->name); + } + f += k->field_n; + } 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? + return; + } + if (k->field_n == 1 && f[0].key == NULL) + { + // value type + switch (f[0].type) + { + case TYPE_INT: + lua_pushinteger(L, 0); + break; + case TYPE_FLOAT: + lua_pushnumber(L, 0); + break; + case TYPE_BOOL: + lua_pushboolean(L, 0); + break; + default: + lua_pushnil(L); + break; + } + } + else + { + lua_createtable(L, 0, k->field_n); + } + lua_setfield(L, -2, k->name); +} + static int -lpairs_simple(lua_State *L) +lpairs_group(lua_State *L) { - struct simple_iter *iter = lua_touserdata(L, 1); - lua_pushcfunction(L, leach_simple); + struct group_iter *iter = lua_touserdata(L, 1); + lua_pushcfunction(L, leach_group); lua_pushvalue(L, 1); - lua_createtable(L, 1, iter->field_n); + lua_createtable(L, 1, iter->nkey); + int i; + int opt = 0; + struct field *f = iter->f; + for (i = 0; i < iter->nkey; 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; i < iter->nkey; 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); return 3; } static int -lsimpleiter(lua_State *L) +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 || key->id == ENTITY_REMOVED || 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; + } + 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); - lua_len(L, 2); - if (lua_type(L, -1) != LUA_TNUMBER) + int nkey = get_len(L, 2); + int field_n = 0; + int i; + if (nkey == 0) { - return luaL_error(L, "Invalid fields"); + return luaL_error(L, "At least one key"); } - int n = lua_tointeger(L, -1); - if (n <= 0) + if (nkey > MAX_COMPONENT) { - return luaL_error(L, "Invalid fields number %d", n); + return luaL_error(L, "Too mant keys"); } - lua_pop(L, 1); - size_t sz = sizeof(struct simple_iter) + (n - 1) * sizeof(struct field); - struct simple_iter *iter = (struct simple_iter *)lua_newuserdatauv(L, sz, 1); + for (i = 0; i < nkey; i++) + { + if (lua_geti(L, 2, i + 1) != LUA_TTABLE) + { + return luaL_error(L, "index %d is not a table", i); + } + field_n += get_len(L, -1); + lua_pop(L, 1); + } + size_t header_size = sizeof(struct group_iter) + sizeof(struct group_key) * (nkey - 1); + const int align_size = sizeof(void *); + // align + header_size = (header_size + align_size - 1) & ~(align_size - 1); + size_t size = header_size + field_n * sizeof(struct field); + struct group_iter *iter = (struct group_iter *)lua_newuserdatauv(L, size, 1); + // refer world lua_pushvalue(L, 1); lua_setiuservalue(L, -2, 1); + iter->nkey = nkey; iter->world = w; - iter->field_n = n; - if (lua_getfield(L, 2, "id") != LUA_TNUMBER) + iter->readonly = 1; + struct field *f = (struct field *)((char *)iter + header_size); + iter->f = f; + for (i = 0; i < nkey; i++) { - return luaL_error(L, "Invalid id"); + lua_geti(L, 2, i + 1); + int n = get_key(w, L, &iter->k[i], f); + struct component_pool *c = &w->c[iter->k[i].id]; + 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; + } + int attrib = iter->k[i].attrib; + int readonly = (attrib & COMPONENT_IN) && !(attrib & COMPONENT_OUT); + if (!readonly) + iter->readonly = 0; } - iter->id = lua_tointeger(L, -1); - lua_pop(L, 1); - if (iter->id < 0 || iter->id >= MAX_COMPONENT || iter->id == ENTITY_REMOVED || w->c[iter->id].cap == 0) + if (iter->k[0].attrib & COMPONENT_OPTIONAL) { - return luaL_error(L, "Invalid id %d", iter->id); + return luaL_error(L, "The first key should not be optional"); } - int i; - for (i = 0; i < n; i++) + if (!(iter->k[0].attrib & COMPONENT_IN) && !(iter->k[0].attrib & COMPONENT_OUT)) { - get_field(L, 2, i + 1, &iter->f[i]); + return luaL_error(L, "The main key can't be temporary"); } - if (luaL_newmetatable(L, "ENTITY_SIMPLEITER")) + if (luaL_newmetatable(L, "ENTITY_GROUPITER")) { - lua_pushcfunction(L, lpairs_simple); + lua_pushcfunction(L, lpairs_group); lua_setfield(L, -2, "__call"); } lua_setmetatable(L, -2); @@ -815,7 +1402,7 @@ luaopen_ecs_core(lua_State *L) {"update", lupdate}, {"clear", lclear_type}, {"_context", lcontext}, - {"_simpleiter", lsimpleiter}, + {"_groupiter", lgroupiter}, {NULL, NULL}, }; luaL_setfuncs(L, l, 0); @@ -833,6 +1420,9 @@ luaopen_ecs_core(lua_State *L) lua_setfield(L, -2, "_TYPEFLOAT"); lua_pushinteger(L, TYPE_BOOL); lua_setfield(L, -2, "_TYPEBOOL"); + lua_pushinteger(L, STRIDE_LUA); + lua_setfield(L, -2, "_LUAOBJECT"); + return 1; } diff --git a/framework/lualib-src/lua-ecs/luaecs.h b/framework/lualib-src/lua-ecs/luaecs.h old mode 100755 new mode 100644 index 99bc5bf..55f9353 --- a/framework/lualib-src/lua-ecs/luaecs.h +++ b/framework/lualib-src/lua-ecs/luaecs.h @@ -10,7 +10,7 @@ struct ecs_capi void *(*iter)(struct entity_world *w, int cid, int index); void (*clear_type)(struct entity_world *w, int cid); void *(*sibling)(struct entity_world *w, int cid, int index, int slibling_id); - void (*add_sibling)(struct entity_world *w, int cid, int index, int slibling_id, const void *buffer, void *L); + void *(*add_sibling)(struct entity_world *w, int cid, int index, int slibling_id, const void *buffer, void *L); void (*remove)(struct entity_world *w, int cid, int index, void *L); }; @@ -51,12 +51,12 @@ entity_sibling(struct ecs_context *ctx, int cid, int index, int slibling_id) return ctx->api->sibling(ctx->world, ctx->cid[cid], index, ctx->cid[slibling_id]); } -static inline void +static inline void * entity_add_sibling(struct ecs_context *ctx, int cid, int index, int slibling_id, const void *buffer) { check_id_(ctx, cid); check_id_(ctx, slibling_id); - ctx->api->add_sibling(ctx->world, ctx->cid[cid], index, ctx->cid[slibling_id], buffer, ctx->L); + return ctx->api->add_sibling(ctx->world, ctx->cid[cid], index, ctx->cid[slibling_id], buffer, ctx->L); } static inline void diff --git a/framework/lualib-src/lua-ecs/test.lua b/framework/lualib-src/lua-ecs/test.lua new file mode 100644 index 0000000..b1ffd4e --- /dev/null +++ b/framework/lualib-src/lua-ecs/test.lua @@ -0,0 +1,165 @@ +local ecs = require "ecs" + +local N = 1 + +local w = ecs.world() +print("memory:", w:memory()) + +w:register{ + name = "vector", + "x:float", + "y:float", +} + +w:register{ + name = "mark", +} + +w:register{ + name = "id", + type = "int", +} + +w:register{ + name = "object", + type = "lua", +} + +w:new{ + object = "Hello", +} + +local t = {} +for i = 1, N do + w:new{ + vector = { + x = 1, + y = 2, + }, + } + t[i] = { + x = 1, + y = 2, + } +end + +w:update() + +local function swap_c() + for v in w:select "vector:update" do + local vec = v.vector + vec.x, vec.y = vec.y, vec.x + end +end + +local function swap_lua() + for _, v in ipairs(t) do + v.x, v.y = v.y, v.x + end +end + +local function timing(f) + local c = os.clock() + for i = 1, 100 do + f() + end + return os.clock() - c +end + +print("memory:", w:memory()) + +print("CSWAP", timing(swap_c)) +print("LUASWAP", timing(swap_lua)) + +w:new{ + vector = { + x = 3, + y = 4, + }, + id = 100, +} + +table.insert(t, { + x = 3, + y = 4, +}) + +w:new{ + vector = { + x = 5, + y = 6, + }, + mark = true, +} + +table.insert(t, { + x = 5, + y = 6, +}) + +w:update() + +local context = w:context{"vector", "mark", "id"} +local test = require "ecs.ctest" +local function csum() + return test.sum(context) +end +print("csum = ", csum()) + +local function luasum() + local s = 0 + for v in w:select "vector:in" do + s = s + v.vector.x + v.vector.y + end + return s +end + +print("luasum = ", luasum()) + +local function luanativesum() + local s = 0 + for _, v in ipairs(t) do + s = s + v.x + v.y + end + return s +end + +print("lnative sum = ", luanativesum()) + +print("CSUM", timing(csum)) +print("LUASUM", timing(luasum)) +print("LNATIVESUM", timing(luanativesum)) + +print "vector:update" +for v in w:select "vector:update" do + local vec = v.vector + print(vec.x, vec.y) + vec.x, vec.y = vec.y, vec.x +end + +print "vector:in id?out" +for v in w:select "vector:in id?out" do + print(v.vector.x, v.vector.y, v.id) + if v.id then + v.id = 200 + end +end + +print "vector:in id:in" + +for v in w:select "vector:in id:in" do + print(v.vector.x, v.vector.y, v.id) +end + +print "object:update" + +for v in w:select "object:update" do + print(v.object) + v.object = v.object .. " world" +end + +print "object:in" + +for v in w:select "object:in" do + print(v.object) +end diff --git a/framework/lualib/3rd/zeus/ecs.lua b/framework/lualib/3rd/zeus/ecs.lua old mode 100755 new mode 100644 index 3df92f4..b8865ac --- a/framework/lualib/3rd/zeus/ecs.lua +++ b/framework/lualib/3rd/zeus/ecs.lua @@ -1,30 +1,63 @@ --- https://github.com/cloudwu/luaecs - local ecs = require "ecs.core" +local function get_attrib(opt, inout) + local desc = {} + if opt == "?" then + desc.opt = true + else + assert(opt == ":") + end + if inout == "in" then + desc.r = true + elseif inout == "out" then + desc.w = true + elseif inout == "update" then + desc.r = true + desc.w = true + else + assert(inout == "temp") + end + return desc +end + local function cache_world(obj, k) local c = { typenames = {}, id = 0, - each = {}, + select = {}, } - local function cache_each(each, key) - local tc = assert(c.typenames[key]) - local type_desc = { - id = tc.id, - } - for i, _ in ipairs(tc) do - type_desc[i] = tc[i] + local function gen_select_pat(pat) + local typenames = c.typenames + local desc = {} + local idx = 1 + for key, opt, inout in pat:gmatch "([_%w]+)([:?])(%l+)" do + local tc = assert(typenames[key]) + local a = get_attrib(opt, inout) + a.name = tc.name + a.id = tc.id + a.type = tc.type + local n = #tc + for i = 1, #tc do + a[i] = tc[i] + end + desc[idx] = a + idx = idx + 1 end - each[key] = k:_simpleiter(type_desc) - return each[key] + return desc end - setmetatable(c.each, { + local function cache_select(cache, pat) + local pat_desc = gen_select_pat(pat) + cache[pat] = k:_groupiter(pat_desc) + return cache[pat] + end + + setmetatable(c.select, { __mode = "kv", - __index = cache_each, + __index = cache_select, }) + obj[k] = c return c end @@ -96,7 +129,11 @@ do -- newtype for i, v in ipairs(typeclass) do c[i] = align(c, parse(v)) end - if c.size > 0 then + if typeclass.type == "lua" then + assert(c.size == 0) + c.size = ecs._LUAOBJECT + c.islua = true + elseif c.size > 0 then align_struct(c, typeclass[1][1]) local pack = "!4=" for i = 1, #c do @@ -110,6 +147,7 @@ do -- newtype c.type = t c.size = typesize[t] c.pack = typepack[t] + c[1] = {t, "v", 0} else c.tag = true end @@ -132,7 +170,9 @@ function M:new(obj) if not tc then error("Invalid key : " .. k) end - if tc.tag then + if tc.islua then + self:_addcomponent(eid, tc.id, v) + elseif tc.tag then self:_addcomponent(eid, tc.id) elseif tc.type then self:_addcomponent(eid, tc.id, string.pack(tc.pack, mapbool[v] or v)) @@ -159,11 +199,8 @@ function M:context(t) return self:_context(id) end -function M:each(name) - local ctx = context[self] - local typenames = ctx.typenames - local c = assert(typenames[name]) - return ctx.each[name]() +function M:select(pat) + return context[self].select[pat]() end return ecs diff --git a/framework/simulation/auth.lua b/framework/simulation/auth.lua new file mode 100644 index 0000000..7a5940a --- /dev/null +++ b/framework/simulation/auth.lua @@ -0,0 +1,50 @@ +local socket = require 'socket' +local crypt = require "skynet.crypt" + +local function encode_token(token) + return string.format("%s@%s:%s:%s", crypt.base64encode(token.user), crypt.base64encode(token.worldId), + crypt.base64encode(token.pass), crypt.base64encode(token.pf)) +end + +local token = { + pf = '1010', + user = 'teefe', + pwd = '123456789', + worldId = 1, +} + +return function(login_host, login_port) + local sock = socket.connect(login_host or '127.0.0.1', login_port or 9527) + local challenge = crypt.base64decode(sock:receive('*l')) + local clientkey = crypt.randomkey() + sock:send(crypt.base64encode(crypt.dhexchange(clientkey)) .. '\n') + + local line = crypt.base64decode(sock:receive '*l') + local secret = crypt.dhsecret(line, clientkey) + + local hmac = crypt.hmac64(challenge, secret) + -- 5. 回应服务器的握手挑战码,确认握手正常 + sock:send(crypt.base64encode(hmac) .. '\n') + + -- 6. DES加密发送 token串 + local etoken = crypt.desencode(secret, encode_token(token)) + sock:send(crypt.base64encode(etoken) .. '\n') + -- 服务器解密后调用定制的auth和login handler处理客户端请求 + + -- 7. 从服务器读取 登录结果: 状态码 和subid + local result = sock:receive '*l' + local code = tonumber(string.sub(result, 1, 3)) + assert(code == 200, "auth error:" .. tostring(code)) + sock:close() + + local substr = crypt.base64decode(string.sub(result, 5)) + local ip, port, uid, subid = substr:match("([^:]+):([^:]+)@([^@]+)@(.+)") + return { + ip = ip, + port = port, + uid = uid, + worldId = token.worldId, + secret = secret, + subid = subid, + } +end diff --git a/framework/simulation/simulation.lua b/framework/simulation/simulation.lua new file mode 100644 index 0000000..938f95d --- /dev/null +++ b/framework/simulation/simulation.lua @@ -0,0 +1,86 @@ +assert(_VERSION == "Lua 5.4") + +package.path = "lualib/?.lua;skynet/lualib/?.lua;simulation/?.lua" +package.cpath = "skynet/luaclib/?.so;luaclib/?.so" + +local auth = require "auth" +local ok, atoken = pcall(auth) +if not ok then + print("please check: login auth err", atoken) + return +end + +local termfx = require "termfx" +local ui = require "simpleui" + +termfx.init() +termfx.inputmode(termfx.input.ALT + termfx.input.MOUSE) +termfx.outputmode(termfx.output.COL256) + +local function main(token) + local sock = require ("network")() + local sok = sock:connect(token.ip, token.port) + assert(sok) + + local output = {} + + local mok, err = pcall(function() + local msg_list = {} + local input + local string = {} + + while true do + termfx.clear(termfx.color.WHITE, termfx.color.BLACK) + + local w, h = termfx.size() + local width = math.floor(w / 2) + + ui.box(2, 2, width, h - 10) + ui.box(w / 2, 2, width, h - 10) + + termfx.printat(2, h - 1, input) + termfx.printat(2, h, table.concat(string, "")) + termfx.present() + + + + local evt = termfx.pollevent(100) + if evt then + if evt.type == 'key' then + if evt.key == termfx.key.CTRL_C then + break + elseif evt.key == termfx.key.BACKSPACE2 then + table.remove(string) + elseif evt.key == termfx.key.ENTER then + input = table.concat(string, '') + string = {} + pcall(act_when_input, input) + elseif evt.key == termfx.key.SPACE then + table.insert(string, ' ') + else + table.insert(string, evt.char) + end + end + else + local success, err = sock:update() + assert(success, err) + + local r, msgid, msg = recv_msg() + if r == true then + table.insert(msg_list, {MN[msgid] or msgid, inspect(msg)}) + elseif r == false then + p('网络断开,结束客户端', msgid) + break + end + end + end + end) + + termfx.shutdown() + if not mok then + print("Error: ", err) + end + print(':\n', table.concat(output, '\n')) +end + +main(atoken)