🔧 build: 调整oop相关库

develop
xiaojin 5 years ago
parent 2f2409bad9
commit 7540100b4b

@ -13,7 +13,13 @@ globals = {
"SERVICE_NAME", "SERVICE_NAME",
"class", "class",
"singleton", "singleton",
"instanceof",
"mixin",
"handler", "handler",
"Property",
"Enum",
"Option",
"Try",
"ZLog", "ZLog",
} }

@ -204,38 +204,6 @@ ladd_component(lua_State *L) {
return 1; 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 static int
lnew_entity(lua_State *L) { lnew_entity(lua_State *L) {
struct entity_world *w = getW(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); assert(index >=0 && index < c->n);
unsigned int eid = c->id[index]; unsigned int eid = c->id[index];
// todo: pcall add_component_ // todo: pcall add_component_
assert(c->stride >= 0);
return add_component_((lua_State *)L, world_index, w, silbling_id, eid, buffer); 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); assert(index >=0 && index < c->n);
unsigned int eid = c->id[index]; unsigned int eid = c->id[index];
// todo: pcall add_component_ // todo: pcall add_component_
assert(c->stride == STRIDE_LUA);
int ret = add_component_id_(L, world_index, w, slibling_id, eid); int ret = add_component_id_(L, world_index, w, slibling_id, eid);
return ret; return ret;
} }
@ -686,18 +652,23 @@ comp_index_s(void *v, const void *a, const void *b) {
return comp_index(a,b,v); 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 static void
entity_sort_key_(struct entity_world *w, int orderid, int cid, void *L, int world_index) { entity_sort_key_(struct entity_world *w, int orderid, int cid, void *L, int world_index) {
struct component_pool *c = &w->c[cid]; struct component_pool *c = &w->c[cid];
assert(c->stride == sizeof(int)); assert(c->stride == sizeof(int));
struct component_pool *order = &w->c[orderid]; struct component_pool *order = &w->c[orderid];
assert(order->stride == STRIDE_ORDER); assert(order->stride == STRIDE_ORDER);
reserve_(w, orderid, c->cap, L, world_index);
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);
}
int i; int i;
for (i = 0; i < c->n ; i++) { for (i = 0; i < c->n ; i++) {
order->id[i] = i; order->id[i] = i;
@ -971,7 +942,11 @@ get_len(lua_State *L, int index) {
#define COMPONENT_OUT 2 #define COMPONENT_OUT 2
#define COMPONENT_OPTIONAL 4 #define COMPONENT_OPTIONAL 4
#define COMPONENT_OBJECT 8 #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 { struct group_key {
const char *name; const char *name;
@ -982,7 +957,7 @@ struct group_key {
static inline int static inline int
is_temporary(int attrib) { is_temporary(int attrib) {
if (attrib & COMPONENT_EXIST) if (attrib & COMPONENT_FILTER)
return 0; return 0;
return (attrib & COMPONENT_IN) == 0 && (attrib & COMPONENT_OUT) == 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; return r;
} }
static int static void
update_last_index(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx) { update_last_index(lua_State *L, int world_index, int lua_index, struct group_iter *iter, int idx) {
int ret = 0;
int i; int i;
int mainkey = iter->k[0].id; int mainkey = iter->k[0].id;
struct component_pool *c = &iter->world->c[mainkey]; struct component_pool *c = &iter->world->c[mainkey];
int disable_mainkey = 0; int disable_mainkey = 0;
if (!(iter->k[0].attrib & COMPONENT_EXIST)) { if (!(iter->k[0].attrib & COMPONENT_FILTER)) {
if (c->stride == STRIDE_TAG) { if (c->stride == STRIDE_TAG) {
// The mainkey is a tag, delay disable // The mainkey is a tag, delay disable
disable_mainkey = ((iter->k[0].attrib & COMPONENT_OUT) && remove_tag(L, lua_index, iter->k[0].name)); 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;i<iter->nkey;i++) { for (i=1;i<iter->nkey;i++) {
struct group_key *k = &iter->k[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]; struct component_pool *c = &iter->world->c[k->id];
if (c->stride == STRIDE_TAG) { if (c->stride == STRIDE_TAG) {
// It's a 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)) { switch (lua_getfield(L, lua_index, k->name)) {
case LUA_TNIL: case LUA_TNIL:
break; break;
@ -1097,6 +1071,11 @@ update_last_index(lua_State *L, int world_index, int lua_index, struct group_ite
} else { } else {
entity_disable_tag_(iter->world, mainkey, idx, k->id); 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; break;
default: 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))); 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) } else if ((k->attrib & COMPONENT_OUT)
&& get_write_component(L, lua_index, k->name, f, c)) { && 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) { if (index == 0) {
luaL_error(L, "Can't find sibling %s of %s", k->name, iter->k[0].name); luaL_error(L, "Can't find sibling %s of %s", k->name, iter->k[0].name);
} }
ret = index;
if (c->stride == STRIDE_LUA) { if (c->stride == STRIDE_LUA) {
if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) { if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) {
luaL_error(L, "Missing lua table for %d", k->id); 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) } else if (is_temporary(k->attrib)
&& get_write_component(L, lua_index, k->name, f, c)) { && get_write_component(L, lua_index, k->name, f, c)) {
if (k->attrib & COMPONENT_REFOBJECT) {
// new ref object
struct group_key *index_k = &iter->k[i-1];
int dead_tag = k->id + 1;
int id;
if (entity_iter_(iter->world, dead_tag, 0)) {
// reuse
id = entity_sibling_index_(iter->world, dead_tag, 0, k->id);
entity_disable_tag_(iter->world, dead_tag, 0, dead_tag);
} else {
id = entity_new_(iter->world, k->id, NULL, L, world_index);
}
if (c->stride == STRIDE_LUA) { 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); 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) { if (lua_getiuservalue(L, world_index, k->id * 2 + 2) != LUA_TTABLE) {
luaL_error(L, "Missing lua table for %d", k->id); 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) { if (disable_mainkey) {
entity_disable_tag_(iter->world, mainkey, idx, mainkey); entity_disable_tag_(iter->world, mainkey, idx, mainkey);
} }
return ret;
} }
static void static void
@ -1174,7 +1185,20 @@ query_index(struct group_iter *iter, int mainkey, int idx, unsigned int index[MA
int j; int j;
for (j=1;j<iter->nkey;j++) { for (j=1;j<iter->nkey;j++) {
struct group_key *k = &iter->k[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); index[j] = entity_sibling_index_(iter->world, mainkey, idx, k->id);
if (index[j] == 0) { if (index[j] == 0) {
if (!(k->attrib & COMPONENT_OPTIONAL)) { if (!(k->attrib & COMPONENT_OPTIONAL)) {
@ -1195,6 +1219,7 @@ read_iter(lua_State *L, int world_index, int obj_index, struct group_iter *iter,
int i; int i;
for (i=0;i<iter->nkey;i++) { for (i=0;i<iter->nkey;i++) {
struct group_key *k = &iter->k[i]; struct group_key *k = &iter->k[i];
if (!(k->attrib & COMPONENT_FILTER)) {
struct component_pool *c = &iter->world->c[k->id]; struct component_pool *c = &iter->world->c[k->id];
if (c->stride == STRIDE_LUA) { if (c->stride == STRIDE_LUA) {
// lua object component // lua object component
@ -1211,7 +1236,6 @@ read_iter(lua_State *L, int world_index, int obj_index, struct group_iter *iter,
lua_setfield(L, obj_index, k->name); lua_setfield(L, obj_index, k->name);
} }
} else if (c->stride != STRIDE_ORDER) { } else if (c->stride != STRIDE_ORDER) {
if (!(k->attrib & COMPONENT_EXIST)) {
if (k->attrib & COMPONENT_IN) { if (k->attrib & COMPONENT_IN) {
if (index[i]) { if (index[i]) {
void *ptr = get_ptr(c, index[i]-1); 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); 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"); return luaL_error(L, "Read pattern fail");
} }
int ret = 0;
if (!iter->readonly) { 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); read_iter(L, 1, 3, iter, index);
if (ret) {
lua_pushinteger(L, ret);
return 1;
} else {
return 0; return 0;
}
} }
static int static int
@ -1302,7 +1320,7 @@ leach_group(lua_State *L) {
static void static void
create_key_cache(lua_State *L, struct group_key *k, struct field *f) { create_key_cache(lua_State *L, struct group_key *k, struct field *f) {
if (k->field_n == 0 // is tag or object? if (k->field_n == 0 // is tag or object?
|| (k->attrib & COMPONENT_EXIST)) { // only existence || (k->attrib & COMPONENT_FILTER)) { // existence or ref
return; return;
} }
if (k->field_n == 1 && f[0].key == NULL) { 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")) { if (check_boolean(L, "exist")) {
attrib |= COMPONENT_EXIST; attrib |= COMPONENT_EXIST;
} }
if (check_boolean(L, "absent")) {
attrib |= COMPONENT_ABSENT;
}
if (check_boolean(L, "ref")) {
attrib |= COMPONENT_REFINDEX;
}
key->attrib = attrib; key->attrib = attrib;
if (is_value(L, f)) { if (is_value(L, f)) {
key->field_n = 1; key->field_n = 1;
@ -1467,7 +1491,14 @@ lgroupiter(lua_State *L) {
if (lua_geti(L, 2, i+1) != LUA_TTABLE) { if (lua_geti(L, 2, i+1) != LUA_TTABLE) {
return luaL_error(L, "index %d is not a table", i); 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); lua_pop(L, 1);
} }
size_t header_size = sizeof(struct group_iter) + sizeof(struct group_key) * (nkey-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++) { for (i=0; i< nkey; i++) {
lua_geti(L, 2, i+1); lua_geti(L, 2, i+1);
int n = get_key(w, L, &iter->k[i], f); 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]; 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; f += n;
lua_pop(L, 1); lua_pop(L, 1);
if (c->stride == STRIDE_LUA) { if (c->stride == STRIDE_LUA) {
@ -1503,19 +1540,23 @@ lgroupiter(lua_State *L) {
} }
int attrib = iter->k[i].attrib; int attrib = iter->k[i].attrib;
int readonly; int readonly;
if (attrib & COMPONENT_EXIST) if (attrib & COMPONENT_FILTER)
readonly = 0; readonly = 0;
else else
readonly = (attrib & COMPONENT_IN) && !(attrib & COMPONENT_OUT); readonly = (attrib & COMPONENT_IN) && !(attrib & COMPONENT_OUT);
if (!readonly) if (!readonly)
iter->readonly = 0; 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"); 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"); 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")) { if (luaL_newmetatable(L, "ENTITY_GROUPITER")) {
lua_pushcfunction(L, lpairs_group); lua_pushcfunction(L, lpairs_group);
lua_setfield(L, -2, "__call"); lua_setfield(L, -2, "__call");
@ -1555,6 +1596,32 @@ lsortkey(lua_State *L) {
return 0; 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<n;i++) {
int refid = get_integer(L, 4, i+1, "order");
if (refid > ref->n) {
return luaL_error(L, "Invalid refid %d", refid);
}
c->id[i] = ref->id[refid - 1];
}
c->n = n;
return 0;
}
static int static int
lbsearch(lua_State *L) { lbsearch(lua_State *L) {
struct entity_world *w = getW(L); struct entity_world *w = getW(L);
@ -1573,7 +1640,7 @@ lbsearch(lua_State *L) {
if (*v == value) { if (*v == value) {
// found // found
lua_createtable(L, 1, 0); lua_createtable(L, 1, 0);
lua_pushinteger(L, mid); lua_pushinteger(L, mid + 1);
lua_seti(L, -2, 1); lua_seti(L, -2, 1);
return 1; return 1;
} }
@ -1611,7 +1678,7 @@ lbsearch(lua_State *L) {
static int static int
lobject(lua_State *L) { lobject(lua_State *L) {
struct group_iter *iter = luaL_checkudata(L, 1, "ENTITY_GROUPITER"); 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; int cid = iter->k[0].id;
struct entity_world * w = iter->world; struct entity_world * w = iter->world;
if (cid < 0 || cid >=MAX_COMPONENT) { if (cid < 0 || cid >=MAX_COMPONENT) {
@ -1671,15 +1738,32 @@ lobject(lua_State *L) {
static int static int
lrelease(lua_State *L) { lrelease(lua_State *L) {
struct entity_world *w = getW(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 refid = luaL_checkinteger(L, 3) - 1;
int live_tag = cid + 1; int dead_tag = cid + 1;
int dead_tag = cid + 2;
entity_disable_tag_(w, cid, refid, live_tag);
entity_enable_tag_(w, cid, refid, dead_tag, L, 1); entity_enable_tag_(w, cid, refid, dead_tag, L, 1);
return 0; 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 LUAMOD_API int
luaopen_ecs_core(lua_State *L) { luaopen_ecs_core(lua_State *L) {
luaL_checkversion(L); luaL_checkversion(L);
@ -1704,10 +1788,12 @@ luaopen_ecs_core(lua_State *L) {
{ "_groupiter", lgroupiter }, { "_groupiter", lgroupiter },
{ "remove", lremove }, { "remove", lremove },
{ "_sortkey", lsortkey }, { "_sortkey", lsortkey },
{ "_orderkey", lorderkey },
{ "_object", lobject }, { "_object", lobject },
{ "_sync", lsync }, { "_sync", lsync },
{ "_release", lrelease }, { "_release", lrelease },
{ "_bsearch", lbsearch }, { "_bsearch", lbsearch },
{ "_reuse", lreuse },
{ NULL, NULL }, { NULL, NULL },
}; };
luaL_setfuncs(L,l,0); luaL_setfuncs(L,l,0);

@ -122,8 +122,7 @@ static inline int
entity_new_ref(struct ecs_context *ctx, int cid) { entity_new_ref(struct ecs_context *ctx, int cid) {
check_id_(ctx, cid); check_id_(ctx, cid);
int object_id = ctx->cid[cid]; int object_id = ctx->cid[cid];
int live_tag = object_id + 1; int dead_tag = object_id + 1;
int dead_tag = object_id + 2;
int id; int id;
if (ctx->api->iter(ctx->world, dead_tag, 0)) { if (ctx->api->iter(ctx->world, dead_tag, 0)) {
// reuse // reuse
@ -134,7 +133,6 @@ entity_new_ref(struct ecs_context *ctx, int cid) {
} else { } else {
id = ctx->api->new_entity(ctx->world, object_id, NULL, ctx->L, 1); 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; return id + 1;
} }
@ -144,9 +142,7 @@ entity_release_ref(struct ecs_context *ctx, int cid, int id) {
return; return;
check_id_(ctx, cid); check_id_(ctx, cid);
int object_id = ctx->cid[cid]; int object_id = ctx->cid[cid];
int live_tag = object_id + 1; int dead_tag = object_id + 1;
int dead_tag = object_id + 2;
ctx->api->disable_tag(ctx->world, object_id, id-1, live_tag);
ctx->api->enable_tag(ctx->world, object_id, id-1, dead_tag, ctx->L, 1); ctx->api->enable_tag(ctx->world, object_id, id-1, dead_tag, ctx->L, 1);
} }

@ -22,6 +22,9 @@ local function get_attrib(opt, inout)
elseif inout == "exist" then elseif inout == "exist" then
desc.exist = true desc.exist = true
assert(not desc.opt) assert(not desc.opt)
elseif inout == "absent" then
desc.absent = true
assert(not desc.opt)
else else
assert(inout == "temp") assert(inout == "temp")
end end
@ -49,7 +52,7 @@ local function cache_world(obj, k)
id = tc.id, id = tc.id,
type = tc.type, type = tc.type,
} }
-- local n = #tc local n = #tc
for i = 1, #tc do for i = 1, #tc do
a[i] = tc[i] a[i] = tc[i]
end end
@ -62,7 +65,7 @@ local function cache_world(obj, k)
local desc = {} local desc = {}
local idx = 1 local idx = 1
for token in pat:gmatch "[^ ]+" do for token in pat:gmatch "[^ ]+" do
local key, padding = token:match "^([_%w]+)(.*)" local key, index, padding = token:match "^([_%w]+)%(?([_%w]*)%)?(.*)"
assert(key, "Invalid pattern") assert(key, "Invalid pattern")
local opt, inout local opt, inout
if padding ~= "" then if padding ~= "" then
@ -73,13 +76,16 @@ local function cache_world(obj, k)
if tc == nil then if tc == nil then
error("Unknown type " .. key) error("Unknown type " .. key)
end end
if tc.ref and inout ~= "new" then if index ~= "" then
local live = typenames[key .. "_live"] local indexc = typenames[index]
local a = { if indexc == nil then
exist = true, error("Unknown index type " .. index)
name = live.name, end
id = live.id, 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 desc[idx] = a
idx = idx + 1 idx = idx + 1
end end
@ -93,6 +99,16 @@ local function cache_world(obj, k)
end end
desc[idx] = a desc[idx] = a
idx = idx + 1 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 end
return desc return desc
end end
@ -155,8 +171,8 @@ do -- newtype
local function parse(s) local function parse(s)
-- s is "name:typename" -- s is "name:typename"
local name, typename = s:match "^([%w_]+):(%w+)$" local name, typename = s:match "^([%w_]+):(%w+)$"
local tid = assert(typeid[typename]) local typeid = assert(typeid[typename])
return {tid, name} return {typeid, name}
end end
local function align(c, field) local function align(c, field)
@ -194,7 +210,7 @@ do -- newtype
if ttype == "lua" then if ttype == "lua" then
assert(c.size == 0) assert(c.size == 0)
c.size = ecs._LUAOBJECT c.size = ecs._LUAOBJECT
c.islua = true assert(c[1] == nil)
elseif c.size > 0 then elseif c.size > 0 then
align_struct(c, typeclass[1][1]) align_struct(c, typeclass[1][1])
else else
@ -212,9 +228,6 @@ do -- newtype
self:_newtype(id, c.size) self:_newtype(id, c.size)
if typeclass.ref then if typeclass.ref then
c.ref = true c.ref = true
self:register{
name = name .. "_live",
}
self:register{ self:register{
name = name .. "_dead", name = name .. "_dead",
} }
@ -222,17 +235,17 @@ do -- newtype
end end
end end
-- local function dump(obj) local function dump(obj)
-- for e,v in pairs(obj) do for e, v in pairs(obj) do
-- if type(v) == "table" then if type(v) == "table" then
-- for k,v in pairs(v) do for k, v in pairs(v) do
-- print(e,k,v) print(e, k, v)
-- end end
-- else else
-- print(e,v) print(e, v)
-- end end
-- end end
-- end end
function M:new(obj) function M:new(obj)
-- dump(obj) -- dump(obj)
@ -248,32 +261,28 @@ function M:new(obj)
end end
end end
local ref_key = setmetatable({}, { function M:ref(name, refobj)
__index = function(cache, key) local obj = assert(refobj[name])
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)
local ctx = context[self] local ctx = context[self]
local typenames = ctx.typenames local typenames = ctx.typenames
local tc = assert(typenames[name]) local tc = assert(typenames[name])
local live = name .. "_live" local refid = self:_reuse(tc.id)
local dead = name .. "_dead" if refid then
obj = obj or tc.tag local p = context[self].select[name .. ":out"]
for v in self:select(dead) do self:_sync(p, refobj)
v[dead] = false else
v[live] = true
v[name] = obj
return self:sync(ref_key[name], v)
end
local eid = self:_newentity() local eid = self:_newentity()
local id = self:_addcomponent(eid, tc.id) refid = self:_addcomponent(eid, tc.id)
self:object(name, id, obj) self:object(name, refid, obj)
self:object(live, self:_addcomponent(eid, typenames[live].id), true) end
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 end
function M:release(name, refid) function M:release(name, refid)
@ -300,7 +309,8 @@ end
function M:sync(pat, iter) function M:sync(pat, iter)
local p = context[self].select[pat] local p = context[self].select[pat]
return self:_sync(p, iter) self:_sync(p, iter)
return iter
end end
function M:clear(name) function M:clear(name)
@ -308,11 +318,10 @@ function M:clear(name)
self:_clear(id) self:_clear(id)
end end
function M:sort(sorted, name) local function gen_sorted_id(self, sorted, name)
local ctx = context[self] local ctx = context[self]
local typenames = ctx.typenames local typenames = ctx.typenames
local t = assert(typenames[name]) local t = assert(typenames[name])
assert(t.type == typeid.int or (#t == 1 and t[1][1] == typeid.int))
local stype = typenames[sorted] local stype = typenames[sorted]
if stype == nil then if stype == nil then
local id = ctx.id + 1 local id = ctx.id + 1
@ -329,7 +338,16 @@ function M:sort(sorted, name)
else else
assert(stype.size == ecs._ORDERKEY) assert(stype.size == ecs._ORDERKEY)
end 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 end
function M:bsearch(sorted, name, value) function M:bsearch(sorted, name, value)
@ -348,7 +366,7 @@ do
function M:singleton(name, v) function M:singleton(name, v)
local pat = context[self].ref[name] local pat = context[self].ref[name]
return _object(pat, v) return _object(pat, v, 1)
end end
end end

@ -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,
})

@ -1,5 +1,4 @@
-- https://github.com/unindented/lua-fsm -- https://github.com/unindented/lua-fsm
local unpack = unpack or table.unpack local unpack = unpack or table.unpack
local type = type local type = type
local assert = assert local assert = assert

@ -8,7 +8,6 @@ local assert = assert
local LRU = {} local LRU = {}
function LRU.new(max_size, max_bytes) function LRU.new(max_size, max_bytes)
assert(max_size >= 1, "max_size must be >= 1") assert(max_size >= 1, "max_size must be >= 1")
assert(not max_bytes or max_bytes >= 1, "max_bytes must be >= 1") assert(not max_bytes or max_bytes >= 1, "max_bytes must be >= 1")

@ -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

@ -43,7 +43,7 @@ end
--- 构造函数 --- 构造函数
-- @param deep 深度 -- @param deep 深度
-- @param bWeak 弱引用 -- @param bWeak 弱引用
function NestingTable:initialize(deep, bWeak) function NestingTable:__init(deep, bWeak)
self.m_deep = deep or 1 self.m_deep = deep or 1
self.m_bWeak = bWeak == true self.m_bWeak = bWeak == true
self.m_count = 0 self.m_count = 0

@ -1,12 +1,11 @@
-- Port of https://github.com/rhysbrettbowen/promise_impl/blob/master/promise.js -- Port of https://github.com/rhysbrettbowen/promise_impl/blob/master/promise.js
-- and https://github.com/rhysbrettbowen/Aplus -- and https://github.com/rhysbrettbowen/Aplus
local queue = {} local queue = {}
local State = { local State = {
PENDING = "pending", PENDING = "pending",
FULFILLED = "fulfilled", FULFILLED = "fulfilled",
REJECTED = "rejected" REJECTED = "rejected",
} }
local passthrough = function(x) local passthrough = function(x)
@ -30,9 +29,11 @@ local transition, resolve, run
local Promise = { local Promise = {
is_promise = true, is_promise = true,
state = State.PENDING state = State.PENDING,
}
Promise.mt = {
__index = Promise,
} }
Promise.mt = {__index = Promise}
local do_async = function(callback) local do_async = function(callback)
if Promise.async then if Promise.async then
@ -51,11 +52,8 @@ local fulfill = function(promise, value)
end end
transition = function(promise, state, value) transition = function(promise, state, value)
if if promise.state == state or promise.state ~= State.PENDING or
promise.state == state or promise.state ~= State.PENDING or (state ~= State.FULFILLED and state ~= State.REJECTED) or value == nil then
(state ~= State.FULFILLED and state ~= State.REJECTED) or
value == nil
then
return return
end end
@ -67,14 +65,11 @@ end
function Promise:next(on_fulfilled, on_rejected) function Promise:next(on_fulfilled, on_rejected)
local promise = Promise.new() local promise = Promise.new()
table.insert( table.insert(self.queue, {
self.queue,
{
fulfill = is_callable(on_fulfilled) and on_fulfilled or nil, fulfill = is_callable(on_fulfilled) and on_fulfilled or nil,
reject = is_callable(on_rejected) and on_rejected or nil, reject = is_callable(on_rejected) and on_rejected or nil,
promise = promise promise = promise,
} })
)
run(self) run(self)
@ -98,14 +93,11 @@ resolve = function(promise, x)
if x.is_promise then if x.is_promise then
-- 2.3.2.1 if x is pending, resolve or reject this promise after completion -- 2.3.2.1 if x is pending, resolve or reject this promise after completion
if x.state == State.PENDING then if x.state == State.PENDING then
x:next( x:next(function(value)
function(value)
resolve(promise, value) resolve(promise, value)
end, end, function(reason)
function(reason)
reject(promise, reason) reject(promise, reason)
end end)
)
return return
end end
-- if x is not pending, transition promise to x's state and value -- if x is not pending, transition promise to x's state and value
@ -115,31 +107,24 @@ resolve = function(promise, x)
local called = false local called = false
-- 2.3.3.1. Catches errors thrown by __index metatable -- 2.3.3.1. Catches errors thrown by __index metatable
local success, reason = local success, reason = pcall(function()
pcall(
function()
local next = x.next local next = x.next
if is_callable(next) then if is_callable(next) then
next( next(x, function(y)
x,
function(y)
if not called then if not called then
resolve(promise, y) resolve(promise, y)
called = true called = true
end end
end, end, function(r)
function(r)
if not called then if not called then
reject(promise, r) reject(promise, r)
called = true called = true
end end
end end)
)
else else
fulfill(promise, x) fulfill(promise, x)
end end
end end)
)
if not success then if not success then
if not called then if not called then
@ -153,23 +138,19 @@ run = function(promise)
return return
end end
do_async( do_async(function()
function()
-- drain promise.queue while allowing pushes from within callbacks -- drain promise.queue while allowing pushes from within callbacks
local q = promise.queue local q = promise.queue
local i = 0 local i = 0
while i < #q do while i < #q do
i = i + 1 i = i + 1
local obj = q[i] local obj = q[i]
local success, result = local success, result = pcall(function()
pcall(
function()
local success = obj.fulfill or passthrough local success = obj.fulfill or passthrough
local failure = obj.reject or errorthrough local failure = obj.reject or errorthrough
local callback = promise.state == State.FULFILLED and success or failure local callback = promise.state == State.FULFILLED and success or failure
return callback(promise.value) return callback(promise.value)
end end)
)
if not success then if not success then
reject(obj.promise, result) reject(obj.promise, result)
@ -180,25 +161,21 @@ run = function(promise)
for j = 1, i do for j = 1, i do
q[j] = nil q[j] = nil
end end
end end)
)
end end
function Promise.new(callback) function Promise.new(callback)
local instance = { local instance = {
queue = {} queue = {},
} }
setmetatable(instance, Promise.mt) setmetatable(instance, Promise.mt)
if callback then if callback then
callback( callback(function(value)
function(value)
resolve(instance, value) resolve(instance, value)
end, end, function(reason)
function(reason)
reject(instance, reason) reject(instance, reason)
end end)
)
end end
return instance return instance
@ -245,19 +222,16 @@ function Promise.all(...)
end end
for i, p in ipairs(promises) do for i, p in ipairs(promises) do
p:next( p:next(function(value)
function(value)
results[i] = value results[i] = value
remaining = remaining - 1 remaining = remaining - 1
check_finished() check_finished()
end, end, function(value)
function(value)
results[i] = value results[i] = value
remaining = remaining - 1 remaining = remaining - 1
state = State.REJECTED state = State.REJECTED
check_finished() check_finished()
end end)
)
end end
check_finished() check_finished()
@ -270,12 +244,9 @@ function Promise.race(...)
local promises = {...} local promises = {...}
local promise = Promise.new() local promise = Promise.new()
Promise.all(...):next( Promise.all(...):next(nil, function(value)
nil,
function(value)
reject(promise, value) reject(promise, value)
end end)
)
local success = function(value) local success = function(value)
fulfill(promise, value) fulfill(promise, value)

@ -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

@ -1,6 +1,5 @@
-- https://github.com/moteus/lua-vararg -- https://github.com/moteus/lua-vararg
local math = math local math = math
local table = table
local error, assert, select = error, assert, select local error, assert, select = error, assert, select
local max, unpack = math.max, table.unpack or unpack local max, unpack = math.max, table.unpack or unpack

@ -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)
- 支持mixinexport的接口会代理到class上直接使用class进行调用
- 支持使用accessorreaderwriter声明成员并生成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()
```

@ -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

@ -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

@ -1,6 +1,5 @@
local type = type -- 支持 function 和 对象内部 handler 不过后者热更会更友好
return function (method, obj, params)
return function(method, obj, params)
return function(...) return function(...)
if type(method) == "string" then if type(method) == "string" then
if params then if params then

@ -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"

@ -0,0 +1,51 @@
-- mixin.lua
--[[提供混入机制
:
Execute = mixin(nil, "execute")
Listener = class(nil, Listener)
mixini'am而mixin强调i'can.
mixinclassmixinselfclass
--]]
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

@ -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

@ -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

@ -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.
]]

@ -1,9 +1,7 @@
collectgarbage("setpause", 120) -- 内存增大 1 倍200/100时自动释放一次内存 200 是默认值) collectgarbage("setpause", 120) -- 内存增大 1 倍200/100时自动释放一次内存 200 是默认值)
collectgarbage("setstepmul", 1000) -- 收集器单步收集的速度相对于内存分配速度的倍率,设置 200 的倍率等于 2 倍200/100200 是默认值) collectgarbage("setstepmul", 1000) -- 收集器单步收集的速度相对于内存分配速度的倍率,设置 200 的倍率等于 2 倍200/100200 是默认值)
class = require("zeus.middleclass") require("oop.init")
singleton = require("zeus.singleton")
handler = require("zeus.handler") -- 支持 function 和 对象内部 handler 不过后者热更会更友好
-- zlog -- zlog
local zenv = require("zenv.init") local zenv = require("zenv.init")
if zenv.DEBUG then if zenv.DEBUG then

Loading…
Cancel
Save