diff --git a/.luacheckrc b/.luacheckrc index 2839660..3a37180 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -12,8 +12,9 @@ exclude_files = { globals = { "SERVICE_NAME", "class", + "singleton", "handler", - "gLog", + "zlog", } ignore = { diff --git a/framework/lualib-src/Makefile b/framework/lualib-src/Makefile index 64d97be..4227a2f 100644 --- a/framework/lualib-src/Makefile +++ b/framework/lualib-src/Makefile @@ -19,6 +19,7 @@ SKIPLIST_SO = $(LUA_CLIB_PATH)/skiplist.so SNAPSHOT_SO = $(LUA_CLIB_PATH)/snapshot.so SHIFTTIMER_SO = $(LUA_CLIB_PATH)/shiftimer.so CLUA_SO = $(LUA_CLIB_PATH)/clua.so +AOI_SO = $(LUA_CLIB_PATH)/aoi.so ##################################################### all: $(LFS_SO) \ $(CJSON_SO) \ @@ -26,6 +27,7 @@ all: $(LFS_SO) \ $(SKIPLIST_SO) \ $(SNAPSHOT_SO) \ $(SHIFTTIMER_SO) \ + $(AOI_SO) \ $(CLUA_SO) ##################################################### @@ -47,6 +49,9 @@ $(SNAPSHOT_SO): $(SHIFTTIMER_SO): cd lua-timer && $(MAKE) PLAT=$(PLAT) +$(AOI_SO): + cd lua-aoi && $(MAKE) PLAT=$(PLAT) + $(CLUA_SO): cd lua-clua && $(MAKE) PLAT=$(PLAT) diff --git a/framework/lualib-src/lua-aoi/Makefile b/framework/lualib-src/lua-aoi/Makefile new file mode 100644 index 0000000..69f1808 --- /dev/null +++ b/framework/lualib-src/lua-aoi/Makefile @@ -0,0 +1,30 @@ +SKYNET_ROOT ?= ../../skynet +include $(SKYNET_ROOT)/platform.mk + +PLAT ?= none + +TARGET = ../../luaclib/aoi.so + +ifeq ($(PLAT), macosx) + CFLAGS = -g -O2 -dynamiclib -Wl,-undefined,dynamic_lookup +else +ifeq ($(PLAT), linux) + CFLAGS = -g -O2 -shared -fPIC +endif +endif + +LUA_LIB ?= $(SKYNET_ROOT)/3rd/lua/ +LUA_INC ?= $(SKYNET_ROOT)/3rd/lua/ +SKYNET_SRC ?= $(SKYNET_ROOT)/skynet-src + +SRC = . + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(foreach dir, $(SRC), $(wildcard $(dir)/*.c)) + $(CC) $(CFLAGS) $(SHARED) -I$(LUA_INC) -I$(SKYNET_SRC) $^ -o $@ + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/framework/lualib-src/lua-aoi/aoi.c b/framework/lualib-src/lua-aoi/aoi.c new file mode 100644 index 0000000..9f1d156 --- /dev/null +++ b/framework/lualib-src/lua-aoi/aoi.c @@ -0,0 +1,848 @@ +//gcc -o aoi aoi.c utils.c +#include +#include +#include +#include + +#include +#include +#include + +#include "utils.h" +#include "aoi.h" + +static int NEARBY_CEIL_OFFSETS[9][2] = { + {0, 0}, // center + {-1, 1}, // TL + {0, 1}, // T + {1, 1}, // TR + + {-1, 0}, // L + {1, 0}, // R + + {-1, -1}, // BL + {0, -1}, // B + {1, -1}, // BR +}; + +static double cost_time_in_lua = 0.0; +static double cost_time_in_cal = 0.0; +static double cost_time_in_get_event = 0.0; + +static int get_grid_idx(World *w, int x, int y) +{ + return (x - 1) * (w->col) + y - 1; +}; + +static void get_xy_by_grididx(World *w, int grid_idx, int *x, int *y) +{ + *x = (grid_idx / w->col) + 1; + *y = grid_idx - ((*x) - 1) * w->col + 1; + return; +} + +static int out_of_range(World *w, int grid_idx, int idx) +{ + int x1, y1; + get_xy_by_grididx(w, grid_idx, &x1, &y1); + int x2, y2; + get_xy_by_grididx(w, idx, &x2, &y2); + if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) > 2) + { + return 1; + } + return 0; +} + +static HashTable *get_nearby_grids(World *w, Grid *grid) +{ + int grid_num = w->row * w->col; + HashTable *ret = hashtbl_create(); + + int x, y; + get_xy_by_grididx(w, grid->idx, &x, &y); + int i, _x, _y; + for (i = 0; i < 9; i++) + { + _x = x + NEARBY_CEIL_OFFSETS[i][0]; + _y = y + NEARBY_CEIL_OFFSETS[i][1]; + if (_x >= 1 && _x <= w->row && _y >= 1 && _y <= w->col) + { + int tmp_idx = get_grid_idx(w, _x, _y); + Grid *grid = w->grids[tmp_idx]; + hashtbl_upsert(ret, tmp_idx, grid); + //printf("get_nearby_grids %d %d\n",grid->idx,tmp_idx); + } + } + + // for (int i = -1; i<=1; i++){ + // for (int j= -1; j<=1; j++){ + // int index = i*w->col + grid->idx + j; + // if(index >= 0 && index < grid_num){ + // Grid* grid = w->grids[index]; + // hashtbl_upsert(ret, index, grid); + // } + // } + // } + // local k = i*col + cur_grididx + j + // local grid = grids[k] + // if grid and not ret[k] then + // ret[k] = grid + // end + // end + // end + // return ret + + return ret; +} + +static void print_event(int grid_idx, char flag, HashTable *events) +{ + HashTableIter iter; + hashtbl_iter_reset(&iter); + while (hashtbl_iter(events, &iter)) + { + Event *e = (Event *)(iter.node->value); + printf("flag:%c grid_idx:%d event:%c,id:%d,x:%d,y:%d\n", flag, grid_idx, e->e, e->id, e->x, e->y); + } +} + +static void get_grid_add_event(Grid *grid, lua_State *L) +{ //你进入某个格子,要知道该格子的事情 + int idx_grid_info = lua_gettop(L); //2 + lua_rawgeti(L, idx_grid_info, grid->idx); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_info, grid->idx); + } + int idx_grid_detail = lua_gettop(L); //3 + lua_rawgeti(L, idx_grid_detail, 1); //key_idx==1 + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_detail, 1); + } + int idx_grid_add_del_update = lua_gettop(L); //4 + size_t tbl_len = lua_rawlen(L, idx_grid_add_del_update); + if (tbl_len == 0) + { + int len = 1; + + HashTableIter iter; + hashtbl_iter_reset(&iter); + while (hashtbl_iter(grid->caches, &iter)) + { + Cache *cache = (Cache *)(iter.node->value); + if (cache->e == 'A' || cache->e == 'U') + { + lua_newtable(L); + lua_pushinteger(L, 1); //'A' + lua_rawseti(L, -2, 1); + lua_pushinteger(L, cache->id); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, cache->x); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, cache->y); + lua_rawseti(L, -2, 4); + + lua_rawseti(L, idx_grid_add_del_update, len); + len = len + 1; + } + } + + hashtbl_iter_reset(&iter); + while (hashtbl_iter(grid->makers, &iter)) + { + Obj *obj = (Obj *)(iter.node->value); + if (!hashtbl_get(grid->caches, obj->id)) + { //如果在cache中且没进入event_adds,说明e是D + lua_newtable(L); + lua_pushinteger(L, 1); //'A' + lua_rawseti(L, -2, 1); + lua_pushinteger(L, obj->id); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, obj->x); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, obj->y); + lua_rawseti(L, -2, 4); + + lua_rawseti(L, idx_grid_add_del_update, len); + len = len + 1; + } + } + } +} + +static void get_grid_del_event(Grid *grid, lua_State *L) +{ //你离开某个格子,要知道该格子的事情 + int idx_grid_info = lua_gettop(L); //2 + lua_rawgeti(L, idx_grid_info, grid->idx); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_info, grid->idx); + } + int idx_grid_detail = lua_gettop(L); //3 + lua_rawgeti(L, idx_grid_detail, 2); //key_idx==2 + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_detail, 2); + } + int idx_grid_add_del_update = lua_gettop(L); //4 + size_t tbl_len = lua_rawlen(L, idx_grid_add_del_update); + if (tbl_len == 0) + { + int len = 1; + + HashTableIter iter; + hashtbl_iter_reset(&iter); + //cache中还没加进makers的就不管了 + while (hashtbl_iter(grid->makers, &iter)) + { + Obj *obj = (Obj *)(iter.node->value); + lua_newtable(L); + + lua_pushinteger(L, 2); //'D' + lua_rawseti(L, -2, 1); + lua_pushinteger(L, obj->id); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, obj->x); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, obj->y); + lua_rawseti(L, -2, 4); + + lua_rawseti(L, idx_grid_add_del_update, len); + len = len + 1; + } + } +} + +static void get_grid_update_event(Grid *grid, lua_State *L) +{ //你在某个格子附近晃,要知道该格子的事情 + int idx_grid_info = lua_gettop(L); //2 + lua_rawgeti(L, idx_grid_info, grid->idx); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_info, grid->idx); + } + int idx_grid_detail = lua_gettop(L); //3 + lua_rawgeti(L, idx_grid_detail, 3); //key_idx==3 + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_detail, 3); + } + int idx_grid_add_del_update = lua_gettop(L); //4 + size_t tbl_len = lua_rawlen(L, idx_grid_add_del_update); + if (tbl_len == 0) + { + int len = 1; + + HashTableIter iter; + hashtbl_iter_reset(&iter); + while (hashtbl_iter(grid->caches, &iter)) + { + Cache *cache = (Cache *)(iter.node->value); + lua_newtable(L); + if (cache->e == 'A') + { + lua_pushinteger(L, 1); //'A' + } + else if (cache->e == 'D') + { + lua_pushinteger(L, 2); + } + else + { + lua_pushinteger(L, 3); + } + lua_rawseti(L, -2, 1); + lua_pushinteger(L, cache->id); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, cache->x); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, cache->y); + lua_rawseti(L, -2, 4); + + lua_rawseti(L, idx_grid_add_del_update, len); + len = len + 1; + } + } +} + +//结果处理 +static void add_grid_events_to_watchers(Grid *grid, char e, int id, lua_State *L) +{ + //printf("add_grid_events_to_watchers %d %c %d\n", grid->idx, e, id); + //clock_t t1 = clock(); + //HashTable* events; + int key_idx; + if (e == 'U') + { //3 + get_grid_update_event(grid, L); + key_idx = 3; + } + else if (e == 'A') + { //1 + get_grid_add_event(grid, L); + key_idx = 1; + } + else if (e == 'D') + { //2 + get_grid_del_event(grid, L); + key_idx = 2; + } + //clock_t t2 = clock(); + //double t3 = (double)(t2-t1)/CLOCKS_PER_SEC; + //cost_time_in_get_event += t3; + + //clock_t time_begin = clock(); + //不copy,直接用引用 + int idx_ret = 1; + //将events upsert进L + //printf("upsert events succ %d\n",idx_grid_add_del_update); + + lua_rawgeti(L, idx_ret, id); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_ret, id); + } + int idx_watcher_ret = lua_gettop(L); //5 + size_t tbl_len = lua_rawlen(L, idx_watcher_ret); + lua_pushvalue(L, 4); + lua_rawseti(L, idx_watcher_ret, tbl_len + 1); + lua_pop(L, 3); + + // lua_rawgeti(L, idx_watcher_ret, grid->idx); + // if(lua_isnil(L,-1)){ + // lua_pop(L,1); + // lua_newtable(L); + // lua_pushvalue(L,-1); + // lua_rawseti(L, idx_watcher_ret, grid->idx); + // } + // int idx_watcher_grid_ret = lua_gettop(L);//6 + // lua_pushvalue(L, 4);//引用,idx_grid_add_del_update==4 + // lua_rawseti(L, idx_watcher_grid_ret, key_idx); + // lua_pop(L,4); + //clock_t time_end = clock(); + //double time_cost = (double)(time_end-time_begin)/CLOCKS_PER_SEC; + //cost_time_in_lua += time_cost; + //printf("===cost_time_in_lua:%f====\n", cost_time_in_lua); +} + +//watcher到了grid +static void resolve_change_watcher(World *w, Grid *grid, HashTable *right_grid_map, int pre_idx, int id, lua_State *L) +{ + //printf("resolve_change_watcher id:%d grid_idx:%d pre_idx:%d\n",id, grid->idx, pre_idx); + HashTableIter iter; + HashTable *left_grid_map; + if (pre_idx != -1) + { + // int x,y; + // get_xy_by_grididx(w,pre_idx,&x,&y); + // int i, _x, _y; + // for(i=0; i<9; i++) { + // _x = x + NEARBY_CEIL_OFFSETS[i][0]; + // _y = y + NEARBY_CEIL_OFFSETS[i][1]; + // if (!(_x>=1&&_x<=w->row&&_y>=1&&_y<=w->col)){ + // continue; + // } + // int tmp_idx = get_grid_idx(w, _x, _y); + // Grid* tmp_grid = w->grids[tmp_idx]; + + // if(out_of_range(w, grid->idx, tmp_idx)){ + // add_grid_events_to_watchers(tmp_grid, 'D', id, L); + // } + // else{ + // add_grid_events_to_watchers(tmp_grid, 'U', id, L); + // } + // } + Grid *old_grid = w->grids[pre_idx]; + left_grid_map = get_nearby_grids(w, old_grid); + + hashtbl_iter_reset(&iter); + while (hashtbl_iter(left_grid_map, &iter)) + { + Grid *tmp_grid = (Grid *)(iter.node->value); + if (hashtbl_get(right_grid_map, tmp_grid->idx)) + { + add_grid_events_to_watchers(tmp_grid, 'U', id, L); + } + else + { + add_grid_events_to_watchers(tmp_grid, 'D', id, L); + } + } + } + + hashtbl_iter_reset(&iter); + while (hashtbl_iter(right_grid_map, &iter)) + { + Grid *tmp_grid = (Grid *)(iter.node->value); + if (pre_idx == -1 || !hashtbl_get(left_grid_map, tmp_grid->idx)) + { + add_grid_events_to_watchers(tmp_grid, 'A', id, L); + } + } + if (pre_idx != -1) + { + hashtbl_destroy(left_grid_map); + } + // for (int i = 0; i<9;i++){ + // Grid* tmp_grid = grid_list[i]; + // if(!tmp_grid){ + // continue; + // } + // if(pre_idx!=-1 && !out_of_range(w, pre_idx, tmp_grid->idx)){ + // continue;//之前已经get 'U'过了 + // } + // add_grid_events_to_watchers(tmp_grid, 'A', id, L); + // } +} +static int grid_add_obj(World *w, Grid *grid, int id, int x, int y, int is_maker, int is_watcher) +{ + if (is_maker) + { + if (hashtbl_get(grid->caches, id)) + { + //之前可能有D 在里面 + Cache *old_cache = (Cache *)hashtbl_get(grid->caches, id); + skynet_free(old_cache); + hashtbl_remove(grid->caches, id); + old_cache = NULL; + //printf("old cache id:%d e:%c x:%d y:%d \n", old_cache->id, old_cache->e, old_cache->x, old_cache->y); + } + Cache *cache = (Cache *)skynet_malloc(sizeof(Cache)); + cache->id = id; + cache->x = x; + cache->y = y; + cache->e = 'A'; + hashtbl_insert(grid->caches, id, cache); + } + if (is_watcher) + { + Obj *obj = (Obj *)skynet_malloc(sizeof(Obj)); + obj->id = id; + obj->x = x; + obj->y = y; + obj->is_maker = is_maker; + obj->is_watcher = is_watcher; + hashtbl_insert(grid->watchers, obj->id, obj); + hashtbl_upsert(w->watcher_grids, grid->idx, NULL); + } +} + +static int grid_del_obj(World *w, Grid *grid, int id) +{ + if (hashtbl_get(grid->makers, id)) + { //已经落地了 + //旧的cache清除 + if (hashtbl_get(grid->caches, id)) + { + Cache *old_cache = (Cache *)hashtbl_get(grid->caches, id); + skynet_free(old_cache); + hashtbl_remove(grid->caches, id); + old_cache = NULL; + } + //添加新的 + Obj *obj = (Obj *)hashtbl_get(grid->makers, id); + Cache *cache = (Cache *)skynet_malloc(sizeof(Cache)); + cache->id = obj->id; + cache->x = obj->x; + cache->y = obj->y; + cache->e = 'D'; + hashtbl_insert(grid->caches, obj->id, cache); + } + else if (hashtbl_get(grid->caches, id)) + { //只是存在cache中 + //当你从来没有出现过 + //printf("grid_del_obj remove_caches %d\n", id); + Cache *cache = (Cache *)hashtbl_get(grid->caches, id); + skynet_free(cache); + hashtbl_remove(grid->caches, id); + cache = NULL; + } + + if (hashtbl_get(grid->watchers, id)) + { + //printf("grid_del_obj remove_watchers %d\n", id); + Obj *obj = (Obj *)hashtbl_get(grid->watchers, id); + hashtbl_remove(grid->watchers, id); + if (!obj->is_maker) + { + //如果只是watcher的话,释放对象 + skynet_free(obj); + obj = NULL; + } + if (grid->watchers->count == 0) + { + hashtbl_remove(w->watcher_grids, grid->idx); + } + } +} + +static void grid_update_obj(Grid *grid, int id) +{ + //watcher不需要update + if (hashtbl_get(grid->makers, id) && !hashtbl_get(grid->caches, id)) + { + //之前已经在的且没有caches的才需要添加cache + Obj *obj = (Obj *)hashtbl_get(grid->makers, id); + Cache *cache = (Cache *)skynet_malloc(sizeof(Cache)); + cache->id = obj->id; + cache->x = obj->x; + cache->y = obj->y; + cache->e = 'U'; + hashtbl_insert(grid->caches, obj->id, cache); + } +} + +static void handle_cache(Grid *grid) +{ + HashTableIter iter; + hashtbl_iter_reset(&iter); + while (hashtbl_iter(grid->caches, &iter)) + { + Cache *cache = (Cache *)(iter.node->value); + iter.node->value = NULL; + if (cache->e == 'A' && !hashtbl_get(grid->makers, cache->id)) + { //之前在makers,D了再A,就会重复,无须处理 + //先D了去了其他格子,再回来,就不需要add_makers了 + Obj *obj; + if (hashtbl_get(grid->watchers, cache->id)) + { + obj = (Obj *)hashtbl_get(grid->watchers, cache->id); + } + else + { + obj = (Obj *)skynet_malloc(sizeof(Obj)); + obj->id = cache->id; + obj->x = cache->x; + obj->y = cache->y; + obj->is_maker = 1; + obj->is_watcher = 0; + } + hashtbl_insert(grid->makers, obj->id, obj); + } + else if (cache->e == 'D') + { + Obj *obj = (Obj *)hashtbl_get(grid->makers, cache->id); + hashtbl_remove(grid->makers, cache->id); + skynet_free(obj); + obj = NULL; + } + skynet_free(cache); + cache = NULL; + } + hashtbl_destroy(grid->caches); + grid->caches = hashtbl_create(); +} + +static void handle_aoi(World *w, Grid *grid, lua_State *L) +{ + + HashTable *grid_map = get_nearby_grids(w, grid); + HashTableIter iter2; + + HashTableIter iter; + hashtbl_iter_reset(&iter); + //Grid** grid_list = get_nearby_grids(w, grid); + while (hashtbl_iter(grid->watchers, &iter)) + { + Obj *watcher = (Obj *)(iter.node->value); + int pre_idx = -1; + int id = watcher->id; + //printf("handle_aoi %d %d\n",grid->idx, id); + if (hashtbl_get(w->pre_where_is, id)) + { + int *p_idx = (int *)hashtbl_get(w->pre_where_is, id); + pre_idx = *p_idx; + } + if (pre_idx == grid->idx) + { + + hashtbl_iter_reset(&iter2); + while (hashtbl_iter(grid_map, &iter2)) + { + Grid *tmp_grid = (Grid *)(iter2.node->value); + // if(id==6) { + // printf("tmp_grid %d\n",tmp_grid->idx); + // } + add_grid_events_to_watchers(tmp_grid, 'U', id, L); + } + // for (int i = 0; i<9;i++){ + // Grid* tmp_grid = grid_list[i]; + // if(!tmp_grid){ + // continue; + // } + // add_grid_events_to_watchers(tmp_grid, 'U', id, L); + // } + } + else + { + resolve_change_watcher(w, grid, grid_map, pre_idx, id, L); + if (hashtbl_get(w->pre_where_is, id)) + { + int *p_idx = (int *)hashtbl_get(w->pre_where_is, id); + *p_idx = grid->idx; + } + else + { + int *p_idx = (int *)skynet_malloc(sizeof(int)); + *p_idx = grid->idx; + hashtbl_insert(w->pre_where_is, id, p_idx); + } + } + } + hashtbl_destroy(grid_map); +} + +World *aoi_create_world(int row, int col) +{ + //printf("aoi_create_world %d %d\n",row, col); + World *w = (World *)skynet_malloc(sizeof(World)); + w->row = row; + w->col = col; + + int grid_num = w->row * w->col; + w->grids = (Grid **)skynet_malloc(grid_num * sizeof(Grid *)); + for (int i = 0; i < grid_num; i++) + { + Grid *grid = (Grid *)skynet_malloc(sizeof(Grid)); + grid->idx = i; + grid->watchers = hashtbl_create(); + grid->makers = hashtbl_create(); + grid->caches = hashtbl_create(); + + w->grids[i] = grid; + } + w->where_is = hashtbl_create(); + w->pre_where_is = hashtbl_create(); + w->watcher_grids = hashtbl_create(); + return w; +} + +void aoi_get_cost_time(void *l) +{ + lua_State *L = (lua_State *)l; + lua_pushnumber(L, cost_time_in_get_event); + lua_pushnumber(L, cost_time_in_lua); + lua_pushnumber(L, cost_time_in_cal); + cost_time_in_get_event = 0.0; + cost_time_in_lua = 0.0; + cost_time_in_cal = 0.0; + return; +} + +void aoi_update_aoi(World *w, void *l) +{ + //clock_t time_begin = clock(); + lua_State *L = (lua_State *)l; + //printf("=========start update_aoi=========\n"); + int grid_num = w->row * w->col; + //抛出事件 + + //for(int i=0;igrids[i]; + // handle_aoi(w, grid, L); + //} + HashTableIter iter; + hashtbl_iter_reset(&iter); + while (hashtbl_iter(w->watcher_grids, &iter)) + { + int grid_idx = (iter.node->key); + //printf("handle_aoi grid_idx %d\n", grid_idx); + Grid *grid = w->grids[grid_idx]; + handle_aoi(w, grid, L); + } + //clock_t time_end = clock(); + //处理cache + //printf("handle_cache\n"); + for (int i = 0; i < grid_num; i++) + { + Grid *grid = w->grids[i]; + handle_cache(grid); + } + + //清空每个格子的events 通过lua_state自己清 + //printf("clean all grids events\n"); + + //printf("=========end update_aoi=========\n"); + //clock_t time_end = clock(); + //double time_cost = (double)(time_end-time_begin)/CLOCKS_PER_SEC; + //cost_time_in_cal += time_cost; + //printf("===time_cost:%f====\n",cost_time_in_cal); +} + +int aoi_add_obj(World *w, int id, int x, int y, int is_maker, int is_watcher) +{ + int idx = get_grid_idx(w, x, y); //函数结束会释放idx的,不能hashtbl_insert(&idx) + Grid *grid = w->grids[idx]; + + if (hashtbl_get(w->where_is, id) || hashtbl_get(grid->caches, id) || hashtbl_get(grid->watchers, id) || hashtbl_get(grid->makers, id)) + { + printf("add_obj duplicated %d\n", id); + return 1; + } + if (!is_maker && !is_watcher) + { + printf("add_obj no watcher_and_maker %d\n", id); + return 2; + } + int *p_idx = (int *)skynet_malloc(sizeof(int)); + *p_idx = idx; //不同于p_idx=&idx + hashtbl_insert(w->where_is, id, p_idx); + grid_add_obj(w, grid, id, x, y, is_maker, is_watcher); + return 0; +} + +int aoi_del_obj(World *w, int id) +{ + int *p_idx = (int *)hashtbl_get(w->where_is, id); + if (!p_idx) + { + printf("del_obj not_exist %d\n", id); + return 1; + } + Grid *grid = w->grids[*p_idx]; + grid_del_obj(w, grid, id); + + hashtbl_remove(w->where_is, id); + skynet_free(p_idx); + return 0; +} + +int aoi_set_obj(World *w, int id, int x, int y) +{ + int *p_idx = (int *)hashtbl_get(w->where_is, id); + if (!p_idx) + { + printf("set_obj not_exist %d\n", id); + return 1; + } + int idx = get_grid_idx(w, x, y); + if (idx == *p_idx) + { + Grid *grid = w->grids[idx]; + grid_update_obj(grid, id); + } + else + { + Grid *old_grid = w->grids[*p_idx]; + Grid *new_grid = w->grids[idx]; + int is_maker = 0; + int is_watcher = 0; + if (hashtbl_get(old_grid->makers, id) || hashtbl_get(old_grid->caches, id)) + { + is_maker = 1; + } + if (hashtbl_get(old_grid->watchers, id)) + { + is_watcher = 1; + } + grid_del_obj(w, old_grid, id); + grid_add_obj(w, new_grid, id, x, y, is_maker, is_watcher); + + *p_idx = idx; + } + return 0; +} + +// int main() { +// // World* w = aoi_create_world(3,3); +// // aoi_add_obj(w,7,2,2,1,1); +// // aoi_add_obj(w,1,2,2,1,1); +// // aoi_add_obj(w,6,3,3,1,1); +// // //aoi_update_aoi(w); +// // //aoi_update_aoi(w); +// // aoi_set_obj(w,7,2,3); +// // //aoi_update_aoi(w); +// // aoi_add_obj(w,2,1,1,1,1); +// // aoi_set_obj(w,2,1,2); +// // //aoi_update_aoi(w); +// // //先加再删,等于从来没出现过 +// // aoi_add_obj(w,3,2,2,1,1); +// // aoi_del_obj(w,3); +// // //aoi_update_aoi(w); + +// // aoi_set_obj(w,2,3,3); +// // aoi_set_obj(w,7,1,1); +// //aoi_update_aoi(w); +// // update_aoi(w); +// // printf("======\n"); +// // del_obj(w,7); +// // update_aoi(w); +// // printf("=======\n"); +// // int grid_idx = get_grid_idx(w,1,2); +// // int x,y; +// // get_xy_by_grididx(w,grid_idx,&x,&y); +// // printf("x:%d y:%d\n", x, y); +// // add_obj(w,2,5,5,1,1); +// // show_world(w); +// // set_obj(w,1,6,6); +// // show_world(w); +// // del_obj(w,1); +// // show_world(w); + +// // World* w = aoi_create_world(3,3); +// // for(int i=1;i<=10000;i++) +// // { +// // aoi_add_obj(w,i,1,1,1,1); +// // } +// // HashTable* aoi_results = hashtbl_create(); +// // aoi_update_aoi(w, aoi_results); +// // //打印aoi_results +// // HashTableIter iter; +// // hashtbl_iter_reset(&iter); +// // int i = 0; +// // while(hashtbl_iter(aoi_results, &iter)){ +// // Result* re=(Result*)(iter.node->value); + +// // printf("watcher:%ld\n", iter.node->key); +// // int watcher_id = (int)iter.node->key; +// // HashTableIter tmp_iter; +// // hashtbl_iter_reset(&tmp_iter); +// // while(hashtbl_iter(re->add_results, &tmp_iter)){ +// // Event* e=(Event*)(tmp_iter.node->value); +// // //printf("event:%c,id:%d,x:%d,y:%d\n",e->e,e->id,e->x,e->y); +// // i=i+1; +// // printf("i is %d\n",i); +// // } +// // // hashtbl_iter_reset(&tmp_iter); +// // // while(hashtbl_iter(re->del_results, &tmp_iter)){ +// // // Event* e=(Event*)(tmp_iter.node->value); +// // // //printf("event:%c,id:%d,x:%d,y:%d\n",e->e,e->id,e->x,e->y); +// // // } +// // // hashtbl_iter_reset(&tmp_iter); +// // // while(hashtbl_iter(re->update_results, &tmp_iter)){ +// // // Event* e=(Event*)(tmp_iter.node->value); +// // // //printf("event:%c,id:%d,x:%d,y:%d\n",e->e,e->id,e->x,e->y); +// // // } + +// // hashtbl_destroy(re->add_results); +// // hashtbl_destroy(re->del_results); +// // hashtbl_destroy(re->update_results); +// // } +// // printf("clean aoi_results\n"); +// // //todo清空aoi_results +// // hashtbl_destroy(aoi_results); +// return 0; +// } diff --git a/framework/lualib-src/lua-aoi/aoi.h b/framework/lualib-src/lua-aoi/aoi.h new file mode 100644 index 0000000..d13383b --- /dev/null +++ b/framework/lualib-src/lua-aoi/aoi.h @@ -0,0 +1,58 @@ +#ifndef _AOI_H +#define _AOI_H + +#include "utils.h" + +typedef struct aoi_event +{ + char e; //"A","U","D" + int id; + int x; + int y; +} Event; + +typedef struct aoi_cache +{ + char e; + int id; + int x; + int y; +} Cache; + +typedef struct aoi_obj +{ + int id; + int x; + int y; + int is_maker; + int is_watcher; +} Obj; + +typedef struct aoi_grid +{ + int idx; + HashTable *watchers; + HashTable *makers; + HashTable *caches; +} Grid; + +typedef struct aoi_world +{ + int row; + int col; + Grid **grids; + HashTable *where_is; //id在哪个格子 实时的 + HashTable *pre_where_is; + HashTable *watcher_grids; +} World; + +World *aoi_create_world(int row, int col); + +int aoi_add_obj(World *w, int id, int x, int y, int is_maker, int is_watcher); +int aoi_del_obj(World *w, int id); +int aoi_set_obj(World *w, int id, int x, int y); + +void aoi_update_aoi(World *w, void *lua_state); + +void aoi_get_cost_time(void *lua_state); +#endif diff --git a/framework/lualib-src/lua-aoi/lua_aoi.c b/framework/lualib-src/lua-aoi/lua_aoi.c new file mode 100644 index 0000000..59b1c73 --- /dev/null +++ b/framework/lualib-src/lua-aoi/lua_aoi.c @@ -0,0 +1,283 @@ +#include +#include +#include + +#include +#include +#include + +#include "utils.h" +#include "aoi.h" + +// https://github.com/ssnobin/c_lua_aoi + +#define GET_INTEGER(L, index, isnum) \ + (int)lua_tointegerx(L, index, &isnum); \ + if (!isnum) \ + { \ + return luaL_argerror(L, 1, "not number"); \ + } + +static int create_world(lua_State *L) +{ + int isnum; + int row = GET_INTEGER(L, 1, isnum); + int col = GET_INTEGER(L, 2, isnum); + World *w = aoi_create_world(row, col); + if (w == NULL) + { + return luaL_error(L, "create world fail"); + } + lua_pushlightuserdata(L, w); + return 1; +} + +static int add_obj(lua_State *L) +{ + World *w = (World *)lua_touserdata(L, 1); + if (w == NULL) + { + return luaL_argerror(L, 1, "no world"); + } + int isnum; + int id = GET_INTEGER(L, 2, isnum); + int x = GET_INTEGER(L, 3, isnum); + int y = GET_INTEGER(L, 4, isnum); + int is_maker = GET_INTEGER(L, 5, isnum); + int is_watcher = GET_INTEGER(L, 6, isnum); + if (aoi_add_obj(w, id, x, y, is_maker, is_watcher)) + { + return luaL_error(L, "add obj fail"); + } + return 1; +} + +static int del_obj(lua_State *L) +{ + World *w = (World *)lua_touserdata(L, 1); + if (w == NULL) + { + return luaL_argerror(L, 1, "no world"); + } + int isnum; + int id = GET_INTEGER(L, 2, isnum); + if (aoi_del_obj(w, id)) + { + return luaL_error(L, "del obj fail"); + } + return 1; +} + +static int set_obj(lua_State *L) +{ + World *w = (World *)lua_touserdata(L, 1); + if (w == NULL) + { + return luaL_argerror(L, 1, "no world"); + } + int isnum; + int id = GET_INTEGER(L, 2, isnum); + int x = GET_INTEGER(L, 3, isnum); + int y = GET_INTEGER(L, 4, isnum); + if (aoi_set_obj(w, id, x, y)) + { + return luaL_error(L, "set obj fail"); + } + return 1; +} + +static int update_aoi(lua_State *L) +{ + World *w = (World *)lua_touserdata(L, 1); + if (w == NULL) + { + return luaL_argerror(L, 1, "no world"); + } + lua_pop(L, 1); + lua_newtable(L); + lua_newtable(L); + aoi_update_aoi(w, L); + lua_pop(L, 1); + return 1; +} + +static int get_time_cost(lua_State *L) +{ + aoi_get_cost_time(L); + return 3; +} + +static void add_results_to_L(lua_State *L, int grid_idx, int id, int key_idx) +{ + int idx_ret = 1; + //将events upsert进L + int idx_grid_info = lua_gettop(L); //2 + lua_rawgeti(L, idx_grid_info, grid_idx); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_info, grid_idx); + } + int idx_grid_detail = lua_gettop(L); //3 + lua_rawgeti(L, idx_grid_detail, key_idx); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_grid_detail, key_idx); + } + int idx_grid_add_del_update = lua_gettop(L); //4 + size_t tbl_len = lua_rawlen(L, idx_grid_add_del_update); + if (tbl_len == 0) + { + //printf("just do once 10000 events\n"); + for (int j = 1; j <= 10000; j++) + { + lua_newtable(L); + lua_pushinteger(L, 1); + lua_rawseti(L, -2, 1); + lua_pushinteger(L, 100); + lua_rawseti(L, -2, 2); + lua_pushinteger(L, 1); + lua_rawseti(L, -2, 3); + lua_pushinteger(L, 1); + lua_rawseti(L, -2, 4); + lua_rawseti(L, idx_grid_add_del_update, j); + } + } + lua_rawgeti(L, idx_ret, id); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_ret, id); + } + int idx_watcher_ret = lua_gettop(L); //5 + lua_rawgeti(L, idx_watcher_ret, grid_idx); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_newtable(L); + lua_pushvalue(L, -1); + lua_rawseti(L, idx_watcher_ret, grid_idx); + } + int idx_watcher_grid_ret = lua_gettop(L); //6 + lua_rawgeti(L, idx_watcher_grid_ret, key_idx); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_pushvalue(L, idx_grid_add_del_update); //引用 + lua_rawseti(L, idx_watcher_grid_ret, key_idx); + } + lua_pop(L, 4); +} + +static int test_lua_table(lua_State *L) +{ + lua_newtable(L); + lua_newtable(L); + for (int id = 1; id <= 10000; id++) + { + for (int grid_idx = 0; grid_idx <= 9; grid_idx++) + { + for (int key_idx = 1; key_idx <= 3; key_idx++) + { + add_results_to_L(L, grid_idx, id, key_idx); + } + } + } + lua_pop(L, 1); + return 1; +} + +static int test_create(lua_State *L) +{ + World *w = (World *)lua_touserdata(L, 1); + if (w == NULL) + { + return luaL_argerror(L, 1, "no world"); + } + lua_newtable(L); + lua_setuservalue(L, 1); + + printf("test_create\n"); + printf("xx %lu\n", sizeof(w->grids[0])); + Grid *grid = w->grids[0]; + printf("push grid userdata\n"); + lua_pushlightuserdata(L, grid); + printf("new uservalue\n"); + lua_newtable(L); + printf("set uservalue\n"); + lua_setuservalue(L, -2); + printf("set grid userdata uservalue\n"); + return 0; +} + +static int test_set(lua_State *L) +{ + World *w = (World *)lua_touserdata(L, 1); + if (w == NULL) + { + return luaL_argerror(L, 1, "no world"); + } + int key = (int)lua_tointeger(L, 2); + int value = (int)lua_tointeger(L, 3); + int type = lua_getuservalue(L, 1); + Obj *obj = (Obj *)skynet_malloc(sizeof(Obj)); + obj->id = key; + obj->x = value; + obj->y = value; + obj->is_maker = 1; + obj->is_watcher = 1; + //lua_pushinteger(L,value); + lua_pushlightuserdata(L, obj); + lua_rawseti(L, -2, key); + return 0; +} + +static int test_get(lua_State *L) +{ + World *w = (World *)lua_touserdata(L, 1); + if (w == NULL) + { + return luaL_argerror(L, 1, "no world"); + } + //int key = (int)lua_tointeger(L, 2); + //int type = lua_getuservalue(L,1); + //lua_rawgeti(L,-1,key); + //Obj* obj = (Obj*)lua_touserdata(L,-1); + //printf("obj key is %d\n", obj->id); + //printf("obj value is %d\n", obj->x); + + Grid *grid = w->grids[0]; + printf("gg1\n"); + lua_pushlightuserdata(L, grid); + printf("gg2\n"); + int type = lua_getuservalue(L, -1); + printf("grid uservalue is%d\n", type); + return 1; +} + +int luaopen_laoi(lua_State *L) +{ + luaL_Reg l[] = { + {"create_world", create_world}, + {"add_obj", add_obj}, + {"del_obj", del_obj}, + {"set_obj", set_obj}, + {"update_aoi", update_aoi}, + {"get_time_cost", get_time_cost}, + {"test_lua_table", test_lua_table}, + {"test_create", test_create}, + {"test_set", test_set}, + {"test_get", test_get}, + {NULL, NULL}, + }; + + luaL_newlib(L, l); + return 1; +} \ No newline at end of file diff --git a/framework/lualib-src/lua-aoi/test_aoi.lua b/framework/lualib-src/lua-aoi/test_aoi.lua new file mode 100644 index 0000000..555d2b8 --- /dev/null +++ b/framework/lualib-src/lua-aoi/test_aoi.lua @@ -0,0 +1,69 @@ +local function print_aoi_events(aoi_events) + print("==========print_aoi_events========") + -- for watcher, grid_info in pairs(aoi_events) do + -- print("watcher", watcher) + -- for grid_idx, event_list in pairs(grid_info) do + -- --print("grid_idx", grid_idx) + -- for i = 1, 3 do + -- local sub_event_list = event_list[i] + -- if sub_event_list then + -- for _, e in ipairs(sub_event_list) do + -- if(e[1]==1 or e[1] == 'A') then + -- print(string.format("event:A,id:%d,x:%d,y:%d",e[2],e[3],e[4])) + -- elseif(e[1]==2 or e[1] == 'D') then + -- print(string.format("event:D,id:%d,x:%d,y:%d",e[2],e[3],e[4])) + -- else + -- print(string.format("event:U,id:%d,x:%d,y:%d",e[2],e[3],e[4])) + -- end + -- end + -- end + -- end + + -- end + -- end + for watcher, watch_ret in pairs(aoi_events) do + print("watcher", watcher) + for _, data in ipairs(watch_ret) do + for _, e in ipairs(data) do + if (e[1] == 1 or e[1] == 'A') then + print(string.format("event:A,id:%d,x:%d,y:%d", e[2], e[3], e[4])) + elseif (e[1] == 2 or e[1] == 'D') then + print(string.format("event:D,id:%d,x:%d,y:%d", e[2], e[3], e[4])) + else + print(string.format("event:U,id:%d,x:%d,y:%d", e[2], e[3], e[4])) + end + end + end + end +end + +local function my_aoi_test() + local my_aoi = require "laoi" + local world = my_aoi.create_world(4, 3) + my_aoi.add_obj(world, 7, 2, 2, 1, 1) -- my_aoi.add_obj(world, 7, 2,2,1,1) + my_aoi.add_obj(world, 1, 2, 2, 1, 1) + my_aoi.add_obj(world, 6, 3, 3, 1, 1) + print_aoi_events(my_aoi.update_aoi(world)) + print_aoi_events(my_aoi.update_aoi(world)) + + my_aoi.set_obj(world, 7, 2, 3) + print_aoi_events(my_aoi.update_aoi(world)) + + my_aoi.add_obj(world, 2, 1, 1, 1, 1) + my_aoi.set_obj(world, 2, 1, 2) + print_aoi_events(my_aoi.update_aoi(world)) + + my_aoi.add_obj(world, 3, 2, 2, 1, 1) + my_aoi.del_obj(world, 3) + print_aoi_events(my_aoi.update_aoi(world)) + + my_aoi.set_obj(world, 2, 3, 3) + my_aoi.set_obj(world, 7, 1, 1) + print_aoi_events(my_aoi.update_aoi(world)) + + my_aoi.set_obj(world, 2, 3, 2) + my_aoi.set_obj(world, 2, 3, 1) + print_aoi_events(my_aoi.update_aoi(world)) +end + +my_aoi_test() diff --git a/framework/lualib-src/lua-aoi/utils.c b/framework/lualib-src/lua-aoi/utils.c new file mode 100644 index 0000000..71c9720 --- /dev/null +++ b/framework/lualib-src/lua-aoi/utils.c @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define HASH_TABLE_MIN_HASH_SIZE 16 +#define HASH_TABLE_MAX_HASH_SIZE 10240 +#define HASH_TABLE_MIN_LOAD_FACTOR 0.1 +#define HASH_TABLE_MAX_LOAD_FACTOR 0.8 +#define HASH_TABLE_HASH_FUNC(tbl, key) ((key) % (tbl)->hash_sz) + +HashTable *hashtbl_create() +{ + HashTable *tbl = (HashTable *)skynet_malloc(sizeof(HashTable)); + + tbl->hash_sz = HASH_TABLE_MIN_HASH_SIZE; + tbl->count = 0; + tbl->min_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MIN_LOAD_FACTOR); + tbl->max_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MAX_LOAD_FACTOR); + HashTableNode **nodes = (HashTableNode **)skynet_malloc(sizeof(HashTableNode *) * tbl->hash_sz); + + int n; + for (n = 0; n < tbl->hash_sz; n++) + { + nodes[n] = NULL; + } + tbl->nodes = nodes; + return tbl; +} + +void hashtbl_destroy(HashTable *tbl) +{ + int n; + HashTableNode *p, *next_p; + for (n = 0; n < tbl->hash_sz; n++) + { + p = tbl->nodes[n]; + tbl->nodes[n] = NULL; + while (p != NULL) + { + next_p = p->next; + skynet_free(p); + p = next_p; + } + } + skynet_free(tbl->nodes); + skynet_free(tbl); + return; +} + +static int _hashtbl_resize(HashTable *tbl, int new_hash_sz) +{ + HashTableNode **new_nodes = (HashTableNode **)skynet_malloc(sizeof(HashTableNode *) * new_hash_sz); + int old_hash_sz = tbl->hash_sz; + HashTableNode **old_nodes = tbl->nodes; + tbl->nodes = new_nodes; + tbl->hash_sz = new_hash_sz; + tbl->min_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MIN_LOAD_FACTOR); + tbl->max_resize_cnt = (int)(tbl->hash_sz * HASH_TABLE_MAX_LOAD_FACTOR); + + int n, hash_key; + HashTableNode *p, *next_p; + for (n = 0; n < new_hash_sz; n++) + { + new_nodes[n] = NULL; + } + + for (n = 0; n < old_hash_sz; n++) + { + p = old_nodes[n]; + old_nodes[n] = NULL; + while (p != NULL) + { + next_p = p->next; + + hash_key = p->key % new_hash_sz; + p->next = new_nodes[hash_key]; + new_nodes[hash_key] = p; + + p = next_p; + } + } + skynet_free(old_nodes); + return 0; +} + +static int _hashtbl_insert(HashTable *tbl, int hash_key, uint64_t key, void *value) +{ + HashTableNode *node = skynet_malloc(sizeof(HashTableNode)); + node->key = key; + node->value = value; + node->next = tbl->nodes[hash_key]; + tbl->nodes[hash_key] = node; + tbl->count++; + + if (tbl->hash_sz < HASH_TABLE_MAX_HASH_SIZE && tbl->count > tbl->max_resize_cnt) + { + _hashtbl_resize(tbl, tbl->hash_sz * 2); + } + return 0; +} + +int hashtbl_insert(HashTable *tbl, uint64_t key, void *value) +{ + int hash_key = HASH_TABLE_HASH_FUNC(tbl, key); + HashTableNode *p = tbl->nodes[hash_key]; + while (p != NULL) + { + if (p->key == key) + { + printf("table_insert fail, key<%lu>, already exist\n", key); + assert(0); + return 1; + } + p = p->next; + } + return _hashtbl_insert(tbl, hash_key, key, value); +} + +int hashtbl_upsert(HashTable *tbl, uint64_t key, void *value) +{ + int hash_key = HASH_TABLE_HASH_FUNC(tbl, key); + HashTableNode *p = tbl->nodes[hash_key]; + while (p != NULL) + { + if (p->key == key) + { + p->value = value; + return 0; + } + p = p->next; + } + return _hashtbl_insert(tbl, hash_key, key, value); +} + +int hashtbl_has(HashTable *tbl, uint64_t key) +{ + HashTableNode *p = tbl->nodes[HASH_TABLE_HASH_FUNC(tbl, key)]; + while (p != NULL) + { + if (p->key == key) + { + return 1; + } + p = p->next; + } + return 0; +} + +void *hashtbl_get(HashTable *tbl, uint64_t key) +{ + HashTableNode *p = tbl->nodes[HASH_TABLE_HASH_FUNC(tbl, key)]; + while (p != NULL) + { + if (p->key == key) + { + return p->value; + } + p = p->next; + } + return 0; +} + +int hashtbl_remove(HashTable *tbl, uint64_t key) +{ + int hash_key = HASH_TABLE_HASH_FUNC(tbl, key); + HashTableNode *free_p; + HashTableNode **p = &(tbl->nodes[hash_key]); + int hash_sz = tbl->hash_sz; + while ((*p) != NULL) + { + if ((*p)->key != key) + { + p = &((*p)->next); + continue; + } + free_p = *p; + *p = free_p->next; + skynet_free(free_p); + free_p = NULL; + tbl->count--; + if ((hash_sz > HASH_TABLE_MIN_LOAD_FACTOR) && (tbl->count < tbl->min_resize_cnt)) + { + int min_hash_sz = (int)(tbl->count / HASH_TABLE_MAX_LOAD_FACTOR) + 1; + if (min_hash_sz < HASH_TABLE_MIN_HASH_SIZE) + { + min_hash_sz = HASH_TABLE_MIN_HASH_SIZE; + } + int max_hash_sz = 2 * min_hash_sz; + while (hash_sz >= min_hash_sz) + { + if (hash_sz < max_hash_sz) + { + break; + } + hash_sz = hash_sz / 2; + } + _hashtbl_resize(tbl, hash_sz); + } + return 1; + } + return 0; +} + +void hashtbl_iter_reset(HashTableIter *iter) +{ + iter->hash_sz = -1; + iter->count = 0; + iter->node = NULL; + return; +} + +int hashtbl_iter(HashTable *tbl, HashTableIter *iter) +{ + if (tbl->count <= iter->count) + { + return 0; + } + if (iter->node) + { + iter->node = iter->node->next; + } + while (!iter->node) + { + iter->hash_sz++; + if (iter->hash_sz >= tbl->hash_sz) + { + break; + } + iter->node = tbl->nodes[iter->hash_sz]; + } + if (!iter->node) + { + return 0; + } + iter->count++; + return 1; +} + +void hashtbl_foreach(HashTable *tbl, HashTableIterFunc func, void *ud) +{ + HashTableIter iter; + hashtbl_iter_reset(&iter); + while (hashtbl_iter(tbl, &iter)) + { + func(ud, iter.node->key, iter.node->value); + } +} \ No newline at end of file diff --git a/framework/lualib-src/lua-aoi/utils.h b/framework/lualib-src/lua-aoi/utils.h new file mode 100644 index 0000000..bc0e42e --- /dev/null +++ b/framework/lualib-src/lua-aoi/utils.h @@ -0,0 +1,53 @@ +#ifndef _UTILS_H +#define _UTILS_H +#include +#include "skynet_malloc.h" + +typedef struct hashtbl_node +{ + uint64_t key; + void *value; + struct hashtbl_node *next; +} HashTableNode; + +typedef struct hashtbl +{ + int hash_sz; + int count; + int max_resize_cnt; + int min_resize_cnt; + HashTableNode **nodes; +} HashTable; + +typedef struct hashtbl_iter +{ + int hash_sz; + int count; + HashTableNode *node; +} HashTableIter; + +typedef void (*HashTableIterFunc)(void *ud, uint64_t key, void *value); + +HashTable *hashtbl_create(); + +void hashtbl_destroy(HashTable *tbl); + +int hashtbl_has(HashTable *tbl, uint64_t key); + +void *hashtbl_get(HashTable *tbl, uint64_t key); + +int hashtbl_insert(HashTable *tbl, uint64_t key, void *value); + +int hashtbl_upsert(HashTable *tbl, uint64_t key, void *value); + +int hashtbl_remove(HashTable *tbl, uint64_t key); + +int hashtbl_resize(HashTable *tbl, int new_hash_size); + +void hashtbl_iter_reset(HashTableIter *iter); + +int hashtbl_iter(HashTable *tbl, HashTableIter *iter); + +void hashtbl_foreach(HashTable *tbl, HashTableIterFunc func, void *ud); + +#endif diff --git a/framework/lualib/3rd/luatz/gettime.lua b/framework/lualib/3rd/luatz/gettime.lua new file mode 100644 index 0000000..55409e4 --- /dev/null +++ b/framework/lualib/3rd/luatz/gettime.lua @@ -0,0 +1,51 @@ +local _M = {} + +_M.source, _M.resolution, _M.gettime = (function() + local has_syscall, syscall = pcall(require, "syscall") + if has_syscall and syscall.clock_gettime and syscall.c.CLOCK then + local clock_id = syscall.c.CLOCK.REALTIME + local function timespec_to_number(timespec) + return tonumber(timespec.tv_sec) + tonumber(timespec.tv_nsec) * 1e-9 + end + return "syscall.clock_gettime(CLOCK_REALTIME)", + syscall.clock_getres and timespec_to_number(syscall.clock_getres(clock_id)) or 1e-9, function() + return timespec_to_number(syscall.clock_gettime(clock_id)) + end + end + + local has_unix, unix = pcall(require, "unix") + -- On Apple devices lunix only uses gettimeofday() + if has_unix and unix.clock_gettime and unix.uname and unix.uname().sysname ~= "Darwin" then + return "unix.clock_gettime(CLOCK_REALTIME)", 1e-9, function() + return unix.clock_gettime() + end + end + + if has_syscall and syscall.gettimeofday then + local function timeval_to_number(timeval) + return tonumber(timeval.tv_sec) + tonumber(timeval.tv_nsec) * 1e-6 + end + return "syscall.gettimeofday()", 1e-6, function() + return timeval_to_number(syscall.gettimeofday()) + end + end + + if has_unix and unix.gettimeofday then + return "unix.gettimeofday()", 1e-6, unix.gettimeofday + end + + local has_socket, socket = pcall(require, "socket") + if has_socket and socket.gettime then + -- on windows, this uses GetSystemTimeAsFileTime, which has resolution of 1e-7 + -- on linux, this uses gettimeofday, which has resolution of 1e-6 + return "socket.gettime()", 1e-6, socket.gettime + end + + if ngx and ngx.now then -- luacheck: ignore 113 + return "ngx.now()", 1e-3, ngx.now -- luacheck: ignore 113 + end + + return "os.time()", 1, os.time +end)() + +return _M diff --git a/framework/lualib/3rd/luatz/init.lua b/framework/lualib/3rd/luatz/init.lua new file mode 100644 index 0000000..6b13fb5 --- /dev/null +++ b/framework/lualib/3rd/luatz/init.lua @@ -0,0 +1,41 @@ +-- https://github.com/daurnimator/luatz +local _M = { + gettime = require "luatz.gettime", + parse = require "luatz.parse", + strftime = require "luatz.strftime", + timetable = require "luatz.timetable", + tzcache = require "luatz.tzcache", +} + +--- Top-level aliases for common functions + +_M.time = _M.gettime.gettime +_M.get_tz = _M.tzcache.get_tz + +--- Handy functions + +_M.time_in = function(tz, now) + return _M.get_tz(tz):localize(now) +end + +_M.now = function() + local ts = _M.gettime.gettime() + return _M.timetable.new_from_timestamp(ts) +end + +--- C-like functions + +_M.gmtime = function(ts) + return _M.timetable.new_from_timestamp(ts) +end + +_M.localtime = function(ts) + ts = _M.time_in(nil, ts) + return _M.gmtime(ts) +end + +_M.ctime = function(ts) + return _M.strftime.asctime(_M.localtime(ts)) +end + +return _M diff --git a/framework/lualib/3rd/luatz/parse.lua b/framework/lualib/3rd/luatz/parse.lua new file mode 100644 index 0000000..1bc98aa --- /dev/null +++ b/framework/lualib/3rd/luatz/parse.lua @@ -0,0 +1,41 @@ +local new_timetable = require"luatz.timetable".new + +--- Parse an RFC 3339 datetime at the given position +-- Returns a time table and the `tz_offset` +-- Return value is not normalised (this preserves a leap second) +-- If the timestamp is only partial (i.e. missing "Z" or time offset) then `tz_offset` will be nil +-- TODO: Validate components are within their boundarys (e.g. 1 <= month <= 12) +local function rfc_3339(str, init) + local year, month, day, hour, min, sec, patt_end = str:match( + "^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)()", init) -- luacheck: ignore 631 + if not year then + return nil, "Invalid RFC 3339 timestamp" + end + year = tonumber(year, 10) + month = tonumber(month, 10) + day = tonumber(day, 10) + hour = tonumber(hour, 10) + min = tonumber(min, 10) + sec = tonumber(sec, 10) + + local tt = new_timetable(year, month, day, hour, min, sec) + + local tz_offset + if str:match("^[Zz]", patt_end) then + tz_offset = 0 + else + local hour_offset, min_offset = str:match("^([+-]%d%d):(%d%d)", patt_end) + if hour_offset then + tz_offset = tonumber(hour_offset, 10) * 3600 + tonumber(min_offset, 10) * 60 + else -- luacheck: ignore 542 + -- Invalid RFC 3339 timestamp offset (should be Z or (+/-)hour:min) + -- tz_offset will be nil + end + end + + return tt, tz_offset +end + +return { + rfc_3339 = rfc_3339, +} diff --git a/framework/lualib/3rd/luatz/strftime.lua b/framework/lualib/3rd/luatz/strftime.lua new file mode 100644 index 0000000..0c39c3f --- /dev/null +++ b/framework/lualib/3rd/luatz/strftime.lua @@ -0,0 +1,206 @@ +local strformat = string.format +local floor = math.floor +local function idiv(n, d) + return floor(n / d) +end + +local c_locale = { + abday = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}, + day = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}, + abmon = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}, + mon = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", + "December"}, + am_pm = {"AM", "PM"}, +} + +--- ISO-8601 week logic +-- ISO 8601 weekday as number with Monday as 1 (1-7) +local function iso_8601_weekday(wday) + if wday == 1 then + return 7 + else + return wday - 1 + end +end +local iso_8601_week +do + -- Years that have 53 weeks according to ISO-8601 + local long_years = {} + for _, v in ipairs {4, 9, 15, 20, 26, 32, 37, 43, 48, 54, 60, 65, 71, 76, 82, 88, 93, 99, 105, 111, 116, 122, 128, + 133, 139, 144, 150, 156, 161, 167, 172, 178, 184, 189, 195, 201, 207, 212, 218, 224, 229, 235, + 240, 246, 252, 257, 263, 268, 274, 280, 285, 291, 296, 303, 308, 314, 320, 325, 331, 336, 342, + 348, 353, 359, 364, 370, 376, 381, 387, 392, 398} do + long_years[v] = true + end + local function is_long_year(year) + return long_years[year % 400] + end + function iso_8601_week(self) + local wday = iso_8601_weekday(self.wday) + local n = self.yday - wday + local year = self.year + if n < -3 then + year = year - 1 + if is_long_year(year) then + return year, 53, wday + else + return year, 52, wday + end + elseif n >= 361 and not is_long_year(year) then + return year + 1, 1, wday + else + return year, idiv(n + 10, 7), wday + end + end +end + +--- Specifiers +local t = {} +function t:a(locale) + return "%s", locale.abday[self.wday] +end +function t:A(locale) + return "%s", locale.day[self.wday] +end +function t:b(locale) + return "%s", locale.abmon[self.month] +end +function t:B(locale) + return "%s", locale.mon[self.month] +end +function t:c(locale) + return "%.3s %.3s%3d %.2d:%.2d:%.2d %d", locale.abday[self.wday], locale.abmon[self.month], self.day, self.hour, + self.min, self.sec, self.year +end +-- Century +function t:C() + return "%02d", idiv(self.year, 100) +end +function t:d() + return "%02d", self.day +end +-- Short MM/DD/YY date, equivalent to %m/%d/%y +function t:D() + return "%02d/%02d/%02d", self.month, self.day, self.year % 100 +end +function t:e() + return "%2d", self.day +end +-- Short YYYY-MM-DD date, equivalent to %Y-%m-%d +function t:F() + return "%d-%02d-%02d", self.year, self.month, self.day +end +-- Week-based year, last two digits (00-99) +function t:g() + return "%02d", iso_8601_week(self) % 100 +end +-- Week-based year +function t:G() + return "%d", iso_8601_week(self) +end +t.h = t.b +function t:H() + return "%02d", self.hour +end +function t:I() + return "%02d", (self.hour - 1) % 12 + 1 +end +function t:j() + return "%03d", self.yday +end +function t:m() + return "%02d", self.month +end +function t:M() + return "%02d", self.min +end +-- New-line character ('\n') +function t:n() -- luacheck: ignore 212 + return "\n" +end +function t:p(locale) + return self.hour < 12 and locale.am_pm[1] or locale.am_pm[2] +end +-- TODO: should respect locale +function t:r(locale) + return "%02d:%02d:%02d %s", (self.hour - 1) % 12 + 1, self.min, self.sec, + self.hour < 12 and locale.am_pm[1] or locale.am_pm[2] +end +-- 24-hour HH:MM time, equivalent to %H:%M +function t:R() + return "%02d:%02d", self.hour, self.min +end +function t:s() + return "%d", self:timestamp() +end +function t:S() + return "%02d", self.sec +end +-- Horizontal-tab character ('\t') +function t:t() -- luacheck: ignore 212 + return "\t" +end +-- ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S +function t:T() + return "%02d:%02d:%02d", self.hour, self.min, self.sec +end +function t:u() + return "%d", iso_8601_weekday(self.wday) +end +-- Week number with the first Sunday as the first day of week one (00-53) +function t:U() + return "%02d", idiv(self.yday - self.wday + 7, 7) +end +-- ISO 8601 week number (00-53) +function t:V() + return "%02d", select(2, iso_8601_week(self)) +end +-- Weekday as a decimal number with Sunday as 0 (0-6) +function t:w() + return "%d", self.wday - 1 +end +-- Week number with the first Monday as the first day of week one (00-53) +function t:W() + return "%02d", idiv(self.yday - iso_8601_weekday(self.wday) + 7, 7) +end +-- TODO make t.x and t.X respect locale +t.x = t.D +t.X = t.T +function t:y() + return "%02d", self.year % 100 +end +function t:Y() + return "%d", self.year +end +-- TODO timezones +function t:z() -- luacheck: ignore 212 + return "+0000" +end +function t:Z() -- luacheck: ignore 212 + return "GMT" +end +-- A literal '%' character. +t["%"] = function(self) -- luacheck: ignore 212 + return "%%" +end + +local function strftime(format_string, timetable) + return (string.gsub(format_string, "%%([EO]?)(.)", function(locale_modifier, specifier) + local func = t[specifier] + if func then + return strformat(func(timetable, c_locale)) + else + error("invalid conversation specifier '%" .. locale_modifier .. specifier .. "'", 3) + end + end)) +end + +local function asctime(timetable) + -- Equivalent to the format string "%c\n" + return strformat(t.c(timetable, c_locale)) .. "\n" +end + +return { + strftime = strftime, + asctime = asctime, +} diff --git a/framework/lualib/3rd/luatz/timetable.lua b/framework/lualib/3rd/luatz/timetable.lua new file mode 100644 index 0000000..4568a28 --- /dev/null +++ b/framework/lualib/3rd/luatz/timetable.lua @@ -0,0 +1,244 @@ +local strftime = require"luatz.strftime".strftime +local strformat = string.format +local floor = math.floor +local idiv +do + -- Try and use actual integer division when available (Lua 5.3+) + local idiv_loader = (loadstring or load)([[return function(n,d) return n//d end]], "idiv") -- luacheck: ignore 113 + if idiv_loader then + idiv = idiv_loader() + else + idiv = function(n, d) + return floor(n / d) + end + end +end + +local mon_lengths = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +-- Number of days in year until start of month; not corrected for leap years +local months_to_days_cumulative = {0} +for i = 2, 12 do + months_to_days_cumulative[i] = months_to_days_cumulative[i - 1] + mon_lengths[i - 1] +end +-- For Sakamoto's Algorithm (day of week) +local sakamoto = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + +local function is_leap(y) + if (y % 4) ~= 0 then + return false + elseif (y % 100) ~= 0 then + return true + else + return (y % 400) == 0 + end +end + +local function month_length(m, y) + if m == 2 then + return is_leap(y) and 29 or 28 + else + return mon_lengths[m] + end +end + +local function leap_years_since(year) + return idiv(year, 4) - idiv(year, 100) + idiv(year, 400) +end + +local function day_of_year(day, month, year) + local yday = months_to_days_cumulative[month] + if month > 2 and is_leap(year) then + yday = yday + 1 + end + return yday + day +end + +local function day_of_week(day, month, year) + if month < 3 then + year = year - 1 + end + return (year + leap_years_since(year) + sakamoto[month] + day) % 7 + 1 +end + +local function borrow(tens, units, base) + local frac = tens % 1 + units = units + frac * base + tens = tens - frac + return tens, units +end + +local function carry(tens, units, base) + if units >= base then + tens = tens + idiv(units, base) + units = units % base + elseif units < 0 then + tens = tens + idiv(units, base) + units = (base + units) % base + end + return tens, units +end + +-- Modify parameters so they all fit within the "normal" range +local function normalise(year, month, day, hour, min, sec) + -- `month` and `day` start from 1, need -1 and +1 so it works modulo + month, day = month - 1, day - 1 + + -- Convert everything (except seconds) to an integer + -- by propagating fractional components down. + year, month = borrow(year, month, 12) + -- Carry from month to year first, so we get month length correct in next line around leap years + year, month = carry(year, month, 12) + month, day = borrow(month, day, month_length(floor(month + 1), year)) + day, hour = borrow(day, hour, 24) + hour, min = borrow(hour, min, 60) + min, sec = borrow(min, sec, 60) + + -- Propagate out of range values up + -- e.g. if `min` is 70, `hour` increments by 1 and `min` becomes 10 + -- This has to happen for all columns after borrowing, as lower radixes may be pushed out of range + min, sec = carry(min, sec, 60) -- TODO: consider leap seconds? + hour, min = carry(hour, min, 60) + day, hour = carry(day, hour, 24) + -- Ensure `day` is not underflowed + -- Add a whole year of days at a time, this is later resolved by adding months + -- TODO[OPTIMIZE]: This could be slow if `day` is far out of range + while day < 0 do + month = month - 1 + if month < 0 then + year = year - 1 + month = 11 + end + day = day + month_length(month + 1, year) + end + year, month = carry(year, month, 12) + + -- TODO[OPTIMIZE]: This could potentially be slow if `day` is very large + while true do + local i = month_length(month + 1, year) + if day < i then + break + end + day = day - i + month = month + 1 + if month >= 12 then + month = 0 + year = year + 1 + end + end + + -- Now we can place `day` and `month` back in their normal ranges + -- e.g. month as 1-12 instead of 0-11 + month, day = month + 1, day + 1 + + return year, month, day, hour, min, sec +end + +local leap_years_since_1970 = leap_years_since(1970) +local function timestamp(year, month, day, hour, min, sec) + year, month, day, hour, min, sec = normalise(year, month, day, hour, min, sec) + + local days_since_epoch = day_of_year(day, month, year) + 365 * (year - 1970) -- Each leap year adds one day + + (leap_years_since(year - 1) - leap_years_since_1970) - 1 + + return days_since_epoch * (60 * 60 * 24) + hour * (60 * 60) + min * 60 + sec +end + +local timetable_methods = {} + +function timetable_methods:unpack() + return assert(self.year, "year required"), assert(self.month, "month required"), assert(self.day, "day required"), + self.hour or 12, self.min or 0, self.sec or 0, self.yday, self.wday +end + +function timetable_methods:normalise() + local year, month, day + year, month, day, self.hour, self.min, self.sec = normalise(self:unpack()) + + self.day = day + self.month = month + self.year = year + self.yday = day_of_year(day, month, year) + self.wday = day_of_week(day, month, year) + + return self +end +timetable_methods.normalize = timetable_methods.normalise -- American English + +function timetable_methods:timestamp() + return timestamp(self:unpack()) +end + +function timetable_methods:rfc_3339() + local year, month, day, hour, min, fsec = self:unpack() + local sec, msec = borrow(fsec, 0, 1000) + msec = math.floor(msec) + return strformat("%04u-%02u-%02uT%02u:%02u:%02d.%03d", year, month, day, hour, min, sec, msec) +end + +function timetable_methods:strftime(format_string) + return strftime(format_string, self) +end + +local timetable_mt + +local function coerce_arg(t) + if getmetatable(t) == timetable_mt then + return t:timestamp() + end + return t +end + +timetable_mt = { + __index = timetable_methods, + __tostring = timetable_methods.rfc_3339, + __eq = function(a, b) + return a:timestamp() == b:timestamp() + end, + __lt = function(a, b) + return a:timestamp() < b:timestamp() + end, + __sub = function(a, b) + return coerce_arg(a) - coerce_arg(b) + end, +} + +local function cast_timetable(tm) + return setmetatable(tm, timetable_mt) +end + +local function new_timetable(year, month, day, hour, min, sec, yday, wday) + return cast_timetable { + year = year, + month = month, + day = day, + hour = hour, + min = min, + sec = sec, + yday = yday, + wday = wday, + } +end + +function timetable_methods:clone() + return new_timetable(self:unpack()) +end + +local function new_from_timestamp(ts) + if type(ts) ~= "number" then + error("bad argument #1 to 'new_from_timestamp' (number expected, got " .. type(ts) .. ")", 2) + end + return new_timetable(1970, 1, 1, 0, 0, ts):normalise() +end + +return { + is_leap = is_leap, + day_of_year = day_of_year, + day_of_week = day_of_week, + normalise = normalise, + timestamp = timestamp, + + new = new_timetable, + new_from_timestamp = new_from_timestamp, + cast = cast_timetable, + timetable_mt = timetable_mt, +} diff --git a/framework/lualib/3rd/luatz/tzcache.lua b/framework/lualib/3rd/luatz/tzcache.lua new file mode 100644 index 0000000..5db499e --- /dev/null +++ b/framework/lualib/3rd/luatz/tzcache.lua @@ -0,0 +1,35 @@ +local read_tzfile = require"luatz.tzfile".read_tzfile + +local base_zoneinfo_path = "/usr/share/zoneinfo/" +local local_zoneinfo_path = "/etc/localtime" +local tz_cache = {} + +local function name_to_zoneinfo_path(name) + if name == nil then + return local_zoneinfo_path + elseif name:sub(1, 1) == "/" then + return name + else + return base_zoneinfo_path .. name + end +end + +local function clear_tz_cache(name) + tz_cache[name_to_zoneinfo_path(name)] = nil +end + +local function get_tz(name) + local path = name_to_zoneinfo_path(name) + -- TODO: stat path + local tzinfo = tz_cache[path] + if tzinfo == nil then + tzinfo = read_tzfile(path) + tz_cache[path] = tzinfo + end + return tzinfo +end + +return { + get_tz = get_tz, + clear_tz_cache = clear_tz_cache, +} diff --git a/framework/lualib/3rd/luatz/tzfile.lua b/framework/lualib/3rd/luatz/tzfile.lua new file mode 100644 index 0000000..a35e28c --- /dev/null +++ b/framework/lualib/3rd/luatz/tzfile.lua @@ -0,0 +1,262 @@ +local tz_info_mt = require"luatz.tzinfo".tz_info_mt +local tt_info_mt = require"luatz.tzinfo".tt_info_mt + +local read_int32be, read_int64be + +-- luacheck: push std max +if string.unpack then + -- Only available in Lua 5.3+ + function read_int32be(fd) + local data, err = fd:read(4) + if data == nil then + return nil, err + end + return string.unpack(">i4", data) + end + + function read_int64be(fd) + local data, err = fd:read(8) + if data == nil then + return nil, err + end + return string.unpack(">i8", data) + end +else -- luacheck: pop + function read_int32be(fd) + local data, err = fd:read(4) + if data == nil then + return nil, err + end + local o1, o2, o3, o4 = data:byte(1, 4) + + local unsigned = o4 + o3 * 2 ^ 8 + o2 * 2 ^ 16 + o1 * 2 ^ 24 + if unsigned >= 2 ^ 31 then + return unsigned - 2 ^ 32 + else + return unsigned + end + end + + function read_int64be(fd) + local data, err = fd:read(8) + if data == nil then + return nil, err + end + local o1, o2, o3, o4, o5, o6, o7, o8 = data:byte(1, 8) + + local unsigned = + o8 + o7 * 2 ^ 8 + o6 * 2 ^ 16 + o5 * 2 ^ 24 + o4 * 2 ^ 32 + o3 * 2 ^ 40 + o2 * 2 ^ 48 + o1 * 2 ^ 56 + if unsigned >= 2 ^ 63 then + return unsigned - 2 ^ 64 + else + return unsigned + end + end +end + +local function read_flags(fd, n) + local data, err = fd:read(n) + if data == nil then + return nil, err + end + + local res = {} + for i = 1, n do + res[i] = data:byte(i, i) ~= 0 + end + return res +end + +local fifteen_nulls = ("\0"):rep(15) +local function read_tz(fd) + assert(fd:read(4) == "TZif", "Invalid TZ file") + local version = assert(fd:read(1)) + if version == "\0" or version == "2" or version == "3" then + local MIN_TIME = -2 ^ 32 + 1 + + assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls") + + -- The number of UTC/local indicators stored in the file. + local tzh_ttisgmtcnt = assert(read_int32be(fd)) + + -- The number of standard/wall indicators stored in the file. + local tzh_ttisstdcnt = assert(read_int32be(fd)) + + -- The number of leap seconds for which data is stored in the file. + local tzh_leapcnt = assert(read_int32be(fd)) + + -- The number of "transition times" for which data is stored in the file. + local tzh_timecnt = assert(read_int32be(fd)) + + -- The number of "local time types" for which data is stored in the file (must not be zero). + local tzh_typecnt = assert(read_int32be(fd)) + + -- The number of characters of "timezone abbreviation strings" stored in the file. + local tzh_charcnt = assert(read_int32be(fd)) + + local transition_times = {} + for i = 1, tzh_timecnt do + transition_times[i] = assert(read_int32be(fd)) + end + local transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)} + + local ttinfos = {} + for i = 1, tzh_typecnt do + ttinfos[i] = { + gmtoff = assert(read_int32be(fd)), + isdst = assert(fd:read(1)) ~= "\0", + abbrind = assert(fd:read(1)):byte(), + } + end + + local abbreviations = assert(fd:read(tzh_charcnt)) + + local leap_seconds = {} -- luacheck: ignore 241 + for i = 1, tzh_leapcnt do + leap_seconds[i] = { + offset = assert(read_int32be(fd)), + n = assert(read_int32be(fd)), + } + end + + local isstd = assert(read_flags(fd, tzh_ttisstdcnt)) + + local isgmt = assert(read_flags(fd, tzh_ttisgmtcnt)) + + local TZ + + if version == "2" or version == "3" then + --[[ + For version-2-format timezone files, the above header and data is followed by a second header and data, + identical in format except that eight bytes are used for each transition time or leap-second time. + ]] + assert(fd:read(4) == "TZif") + assert(fd:read(1) == version) + assert(assert(fd:read(15)) == fifteen_nulls, "Expected 15 nulls") + + MIN_TIME = -2 ^ 64 + 1 + + -- The number of UTC/local indicators stored in the file. + tzh_ttisgmtcnt = assert(read_int32be(fd)) + + -- The number of standard/wall indicators stored in the file. + tzh_ttisstdcnt = assert(read_int32be(fd)) + + -- The number of leap seconds for which data is stored in the file. + tzh_leapcnt = assert(read_int32be(fd)) + + -- The number of "transition times" for which data is stored in the file. + tzh_timecnt = assert(read_int32be(fd)) + + -- The number of "local time types" for which data is stored in the file (must not be zero). + tzh_typecnt = assert(read_int32be(fd)) + + -- The number of characters of "timezone abbreviation strings" stored in the file. + tzh_charcnt = assert(read_int32be(fd)) + + transition_times = {} + for i = 1, tzh_timecnt do + transition_times[i] = assert(read_int64be(fd)) + end + transition_time_ind = {assert(fd:read(tzh_timecnt)):byte(1, -1)} + + ttinfos = {} + for i = 1, tzh_typecnt do + ttinfos[i] = { + gmtoff = assert(read_int32be(fd)), + isdst = assert(fd:read(1)) ~= "\0", + abbrind = assert(fd:read(1)):byte(), + } + end + + abbreviations = assert(fd:read(tzh_charcnt)) + + leap_seconds = {} + for i = 1, tzh_leapcnt do + leap_seconds[i] = { + offset = assert(read_int64be(fd)), + n = assert(read_int32be(fd)), + } + end + + isstd = assert(read_flags(fd, tzh_ttisstdcnt)) + + isgmt = assert(read_flags(fd, tzh_ttisgmtcnt)) + + --[[ + After the second header and data comes a newline-enclosed, POSIX-TZ-environment-variable-style string + for use in handling instants after the last transition time stored in the file + (with nothing between the newlines if there is no POSIX representation for such instants). + ]] + + --[[ + For version-3-format time zone files, the POSIX-TZ-style string may + use two minor extensions to the POSIX TZ format, as described in newtzset (3). + First, the hours part of its transition times may be signed and range from + -167 through 167 instead of the POSIX-required unsigned values + from 0 through 24. Second, DST is in effect all year if it starts + January 1 at 00:00 and ends December 31 at 24:00 plus the difference + between daylight saving and standard time. + ]] + + assert(assert(fd:read(1)) == "\n", "Expected newline at end of version 2 header") + + TZ = assert(fd:read("*l")) + if #TZ == 0 then + TZ = nil + end + end + + for i = 1, tzh_typecnt do + local v = ttinfos[i] + v.abbr = abbreviations:sub(v.abbrind + 1, v.abbrind + 3) + v.isstd = isstd[i] or false + v.isgmt = isgmt[i] or false + setmetatable(v, tt_info_mt) + end + + --[[ + Use the first standard-time ttinfo structure in the file + (or simply the first ttinfo structure in the absence of a standard-time structure) + if either tzh_timecnt is zero or the time argument is less than the first transition time recorded in the file. + ]] + local first = 1 + do + for i = 1, tzh_ttisstdcnt do + if isstd[i] then + first = i + break + end + end + end + + local res = { + future = TZ, + [0] = { + transition_time = MIN_TIME, + info = ttinfos[first], + }, + } + for i = 1, tzh_timecnt do + res[i] = { + transition_time = transition_times[i], + info = ttinfos[transition_time_ind[i] + 1], + } + end + return setmetatable(res, tz_info_mt) + else + error("Unsupported version") + end +end + +local function read_tzfile(path) + local fd = assert(io.open(path, "rb")) + local tzinfo = read_tz(fd) + fd:close() + return tzinfo +end + +return { + read_tz = read_tz, + read_tzfile = read_tzfile, +} diff --git a/framework/lualib/3rd/luatz/tzinfo.lua b/framework/lualib/3rd/luatz/tzinfo.lua new file mode 100644 index 0000000..8f36ced --- /dev/null +++ b/framework/lualib/3rd/luatz/tzinfo.lua @@ -0,0 +1,99 @@ +local gettime = require"luatz.gettime".gettime +local timetable_mt = require"luatz.timetable".timetable_mt + +local function to_timestamp(o) + if type(o) == "number" then + return o + elseif getmetatable(o) == timetable_mt then + return o:timestamp() + end +end + +local tz_info_methods = {} +local tz_info_mt = { + __name = "luatz.tz_info", + __index = tz_info_methods, +} +local tt_info_mt = { + __name = "luatz.tt_info", + __tostring = function(self) + return string.format("tt_info:%s=%d", self.abbr, self.gmtoff) + end, +} + +-- Binary search +local function find_current(tzinfo, target, i, j) + if i >= j then + return j + end + + local half = math.ceil((j + i) / 2) + + if target >= tzinfo[half].transition_time then + return find_current(tzinfo, target, half, j) + else + return find_current(tzinfo, target, i, half - 1) + end +end + +local function find_current_local(tzinfo, ts_local) + -- Find two best possibilities by searching back and forward a day (assumes transition is never by more than 24 hours) + local tz_first = find_current(tzinfo, ts_local - 86400, 0, #tzinfo) + local tz_last = find_current(tzinfo, ts_local + 86400, 0, #tzinfo) + + local n_candidates = tz_last - tz_first + 1 + + if n_candidates == 1 then + return tz_first + elseif n_candidates == 2 then + local tz_first_ob = tzinfo[tz_first] + local tz_last_ob = tzinfo[tz_last] + + local first_gmtoffset = tz_first_ob.info.gmtoff + local last_gmtoffset = tz_last_ob.info.gmtoff + + local t_start = tz_last_ob.transition_time + first_gmtoffset + local t_end = tz_last_ob.transition_time + last_gmtoffset + + -- If timestamp is before start or after end + if ts_local < t_start then + return tz_first + elseif ts_local > t_end then + return tz_last + end + + -- If we get this far, the local time is ambiguous + return tz_first, tz_last + else + error("Too many transitions in a 2 day period") + end +end + +function tz_info_methods:find_current(current) + current = assert(to_timestamp(current), "invalid timestamp to :find_current") + return self[find_current(self, current, 0, #self)].info +end + +function tz_info_methods:localise(utc_ts) + utc_ts = utc_ts or gettime() + return utc_ts + self:find_current(utc_ts).gmtoff +end +tz_info_methods.localize = tz_info_methods.localise + +function tz_info_methods:utctime(ts_local) + ts_local = assert(to_timestamp(ts_local), "invalid timestamp to :utctime") + local tz1, tz2 = find_current_local(self, ts_local) + tz1 = self[tz1].info + if tz2 == nil then + return ts_local - tz1.gmtoff + else -- Local time is ambiguous + tz2 = self[tz2].info + + return ts_local - tz2.gmtoff, ts_local - tz2.gmtoff + end +end + +return { + tz_info_mt = tz_info_mt, + tt_info_mt = tt_info_mt, +} diff --git a/framework/lualib/3rd/luaunit/lester.lua b/framework/lualib/3rd/unitest/lester.lua similarity index 100% rename from framework/lualib/3rd/luaunit/lester.lua rename to framework/lualib/3rd/unitest/lester.lua diff --git a/framework/lualib/3rd/luaunit/luaunit.lua b/framework/lualib/3rd/unitest/luaunit.lua similarity index 100% rename from framework/lualib/3rd/luaunit/luaunit.lua rename to framework/lualib/3rd/unitest/luaunit.lua diff --git a/framework/lualib/3rd/zeus/middleclass.lua b/framework/lualib/3rd/zeus/middleclass.lua index e90c7b8..ae603af 100644 --- a/framework/lualib/3rd/zeus/middleclass.lua +++ b/framework/lualib/3rd/zeus/middleclass.lua @@ -72,7 +72,7 @@ local function _createClass(name, super) dict.__index = dict local aClass = { - name = name, + __cname = name, super = super, static = {}, __instanceDict = dict, diff --git a/framework/lualib/3rd/zeus/singleton.lua b/framework/lualib/3rd/zeus/singleton.lua new file mode 100644 index 0000000..25e2736 --- /dev/null +++ b/framework/lualib/3rd/zeus/singleton.lua @@ -0,0 +1,24 @@ +-- 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/zeus/skynet/queue2.lua b/framework/lualib/zeus/skynet/cs.lua similarity index 95% rename from framework/lualib/zeus/skynet/queue2.lua rename to framework/lualib/zeus/skynet/cs.lua index 711e80a..66c5354 100644 --- a/framework/lualib/zeus/skynet/queue2.lua +++ b/framework/lualib/zeus/skynet/cs.lua @@ -1,11 +1,12 @@ local skynet = require "skynet" + local coroutine = coroutine local assert = assert local tinsert = table.insert local tremove = table.remove local setmetatable = setmetatable -function skynet.queue2() +function skynet.cs() local current_thread local ref = 0 local thread_queue = {} @@ -36,4 +37,4 @@ function skynet.queue2() end end -return skynet.queue2 +return skynet.cs diff --git a/framework/lualib/zeus/skynet/env.lua b/framework/lualib/zeus/skynet/env.lua index 5f83337..56b42f8 100644 --- a/framework/lualib/zeus/skynet/env.lua +++ b/framework/lualib/zeus/skynet/env.lua @@ -1,4 +1,3 @@ --- --[[ 这是 skynet.getenv 和 skynet.setenv 的中间层,方便使用 @@ -6,7 +5,8 @@ env 自动将 "true" 和 "false" 转换成 bool,将 "number" 字符串转成 number ]] -local skynet = require("skynet") + +local skynet = require "skynet" local cache = {} @@ -50,4 +50,6 @@ function env.set(key, value) skynet.setenv(key, value) cache[key] = convert(value) end + + return env diff --git a/framework/lualib/zeus/mainloop/mainloop.lua b/framework/lualib/zeus/skynet/mainloop.lua similarity index 81% rename from framework/lualib/zeus/mainloop/mainloop.lua rename to framework/lualib/zeus/skynet/mainloop.lua index 0fa594c..74d399a 100644 --- a/framework/lualib/zeus/mainloop/mainloop.lua +++ b/framework/lualib/zeus/skynet/mainloop.lua @@ -1,5 +1,4 @@ -- https://github.com/ggabriel96/lasso - local skynet = require "skynet" local default_smoothing = 0.75 @@ -7,7 +6,7 @@ local default_simulations = 60 -- 多少帧 local MainLoop = class("MainLoop") -function MainLoop:ctor(simulations_per_second, fps_smoothing) +function MainLoop:initialize(simulations_per_second, fps_smoothing) self.loop_status = { fps = 0, time_frame = 0, @@ -16,7 +15,7 @@ function MainLoop:ctor(simulations_per_second, fps_smoothing) time_simulation_available = 0, tick = 0, time_prev = self:get_time(), - time_curr = self:get_time() + time_curr = self:get_time(), } -- 1 秒 按 100 做单位(skynet 进度到 10ms 的原因) @@ -29,9 +28,8 @@ function MainLoop:get_time() end function MainLoop:compute_fps() - self.loop_status.fps = - math.floor(self.loop_status.fps * self.fps_smoothing) + - math.floor((1 / self.loop_status.time_frame) * (1.0 - self.fps_smoothing)) + self.loop_status.fps = math.floor(self.loop_status.fps * self.fps_smoothing) + + math.floor((1 / self.loop_status.time_frame) * (1.0 - self.fps_smoothing)) end -- game_logic 需要实现 方法 init simulate render is_done terminate @@ -42,8 +40,8 @@ function MainLoop:run(game_logic) self.loop_status.time_curr = self:get_time() self.loop_status.time_frame = self.loop_status.time_curr - self.loop_status.time_prev - self.loop_status.time_simulation_available = - self.loop_status.time_simulation_available + self.loop_status.time_frame + self.loop_status.time_simulation_available = self.loop_status.time_simulation_available + + self.loop_status.time_frame while (self.loop_status.time_simulation_available >= self.delta) do self.loop_status.tick = self.loop_status.tick + 1 diff --git a/framework/lualib/zeus/skynet/parallels.lua b/framework/lualib/zeus/skynet/parallels.lua index d1c62bd..50ad956 100644 --- a/framework/lualib/zeus/skynet/parallels.lua +++ b/framework/lualib/zeus/skynet/parallels.lua @@ -1,17 +1,9 @@ -local skynet = require("skynet") -local xpcall = xpcall -local coroutine = coroutine -local tostring = tostring -local error = error -local next = next -local setmetatable = setmetatable -local assert = assert -local gLog = gLog +local skynet = require "skynet" --[[ 处理 连续多个服务调用时完全独立的,不必等到service1 返回后才调用service2 ... 操作 - 不过 最新 skynet 已经 有 skynet.request { ... } : select(time) 还没有测试使用 - 所以 暂时 先 保留 + 不过 最新 skynet 已经 有 skynet.request { ... } : select(time) 还没有测试使用 所以 暂时 先 保留 + https://github.com/cloudwu/skynet/issues/1175 local pa = require("parallels")() for _, uid in ipairs(uidOrUids) do @@ -50,16 +42,11 @@ function M:wakeup_waitco(err) end end --- 打印异常 local function exception(e) - gLog.error("[parallel exception]%s", e) + skynet.error("[parallel exception]%s", e) return e end ----@param self parallels ----@param func fun(...: any): any... ----@param ... any ----@return void function M:add(func, ...) local token = {} local list = self.list diff --git a/framework/lualib/zeus/skynet/rpc.lua b/framework/lualib/zeus/skynet/rpc.lua index cf9f1ec..bb47128 100644 --- a/framework/lualib/zeus/skynet/rpc.lua +++ b/framework/lualib/zeus/skynet/rpc.lua @@ -1,22 +1,91 @@ -local class = class --- local skynet = require "skynet" +local skynet = require "skynet" +local cluster = require "skynet.cluster" + +local skynet_env = require("skynet.env") +local cur_nodeid = skynet_env.get("nodeid") + +local zenv = require("zenv.init") + +local function _xpret(_, ...) + return ... +end +--------------------------------------------------- local Rpc = class("Rpc") -function Rpc:initialize() +function Rpc:initialize(name, nodeid) + self._name = name + self._nodeid = nodeid +end + +function Rpc:_get_local_nodeid() + local nodeid = self._nodeid + if not nodeid or nodeid == cur_nodeid then + return true + end + return false, nodeid +end + +function Rpc:call(...) + local is_local, nodeid = self:_get_local_nodeid() + if is_local then + return skynet.call(self._name, "lua", ...) + end + + local nodename = zenv.get_node_conf(nodeid).name + return cluster.call(nodename, self._name, ...) +end + +function Rpc:send(...) + local is_local, nodeid = self:_get_local_nodeid() + if is_local then + skynet.send(self._name, "lua", ...) + else + local node = zenv.get_node_conf(nodeid).name + cluster.send(node, self._name, ...) + end end -function Rpc:parallels() +function Rpc:xpcall(...) + return xpcall(self.call, skynet.error, self, ...) end -function Rpc:call() - -- return skynet.call() +function Rpc:xpcallret(...) + return _xpret(self:xpcall(...)) end -function Rpc:send() +function Rpc:psend(...) + pcall(self.send, self, ...) end -function Rpc:timeout_call() +function Rpc:timeout_call(time, cb, ...) + local timeout = false + local ok = false + local co = coroutine.running() + + skynet.fork(function(...) + local function f(suc, ...) + if suc then + if not timeout then + ok = true + skynet.wakeup(co) + end + else + skynet.wakeup(co) + end + + if cb then + cb(ok, ...) + end + end + + f(self:xpcall(...)) + end, ...) + + skynet.sleep(time * 100) + timeout = true + + return ok end return Rpc diff --git a/framework/lualib/zeus/skynet/timer.lua b/framework/lualib/zeus/skynet/shiftimer.lua similarity index 95% rename from framework/lualib/zeus/skynet/timer.lua rename to framework/lualib/zeus/skynet/shiftimer.lua index a540667..07ab38f 100755 --- a/framework/lualib/zeus/skynet/timer.lua +++ b/framework/lualib/zeus/skynet/shiftimer.lua @@ -1,9 +1,5 @@ local skynet = require "skynet" local shiftimer = require "shiftimer.c" -local assert = assert -local type = type -local ipairs = ipairs -local setmetatable = setmetatable --- 状态 local state = { @@ -40,7 +36,6 @@ function mt:add(ti, func, args) end --- 启动计时器 --- @return nil function mt:start() if self.state == state.INIT then self.state = state.RUNNING @@ -49,7 +44,6 @@ function mt:start() end --- 暂停计时器 --- @return nil function mt:pause() if self.co and self.state ~= state.PAUSE then self.state = state.PAUSE @@ -57,7 +51,6 @@ function mt:pause() end --- 恢复 --- @return nil function mt:resume() if self.co and self.state == state.PAUSE then self.state = state.RUNNING @@ -66,7 +59,6 @@ function mt:resume() end --- 停止计时器 --- @return nil function mt:stop() if self.co and self.state ~= state.STOP then self.state = state.STOP @@ -128,7 +120,6 @@ function mt:isrun() end --- 每帧调用 --- @return nil function mt:__update() while true do if self.state == state.RUNNING then @@ -153,6 +144,7 @@ function mt:__update() end local M = {} + function M.new() local obj = {} obj.st = shiftimer() @@ -160,4 +152,5 @@ function M.new() obj.state = state.INIT return setmetatable(obj, mt) end + return M diff --git a/framework/lualib/zeus/skynet/timeout_call.lua b/framework/lualib/zeus/skynet/timeout_call.lua deleted file mode 100644 index 5b2fa9f..0000000 --- a/framework/lualib/zeus/skynet/timeout_call.lua +++ /dev/null @@ -1,33 +0,0 @@ -local skynet = require("skynet") - -local coroutine_running = coroutine.running -local table_pack = table.pack -local table_unpack = table.unpack -local pcall = pcall -local error = error - --- 注意,这个接口一定会挂起 -return function(ti, f, ...) - local co = coroutine_running() - local ret - - skynet.fork(function(...) - ret = table_pack(pcall(f, ...)) - if co then - skynet.wakeup(co) - end - end, ...) - - skynet.sleep(ti) - co = nil -- prevent wakeup after call - if ret then - if ret[1] then - return table_unpack(ret, 1, ret.n) - else - error(ret[2]) - end - else - -- timeout - return false - end -end diff --git a/framework/lualib/zeus/zenv/init.lua b/framework/lualib/zeus/zenv/init.lua new file mode 100644 index 0000000..8b74b3c --- /dev/null +++ b/framework/lualib/zeus/zenv/init.lua @@ -0,0 +1,26 @@ +local M = { + DEBUG = true, +} + +M.Nodes = { + [1] = { + nodeid = 1, + name = "login", + }, + + [50] = { + nodeid = 50, + name = "center", + }, + + [100] = { + nodeid = 100, + name = "game", + }, +} + +function M.get_node_conf(nodeid) + return M.Nodes[nodeid] +end + +return M diff --git a/framework/service/template.lua b/framework/service/template.lua new file mode 100644 index 0000000..2deb775 --- /dev/null +++ b/framework/service/template.lua @@ -0,0 +1,62 @@ +-- 服务处理模版 +local fsm = require "zeus.fsm" + +local Template = class("Template"):include(singleton) + +function Template:initialize() + self._cmds = {} -- 已注册的命令 + self._exceptions = {} -- 已注册异常处理 + self._updaters = {} -- 定时器 + self._newday = { -- newday只允许注册一次且不可删除 + curDay = os.date("%d", os.time()), -- 当前天 + handle = nil, -- newday updater的句柄 + } + + self._fsm = fsm.create({ + initial = "none", + events = {{ + name = "initial", + from = "none", + to = "initial", + }, { + name = "running", + from = "initial", + to = "running", + }, { + name = "stop", + from = "running", + to = "stop", + }, { + name = "exit", + from = "stop", + to = "exit", + }}, + callbacks = { + on_initial = handler("_on_initial", self), + on_running = handler("_on_running", self), + on_stop = handler("_on_stop", self), + on_exit = handler("_on_exit", self), + }, + }) +end + +function Template:get_fsm() + return self._fsm +end + +function Template:_on_initial() +end + +function Template:_on_running() +end + +function Template:_on_stop() +end + +function Template:_on_exit() +end + +function Template:reg_cmd() +end + +return Template diff --git a/framework/service/zeus.lua b/framework/service/zeus.lua new file mode 100644 index 0000000..645ca61 --- /dev/null +++ b/framework/service/zeus.lua @@ -0,0 +1 @@ +--统一的服务基础逻辑 \ No newline at end of file diff --git a/server/src/preload/env.lua b/server/src/preload/env.lua deleted file mode 100644 index 3f4c332..0000000 --- a/server/src/preload/env.lua +++ /dev/null @@ -1,4 +0,0 @@ -return { - DEBUG = true, - NODEID = 1, -} diff --git a/server/src/preload/preload.lua b/server/src/preload/preload.lua index 104db7a..e35a7f6 100644 --- a/server/src/preload/preload.lua +++ b/server/src/preload/preload.lua @@ -2,10 +2,11 @@ collectgarbage("setpause", 120) -- 内存增大 1 倍(200/100)时自动释 collectgarbage("setstepmul", 1000) -- 收集器单步收集的速度相对于内存分配速度的倍率,设置 200 的倍率等于 2 倍(200/100)。(200 是默认值) class = require("zeus.middleclass") +singleton = require("zeus.singleton") handler = require("zeus.handler") -- 支持 function 和 对象内部 handler 不过后者热更会更友好 - -local env = require("env") -if env.DEBUG then +-- zlog +local zenv = require("zenv.init") +if zenv.DEBUG then require("tracing.skynet") require("swt.debug") end