From 8d44c1e8b48c512a9e305402f94d8ded33f74ed5 Mon Sep 17 00:00:00 2001 From: xiaojin Date: Wed, 28 Jul 2021 20:47:36 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=B3=20chore(=E5=B7=A5=E5=85=B7):=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=20lua=20ecs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/lualib-src/lua-ecs/luaecs.c | 166 ++++--- framework/lualib-src/lua-ecs/test.lua | 234 +++++++++ framework/lualib-src/lua-ecs/test2.lua | 58 +++ framework/lualib-src/lua-ecs/test3.lua | 126 +++++ framework/lualib-src/lua-ecs/test4.lua | 51 ++ framework/lualib-src/lua-ecs/test5.lua | 29 ++ framework/lualib-src/lua-ecs/test6.lua | 35 ++ framework/lualib-src/lua-ecs/test7.lua | 19 + framework/lualib-src/lua-ecs/test8.lua | 33 ++ framework/lualib/3rd/misc/ecs.lua | 649 +++++++++++++------------ 10 files changed, 995 insertions(+), 405 deletions(-) create mode 100644 framework/lualib-src/lua-ecs/test.lua create mode 100644 framework/lualib-src/lua-ecs/test2.lua create mode 100644 framework/lualib-src/lua-ecs/test3.lua create mode 100644 framework/lualib-src/lua-ecs/test4.lua create mode 100644 framework/lualib-src/lua-ecs/test5.lua create mode 100644 framework/lualib-src/lua-ecs/test6.lua create mode 100644 framework/lualib-src/lua-ecs/test7.lua create mode 100644 framework/lualib-src/lua-ecs/test8.lua diff --git a/framework/lualib-src/lua-ecs/luaecs.c b/framework/lualib-src/lua-ecs/luaecs.c index 6ddbfdc..b403f48 100644 --- a/framework/lualib-src/lua-ecs/luaecs.c +++ b/framework/lualib-src/lua-ecs/luaecs.c @@ -293,11 +293,9 @@ lookup_component(struct component_pool *pool, unsigned int eid, int guess_index) } static inline void -replace_id(struct component_pool *c, int index, unsigned int eid) { - unsigned int rid = c->id[index]; - c->id[index] = eid; +replace_id(struct component_pool *c, int from, int to, unsigned int eid) { int i; - for (i=index+1;in && c->id[i] == rid;i++) { + for (i=from;iid[i] = eid; } } @@ -313,20 +311,26 @@ entity_disable_tag_(struct entity_world *w, int cid, int index, int tag_id) { if (index < 0) return; } - int i; - for (i=index - 1; i>=0; i--) { - if (c->id[i] != eid) { - replace_id(c, i+1, c->id[i]); + 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 (i=index+1;in;i++) { - if (c->id[i] != eid) { - replace_id(c, index, c->id[i]); - return; - } + for (from = index-1; from>=0; from--) { + if (c->id[from] != eid) + break; } - c->n = 0; + c->n = from + 1; } static void @@ -517,10 +521,8 @@ entity_iter_(struct entity_world *w, int cid, int index) { if (c->stride == STRIDE_TAG) { // it's a tag unsigned int eid = c->id[index]; - if (index > 0 && eid == c->id[index-1]) { - remove_dup(c, index); - if (index >= c->n) - return NULL; + if (index < c->n - 1 && eid == c->id[index+1]) { + remove_dup(c, index+1); } return DUMMY_PTR; } @@ -1025,37 +1027,14 @@ remove_tag(lua_State *L, int lua_index, const char *name) { } static void -update_last_index(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx) { +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; - 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); - } - } + for (i=0;ik[i].field_n; } - - struct field *f = iter->f + iter->k[0].field_n; - - for (i=1;inkey;i++) { + 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]; @@ -1149,6 +1128,38 @@ update_last_index(lua_State *L, int world_index, int lua_index, struct group_ite } 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); } @@ -1177,13 +1188,12 @@ read_component_in_field(lua_State *L, int lua_index, const char *name, int n, st // -1 : end ; 0 : next ; 1 : succ static int -query_index(struct group_iter *iter, int mainkey, int idx, unsigned int index[MAX_COMPONENT]) { +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; } - index[0] = idx+1; int j; - for (j=1;jnkey;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)) { @@ -1255,24 +1265,30 @@ read_iter(lua_State *L, int world_index, int obj_index, struct group_iter *iter, } static int -lsync(lua_State *L) { - struct group_iter *iter = luaL_checkudata(L, 2, "ENTITY_GROUPITER"); - luaL_checktype(L, 3, LUA_TTABLE); - if (lua_rawgeti(L, 3, 1) != LUA_TNUMBER) { - return luaL_error(L, "Invalid iterator"); +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 idx = lua_tointeger(L, -1) - 1; - if (idx < 0) - return luaL_error(L, "Invalid iterator index %d", idx); + 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, iter->k[0].id, idx, index) <=0) { + if (query_index(iter, 0, mainkey, idx, index) <=0) { return luaL_error(L, "Read pattern fail"); } if (!iter->readonly) { - update_last_index(L, 1, 3, iter, idx); + update_iter(L, 1, 3, iter, idx, mainkey, 0); } read_iter(L, 1, 3, iter, index); return 0; @@ -1301,7 +1317,9 @@ leach_group(lua_State *L) { int mainkey = iter->k[0].id; unsigned int index[MAX_COMPONENT]; for (;;) { - int ret = query_index(iter, mainkey, i++, index); + int idx = i++; + index[0] = idx + 1; + int ret = query_index(iter, 1, mainkey, idx, index); if (ret < 0) return 0; if (ret > 0) @@ -1548,12 +1566,6 @@ lgroupiter(lua_State *L) { iter->readonly = 0; } int mainkey_attrib = iter->k[0].attrib; - if (mainkey_attrib & COMPONENT_OPTIONAL) { - return luaL_error(L, "The main key should not be optional"); - } - if (is_temporary(mainkey_attrib)) { - return luaL_error(L, "The main key can't be temporary"); - } if (mainkey_attrib & COMPONENT_ABSENT) { return luaL_error(L, "The main key can't be absent"); } @@ -1565,18 +1577,6 @@ lgroupiter(lua_State *L) { return 1; } -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 lremove(lua_State *L) { struct entity_world *w = getW(L); @@ -1639,9 +1639,11 @@ lbsearch(lua_State *L) { int * v = entity_iter_(w, value_id, mid); if (*v == value) { // found - lua_createtable(L, 1, 0); + lua_createtable(L, 2, 0); lua_pushinteger(L, mid + 1); lua_seti(L, -2, 1); + lua_pushinteger(L, sorted_id); + lua_seti(L, -2, 2); return 1; } if (*v < value) { @@ -1660,9 +1662,11 @@ lbsearch(lua_State *L) { int * v = entity_iter_(w, value_id, index - 1); if (*v == value) { // found - lua_createtable(L, 1, 0); + lua_createtable(L, 2, 0); lua_pushinteger(L, index); lua_seti(L, -2, 1); + lua_pushinteger(L, sorted_id); + lua_seti(L, -2, 2); return 1; } if (*v < value) { diff --git a/framework/lualib-src/lua-ecs/test.lua b/framework/lualib-src/lua-ecs/test.lua new file mode 100644 index 0000000..752f563 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test.lua @@ -0,0 +1,234 @@ +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", +} + +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() + +w:register { + name = "singleton", + type = "lua" +} + +local context = w:context { + "vector", + "mark", + "id", + "singleton", +} + +w:new { singleton = "Hello World" } + +w:update() + +local test = require "ecs.ctest" + +print(test.get(context)) + +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?temp" 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 + + +w:new { object = "Hello" , mark = true } +w:new { object = "World" , mark = true } + +w:update() + +print "mark:update object:in" + +for v in w:select "mark:update object:in" do + print(v.object) + if v.object == "World" then + print "Disable mark where object == World" + v.mark = false + end +end + +print "mark:exist object:in" + +for v in w:select "mark:exist object:in" do + print(v.object) +end + +for v in w:select "object:exist mark:out" do + v.mark = false +end + +for v in w:select "mark:exist" do + print("Remove") + w:remove(v) +end + +for v in w:select "REMOVED:exist vector:in" do + print(v.vector.x, v.vector.y, "removed") +end + +w:update() -- remove all + +local n = 0 +for v in w:select "mark:in" do + n = n + 1 +end +print("Marked", n) + + +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 + +w:register { + name = "sum", + type = "float", +} + +for v in w:select "vector:in sum:temp" do + print(v.vector.x, "+", v.vector.y) + v.sum = v.vector.x + v.vector.y +end + +for v in w:select "sum:in" do + print(v.sum) +end + +w:clear "sum" + +for v in w:select "sum:exist" do + error "Not empty" +end diff --git a/framework/lualib-src/lua-ecs/test2.lua b/framework/lualib-src/lua-ecs/test2.lua new file mode 100644 index 0000000..6ab56b8 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test2.lua @@ -0,0 +1,58 @@ +-- test sort + +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "data", + type = "float", +} + +w:register { + name = "index", + type = "int", +} + +local tmp = { 10,9,8,7,6,5,4,3,2,1 } + +for i = 1, 10, 2 do + w:new { data = tmp[i], index = tmp[i] } + w:new { index = tmp[i+1] } +end + +w:update() + +for v in w:select "index data?in" do + print(v.data) +end + +w:sort("sort", "index") + +print "sorted" + +for v in w:select "sort data:in index:in" do + print(v.data, v.index) +end + +local iter = w:bsearch("sort", "index", 5) +w:sync("data:in", iter) +print("Found", iter.data) + +w:register { + name = "sorted_index", + type = "int", +} + +for i = 1, 10 do + w:new { data = i * 0.5 , sorted_index = i * 2 } +end + +for v in w:select "sorted_index:in data?in" do + print(v.sorted_index, "=>", v.data) +end + +local iter = w:bsearch("sorted_index", "sorted_index", 4) +w:sync("data:in", iter) +print("Found", iter.data) +--w:remove(iter) diff --git a/framework/lualib-src/lua-ecs/test3.lua b/framework/lualib-src/lua-ecs/test3.lua new file mode 100644 index 0000000..f922c60 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test3.lua @@ -0,0 +1,126 @@ +-- test object +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "refobject", + type = "int", + ref = true, +} + +local id1 = w:ref("refobject", { refobject = 42 }) +local id2 = w:ref("refobject", { refobject = 0 }) +local id3 = w:ref("refobject", { refobject = 100 }) +print ("New", id1, id2, id3) +print(w:object("refobject", id1)) + +print("Release", id1) + +w:release("refobject", id1) + +for v in w:select "refobject:in" do + print(v.refobject) +end + +local id4 = w:ref("refobject", { refobject = -42 }) +print ("New", id4) + +print ("Release", id2) + +w:release("refobject", id2) + +print ("Release", id3) + +w:release("refobject", id3) + +print "List refobject" + +for v in w:select "refobject:in" do + print(v.refobject) +end + +w:register { + name = "index", + type = "int", +} + +w:new { + index = id4 +} + +for v in w:select "index:in" do + print(v.index) +end + +print "Index refobject" + +for v in w:select "refobject(index):in" do + print(v.refobject) +end + +w:register { + name = "name", + type = "lua", +} + +w:new { + name = "Hello" +} + +for v in w:select "index:in" do + print(v.index) +end + + +for v in w:select "name refobject(index):temp" do + v.refobject = 42 +end + +for v in w:select "refobject:in" do + print(v.refobject) +end + +for v in w:select "refobject(index):update" do + v.refobject = v.refobject + 1 +end + +for v in w:select "refobject:in" do + print(v.refobject) +end + +w:register { + name = "mark" +} + +local ref = w:object_ref("refobject", id4) +ref.mark = true +w:sync("mark?out", ref) + + +w:ref ("refobject", { + refobject = 42, + mark = true +}) + +print "Marked refobject" + +for v in w:select "mark refobject?in" do + print(v.refobject) +end + +w:register { + name = "refobject2", + type = "int", + ref = true, +} + +w:register { + name = "mark2" +} + +w:ref ("refobject2", { + refobject2 = 42, + mark = true, + mark2 = false, +}) diff --git a/framework/lualib-src/lua-ecs/test4.lua b/framework/lualib-src/lua-ecs/test4.lua new file mode 100644 index 0000000..7d99815 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test4.lua @@ -0,0 +1,51 @@ +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "a", + type = "int", +} + +w:register { + name = "b", + type = "float", +} + +for i = 1, 20 do + w:new { a = i } + w:new { b = i } +end + +for i = 20, 40 do + w:new { a = i , b = i } +end + +w:update() + +for v in w:select "a:in" do + if v.a % 2 == 1 then + w:remove(v) + end +end + +for v in w:select "b:in" do + if v.b < 10 then + w:remove(v) + end +end + +for v in w:select "REMOVED a?in b?in" do + print(v.a, v.b, "Removed") +end + +w:update() + +for v in w:select "a:in" do + print(v.a) +end + +for v in w:select "b:in" do + print(v.b) +end + diff --git a/framework/lualib-src/lua-ecs/test5.lua b/framework/lualib-src/lua-ecs/test5.lua new file mode 100644 index 0000000..f7495a5 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test5.lua @@ -0,0 +1,29 @@ +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "a", + type = "int", +} + +w:register { + name = "temp", + type = "int", +} + +for i = 1, 10 do + w:new { a = i } +end + +for v in w:select "a:in" do + if v.a %2 == 0 then + v.a = -v.a + v.temp = 42 + w:sync("a:out temp:temp", v) + end +end + +for v in w:select "a:in temp?in" do + print(v.a, v.temp) +end \ No newline at end of file diff --git a/framework/lualib-src/lua-ecs/test6.lua b/framework/lualib-src/lua-ecs/test6.lua new file mode 100644 index 0000000..8992724 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test6.lua @@ -0,0 +1,35 @@ +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "t", + "a:bool", + "b:userdata", +} + +w:new { + t = { + a = false, + b = ecs.NULL, + } +} + +local function print_v() + local v = w:singleton "t" + + print(".a = ",v.a) + print(".b = ",v.b) +end + +local ctx = w:context { "t" } + +print("ctx = ", ctx) + +local test = require "ecs.ctest" + +print_v() + +test.testuserdata(ctx) + +print_v() diff --git a/framework/lualib-src/lua-ecs/test7.lua b/framework/lualib-src/lua-ecs/test7.lua new file mode 100644 index 0000000..6e11eae --- /dev/null +++ b/framework/lualib-src/lua-ecs/test7.lua @@ -0,0 +1,19 @@ +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "object", + type = "int", + ref = true, +} + +for i = 1, 10 do + w:ref("object", { object = i * 10 }) +end + +w:order("order", "object", { 9,7,5,3,1 }) + +for v in w:select "order object:in" do + print(v.object) +end \ No newline at end of file diff --git a/framework/lualib-src/lua-ecs/test8.lua b/framework/lualib-src/lua-ecs/test8.lua new file mode 100644 index 0000000..8af81e5 --- /dev/null +++ b/framework/lualib-src/lua-ecs/test8.lua @@ -0,0 +1,33 @@ +local ecs = require "ecs" + +local w = ecs.world() + +w:register { + name = "v", + type = "int", +} + +w:register { + name = "marked" +} + +for i = 1, 10 do + w:new { + v = i, + marked = i % 2 == 1, + } +end + +for v in w:select "v:in marked?in" do + print(v.v, v.marked) +end + +print "Marked" +for v in w:select "v:in marked" do + print(v.v) +end + +print "Not Marked" +for v in w:select "v:in marked:absent" do + print(v.v) +end \ No newline at end of file diff --git a/framework/lualib/3rd/misc/ecs.lua b/framework/lualib/3rd/misc/ecs.lua index 4f3f196..41b6ca1 100644 --- a/framework/lualib/3rd/misc/ecs.lua +++ b/framework/lualib/3rd/misc/ecs.lua @@ -1,384 +1,385 @@ local ecs = require "ecs.core" local function get_attrib(opt, inout) - if opt == nil then - return { - exist = true, - } - end - local desc = {} - if opt == "?" then - desc.opt = true - else - assert(opt == ":") - end - if inout == "in" then - desc.r = true - elseif inout == "out" or inout == "new" then - desc.w = true - elseif inout == "update" then - desc.r = true - desc.w = true - elseif inout == "exist" then - desc.exist = true - assert(not desc.opt) - elseif inout == "absent" then - desc.absent = true - assert(not desc.opt) - else - assert(inout == "temp") - end - return desc + if opt == nil then + return { exist = true } + end + local desc = {} + if opt == "?" then + desc.opt = true + else + assert(opt == ":") + end + if inout == "in" then + desc.r = true + elseif inout == "out" or inout == "new" then + desc.w = true + elseif inout == "update" then + desc.r = true + desc.w = true + elseif inout == "exist" then + desc.exist = true + assert(not desc.opt) + elseif inout == "absent" then + desc.absent = true + assert(not desc.opt) + else + assert(inout == "temp") + end + return desc end local function cache_world(obj, k) - local c = { - typenames = {}, - id = 0, - select = {}, - ref = {}, - } - - local function gen_ref_pat(key) - local typenames = c.typenames - local desc = {} - local tc = typenames[key] - if tc == nil then - error("Unknown type " .. key) - end - local a = { - exist = true, - name = tc.name, - id = tc.id, - type = tc.type, - } - local n = #tc - for i = 1, #tc do - a[i] = tc[i] - end - desc[1] = a - return desc - end - - local function gen_select_pat(pat) - local typenames = c.typenames - local desc = {} - local idx = 1 - for token in pat:gmatch "[^ ]+" do - local key, index, padding = token:match "^([_%w]+)%(?([_%w]*)%)?(.*)" - assert(key, "Invalid pattern") - local opt, inout - if padding ~= "" then - opt, inout = padding:match "^([:?])(%l+)$" - assert(opt, "Invalid pattern") - end - local tc = typenames[key] - if tc == nil then - error("Unknown type " .. key) - end - if index ~= "" then - local indexc = typenames[index] - if indexc == nil then - error("Unknown index type " .. index) - end - local a = get_attrib(opt, inout == "temp" and "temp" or "in") - a.name = index - a.id = indexc.id - a.type = indexc.type - a.ref = true - desc[idx] = a - idx = idx + 1 - end - 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 - if tc.ref and index == "" then - local dead = typenames[key .. "_dead"] - local a = { - absent = true, - name = dead.name, - id = dead.id, - } - desc[idx] = a - idx = idx + 1 - end - end - return desc - end - - 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_select, - }) - - local function cache_ref(cache, pat) - local pat_desc = gen_ref_pat(pat) - cache[pat] = k:_groupiter(pat_desc) - return cache[pat] - end - - setmetatable(c.ref, { - __mode = "kv", - __index = cache_ref, - }) - - obj[k] = c - return c + local c = { + typenames = {}, + id = 0, + select = {}, + ref = {}, + } + + local function gen_ref_pat(key) + local typenames = c.typenames + local desc = {} + local tc = typenames[key] + if tc == nil then + error("Unknown type " .. key) + end + local a = { + exist = true, + name = tc.name, + id = tc.id, + type = tc.type, + } + local n = #tc + for i=1,#tc do + a[i] = tc[i] + end + desc[1] = a + return desc + end + + local function gen_select_pat(pat) + local typenames = c.typenames + local desc = {} + local idx = 1 + for token in pat:gmatch "[^ ]+" do + local key, index, padding = token:match "^([_%w]+)%(?([_%w]*)%)?(.*)" + assert(key, "Invalid pattern") + local opt, inout + if padding ~= "" then + opt, inout = padding:match "^([:?])(%l+)$" + assert(opt, "Invalid pattern") + end + local tc = typenames[key] + if tc == nil then + error("Unknown type " .. key) + end + if index ~= "" then + local indexc = typenames[index] + if indexc == nil then + error("Unknown index type "..index) + end + local a = get_attrib(opt, inout == "temp" and "temp" or "in") + a.name = index + a.id = indexc.id + a.type = indexc.type + a.ref = true + desc[idx] = a + idx = idx + 1 + end + 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 + if tc.ref and index == "" then + local dead = typenames[key .. "_dead"] + local a = { + absent = true, + name = dead.name, + id = dead.id, + } + desc[idx] = a + idx = idx + 1 + end + end + return desc + end + + 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_select, + }) + + local function cache_ref(cache, pat) + local pat_desc = gen_ref_pat(pat) + cache[pat] = k:_groupiter(pat_desc) + return cache[pat] + end + + setmetatable(c.ref, { + __mode = "kv", + __index = cache_ref, + }) + + obj[k] = c + return c end -local context = setmetatable({}, { - __index = cache_world, -}) +local context = setmetatable({}, { __index = cache_world }) local typeid = { - int = assert(ecs._TYPEINT), - float = assert(ecs._TYPEFLOAT), - bool = assert(ecs._TYPEBOOL), - int64 = assert(ecs._TYPEINT64), - dword = assert(ecs._TYPEDWORD), - word = assert(ecs._TYPEWORD), - byte = assert(ecs._TYPEBYTE), - double = assert(ecs._TYPEDOUBLE), - userdata = assert(ecs._TYPEUSERDATA), + int = assert(ecs._TYPEINT), + float = assert(ecs._TYPEFLOAT), + bool = assert(ecs._TYPEBOOL), + int64 = assert(ecs._TYPEINT64), + dword = assert(ecs._TYPEDWORD), + word = assert(ecs._TYPEWORD), + byte = assert(ecs._TYPEBYTE), + double = assert(ecs._TYPEDOUBLE), + userdata = assert(ecs._TYPEUSERDATA), } local typesize = { - [typeid.int] = 4, - [typeid.float] = 4, - [typeid.bool] = 1, - [typeid.int64] = 8, - [typeid.dword] = 4, - [typeid.word] = 2, - [typeid.byte] = 1, - [typeid.double] = 8, - [typeid.userdata] = 8, + [typeid.int] = 4, + [typeid.float] = 4, + [typeid.bool] = 1, + [typeid.int64] = 8, + [typeid.dword] = 4, + [typeid.word] = 2, + [typeid.byte] = 1, + [typeid.double] = 8, + [typeid.userdata] = 8, } local M = ecs._METHODS -do -- newtype - local function parse(s) - -- s is "name:typename" - local name, typename = s:match "^([%w_]+):(%w+)$" - local typeid = assert(typeid[typename]) - return {typeid, name} - end - - local function align(c, field) - local t = field[1] - local tsize = typesize[t] - local offset = ((c.size + tsize - 1) & ~(tsize - 1)) - c.size = offset + tsize - field[3] = offset - return field - end - - local function align_struct(c, t) - if t then - local s = typesize[t] - 1 - c.size = ((c.size + s) & ~s) - end - end - - function M:register(typeclass) - local name = assert(typeclass.name) - local ctx = context[self] - local typenames = ctx.typenames - local id = ctx.id + 1 - assert(typenames[name] == nil and id <= ecs._MAXTYPE) - ctx.id = id - local c = { - id = id, - name = name, - size = 0, - } - for i, v in ipairs(typeclass) do - c[i] = align(c, parse(v)) - end - local ttype = typeclass.type - if ttype == "lua" then - assert(c.size == 0) - c.size = ecs._LUAOBJECT - assert(c[1] == nil) - elseif c.size > 0 then - align_struct(c, typeclass[1][1]) - else - -- size == 0, one value - if ttype then - local t = assert(typeid[typeclass.type]) - c.type = t - c.size = typesize[t] - c[1] = {t, "v", 0} - else - c.tag = true - end - end - typenames[name] = c - self:_newtype(id, c.size) - if typeclass.ref then - c.ref = true - self:register{ - name = name .. "_dead", - } - end - end +do -- newtype + local function parse(s) + -- s is "name:typename" + local name, typename = s:match "^([%w_]+):(%w+)$" + local typeid = assert(typeid[typename]) + return { typeid, name } + end + + local function align(c, field) + local t = field[1] + local tsize = typesize[t] + local offset = ((c.size + tsize - 1) & ~(tsize-1)) + c.size = offset + tsize + field[3] = offset + return field + end + + local function align_struct(c, t) + if t then + local s = typesize[t] - 1 + c.size = ((c.size + s) & ~s) + end + end + + function M:register(typeclass) + local name = assert(typeclass.name) + local ctx = context[self] + local typenames = ctx.typenames + local id = ctx.id + 1 + assert(typenames[name] == nil and id <= ecs._MAXTYPE) + ctx.id = id + local c = { + id = id, + name = name, + size = 0, + } + for i, v in ipairs(typeclass) do + c[i] = align(c, parse(v)) + end + local ttype = typeclass.type + if ttype == "lua" then + assert(c.size == 0) + c.size = ecs._LUAOBJECT + assert(c[1] == nil) + elseif c.size > 0 then + align_struct(c, typeclass[1][1]) + else + -- size == 0, one value + if ttype then + local t = assert(typeid[typeclass.type]) + c.type = t + c.size = typesize[t] + c[1] = { t, "v", 0 } + else + c.tag = true + end + end + typenames[name] = c + self:_newtype(id, c.size) + if typeclass.ref then + c.ref = true + self:register { name = name .. "_dead" } + end + end end local function dump(obj) - for e, v in pairs(obj) do - if type(v) == "table" then - for k, v in pairs(v) do - print(e, k, v) - end - else - print(e, v) - end - end + for e,v in pairs(obj) do + if type(v) == "table" then + for k,v in pairs(v) do + print(e,k,v) + end + else + print(e,v) + end + end end function M:new(obj) - -- dump(obj) - local eid = self:_newentity() - local typenames = context[self].typenames - for k, v in pairs(obj) do - local tc = typenames[k] - if not tc then - error("Invalid key : " .. k) - end - local id = self:_addcomponent(eid, tc.id) - self:object(k, id, v) - end +-- dump(obj) + local eid = self:_newentity() + local typenames = context[self].typenames + for k,v in pairs(obj) do + local tc = typenames[k] + if not tc then + error ("Invalid key : ".. k) + end + local id = self:_addcomponent(eid, tc.id) + self:object(k, id, v) + end end function M:ref(name, refobj) - local obj = assert(refobj[name]) - local ctx = context[self] - local typenames = ctx.typenames - local tc = assert(typenames[name]) - local refid = self:_reuse(tc.id) - if refid then - local p = context[self].select[name .. ":out"] - self:_sync(p, refobj) - else - local eid = self:_newentity() - refid = self:_addcomponent(eid, tc.id) - self:object(name, refid, obj) - end - refobj[1] = refid - for k, v in pairs(refobj) do - if (v == true or v == false) and name ~= k then - local p = context[self].select[string.format("%s %s?out", name, k)] - self:_sync(p, refobj) - end - end - return refid + local obj = assert(refobj[name]) + local ctx = context[self] + local typenames = ctx.typenames + local tc = assert(typenames[name]) + local refid = self:_reuse(tc.id) + refobj[2] = tc.id + if refid then + local p = context[self].select[name .. ":out"] + refobj[1] = refid + self:_sync(p, refobj) + else + local eid = self:_newentity() + refid = self:_addcomponent(eid, tc.id) + refobj[1] = refid + self:object(name, refid, obj) + end + for k,v in pairs(refobj) do + if (v == true or v == false) and name ~= k then + local p = context[self].select[k .. "?out"] + self:_sync(p, refobj) + end + end + return refid +end + +function M:object_ref(name, refid) + local typenames = context[self].typenames + return { refid, typenames[name].id } end function M:release(name, refid) - local id = assert(context[self].typenames[name].id) - self:_release(id, refid) + local id = assert(context[self].typenames[name].id) + self:_release(id, refid) end function M:context(t) - local typenames = context[self].typenames - local id = {} - for i, name in ipairs(t) do - local tc = typenames[name] - if not tc then - error("Invalid component name " .. name) - end - id[i] = tc.id - end - return self:_context(id) + local typenames = context[self].typenames + local id = {} + for i, name in ipairs(t) do + local tc = typenames[name] + if not tc then + error ("Invalid component name " .. name) + end + id[i] = tc.id + end + return self:_context(id) end function M:select(pat) - return context[self].select[pat]() + return context[self].select[pat]() end function M:sync(pat, iter) - local p = context[self].select[pat] - self:_sync(p, iter) - return iter + local p = context[self].select[pat] + self:_sync(p, iter) + return iter end function M:clear(name) - local id = assert(context[self].typenames[name].id) - self:_clear(id) + local id = assert(context[self].typenames[name].id) + self:_clear(id) end local function gen_sorted_id(self, sorted, name) - local ctx = context[self] - local typenames = ctx.typenames - local t = assert(typenames[name]) - local stype = typenames[sorted] - if stype == nil then - local id = ctx.id + 1 - assert(id <= ecs._MAXTYPE) - ctx.id = id - stype = { - id = id, - name = sorted, - size = ecs._ORDERKEY, - tag = true, - } - self:_newtype(id, stype.size) - typenames[sorted] = stype - else - assert(stype.size == ecs._ORDERKEY) - end - return stype.id, t.id + local ctx = context[self] + local typenames = ctx.typenames + local t = assert(typenames[name]) + local stype = typenames[sorted] + if stype == nil then + local id = ctx.id + 1 + assert(id <= ecs._MAXTYPE) + ctx.id = id + stype = { + id = id, + name = sorted, + size = ecs._ORDERKEY, + tag = true + } + self:_newtype(id, stype.size) + typenames[sorted] = stype + else + assert(stype.size == ecs._ORDERKEY) + end + return stype.id, t.id end function M:sort(sorted, name) - self:_sortkey(gen_sorted_id(self, sorted, name)) + self:_sortkey(gen_sorted_id(self, sorted, name)) end function M:order(sorted, refname, order_array) - local sid, rid = gen_sorted_id(self, sorted, refname) - self:_orderkey(sid, rid, order_array) + local sid, rid = gen_sorted_id(self, sorted, refname) + self:_orderkey(sid, rid, order_array) end function M:bsearch(sorted, name, value) - local typenames = context[self].typenames - local sorted_id = typenames[sorted].id - local value_id = typenames[name].id - return self:_bsearch(sorted_id, value_id, value) + local typenames = context[self].typenames + local sorted_id = typenames[sorted].id + local value_id = typenames[name].id + return self:_bsearch(sorted_id, value_id, value) end do - local _object = M._object - function M:object(name, refid, v) - local pat = context[self].ref[name] - return _object(pat, v, refid) - end - - function M:singleton(name, v) - local pat = context[self].ref[name] - return _object(pat, v, 1) - end + local _object = M._object + function M:object(name, refid, v) + local pat = context[self].ref[name] + return _object(pat, v, refid) + end + + function M:singleton(name, v) + local pat = context[self].ref[name] + return _object(pat, v, 1) + end end function ecs.world() - local w = ecs._world() - context[w].typenames.REMOVED = { - name = "REMOVED", - id = ecs._REMOVED, - size = 0, - tag = true, - } - return w + local w = ecs._world() + context[w].typenames.REMOVED = { + name = "REMOVED", + id = ecs._REMOVED, + size = 0, + tag = true, + } + return w end return ecs