🔧 build: 调整 库 目录
parent
43b95ceaad
commit
dd6ab6d6a7
@ -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,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,
|
||||
}
|
||||
@ -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,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
|
||||
|
||||
function Rpc:parallels()
|
||||
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:call()
|
||||
-- return skynet.call()
|
||||
function Rpc:xpcall(...)
|
||||
return xpcall(self.call, skynet.error, self, ...)
|
||||
end
|
||||
|
||||
function Rpc:send()
|
||||
function Rpc:xpcallret(...)
|
||||
return _xpret(self:xpcall(...))
|
||||
end
|
||||
|
||||
function Rpc:timeout_call()
|
||||
function Rpc:psend(...)
|
||||
pcall(self.send, self, ...)
|
||||
end
|
||||
|
||||
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,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,
|
||||
}
|
||||
Loading…
Reference in New Issue