From 7540100b4b41290bcd5ad2536c89289a9bdbef3a Mon Sep 17 00:00:00 2001 From: xiaojin Date: Sat, 24 Jul 2021 15:39:20 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20build:=20=E8=B0=83=E6=95=B4oop?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .luacheckrc | 6 + framework/lualib-src/lua-ecs/luaecs.c | 264 ++++++---- framework/lualib-src/lua-ecs/luaecs.h | 8 +- framework/lualib/3rd/misc/ecs.lua | 124 +++-- framework/lualib/3rd/misc/enum.lua | 33 -- framework/lualib/3rd/misc/fsm.lua | 1 - framework/lualib/3rd/misc/lru.lua | 1 - .../lualib/3rd/{unitest => misc}/luaunit.lua | 0 framework/lualib/3rd/misc/middleclass.lua | 199 -------- framework/lualib/3rd/misc/nesting.lua | 2 +- framework/lualib/3rd/misc/promise.lua | 179 +++---- framework/lualib/3rd/misc/singleton.lua | 24 - framework/lualib/3rd/misc/vararg.lua | 1 - framework/lualib/3rd/oop/README.md | 124 +++++ framework/lualib/3rd/oop/class.lua | 294 +++++++++++ framework/lualib/3rd/oop/enum.lua | 126 +++++ .../lualib/3rd/{misc => oop}/handler.lua | 5 +- framework/lualib/3rd/oop/init.lua | 8 + framework/lualib/3rd/oop/mixin.lua | 51 ++ framework/lualib/3rd/oop/oop_test.lua | 60 +++ framework/lualib/3rd/{misc => oop}/option.lua | 0 framework/lualib/3rd/oop/property.lua | 56 +++ framework/lualib/3rd/{misc => oop}/try.lua | 0 framework/lualib/3rd/unitest/lester.lua | 473 ------------------ server/src/preload/preload.lua | 4 +- 25 files changed, 1052 insertions(+), 991 deletions(-) delete mode 100644 framework/lualib/3rd/misc/enum.lua rename framework/lualib/3rd/{unitest => misc}/luaunit.lua (100%) delete mode 100644 framework/lualib/3rd/misc/middleclass.lua delete mode 100644 framework/lualib/3rd/misc/singleton.lua create mode 100644 framework/lualib/3rd/oop/README.md create mode 100644 framework/lualib/3rd/oop/class.lua create mode 100644 framework/lualib/3rd/oop/enum.lua rename framework/lualib/3rd/{misc => oop}/handler.lua (81%) create mode 100644 framework/lualib/3rd/oop/init.lua create mode 100644 framework/lualib/3rd/oop/mixin.lua create mode 100644 framework/lualib/3rd/oop/oop_test.lua rename framework/lualib/3rd/{misc => oop}/option.lua (100%) create mode 100644 framework/lualib/3rd/oop/property.lua rename framework/lualib/3rd/{misc => oop}/try.lua (100%) delete mode 100644 framework/lualib/3rd/unitest/lester.lua diff --git a/.luacheckrc b/.luacheckrc index ba9d19b..8818f09 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -13,7 +13,13 @@ globals = { "SERVICE_NAME", "class", "singleton", + "instanceof", + "mixin", "handler", + "Property", + "Enum", + "Option", + "Try", "ZLog", } diff --git a/framework/lualib-src/lua-ecs/luaecs.c b/framework/lualib-src/lua-ecs/luaecs.c index 52e82e7..6ddbfdc 100644 --- a/framework/lualib-src/lua-ecs/luaecs.c +++ b/framework/lualib-src/lua-ecs/luaecs.c @@ -204,38 +204,6 @@ ladd_component(lua_State *L) { return 1; } -/* -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 stride = w->c[cid].stride; - if (stride <= 0) { - int index = add_component_id_(L, 1, 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); - } - lua_pushinteger(L, index + 1); - } else { - 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, 1, w, cid, eid, buffer); - lua_pushinteger(L, w->c[cid].n); - } - return 1; -} -*/ static int lnew_entity(lua_State *L) { struct entity_world *w = getW(L); @@ -641,7 +609,6 @@ entity_add_sibling_(struct entity_world *w, int cid, int index, int silbling_id, assert(index >=0 && index < c->n); unsigned int eid = c->id[index]; // todo: pcall add_component_ - assert(c->stride >= 0); return add_component_((lua_State *)L, world_index, w, silbling_id, eid, buffer); } @@ -668,7 +635,6 @@ entity_add_sibling_index_(lua_State *L, int world_index, struct entity_world *w, assert(index >=0 && index < c->n); unsigned int eid = c->id[index]; // todo: pcall add_component_ - assert(c->stride == STRIDE_LUA); int ret = add_component_id_(L, world_index, w, slibling_id, eid); return ret; } @@ -686,18 +652,23 @@ 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); - - if (order->id == NULL || order->cap < c->cap) { - order->cap = c->cap; - order->id = (unsigned int *)lua_newuserdatauv(L, c->cap * sizeof(unsigned int), 0); - lua_setiuservalue(L, world_index, orderid * 2 + 1); - } + reserve_(w, orderid, c->cap, L, world_index); int i; for (i = 0; i < c->n ; i++) { order->id[i] = i; @@ -971,7 +942,11 @@ get_len(lua_State *L, int index) { #define COMPONENT_OUT 2 #define COMPONENT_OPTIONAL 4 #define COMPONENT_OBJECT 8 -#define COMPONENT_EXIST 16 +#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; @@ -982,7 +957,7 @@ struct group_key { static inline int is_temporary(int attrib) { - if (attrib & COMPONENT_EXIST) + if (attrib & COMPONENT_FILTER) return 0; return (attrib & COMPONENT_IN) == 0 && (attrib & COMPONENT_OUT) == 0; } @@ -1049,14 +1024,13 @@ remove_tag(lua_State *L, int lua_index, const char *name) { return r; } -static int +static void update_last_index(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx) { - int ret = 0; 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_EXIST)) { + 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)); @@ -1083,11 +1057,11 @@ update_last_index(lua_State *L, int world_index, int lua_index, struct group_ite for (i=1;inkey;i++) { struct group_key *k = &iter->k[i]; - if (!(k->attrib & COMPONENT_EXIST)) { + 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) || is_temporary(k->attrib)) { + if ((k->attrib & COMPONENT_OUT)) { switch (lua_getfield(L, lua_index, k->name)) { case LUA_TNIL: break; @@ -1097,6 +1071,11 @@ update_last_index(lua_State *L, int world_index, int lua_index, struct group_ite } 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))); @@ -1105,11 +1084,20 @@ update_last_index(lua_State *L, int world_index, int lua_index, struct group_ite } } else 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); + 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); } - ret = index; 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); @@ -1122,7 +1110,31 @@ update_last_index(lua_State *L, int world_index, int lua_index, struct group_ite } } else if (is_temporary(k->attrib) && get_write_component(L, lua_index, k->name, f, c)) { - if (c->stride == STRIDE_LUA) { + 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); + } + 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); @@ -1140,7 +1152,6 @@ update_last_index(lua_State *L, int world_index, int lua_index, struct group_ite if (disable_mainkey) { entity_disable_tag_(iter->world, mainkey, idx, mainkey); } - return ret; } static void @@ -1174,7 +1185,20 @@ query_index(struct group_iter *iter, int mainkey, int idx, unsigned int index[MA int j; for (j=1;jnkey;j++) { struct group_key *k = &iter->k[j]; - if (!is_temporary(k->attrib)) { + 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)) { @@ -1195,23 +1219,23 @@ read_iter(lua_State *L, int world_index, int obj_index, struct group_iter *iter, int i; for (i=0;inkey;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) { - luaL_error(L, "Missing lua table for %d", k->id); - } + 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_EXIST)) { + 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); @@ -1225,8 +1249,8 @@ read_iter(lua_State *L, int world_index, int obj_index, struct group_iter *iter, lua_setfield(L, obj_index, k->name); } } - f += k->field_n; } + f += k->field_n; } } @@ -1247,17 +1271,11 @@ lsync(lua_State *L) { return luaL_error(L, "Read pattern fail"); } - int ret = 0; if (!iter->readonly) { - ret = update_last_index(L, 1, 3, iter, idx); + update_last_index(L, 1, 3, iter, idx); } read_iter(L, 1, 3, iter, index); - if (ret) { - lua_pushinteger(L, ret); - return 1; - } else { - return 0; - } + return 0; } static int @@ -1292,7 +1310,7 @@ leach_group(lua_State *L) { lua_pushinteger(L, i); lua_rawseti(L, 2, 1); - + read_iter(L, world_index, 2, iter, index); lua_settop(L, 2); @@ -1302,7 +1320,7 @@ leach_group(lua_State *L) { 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_EXIST)) { // only existence + || (k->attrib & COMPONENT_FILTER)) { // existence or ref return; } if (k->field_n == 1 && f[0].key == NULL) { @@ -1430,6 +1448,12 @@ get_key(struct entity_world *w, lua_State *L, struct group_key *key, struct fiel 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; @@ -1467,7 +1491,14 @@ lgroupiter(lua_State *L) { 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); + int n = get_len(L, -1); + if (n == 0) { + struct field f; + if (is_value(L, &f)) { + n = 1; + } + } + field_n += n; lua_pop(L, 1); } size_t header_size = sizeof(struct group_iter) + sizeof(struct group_key) * (nkey-1); @@ -1487,7 +1518,13 @@ lgroupiter(lua_State *L) { 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) { @@ -1503,19 +1540,23 @@ lgroupiter(lua_State *L) { } int attrib = iter->k[i].attrib; int readonly; - if (attrib & COMPONENT_EXIST) + if (attrib & COMPONENT_FILTER) readonly = 0; else readonly = (attrib & COMPONENT_IN) && !(attrib & COMPONENT_OUT); if (!readonly) iter->readonly = 0; } - if (iter->k[0].attrib & COMPONENT_OPTIONAL) { + 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(iter->k[0].attrib)) { + 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"); + } if (luaL_newmetatable(L, "ENTITY_GROUPITER")) { lua_pushcfunction(L, lpairs_group); lua_setfield(L, -2, "__call"); @@ -1555,6 +1596,32 @@ lsortkey(lua_State *L) { 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 lbsearch(lua_State *L) { struct entity_world *w = getW(L); @@ -1573,7 +1640,7 @@ lbsearch(lua_State *L) { if (*v == value) { // found lua_createtable(L, 1, 0); - lua_pushinteger(L, mid); + lua_pushinteger(L, mid + 1); lua_seti(L, -2, 1); return 1; } @@ -1611,7 +1678,7 @@ lbsearch(lua_State *L) { static int lobject(lua_State *L) { struct group_iter *iter = luaL_checkudata(L, 1, "ENTITY_GROUPITER"); - int index = luaL_optinteger(L, 3, 1) - 1; + 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) { @@ -1671,15 +1738,32 @@ lobject(lua_State *L) { static int lrelease(lua_State *L) { struct entity_world *w = getW(L); - int cid = luaL_checkinteger(L, 2); + int cid = check_cid(L, w, 2); int refid = luaL_checkinteger(L, 3) - 1; - int live_tag = cid + 1; - int dead_tag = cid + 2; - entity_disable_tag_(w, cid, refid, live_tag); + 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; +} + LUAMOD_API int luaopen_ecs_core(lua_State *L) { luaL_checkversion(L); @@ -1704,10 +1788,12 @@ luaopen_ecs_core(lua_State *L) { { "_groupiter", lgroupiter }, { "remove", lremove }, { "_sortkey", lsortkey }, + { "_orderkey", lorderkey }, { "_object", lobject }, { "_sync", lsync }, { "_release", lrelease }, { "_bsearch", lbsearch }, + { "_reuse", lreuse }, { NULL, NULL }, }; luaL_setfuncs(L,l,0); diff --git a/framework/lualib-src/lua-ecs/luaecs.h b/framework/lualib-src/lua-ecs/luaecs.h index 5bca81a..5db8e83 100644 --- a/framework/lualib-src/lua-ecs/luaecs.h +++ b/framework/lualib-src/lua-ecs/luaecs.h @@ -122,8 +122,7 @@ static inline int entity_new_ref(struct ecs_context *ctx, int cid) { check_id_(ctx, cid); int object_id = ctx->cid[cid]; - int live_tag = object_id + 1; - int dead_tag = object_id + 2; + int dead_tag = object_id + 1; int id; if (ctx->api->iter(ctx->world, dead_tag, 0)) { // reuse @@ -134,7 +133,6 @@ entity_new_ref(struct ecs_context *ctx, int cid) { } else { id = ctx->api->new_entity(ctx->world, object_id, NULL, ctx->L, 1); } - ctx->api->enable_tag(ctx->world, object_id, id, live_tag, ctx->L, 1); return id + 1; } @@ -144,9 +142,7 @@ entity_release_ref(struct ecs_context *ctx, int cid, int id) { return; check_id_(ctx, cid); int object_id = ctx->cid[cid]; - int live_tag = object_id + 1; - int dead_tag = object_id + 2; - ctx->api->disable_tag(ctx->world, object_id, id-1, live_tag); + int dead_tag = object_id + 1; ctx->api->enable_tag(ctx->world, object_id, id-1, dead_tag, ctx->L, 1); } diff --git a/framework/lualib/3rd/misc/ecs.lua b/framework/lualib/3rd/misc/ecs.lua index 8a4d755..4f3f196 100644 --- a/framework/lualib/3rd/misc/ecs.lua +++ b/framework/lualib/3rd/misc/ecs.lua @@ -22,6 +22,9 @@ local function get_attrib(opt, inout) 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 @@ -49,7 +52,7 @@ local function cache_world(obj, k) id = tc.id, type = tc.type, } - -- local n = #tc + local n = #tc for i = 1, #tc do a[i] = tc[i] end @@ -62,7 +65,7 @@ local function cache_world(obj, k) local desc = {} local idx = 1 for token in pat:gmatch "[^ ]+" do - local key, padding = token:match "^([_%w]+)(.*)" + local key, index, padding = token:match "^([_%w]+)%(?([_%w]*)%)?(.*)" assert(key, "Invalid pattern") local opt, inout if padding ~= "" then @@ -73,13 +76,16 @@ local function cache_world(obj, k) if tc == nil then error("Unknown type " .. key) end - if tc.ref and inout ~= "new" then - local live = typenames[key .. "_live"] - local a = { - exist = true, - name = live.name, - id = live.id, - } + 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 @@ -93,6 +99,16 @@ local function cache_world(obj, k) 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 @@ -155,8 +171,8 @@ do -- newtype local function parse(s) -- s is "name:typename" local name, typename = s:match "^([%w_]+):(%w+)$" - local tid = assert(typeid[typename]) - return {tid, name} + local typeid = assert(typeid[typename]) + return {typeid, name} end local function align(c, field) @@ -194,7 +210,7 @@ do -- newtype if ttype == "lua" then assert(c.size == 0) c.size = ecs._LUAOBJECT - c.islua = true + assert(c[1] == nil) elseif c.size > 0 then align_struct(c, typeclass[1][1]) else @@ -212,9 +228,6 @@ do -- newtype self:_newtype(id, c.size) if typeclass.ref then c.ref = true - self:register{ - name = name .. "_live", - } self:register{ name = name .. "_dead", } @@ -222,17 +235,17 @@ do -- newtype 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 --- 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 +end function M:new(obj) -- dump(obj) @@ -248,32 +261,28 @@ function M:new(obj) end end -local ref_key = setmetatable({}, { - __index = function(cache, key) - local select_key = string.format("%s_dead:out %s_live?out %s:new", key, key, key) - cache[key] = select_key - return select_key - end, -}) - -function M:ref(name, obj) +function M:ref(name, refobj) + local obj = assert(refobj[name]) local ctx = context[self] local typenames = ctx.typenames local tc = assert(typenames[name]) - local live = name .. "_live" - local dead = name .. "_dead" - obj = obj or tc.tag - for v in self:select(dead) do - v[dead] = false - v[live] = true - v[name] = obj - return self:sync(ref_key[name], v) + 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 - local eid = self:_newentity() - local id = self:_addcomponent(eid, tc.id) - self:object(name, id, obj) - self:object(live, self:_addcomponent(eid, typenames[live].id), true) - return id + 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 end function M:release(name, refid) @@ -300,7 +309,8 @@ end function M:sync(pat, iter) local p = context[self].select[pat] - return self:_sync(p, iter) + self:_sync(p, iter) + return iter end function M:clear(name) @@ -308,11 +318,10 @@ function M:clear(name) self:_clear(id) end -function M:sort(sorted, name) +local function gen_sorted_id(self, sorted, name) local ctx = context[self] local typenames = ctx.typenames local t = assert(typenames[name]) - assert(t.type == typeid.int or (#t == 1 and t[1][1] == typeid.int)) local stype = typenames[sorted] if stype == nil then local id = ctx.id + 1 @@ -329,7 +338,16 @@ function M:sort(sorted, name) else assert(stype.size == ecs._ORDERKEY) end - self:_sortkey(stype.id, t.id) + return stype.id, t.id +end + +function M:sort(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) end function M:bsearch(sorted, name, value) @@ -348,7 +366,7 @@ do function M:singleton(name, v) local pat = context[self].ref[name] - return _object(pat, v) + return _object(pat, v, 1) end end diff --git a/framework/lualib/3rd/misc/enum.lua b/framework/lualib/3rd/misc/enum.lua deleted file mode 100644 index 35f9b74..0000000 --- a/framework/lualib/3rd/misc/enum.lua +++ /dev/null @@ -1,33 +0,0 @@ --- https://github.com/Tjakka5/Enum - -local Enum = {} -local Meta = { - __index = function(_, k) - error("Attempt to index non-existant enum '" .. tostring(k) .. "'.", 2) - end, - __newindex = function() - error("Attempt to write to static enum", 2) - end, -} - -function Enum.new(...) - local values = {...} - - if type(values[1]) == "table" then - values = values[1] - end - - local enum = {} - - for i = 1, #values do - enum[values[i]] = values[i] - end - - return setmetatable(enum, Meta) -end - -return setmetatable(Enum, { - __call = function(_, ...) - return Enum.new(...) - end, -}) diff --git a/framework/lualib/3rd/misc/fsm.lua b/framework/lualib/3rd/misc/fsm.lua index 35e0282..6ccde36 100644 --- a/framework/lualib/3rd/misc/fsm.lua +++ b/framework/lualib/3rd/misc/fsm.lua @@ -1,5 +1,4 @@ -- https://github.com/unindented/lua-fsm - local unpack = unpack or table.unpack local type = type local assert = assert diff --git a/framework/lualib/3rd/misc/lru.lua b/framework/lualib/3rd/misc/lru.lua index ed58640..6f12b5d 100644 --- a/framework/lualib/3rd/misc/lru.lua +++ b/framework/lualib/3rd/misc/lru.lua @@ -8,7 +8,6 @@ local assert = assert local LRU = {} function LRU.new(max_size, max_bytes) - assert(max_size >= 1, "max_size must be >= 1") assert(not max_bytes or max_bytes >= 1, "max_bytes must be >= 1") diff --git a/framework/lualib/3rd/unitest/luaunit.lua b/framework/lualib/3rd/misc/luaunit.lua similarity index 100% rename from framework/lualib/3rd/unitest/luaunit.lua rename to framework/lualib/3rd/misc/luaunit.lua diff --git a/framework/lualib/3rd/misc/middleclass.lua b/framework/lualib/3rd/misc/middleclass.lua deleted file mode 100644 index 57f702c..0000000 --- a/framework/lualib/3rd/misc/middleclass.lua +++ /dev/null @@ -1,199 +0,0 @@ --- https://github.com/jojo59516/middleclass -local middleclass = {} - -local function _createIndexWrapper(aClass, f) - if f == nil then - return aClass.__instanceDict - elseif type(f) == "function" then - return function(self, name) - local value = aClass.__instanceDict[name] - - if value ~= nil then - return value - else - return (f(self, name)) - end - end - else -- if type(f) == "table" then - return function(self, name) - local value = aClass.__instanceDict[name] - - if value ~= nil then - return value - else - return f[name] - end - end - end -end - -local function _propagateInstanceMethod(aClass, name, f) - f = name == "__index" and _createIndexWrapper(aClass, f) or f - aClass.__instanceDict[name] = f - - for subclass in pairs(aClass.subclasses) do - if rawget(subclass.__declaredMethods, name) == nil then - _propagateInstanceMethod(subclass, name, f) - end - end -end - -local function _declareInstanceMethod(aClass, name, f) - aClass.__declaredMethods[name] = f - - if f == nil and aClass.super then - f = aClass.super.__instanceDict[name] - end - - _propagateInstanceMethod(aClass, name, f) -end - -local function _tostring(self) - return "class " .. self.name -end -local function _call(self, ...) - return self:new(...) -end - -local function _createClass(name, super) - local dict = {} - dict.__index = dict - - local aClass = { - name = name, - super = super, - static = {}, - __instanceDict = dict, - __declaredMethods = {}, - subclasses = setmetatable({}, { - __mode = 'k', - }), - } - - if super then - setmetatable(aClass.static, { - __index = function(_, k) - local result = rawget(dict, k) - if result == nil then - return super.static[k] - end - return result - end, - }) - else - setmetatable(aClass.static, { - __index = function(_, k) - return rawget(dict, k) - end, - }) - end - - setmetatable(aClass, { - __index = aClass.static, - __tostring = _tostring, - __call = _call, - __newindex = _declareInstanceMethod, - }) - - return aClass -end - -local function _includeMixin(aClass, mixin) - assert(type(mixin) == 'table', "mixin must be a table") - - for name, method in pairs(mixin) do - if name ~= "included" and name ~= "static" then - aClass[name] = method - end - end - - for name, method in pairs(mixin.static or {}) do - aClass.static[name] = method - end - - if type(mixin.included) == "function" then - mixin:included(aClass) - end - return aClass -end - -local DefaultMixin = { - __tostring = function(self) - return "instance of " .. tostring(self.class) - end, - - initialize = function(self, ...) - end, - - isInstanceOf = function(self, aClass) - return type(aClass) == 'table' and type(self) == 'table' and - (self.class == aClass or type(self.class) == 'table' and type(self.class.isSubclassOf) == 'function' and - self.class:isSubclassOf(aClass)) - end, - - static = { - allocate = function(self) - assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'") - return setmetatable({ - class = self, - }, self.__instanceDict) - end, - - new = function(self, ...) - assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'") - local instance = self:allocate() - instance:initialize(...) - return instance - end, - - subclass = function(self, name) - assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'") - assert(type(name) == "string", "You must provide a name(string) for your class") - - local subclass = _createClass(name, self) - - for methodName, f in pairs(self.__instanceDict) do - if not (methodName == "__index" and type(f) == "table") then - _propagateInstanceMethod(subclass, methodName, f) - end - end - subclass.initialize = function(instance, ...) - return self.initialize(instance, ...) - end - - self.subclasses[subclass] = true - self:subclassed(subclass) - - return subclass - end, - - subclassed = function(self, other) - end, - - isSubclassOf = function(self, other) - return type(other) == 'table' and type(self.super) == 'table' and - (self.super == other or self.super:isSubclassOf(other)) - end, - - include = function(self, ...) - assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'") - for _, mixin in ipairs({...}) do - _includeMixin(self, mixin) - end - return self - end, - }, -} - -function middleclass.class(name, super) - assert(type(name) == 'string', "A name (string) is needed for the new class") - return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin) -end - -setmetatable(middleclass, { - __call = function(_, ...) - return middleclass.class(...) - end, -}) - -return middleclass diff --git a/framework/lualib/3rd/misc/nesting.lua b/framework/lualib/3rd/misc/nesting.lua index 611cdda..5d92f01 100644 --- a/framework/lualib/3rd/misc/nesting.lua +++ b/framework/lualib/3rd/misc/nesting.lua @@ -43,7 +43,7 @@ end --- 构造函数 -- @param deep 深度 -- @param bWeak 弱引用 -function NestingTable:initialize(deep, bWeak) +function NestingTable:__init(deep, bWeak) self.m_deep = deep or 1 self.m_bWeak = bWeak == true self.m_count = 0 diff --git a/framework/lualib/3rd/misc/promise.lua b/framework/lualib/3rd/misc/promise.lua index 3879b70..632eef5 100644 --- a/framework/lualib/3rd/misc/promise.lua +++ b/framework/lualib/3rd/misc/promise.lua @@ -1,12 +1,11 @@ -- Port of https://github.com/rhysbrettbowen/promise_impl/blob/master/promise.js -- and https://github.com/rhysbrettbowen/Aplus - local queue = {} local State = { PENDING = "pending", FULFILLED = "fulfilled", - REJECTED = "rejected" + REJECTED = "rejected", } local passthrough = function(x) @@ -30,9 +29,11 @@ local transition, resolve, run local Promise = { is_promise = true, - state = State.PENDING + state = State.PENDING, +} +Promise.mt = { + __index = Promise, } -Promise.mt = {__index = Promise} local do_async = function(callback) if Promise.async then @@ -51,11 +52,8 @@ local fulfill = function(promise, value) end transition = function(promise, state, value) - if - promise.state == state or promise.state ~= State.PENDING or - (state ~= State.FULFILLED and state ~= State.REJECTED) or - value == nil - then + if promise.state == state or promise.state ~= State.PENDING or + (state ~= State.FULFILLED and state ~= State.REJECTED) or value == nil then return end @@ -67,14 +65,11 @@ end function Promise:next(on_fulfilled, on_rejected) local promise = Promise.new() - table.insert( - self.queue, - { - fulfill = is_callable(on_fulfilled) and on_fulfilled or nil, - reject = is_callable(on_rejected) and on_rejected or nil, - promise = promise - } - ) + table.insert(self.queue, { + fulfill = is_callable(on_fulfilled) and on_fulfilled or nil, + reject = is_callable(on_rejected) and on_rejected or nil, + promise = promise, + }) run(self) @@ -98,14 +93,11 @@ resolve = function(promise, x) if x.is_promise then -- 2.3.2.1 if x is pending, resolve or reject this promise after completion if x.state == State.PENDING then - x:next( - function(value) - resolve(promise, value) - end, - function(reason) - reject(promise, reason) - end - ) + x:next(function(value) + resolve(promise, value) + end, function(reason) + reject(promise, reason) + end) return end -- if x is not pending, transition promise to x's state and value @@ -115,31 +107,24 @@ resolve = function(promise, x) local called = false -- 2.3.3.1. Catches errors thrown by __index metatable - local success, reason = - pcall( - function() - local next = x.next - if is_callable(next) then - next( - x, - function(y) - if not called then - resolve(promise, y) - called = true - end - end, - function(r) - if not called then - reject(promise, r) - called = true - end - end - ) - else - fulfill(promise, x) - end + local success, reason = pcall(function() + local next = x.next + if is_callable(next) then + next(x, function(y) + if not called then + resolve(promise, y) + called = true + end + end, function(r) + if not called then + reject(promise, r) + called = true + end + end) + else + fulfill(promise, x) end - ) + end) if not success then if not called then @@ -153,52 +138,44 @@ run = function(promise) return end - do_async( - function() - -- drain promise.queue while allowing pushes from within callbacks - local q = promise.queue - local i = 0 - while i < #q do - i = i + 1 - local obj = q[i] - local success, result = - pcall( - function() - local success = obj.fulfill or passthrough - local failure = obj.reject or errorthrough - local callback = promise.state == State.FULFILLED and success or failure - return callback(promise.value) - end - ) - - if not success then - reject(obj.promise, result) - else - resolve(obj.promise, result) - end - end - for j = 1, i do - q[j] = nil + do_async(function() + -- drain promise.queue while allowing pushes from within callbacks + local q = promise.queue + local i = 0 + while i < #q do + i = i + 1 + local obj = q[i] + local success, result = pcall(function() + local success = obj.fulfill or passthrough + local failure = obj.reject or errorthrough + local callback = promise.state == State.FULFILLED and success or failure + return callback(promise.value) + end) + + if not success then + reject(obj.promise, result) + else + resolve(obj.promise, result) end end - ) + for j = 1, i do + q[j] = nil + end + end) end function Promise.new(callback) local instance = { - queue = {} + queue = {}, } setmetatable(instance, Promise.mt) if callback then - callback( - function(value) - resolve(instance, value) - end, - function(reason) - reject(instance, reason) - end - ) + callback(function(value) + resolve(instance, value) + end, function(reason) + reject(instance, reason) + end) end return instance @@ -245,19 +222,16 @@ function Promise.all(...) end for i, p in ipairs(promises) do - p:next( - function(value) - results[i] = value - remaining = remaining - 1 - check_finished() - end, - function(value) - results[i] = value - remaining = remaining - 1 - state = State.REJECTED - check_finished() - end - ) + p:next(function(value) + results[i] = value + remaining = remaining - 1 + check_finished() + end, function(value) + results[i] = value + remaining = remaining - 1 + state = State.REJECTED + check_finished() + end) end check_finished() @@ -270,12 +244,9 @@ function Promise.race(...) local promises = {...} local promise = Promise.new() - Promise.all(...):next( - nil, - function(value) - reject(promise, value) - end - ) + Promise.all(...):next(nil, function(value) + reject(promise, value) + end) local success = function(value) fulfill(promise, value) diff --git a/framework/lualib/3rd/misc/singleton.lua b/framework/lualib/3rd/misc/singleton.lua deleted file mode 100644 index 25e2736..0000000 --- a/framework/lualib/3rd/misc/singleton.lua +++ /dev/null @@ -1,24 +0,0 @@ --- https://github.com/ichesnokov/middleclass-mixin-singleton - -local singleton = { - static = {}, -} - -function singleton:included(class) - -- Override new to throw an error, but store a reference to the old "new" method - class.static._new = class.static.new - class.static.new = function() - error("Use " .. class.name .. ":instance() instead of :new()") - end -end - -function singleton.static:instance(...) - self._instance = self._instance or self._new(self, ...) -- use old "new" method - return self._instance -end - -function singleton.static:clear_instance() - self._instance = nil -end - -return singleton diff --git a/framework/lualib/3rd/misc/vararg.lua b/framework/lualib/3rd/misc/vararg.lua index 1f0cf20..835dcec 100755 --- a/framework/lualib/3rd/misc/vararg.lua +++ b/framework/lualib/3rd/misc/vararg.lua @@ -1,6 +1,5 @@ -- https://github.com/moteus/lua-vararg local math = math -local table = table local error, assert, select = error, assert, select local max, unpack = math.max, table.unpack or unpack diff --git a/framework/lualib/3rd/oop/README.md b/framework/lualib/3rd/oop/README.md new file mode 100644 index 0000000..53e02c4 --- /dev/null +++ b/framework/lualib/3rd/oop/README.md @@ -0,0 +1,124 @@ +# luaoop +一个 lua 面向对象机制的实现。 +https://github.com/xiyoo0812/luaoop + +# 依赖 +- [lua](https://github.com/xiyoo0812/lua.git)5.2以上,(析构需要lua5.4) + +# 功能 +- 支持class +- 支持enum +- 支持单例 +- 支持构造(__init)和析构(__release) +- 支持mixin,export的接口会代理到class上,直接使用class进行调用 +- 支持使用accessor,reader,writer声明成员,并生成get/set方法 +- 多个mixin的同名非导出接口可以使用invoke进行串行调用,使用collect串行调用并收集执行结果。 + +# 限制 +- 访问限制暂时没有 +- 析构有限制,只能在gc的时候调用,会有延迟 +- 仅支持单继承,使用mixin机制扩展多继承,export函数重名会告警 +- 一个lua文件仅能声明一个class和mixin,主要是不想在声明类的时候带上类名参数,使用了文件名作为类标识,如果不喜欢可以修改实现。 +```lua +--当前声明类方式 +local ACLASS = class() +--不想使用下面这种方式 +local ACLASS = class("ACLASS") +``` + +# 测试代码 +- lua test/oop_test.lua + +# 使用方法 +```lua +--enum枚举定义 +--枚举名称,起始值,枚举变量列表 +local TEST1 = enum("TEST1", 0, "ONE", "THREE", "TWO") +print(TEST1.TWO) + +--枚举定义 +local TEST2 = enum("TEST2", 1, "ONE", "THREE", "TWO") +--使用枚举名定义新值,会在原来基础上累加 +TEST2.FOUR = TEST2() +print(TEST2.TWO, TEST2.FOUR) + +--使用下面方式定义会更优雅一点吧 +local TEST3 = enum("TEST3", 0) +TEST3("ONE") +TEST3("TWO") +TEST3("FOUR", 4) +--新定义会返回新值 +local five = TEST3("FIVE") + +print(TEST3.TWO, TEST3.FOUR, TEST3.FIVE, five) + +--mixin定义 +--mixin类似多继承,但是继承强调i'am,而mixin强调i'can. +--mixin无法实例化,必须依附到class上,mixin函数的self都是属主class对象 +--mixin除了不能实例化,其他和class使用方式一致 +--mixin export的接口会代理到class上,直接使用class进行调用 +local IObject = mixin( + "test1", + "test2", + "test3", + "test4" +) +--使用property定义属性,并生成get/set方法 +local prop = property(IObject) +prop:accessor("key1", 1) +prop:accessor("key2", 2) + +--构造函数 +function IObject:__init() +end + +function IObject:test1() + print("key1", self:get_key1()) + self:set_key2(4) + print("key2", self:get_key2()) + self:set_key3(6) + print("key3", self:get_key3()) +end + +function IObject:test2() + print("key2", self.key2) +end + +function IObject:test3() + print("key3", self.key3) +end + +--声明一个类 +--第一个函数为父类,后面是mixin接口列表 +local Object = class(nil, IObject) +local prop2 = property(Object) +prop2:accessor("key3", 3) +--构造函数 +function Object:__init() +end + +--析构函数 +function Object:__release() + print("release", self) +end + +function Object:run() + print("key3", self:get_key3()) + print("key1", self:get_key1()) + print("key2", self:get_key2()) + self:invoke("test1") +end + +--声明单例,和class一样使用 +local AAMgr = singleton(nil, IObject) +function AAMgr:__init() +end + +--创建单例对象 +local aamgr = AAMgr.inst() + +--创建一个对象,并调用函数 +local obj = Object() +obj:run() + +``` \ No newline at end of file diff --git a/framework/lualib/3rd/oop/class.lua b/framework/lualib/3rd/oop/class.lua new file mode 100644 index 0000000..6ac5659 --- /dev/null +++ b/framework/lualib/3rd/oop/class.lua @@ -0,0 +1,294 @@ +local skynet = require "skynet" + +local type = type +local pcall = pcall +local pairs = pairs +local ipairs = ipairs +local rawget = rawget +local rawset = rawset +local tostring = tostring +local ssub = string.sub +local sformat = string.format +local dgetinfo = debug.getinfo +local setmetatable = setmetatable +local tinsert = table.insert + +local is_class + +-- 类模板 +local class_tpls = {} + +local function deep_copy(src, dst) + local ndst = dst or {} + for key, value in pairs(src or {}) do + if is_class(value) then + ndst[key] = value() + elseif (type(value) == "table") then + ndst[key] = deep_copy(value) + else + ndst[key] = value + end + end + return ndst +end + +local function mixin_init(class, object, ...) + if class.__super then + mixin_init(class.__super, object, ...) + end + for _, mixin in ipairs(class.__mixins) do + if type(mixin.__init) == "function" then + mixin.__init(object, ...) + end + end + return object +end + +local function object_init(class, object, ...) + if class.__super then + object_init(class.__super, object, ...) + end + if type(class.__init) == "function" then + class.__init(object, ...) + end + return object +end + +local function object_release(class, object, ...) + if type(class.__release) == "function" then + class.__release(object, ...) + end + if class.__super then + object_release(class.__super, object, ...) + end +end + +local function object_defer(class, object, ...) + if type(class.__defer) == "function" then + class.__defer(object, ...) + end + if class.__super then + object_defer(class.__super, object, ...) + end +end + +local function object_default(class, object) + if class.__super then + object_default(class.__super, object) + end + local defaults = deep_copy(class.__default) + for name, param in pairs(defaults) do + object[name] = param[1] + end +end + +local function object_tostring(object) + if type(object.tostring) == "function" then + return object:tostring() + end + return sformat("class:%s(%s)", object.__moudle, object.__addr) +end + +local function object_constructor(class, ...) + local obj = {} + object_default(class, obj) + obj.__addr = ssub(tostring(obj), 7) + local object = setmetatable(obj, class.__vtbl) + object_init(class, object, ...) + mixin_init(class, object, ...) + return object +end + +local function new(class, ...) + if class.__singleton then + local inst_obj = rawget(class, "__inst") + if not inst_obj then + inst_obj = object_constructor(class, ...) + -- 定义单例方法 + local inst_func = function() + return inst_obj + end + rawset(class, "__inst", inst_obj) + rawset(class, "inst", inst_func) + end + return inst_obj + else + return object_constructor(class, ...) + end +end + +local function index(class, field) + return class.__vtbl[field] +end + +local function newindex(class, field, value) + class.__vtbl[field] = value +end + +local function release(obj) + object_release(obj.__class, obj) +end + +local function defer(obj) + object_defer(obj.__class, obj) +end + +local classMT = { + __call = new, + __index = index, + __newindex = newindex, +} + +local function invoke(object, method, ...) + local class = object.__class + for _, mixin in ipairs(class.__mixins) do + local mixin_method = mixin[method] + if mixin_method then + local ok, res = pcall(mixin_method, object, ...) + if not ok then + error(sformat("mixin: %s invoke '%s' failed: %s.", mixin.__moudle, method, res)) + end + end + end +end + +-- 返回true表示所有接口都完成 +local function collect(object, method, ...) + local class = object.__class + for _, mixin in ipairs(class.__mixins) do + local mixin_method = mixin[method] + if mixin_method then + local ok, res = pcall(mixin_method, object, ...) + if not ok then + error(sformat("mixin: %s collect '%s' failed: %s.", mixin.__moudle, method, res)) + return false + end + if not res then + skynet.error(sformat("mixin: %s collect '%s' failed: %s.", mixin.__moudle, method, res)) + return false + end + end + end + return true +end + +-- 代理一个类的所有接口,并检测接口是否实现 +local function implemented(class, mixins) + class.invoke = invoke + class.collect = collect + for _, mixin in ipairs(mixins) do + -- 属性处理 + for name, value in pairs(mixin.__default) do + if class.__default[name] then + skynet.error(sformat("the mixin default %s has repeat defined.", name)) + end + class.__default[name] = value + local access_prefix = {"is_", "get_", "set_"} + for _, prefix in pairs(access_prefix) do + local access_method = prefix .. name + if mixin[access_method] then + tinsert(mixin.__methods, access_method) + end + end + end + for _, method in pairs(mixin.__methods) do + if not mixin[method] then + skynet.error(sformat("the mixin method %s hasn't implemented.", method)) + mixin[method] = function() + skynet.error(sformat("the mixin method %s hasn't implemented.", method)) + end + end + if class[method] then + skynet.error(sformat("the mixin method %s has repeat implemented.", method)) + goto continue + end + -- 接口代理 + class[method] = function(...) + return mixin[method](...) + end + ::continue:: + end + tinsert(class.__mixins, mixin) + end +end + +local function class_constructor(class, super, ...) + local info = dgetinfo(2, "S") + local moudle = info.short_src + local class_tpl = class_tpls[moudle] + if not class_tpl then + local vtbl = { + __class = class, + __moudle = moudle, + __tostring = object_tostring, + } + vtbl.__gc = release + vtbl.__close = defer + vtbl.__index = vtbl + if super then + setmetatable(vtbl, { + __index = super, + }) + end + class.__vtbl = vtbl + class.__super = super + class.__default = {} + class.__mixins = {} + class_tpl = setmetatable(class, classMT) + implemented(class, {...}) + class_tpls[moudle] = class_tpl + end + return class_tpl +end + +function class(super, ...) + return class_constructor({}, super, ...) +end + +function singleton(super, ...) + return class_constructor({ + __singleton = true, + }, super, ...) +end + +-- function super(class) +-- return rawget(class, "__super") +-- end + +-- function classof(object) +-- return object.__class +-- end + +is_class = function(class) + return classMT == getmetatable(class) +end + +local function is_subclass(class, super) + while class do + if class == super then + return true + end + class = rawget(class, "__super") + end + return false +end + +function instanceof(object, class) + if not object or not class then + return false + end + local obj_class = object.__class + if obj_class then + return is_subclass(obj_class, class) + end + return false +end + +-- function conv_class(name) +-- local runtime = sformat("local obj = %s() return obj", name) +-- local ok, obj = pcall(load(runtime)) +-- if ok then +-- return obj +-- end +-- end + diff --git a/framework/lualib/3rd/oop/enum.lua b/framework/lualib/3rd/oop/enum.lua new file mode 100644 index 0000000..4b52b01 --- /dev/null +++ b/framework/lualib/3rd/oop/enum.lua @@ -0,0 +1,126 @@ +-- enum.lua +--[[提供枚举机制 +示例: + local enum = require(enum) + 用法1: + local TEST1 = enum("TEST1", 0, "ONE", "THREE", "TWO") + print(TEST1.TWO) + 用法2: + local TEST2 = enum("TEST2", 1, "ONE", "THREE", "TWO") + TEST2.FOUR = TEST2() + print(TEST2.TWO, TEST2.FOUR) + 用法3: + local TEST3 = enum("TEST3", 2) + TEST3("ONE") + TEST3("TWO") + TEST3("FOUR", 4) + local five = TEST3("FIVE") + print(TEST3.TWO, TEST3.FOUR, TEST3.FIVE, five) +--]] + +local skynet = require "skynet" + +local ipairs = ipairs +local rawget = rawget +local rawset = rawset +local tconcat = table.concat +local sformat = string.format +local dgetinfo = debug.getinfo +local setmetatable = setmetatable + +local enum_tpls = {} + +local function enum_tostring(eo) + return sformat("enum:%s(max:%s, elems: {%s})", eo.__name, eo.__vmax, tconcat(eo.__vlist, ",")) +end + +local function enum_new(emobj, field, value) + value = value or emobj.__vmax + if field then + emobj.__vlist[field] = value + if value >= emobj.__vmax then + emobj.__vmax = value + 1 + end + end + return value +end + +local function enum_index(emobj, field) + return emobj.__vlist[field] +end + +local function enum_newindex(emobj, field, value) + local vlist = emobj.__vlist + if vlist[field] then + skynet.error("enum %s redefine field %s!", emobj.__name, field) + end + vlist[field] = value + if value >= emobj.__vmax then + emobj.__vmax = value + 1 + end +end + +local enumMT = { + __call = enum_new, + __index = enum_index, + __newindex = enum_newindex, + __tostring = enum_tostring, +} + +local function enum_init(emobj, base, ...) + emobj.__vlist = {} + emobj.__vmax = base + for _, field in ipairs({...}) do + emobj.__vlist[field] = emobj.__vmax + emobj.__vmax = emobj.__vmax + 1 + end +end + +local function enum_list(ems) + local elist = rawget(ems, "__list") + if not elist then + elist = {} + rawset(ems, "__list", elist) + end + return elist +end + +local function new(ems, name, base, ...) + local info = dgetinfo(2, "S") + local moudle = info.short_src + local lists = enum_list(ems) + local eobj = lists[name] + if eobj then + if eobj.__moudle ~= moudle then + skynet.error("enum %s redefined! moudle:%s", name, moudle) + end + else + eobj = { + __name = name, + __moudle = moudle, + } + end + enum_init(eobj, base, ...) + setmetatable(eobj, enumMT) + lists[name] = eobj + return eobj +end + +local function index(ems, field) + local lists = enum_list(ems) + return lists[field] +end + +local MT = { + __call = new, + __index = index, +} +setmetatable(enum_tpls, MT) + +function Enum(name, base, ...) + if base then + return enum_tpls(name, base, ...) + end + -- 没有传base参数表示查询 + return enum_tpls[name] +end diff --git a/framework/lualib/3rd/misc/handler.lua b/framework/lualib/3rd/oop/handler.lua similarity index 81% rename from framework/lualib/3rd/misc/handler.lua rename to framework/lualib/3rd/oop/handler.lua index de7131e..57ad100 100644 --- a/framework/lualib/3rd/misc/handler.lua +++ b/framework/lualib/3rd/oop/handler.lua @@ -1,6 +1,5 @@ -local type = type - -return function(method, obj, params) +-- 支持 function 和 对象内部 handler 不过后者热更会更友好 +return function (method, obj, params) return function(...) if type(method) == "string" then if params then diff --git a/framework/lualib/3rd/oop/init.lua b/framework/lualib/3rd/oop/init.lua new file mode 100644 index 0000000..ca350ee --- /dev/null +++ b/framework/lualib/3rd/oop/init.lua @@ -0,0 +1,8 @@ +require "oop.class" +require "oop.mixin" +require "oop.property" +require "oop.enum" + +handler = require "oop.handler" +Option = require "oop.option" +Try = require "oop.try" \ No newline at end of file diff --git a/framework/lualib/3rd/oop/mixin.lua b/framework/lualib/3rd/oop/mixin.lua new file mode 100644 index 0000000..f1c0892 --- /dev/null +++ b/framework/lualib/3rd/oop/mixin.lua @@ -0,0 +1,51 @@ +-- mixin.lua +--[[提供混入机制 +示例: + Execute = mixin(nil, "execute") + Listener = class(nil, Listener) +备注: + mixin类似多继承,但是继承强调i'am,而mixin强调i'can. + mixin无法实例化,必须依附到class上,mixin函数的self都是属主class对象 +--]] + +local dgetinfo = debug.getinfo +local sformat = string.format +local setmetatable = setmetatable + +local mixin_tpls = {} + +local function index(mixin, field) + return mixin.__vtbl[field] +end + +local function newindex(mixin, field, value) + mixin.__vtbl[field] = value +end + +local mixinMT = { + __index = index, + __newindex = newindex, +} + +local function mixin_tostring(mixin) + return sformat("mixin:%s", mixin.__moudle) +end + +-- 接口定义函数 +function mixin(...) + local info = dgetinfo(2, "S") + local moudle = info.short_src + local mixin_tpl = mixin_tpls[moudle] + if not mixin_tpl then + local mixin = { + __vtbl = {}, + __default = {}, + __moudle = moudle, + __methods = {...}, + __tostring = mixin_tostring, + } + mixin_tpl = setmetatable(mixin, mixinMT) + mixin_tpls[moudle] = mixin_tpl + end + return mixin_tpl +end diff --git a/framework/lualib/3rd/oop/oop_test.lua b/framework/lualib/3rd/oop/oop_test.lua new file mode 100644 index 0000000..10a424a --- /dev/null +++ b/framework/lualib/3rd/oop/oop_test.lua @@ -0,0 +1,60 @@ +require "oop.init" + +local IObject = mixin("test1", "test2", "test3", "test4") + +local prop = Property(IObject) +prop:accessor("key1", 1) +prop:accessor("key2", 2) + +function IObject:__init() +end + +function IObject:test1() + print("key1", self:get_key1()) + self:set_key2(4) + print("key2", self:get_key2()) + self:set_key3(6) + print("key3", self:get_key3()) +end + +function IObject:test2() + print("key2", self.key2) +end + +function IObject:test3() + print("key3", self.key3) +end + +local Object = class(nil, IObject) +local prop2 = Property(Object) +prop2:accessor("key3", 3) +function Object:__init() +end + +function Object:__release() + print("release", self) +end + +function Object:run() + print("key3", self:get_key3()) + print("key1", self:get_key1()) + print("key2", self:get_key2()) + self:invoke("test1") +end + +local TEST1 = Enum("TEST1", 0, "ONE", "THREE", "TWO") +print(TEST1.TWO) +local TEST2 = Enum("TEST2", 1, "ONE", "THREE", "TWO") +TEST2.FOUR = TEST2() +print(TEST2.TWO, TEST2.FOUR) +local TEST3 = Enum("TEST3", 0) +TEST3("ONE") +TEST3("TWO") +TEST3("FOUR", 4) +local five = TEST3("FIVE") +print(TEST3.TWO, TEST3.FOUR, TEST3.FIVE, five) + +local obj = Object() +obj:run() + +return Object diff --git a/framework/lualib/3rd/misc/option.lua b/framework/lualib/3rd/oop/option.lua similarity index 100% rename from framework/lualib/3rd/misc/option.lua rename to framework/lualib/3rd/oop/option.lua diff --git a/framework/lualib/3rd/oop/property.lua b/framework/lualib/3rd/oop/property.lua new file mode 100644 index 0000000..bda9f29 --- /dev/null +++ b/framework/lualib/3rd/oop/property.lua @@ -0,0 +1,56 @@ +--[[property.lua + local Object = class() + prop = property(Object) + prop:reader("id", 0) + prop:accessor("name", "") +--]] +local ACCESSOR = 1 +local WRITER = 2 +local READER = 3 + +local function prop_accessor(_, class, name, default, mode, cb) + class.__default[name] = {default} + if mode <= WRITER then + class["set_" .. name] = function(self, value) + if self[name] == nil or self[name] ~= value then + self[name] = value + if cb then + cb(self, name, value) + end + end + end + mode = mode + 2 + end + if mode <= READER then + class["get_" .. name] = function(self) + if self[name] == nil then + return default + end + return self[name] + end + if type(default) == "boolean" then + class["is_" .. name] = class["get_" .. name] + end + end +end + +local property_reader = function(self, name, default) + prop_accessor(self, self.__class, name, default, READER) +end +local property_writer = function(self, name, default, cb) + prop_accessor(self, self.__class, name, default, WRITER, cb) +end +local property_accessor = function(self, name, default, cb) + prop_accessor(self, self.__class, name, default, ACCESSOR, cb) +end + +function Property(class) + local prop = { + __class = class, + reader = property_reader, + writer = property_writer, + accessor = property_accessor, + } + return prop +end + diff --git a/framework/lualib/3rd/misc/try.lua b/framework/lualib/3rd/oop/try.lua similarity index 100% rename from framework/lualib/3rd/misc/try.lua rename to framework/lualib/3rd/oop/try.lua diff --git a/framework/lualib/3rd/unitest/lester.lua b/framework/lualib/3rd/unitest/lester.lua deleted file mode 100644 index 64df041..0000000 --- a/framework/lualib/3rd/unitest/lester.lua +++ /dev/null @@ -1,473 +0,0 @@ ---[[ -Minimal test framework for Lua. -lester - v0.1.2 - 15/Feb/2021 -Eduardo Bart - edub4rt@gmail.com -https://github.com/edubart/lester -Minimal Lua test framework. -See end of file for LICENSE. -]] --[[-- -Lester is a minimal unit testing framework for Lua with a focus on being simple to use. - -## Features - -* Minimal, just one file. -* Self contained, no external dependencies. -* Simple and hackable when needed. -* Use `describe` and `it` blocks to describe tests. -* Supports `before` and `after` handlers. -* Colored output. -* Configurable via the script or with environment variables. -* Quiet mode, to use in live development. -* Optionally filter tests by name. -* Show traceback on errors. -* Show time to complete tests. -* Works with Lua 5.1+. -* Efficient. - -## Usage - -Copy `lester.lua` file to a project and require it, -which returns a table that includes all of the functionality: - -```lua -local lester = require 'lester' -local describe, it, expect = lester.describe, lester.it, lester.expect - --- Customize lester configuration. -lester.show_traceback = false - -describe('my project', function() - lester.before(function() - -- This function is run before every test. - end) - - describe('module1', function() -- Describe blocks can be nested. - it('feature1', function() - expect.equal('something', 'something') -- Pass. - end) - - it('feature2', function() - expect.truthy(false) -- Fail. - end) - end) -end) - -lester.report() -- Print overall statistic of the tests run. -lester.exit() -- Exit with success if all tests passed. -``` - -## Customizing output with environment variables - -To customize the output of lester externally, -you can set the following environment variables before running a test suite: - -* `LESTER_QUIET="true"`, omit print of passed tests. -* `LESTER_COLORED="false"`, disable colored output. -* `LESTER_SHOW_TRACEBACK="false"`, disable traceback on test failures. -* `LESTER_SHOW_ERROR="false"`, omit print of error description of failed tests. -* `LESTER_STOP_ON_FAIL="true"`, stop on first test failure. -* `LESTER_UTF8TERM="false"`, disable printing of UTF-8 characters. -* `LESTER_FILTER="some text"`, filter the tests that should be run. - -Note that these configurations can be changed via script too, check the documentation. - -]] -- Returns whether the terminal supports UTF-8 characters. -local function is_utf8term() - local lang = os.getenv('LANG') - return (lang and lang:lower():match('utf%-8$')) and true or false -end - --- Returns whether a system environment variable is "true". -local function getboolenv(varname, default) - local val = os.getenv(varname) - if val == 'true' then - return true - elseif val == 'false' then - return false - end - return default -end - --- The lester module. -local lester = { - --- Weather lines of passed tests should not be printed. False by default. - quiet = getboolenv('LESTER_QUIET', false), - --- Weather the output should be colorized. True by default. - colored = getboolenv('LESTER_COLORED', true), - --- Weather a traceback must be shown on test failures. True by default. - show_traceback = getboolenv('LESTER_SHOW_TRACEBACK', true), - --- Weather the error description of a test failure should be shown. True by default. - show_error = getboolenv('LESTER_SHOW_ERROR', true), - --- Weather test suite should exit on first test failure. False by default. - stop_on_fail = getboolenv('LESTER_STOP_ON_FAIL', false), - --- Weather we can print UTF-8 characters to the terminal. True by default when supported. - utf8term = getboolenv('LESTER_UTF8TERM', is_utf8term()), - --- A string with a lua pattern to filter tests. Nil by default. - filter = os.getenv('LESTER_FILTER'), - --- Function to retrieve time in seconds with milliseconds precision, `os.clock` by default. - seconds = os.clock, -} - --- Variables used internally for the lester state. -local lester_start = nil -local last_succeeded = false -local level = 0 -local successes = 0 -local total_successes = 0 -local failures = 0 -local total_failures = 0 -local start = 0 -local befores = {} -local afters = {} -local names = {} - --- Color codes. -local color_codes = { - reset = string.char(27) .. '[0m', - bright = string.char(27) .. '[1m', - red = string.char(27) .. '[31m', - green = string.char(27) .. '[32m', - blue = string.char(27) .. '[34m', - magenta = string.char(27) .. '[35m', -} - --- Colors table, returning proper color code if colored mode is enabled. -local colors = setmetatable({}, { - __index = function(_, key) - return lester.colored and color_codes[key] or '' - end, -}) - ---- Table of terminal colors codes, can be customized. -lester.colors = colors - ---- Describe a block of tests, which consists in a set of tests. --- Describes can be nested. --- @param name A string used to describe the block. --- @param func A function containing all the tests or other describes. -function lester.describe(name, func) - if level == 0 then -- Get start time for top level describe blocks. - start = lester.seconds() - if not lester_start then - lester_start = start - end - end - -- Setup describe block variables. - failures = 0 - successes = 0 - level = level + 1 - names[level] = name - -- Run the describe block. - func() - -- Cleanup describe block. - afters[level] = nil - befores[level] = nil - names[level] = nil - level = level - 1 - -- Pretty print statistics for top level describe block. - if level == 0 and not lester.quiet and (successes > 0 or failures > 0) then - local io_write = io.write - local colors_reset, colors_green = colors.reset, colors.green - io_write(failures == 0 and colors_green or colors.red, '[====] ', colors.magenta, name, colors_reset, ' | ', - colors_green, successes, colors_reset, ' successes / ') - if failures > 0 then - io_write(colors.red, failures, colors_reset, ' failures / ') - end - io_write(colors.bright, string.format('%.6f', lester.seconds() - start), colors_reset, ' seconds\n') - end -end - --- Error handler used to get traceback for errors. -local function xpcall_error_handler(err) - return debug.traceback(tostring(err), 2) -end - --- Pretty print the line on the test file where an error happened. -local function show_error_line(err) - local info = debug.getinfo(3) - local io_write = io.write - local colors_reset = colors.reset - local short_src, currentline = info.short_src, info.currentline - io_write(' (', colors.blue, short_src, colors_reset, ':', colors.bright, currentline, colors_reset) - if err and lester.show_traceback then - local fnsrc = short_src .. ':' .. currentline - for cap1, cap2 in err:gmatch('\t[^\n:]+:(%d+): in function <([^>]+)>\n') do - if cap2 == fnsrc then - io_write('/', colors.bright, cap1, colors_reset) - break - end - end - end - io_write(')') -end - --- Pretty print the test name, with breadcrumb for the describe blocks. -local function show_test_name(name) - local io_write = io.write - local colors_reset = colors.reset - for _, descname in ipairs(names) do - io_write(colors.magenta, descname, colors_reset, ' | ') - end - io_write(colors.bright, name, colors_reset) -end - ---- Declare a test, which consists of a set of assertions. --- @param name A name for the test. --- @param func The function containing all assertions. -function lester.it(name, func) - -- Skip the test if it does not match the filter. - if lester.filter then - local fullname = table.concat(names, ' | ') .. ' | ' .. name - if not fullname:match(lester.filter) then - return - end - end - -- Execute before handlers. - for _, levelbefores in ipairs(befores) do - for _, beforefn in ipairs(levelbefores) do - beforefn(name) - end - end - -- Run the test, capturing errors if any. - local success, err - if lester.show_traceback then - success, err = xpcall(func, xpcall_error_handler) - else - success, err = pcall(func) - if not success and err then - err = tostring(err) - end - end - -- Count successes and failures. - if success then - successes = successes + 1 - total_successes = total_successes + 1 - else - failures = failures + 1 - total_failures = total_failures + 1 - end - local io_write = io.write - local colors_reset = colors.reset - -- Print the test run. - if not lester.quiet then -- Show test status and complete test name. - if success then - io_write(colors.green, '[PASS] ', colors_reset) - else - io_write(colors.red, '[FAIL] ', colors_reset) - end - show_test_name(name) - if not success then - show_error_line(err) - end - io_write('\n') - else - if success then -- Show just a character hinting that the test succeeded. - local o = (lester.utf8term and lester.colored) and string.char(226, 151, 143) or 'o' - io_write(colors.green, o, colors_reset) - else -- Show complete test name on failure. - io_write(last_succeeded and '\n' or '', colors.red, '[FAIL] ', colors_reset) - show_test_name(name) - show_error_line(err) - io_write('\n') - end - end - -- Print error message, colorizing its output if possible. - if err and lester.show_error then - if lester.colored then - local errfile, errline, errmsg, rest = err:match('^([^:\n]+):(%d+): ([^\n]+)(.*)') - if errfile and errline and errmsg and rest then - io_write(colors.blue, errfile, colors_reset, ':', colors.bright, errline, colors_reset, ': ') - if errmsg:match('^%w([^:]*)$') then - io_write(colors.red, errmsg, colors_reset) - else - io_write(errmsg) - end - err = rest - end - end - io_write(err, '\n\n') - end - io.flush() - -- Stop on failure. - if not success and lester.stop_on_fail then - if lester.quiet then - io_write('\n') - io.flush() - end - lester.exit() - end - -- Execute after handlers. - for _, levelafters in ipairs(afters) do - for _, afterfn in ipairs(levelafters) do - afterfn(name) - end - end - last_succeeded = success -end - ---- Set a function that is called before every test inside a describe block. --- A single string containing the name of the test about to be run will be passed to `func`. -function lester.before(func) - local levelbefores = befores[level] - if not levelbefores then - levelbefores = {} - befores[level] = levelbefores - end - levelbefores[#levelbefores + 1] = func -end - ---- Set a function that is called after every test inside a describe block. --- A single string containing the name of the test that was finished will be passed to `func`. --- The function is executed independently if the test passed or failed. -function lester.after(func) - local levelafters = afters[level] - if not levelafters then - levelafters = {} - afters[level] = levelafters - end - levelafters[#levelafters + 1] = func -end - ---- Pretty print statistics of all test runs. --- With total success, total failures and run time in seconds. -function lester.report() - local now = lester.seconds() - local colors_reset = colors.reset - io.write(lester.quiet and '\n' or '', colors.green, total_successes, colors_reset, ' successes / ', colors.red, - total_failures, colors_reset, ' failures / ', colors.bright, string.format('%.6f', now - (lester_start or now)), - colors_reset, ' seconds\n') - io.flush() - return total_failures == 0 -end - ---- Exit the application with success code if all tests passed, or failure code otherwise. -function lester.exit() - os.exit(total_failures == 0) -end - -local expect = {} ---- Expect module, containing utility function for doing assertions inside a test. -lester.expect = expect - ---- Check if a function fails with an error. --- If `expected` is nil then any error is accepted. --- If `expected` is a string then we check if the error contains that string. --- If `expected` is anything else then we check if both are equal. -function expect.fail(func, expected) - local ok, err = pcall(func) - if ok then - error('expected function to fail', 2) - elseif expected ~= nil then - local found = expected == err - if not found and type(expected) == 'string' then - found = string.find(tostring(err), expected, 1, true) - end - if not found then - error('expected function to fail\nexpected:\n' .. tostring(expected) .. '\ngot:\n' .. tostring(err), 2) - end - end -end - ---- Check if a function does not fail with a error. -function expect.not_fail(func) - local ok, err = pcall(func) - if not ok then - error('expected function to not fail\ngot error:\n' .. tostring(err), 2) - end -end - ---- Check if a value is not `nil`. -function expect.exist(v) - if v == nil then - error('expected value to exist\ngot:\n' .. tostring(v), 2) - end -end - ---- Check if a value is `nil`. -function expect.not_exist(v) - if v ~= nil then - error('expected value to not exist\ngot:\n' .. tostring(v), 2) - end -end - ---- Check if an expression is evaluates to `true`. -function expect.truthy(v) - if not v then - error('expected expression to be true\ngot:\n' .. tostring(v), 2) - end -end - ---- Check if an expression is evaluates to `false`. -function expect.falsy(v) - if v then - error('expected expression to be false\ngot:\n' .. tostring(v), 2) - end -end - ---- Compare if two values are equal, considering nested tables. -local function strict_eq(t1, t2) - if rawequal(t1, t2) then - return true - end - if type(t1) ~= type(t2) then - return false - end - if type(t1) ~= 'table' then - return t1 == t2 - end - if getmetatable(t1) ~= getmetatable(t2) then - return false - end - for k, v1 in pairs(t1) do - if not strict_eq(v1, t2[k]) then - return false - end - end - for k, v2 in pairs(t2) do - if not strict_eq(v2, t1[k]) then - return false - end - end - return true -end - ---- Check if two values are equal. -function expect.equal(v1, v2) - if not strict_eq(v1, v2) then - error('expected values to be equal\nfirst value:\n' .. tostring(v1) .. '\nsecond value:\n' .. tostring(v2), 2) - end -end - ---- Check if two values are not equal. -function expect.not_equal(v1, v2) - if strict_eq(v1, v2) then - error('expected values to be not equal\nfirst value:\n' .. tostring(v1) .. '\nsecond value:\n' .. tostring(v2), - 2) - end -end - -return lester - ---[[ - The MIT License (MIT) - - Copyright (c) 2021 Eduardo Bart (https://github.com/edubart) - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - ]] diff --git a/server/src/preload/preload.lua b/server/src/preload/preload.lua index e35a7f6..0d316ac 100644 --- a/server/src/preload/preload.lua +++ b/server/src/preload/preload.lua @@ -1,9 +1,7 @@ collectgarbage("setpause", 120) -- 内存增大 1 倍(200/100)时自动释放一次内存 (200 是默认值) collectgarbage("setstepmul", 1000) -- 收集器单步收集的速度相对于内存分配速度的倍率,设置 200 的倍率等于 2 倍(200/100)。(200 是默认值) -class = require("zeus.middleclass") -singleton = require("zeus.singleton") -handler = require("zeus.handler") -- 支持 function 和 对象内部 handler 不过后者热更会更友好 +require("oop.init") -- zlog local zenv = require("zenv.init") if zenv.DEBUG then