🔧 build: 调整 库 目录

develop
xiaojin 5 years ago
parent 43b95ceaad
commit dd6ab6d6a7

@ -12,8 +12,9 @@ exclude_files = {
globals = {
"SERVICE_NAME",
"class",
"singleton",
"handler",
"gLog",
"zlog",
}
ignore = {

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

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

@ -0,0 +1,848 @@
//gcc -o aoi aoi.c utils.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#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))
{ //之前在makersD了再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;i<grid_num;i++){
// Grid* grid = w->grids[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;
// }

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

@ -0,0 +1,283 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#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;
}

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

@ -0,0 +1,252 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#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);
}
}

@ -0,0 +1,53 @@
#ifndef _UTILS_H
#define _UTILS_H
#include <stdint.h>
#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

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

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

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

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

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

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

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

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

@ -72,7 +72,7 @@ local function _createClass(name, super)
dict.__index = dict
local aClass = {
name = name,
__cname = name,
super = super,
static = {},
__instanceDict = dict,

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

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

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

@ -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,8 +28,7 @@ function MainLoop:get_time()
end
function MainLoop:compute_fps()
self.loop_status.fps =
math.floor(self.loop_status.fps * 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
@ -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

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

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

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

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

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

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

@ -0,0 +1 @@
--统一的服务基础逻辑

@ -1,4 +0,0 @@
return {
DEBUG = true,
NODEID = 1,
}

@ -2,10 +2,11 @@ collectgarbage("setpause", 120) -- 内存增大 1 倍200/100时自动释
collectgarbage("setstepmul", 1000) -- 收集器单步收集的速度相对于内存分配速度的倍率,设置 200 的倍率等于 2 倍200/100200 是默认值)
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

Loading…
Cancel
Save