Commit 314bc5df00ab866ab762f4e22f3ceec4681cc52e
1 parent
7cbc0876
提交服务器初始代码
Showing
55 changed files
with
6014 additions
and
0 deletions
Show diff stats
.gitmodules
@@ -0,0 +1,163 @@ | @@ -0,0 +1,163 @@ | ||
1 | +call 的调用流程分析 | ||
2 | + | ||
3 | +一个lua vm 中 任意时刻 只能运行一个 coroutine | ||
4 | +导入重入的原因是,call 操作中 让出执行权,另一个coroutine 修改了某个值,则唤醒call的coroutine时,再来操作那个值,可能已经改变了 | ||
5 | + | ||
6 | +coroutine 理解: | ||
7 | +--------------------------------------------------- | ||
8 | +function foo (a) | ||
9 | + print("foo", a) | ||
10 | + return coroutine.yield(2*a) | ||
11 | +end | ||
12 | + | ||
13 | +co = coroutine.create(function (a,b) | ||
14 | + print("co-body", a, b) | ||
15 | + local r = foo(a+1) | ||
16 | + print("co-body", r) | ||
17 | + local r, s = coroutine.yield(a+b, a-b) | ||
18 | + print("co-body", r, s) | ||
19 | + return b, "end" | ||
20 | +end) | ||
21 | + | ||
22 | +print("main", coroutine.resume(co, 1, 10)) | ||
23 | +print("main", coroutine.resume(co, "r")) | ||
24 | +print("main", coroutine.resume(co, "x", "y")) | ||
25 | +print("main", coroutine.resume(co, "x", "y")) | ||
26 | +当你运行它,将产生下列输出: | ||
27 | + | ||
28 | +co-body 1 10 | ||
29 | +foo 2 | ||
30 | +main true 4 | ||
31 | +co-body r | ||
32 | +main true 11 -9 | ||
33 | +co-body x y | ||
34 | +main true 10 end | ||
35 | +main false cannot resume dead coroutine | ||
36 | + | ||
37 | +coroutine.resume 将会返回 yield的参数 | ||
38 | +继续执行coroutine.resume,coroutine.yield将返回coroutine.resume除第一个参数的其他参数 | ||
39 | + | ||
40 | +coroutine.resume | ||
41 | +开始或继续协程 co 的运行。 当你第一次延续一个协程,它会从主体函数处开始运行。 | ||
42 | + val1, ... 这些值会以参数形式传入主体函数。 如果该协程被让出,resume 会重新启动它; | ||
43 | + val1, ... 这些参数会作为让出点的返回值。 | ||
44 | + | ||
45 | +如果协程运行起来没有错误, resume 返回 true 加上传给 yield 的所有值 (当协程让出), | ||
46 | + 或是主体函数的所有返回值(当协程中止)。 如果有任何错误发生, resume 返回 false 加错误消息。 | ||
47 | +--------------------------------------------------- | ||
48 | +A call B | ||
49 | + | ||
50 | +skynet.call(addr, typename, ...) | ||
51 | + | ||
52 | +[A] | ||
53 | +1. 根据 typename 找到 消息的 pack 和 unpack 类型 | ||
54 | +2. 将消息发送出去 将消息 pack 压入 对方的消息队列 | ||
55 | +3. 将当前coroutine 让出执行权, | ||
56 | + | ||
57 | +考虑 skynet.call 所在coroutine | ||
58 | +在dispatch 消息中 | ||
59 | +494: suspend(co, coroutine.resume(co, session,source, p.unpack(msg,sz, ...))) | ||
60 | +suspend(co, true, "CALL", session) | ||
61 | +suspend(co, result, command, param, size) | ||
62 | +result = true, command = "CALL", param = session | ||
63 | + | ||
64 | +4. 初始化 session_id_coroutine[session] = co | ||
65 | + | ||
66 | +[B] | ||
67 | +1. 通过dispatch_message,处理完相关请求 | ||
68 | +2. 若中间出现错误,将错误信息转发至A, 正常情况下通过skynet.ret()返回 | ||
69 | + | ||
70 | +考虑处理消息所在coroutine | ||
71 | +在dispatch 消息中 (记录 co->session, so->source) | ||
72 | +494: suspend(co, coroutine.resume(co, session,source, p.unpack(msg,sz, ...))) | ||
73 | +suspend(co, true, "RETURN", msg, sz) | ||
74 | +suspend(co, result, command, param, size) | ||
75 | +result = true, command = "RETURN", param = msg, size = sz | ||
76 | +c.send(co_address, skynet.PTYPE_RESPONSE, co_session, param, size) | ||
77 | +将消息发送回去 | ||
78 | + | ||
79 | +考虑在B coroutine中 发生错误 | ||
80 | +在 lua-skynet.c _cb函数中,对消息分发函数(lua function)作了pcall处理,若发生错误,将错误内容输出到logger服务中 | ||
81 | +在 skynet.lua 中 raw_dispatch_message 函数中 | ||
82 | +suspend(co, coroutine.resume(co, session, source, p.unpack(msg,sz, ...))) | ||
83 | + | ||
84 | +若 coroutine.resume 失败 | ||
85 | +将返回 false, 以及错误信息 | ||
86 | +在 suspend 函数中 if not result then 判断下 将错误信息发送给A(这里有更多信息) 并掉用 | ||
87 | +error(debug.traceback(co,tostring(command))) | ||
88 | +将错误堆栈 让 c层 捕获到 输出到 logger服务中 | ||
89 | + | ||
90 | +[A] | ||
91 | +1. 从 session_id_coroutine 中 根据session找到coroutine, 并置空session_id_coroutine | ||
92 | +2. 在raw_dispatch_message 中 | ||
93 | +-- skynet.PTYPE_RESPONSE = 1, read skynet.h | ||
94 | +if prototype == 1 then | ||
95 | +处理suspend(co, coroutine.resume(co, true, msg, sz)) | ||
96 | +这时候在yield_call中 | ||
97 | +local succ, msg, sz = coroutine_yield("CALL", session) | ||
98 | +返回coroutine.resume 除第一个参数的其他参数 | ||
99 | +local succ, msg, sz = true, msg, sz | ||
100 | + | ||
101 | +==================================================================== | ||
102 | +send 操作就比较简单,A端发送出去就不管了,B端发生错误也不会理睬, | ||
103 | +它在 B 端 dispatch_message 阶段 suspend 中 走的 command == nil | ||
104 | + | ||
105 | + | ||
106 | +重要的数据结构 | ||
107 | +在被调用的对象中,记录调用者信息 | ||
108 | +session_coroutine_id[co] = session | ||
109 | +session_coroutine_address[co] = sourceAddr | ||
110 | +因为 coroutine 是需要被复用的,所以coroutine退出时候,会清理这两个结构 | ||
111 | + | ||
112 | +调用者 | ||
113 | +watching_session[targetAddr] = session | ||
114 | +在挂起当前协程的时候,记录 地址和session | ||
115 | +1. 出错信息 | ||
116 | +2. 当前vm退出,可告知被call对方,出错信息 | ||
117 | + | ||
118 | +==================================================================== | ||
119 | + | ||
120 | +queue 的实现 | ||
121 | +当运行该coroutine的时候,调用skynet.wake,生成session,coroutine_yield("SLEEP", session) | ||
122 | + | ||
123 | +在 suspend 中,设置 | ||
124 | +session_id_coroutine[session] = co | ||
125 | +sleep_session[co] = session | ||
126 | + | ||
127 | +dispatch_wakeup 中 | ||
128 | +coroutine.resume(co, false, "BREAK")) | ||
129 | +继续运行 | ||
130 | +sleep_session[co] = nil | ||
131 | +session_id_coroutine[session] = nil | ||
132 | + | ||
133 | +function () | ||
134 | + option { | ||
135 | + yield "sleep" | ||
136 | + } | ||
137 | + | ||
138 | + # A call B, 则 A 是 yield "call"; B 是 yield "return" | ||
139 | + operation { | ||
140 | + option yield "call" | ||
141 | + option yield "return" | ||
142 | + } | ||
143 | + | ||
144 | + yield "exit" | ||
145 | +end | ||
146 | + | ||
147 | +==================================================================== | ||
148 | + | ||
149 | +redirect 实现 | ||
150 | +skynet.redirect = function(dest, source, typename, ...) | ||
151 | + return c.redirect(dest, source, proto[typename].id, ...) | ||
152 | +end | ||
153 | + | ||
154 | +使用范例: | ||
155 | + skynet.redirect(agent, c.client or 0, "client", 0, msg, sz) | ||
156 | + | ||
157 | +1. dest | ||
158 | +2. source | ||
159 | +3. typeId | ||
160 | +4. session | ||
161 | +5,6 skynet.pack(...) | ||
162 | + | ||
163 | +==================================================================== |
@@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
1 | +==================================================================== | ||
2 | +session 从 1 到 0x7FFFFFFF | ||
3 | +若收到消息 | ||
4 | + | ||
5 | +如果 session == 0 | ||
6 | + | ||
7 | +可能 socket 消息 | ||
8 | + | ||
9 | +可能 text error 消息 | ||
10 | + | ||
11 | +如果 type == 1 | ||
12 | + | ||
13 | +可能 response 消息 来源 : call 回复消息 或者 timeout 通知消息 | ||
14 | + | ||
15 | +==================================================================== | ||
16 | +若发送信息: | ||
17 | +c.send(dest, type, session, msg, sz) | ||
18 | + | ||
19 | +如果 session == 0 则为 send 消息 不需要返回 | ||
20 | +如果 session 为 nil 则 需要指定底层分配 如 call 操作 | ||
21 | + | ||
22 | +type 若 session 为 nil, 则 type | PTYPE_TAG_ALLOCSESSION | ||
23 | + | ||
24 | +谁来清空内存??? | ||
25 | + | ||
26 | +在发送端生成,由接受者来清理 | ||
27 | + | ||
28 | +如果是 LUA_TSTRING 分配内存 | ||
29 | + | ||
30 | +如果是 LUA_TLIGHTUSERDATA seri 的时候 分配内存,传递指针,dispatch_message的时候释放内存 | ||
31 | + | ||
32 | +==================================================================== |
@@ -0,0 +1,73 @@ | @@ -0,0 +1,73 @@ | ||
1 | +lua_createtable(L, 0, 3); 创建大小为3的table | ||
2 | + | ||
3 | +lua_pushcfunction(L, lcopy); 将lcopy压栈 | ||
4 | + | ||
5 | +lua_setfield(L, -2, "copy"); | ||
6 | + | ||
7 | +{ | ||
8 | + copy = lcopy, | ||
9 | +} | ||
10 | + | ||
11 | +==================================================================== | ||
12 | + | ||
13 | +lua_createtable(L, 0, 2); 创建大小为2的table | ||
14 | + | ||
15 | +lua_pushcfunction(L, ldeletewriter), | ||
16 | +lua_setfield(L, -2, "__gc"); | ||
17 | + | ||
18 | +lua_pushcfunction(L, lupdate), | ||
19 | +lua_setfield(L, -2, "__call"); | ||
20 | + | ||
21 | +table = { | ||
22 | + __gc = ldeletewriter, | ||
23 | + __call = lupdate, | ||
24 | +} | ||
25 | + | ||
26 | +luaL_Reg writer[] = { | ||
27 | + { "new", lnewwriter }, | ||
28 | + { NULL, NULL }, | ||
29 | +}; | ||
30 | +luaL_setfuncs(L, writer, 1); | ||
31 | +将 writer 压入栈顶,并将table作为上值 | ||
32 | + | ||
33 | +==================================================================== | ||
34 | + | ||
35 | +lua_createtable(L, 0, 2); | ||
36 | +lua_pushcfunction(L, ldeletereader), | ||
37 | +lua_setfield(L, -2, "__gc"); | ||
38 | +lua_pushcfunction(L, lread), | ||
39 | +lua_setfield(L, -2, "__call"); | ||
40 | + | ||
41 | +table = { | ||
42 | + __gc = ldeletereader, | ||
43 | + __call = lread, | ||
44 | +} | ||
45 | + | ||
46 | +luaL_Reg reader[] = { | ||
47 | + { "newcopy", lnewreader }, | ||
48 | + { NULL, NULL }, | ||
49 | +}; | ||
50 | +luaL_setfuncs(L, reader, 1); | ||
51 | +将 reader 压入栈顶,并将table作为上值 | ||
52 | + | ||
53 | +==================================================================== | ||
54 | + | ||
55 | +{ | ||
56 | + copy = lcopy, | ||
57 | + writer = ..., | ||
58 | + reader = ..., | ||
59 | +} | ||
60 | + | ||
61 | +==================================================================== | ||
62 | + | ||
63 | + luaL_Reg m[] = { | ||
64 | + { "pop", lpop }, | ||
65 | + { "push", lpush }, | ||
66 | + { "size", lsize }, | ||
67 | + { NULL, NULL }, | ||
68 | + }; | ||
69 | + luaL_newmetatable(L, "dangge.deque"); 创建一个meta表并压栈 | ||
70 | + lua_pushvalue(L, -1); 复制一个meta表 | ||
71 | + lua_setfield(L, -2, "__index"); meta = {__index = meta} | ||
72 | + luaL_setfuncs(L, m, 0); meta = {pop = lpop, push = lpush, size = lsize} | ||
73 | + |
@@ -0,0 +1,84 @@ | @@ -0,0 +1,84 @@ | ||
1 | +stringUtil.lua 中提供的接口 | ||
2 | +============ | ||
3 | +接口设计参考 redis 方式 | ||
4 | + | ||
5 | +针对 "K1=V1 K2=V2 ... Kn=Vn" 结构的CRUD(增删改查)操作 | ||
6 | +K1~Kn是唯一的;K1~Kn,V1~Vn都为整数,不可为浮点数 | ||
7 | + | ||
8 | +setv 操作为修改 k 的值 v,若不存在 k 则将k=v附在字符串末尾 | ||
9 | +string.setv(str, k, v, delimiter) | ||
10 | + | ||
11 | +msetv 操作为修改 {[k1]=v1, [k2]=v2, ..., [kn]=vn} 中对应的kv值,若字符串中不包含则以 k=v 的形式附在末尾 | ||
12 | +string.msetv(str, vs, delimiter) | ||
13 | + | ||
14 | +incrv 操作为修改 k 对应的值 +delta,若字符串中不包含则以 k=delta 的形式附在末尾 | ||
15 | +string.incrv(str, k, delta, delimiter) | ||
16 | + | ||
17 | +mincrv 操作为修改 {[k1]=d1, [k2]=d2, ..., [kn]=dn} 中对应的k 是与之对应的 v+d,若字符串中不包含则以 k=d 的形式附在末尾 | ||
18 | +string.mincrv(str, ds, delimiter) | ||
19 | + | ||
20 | +delk 操作为 删除字符串中 包含k 的k=v结构, 若不存在则跳过 | ||
21 | +string.delk(str, k, delimiter) | ||
22 | + | ||
23 | +mdelk 操作为 删除字符串中 包含 {k1, k2, ..., kn} 中任意元素的结构,若不存在则跳过 | ||
24 | +string.mdelk(str, ks, delimiter) | ||
25 | + | ||
26 | +getv 操作为 获取字符串中 包含 k 中的 v 值,若不存在返回default值 | ||
27 | +string.getv(str, k, default, delimiter) | ||
28 | + | ||
29 | +如果要获取多个k的值,则可通过 string.toNumMap() 将字符串转化为 一个table,通过访问table获取相应的值 | ||
30 | +ps:尽量将多个操作集中为一个table 然后调用 string.msetv or string.mdelk | ||
31 | + | ||
32 | +============ | ||
33 | +string.setv_dk(str, k1, k2, v, delimiter) | ||
34 | +string.msetv_dk(str, vs, delimiter) | ||
35 | +string.incrv_dk(str, k1, k2, delta, delimiter) | ||
36 | +string.mincrv_dk(str, ds, delimiter) | ||
37 | +string.delk_dk(str, k1, k2, delimiter) | ||
38 | +string.mdelk_dk(str, ks, delimiter) | ||
39 | +string.getv_dk(str, k1, k2, default, delimiter) | ||
40 | +string.toNumMap(str, delimiter) | ||
41 | + | ||
42 | +这里是多键结构,操作含义如上 | ||
43 | + | ||
44 | +============ | ||
45 | + | ||
46 | +"N1#N2#...#Nn" 是一个集合 | ||
47 | +toArray #为任意分隔符 生成 {N1, N2, ..., Nn} | ||
48 | +string.toArray(str, toNum, delimiter) | ||
49 | +value 获取某个序号的元素 序号范围 1~n | ||
50 | +string.value(str, index, default, notNum, delimiter) | ||
51 | +sismember 是否存在 value 元素 | ||
52 | +string.sismember(str, value, delimiter) | ||
53 | +sadd 添加 值为value 元素,若存在则跳过 | ||
54 | +string.sadd(str, value, delimiter) | ||
55 | +srem 删除 值为value 元素 | ||
56 | +string.srem(str, value, delimiter) | ||
57 | + | ||
58 | +============ | ||
59 | + | ||
60 | +直接从字符串中 随机 | ||
61 | +string.randWeight(str, bMulti) | ||
62 | +string.randLine(str) | ||
63 | + | ||
64 | +============ | ||
65 | + | ||
66 | +"1010101" 由1,0组成的元素 | ||
67 | +getbit 获取序号pos 的元素 | ||
68 | +string.getbit(str, pos) | ||
69 | +bitcnt 获取 元素值为1 的个数 | ||
70 | +string.bitcnt(str) | ||
71 | +设置位置为pos的值为yes | ||
72 | +string.setbit(str, pos, yes) | ||
73 | + | ||
74 | +============ | ||
75 | + | ||
76 | +lua-strh.c 中为lua层提供的接口,这些都是辅助接口,不要直接在逻辑代码中直接使用,使用stringUtil.lua中封装后的接口 | ||
77 | +modify | ||
78 | +moddk | ||
79 | +getv | ||
80 | +getvdk | ||
81 | +toarray | ||
82 | +tonummap | ||
83 | +value | ||
84 | + |
@@ -0,0 +1,114 @@ | @@ -0,0 +1,114 @@ | ||
1 | +function skynet.launch(...) | ||
2 | + c.command("LAUNCH", table.concat({...}, " ")) | ||
3 | + ...... | ||
4 | +end | ||
5 | + | ||
6 | +lua-skynet.c | _command | skynet_command | ||
7 | +skynet_server.c | cmd_launch | skynet_context_new | ||
8 | + | ||
9 | +==================================================================== | ||
10 | + | ||
11 | +function skynet.newservice(name, ...) | ||
12 | + return skynet.call(".launcher", "lua" , "LAUNCH", "snlua", name, ...) | ||
13 | +end | ||
14 | + | ||
15 | +service/launcher.lua | ||
16 | + | ||
17 | +返回 "RESPONSE", skynet.pack | ||
18 | + | ||
19 | +newservice 收到 "RESPONSE" | ||
20 | + | ||
21 | +launch 服务就有所有服务的地址 | ||
22 | + | ||
23 | +可以统计所有服务,gc某个服务,stats某个服务 | ||
24 | + | ||
25 | +==================================================================== | ||
26 | + | ||
27 | +再来看 skynet_context_new | ||
28 | + | ||
29 | +1. 获取 skynet_module | ||
30 | +struct skynet_module { | ||
31 | + const char * name; 模块名 | ||
32 | + void * module; 模块对应的动态库(snlua, 或者其他动态库) | ||
33 | + skynet_dl_create create; 创建函数 | ||
34 | + skynet_dl_init init; 初始化函数 | ||
35 | + skynet_dl_release release; 释放函数 | ||
36 | + skynet_dl_signal signal; 信号函数 | ||
37 | +}; | ||
38 | + | ||
39 | +2. 调用 M->create() 函数 | ||
40 | +创建 lua vm | ||
41 | +struct snlua { | ||
42 | + lua_State * L; | ||
43 | + struct skynet_context * ctx; | ||
44 | +}; | ||
45 | +构建 snlua 结构 | ||
46 | + | ||
47 | +3. 创建并初始化 上下文 | ||
48 | +struct skynet_context { | ||
49 | + void * instance; snlua结构 | ||
50 | + struct skynet_module * mod; | ||
51 | + void * cb_ud; | ||
52 | + skynet_cb cb; callback函数指针 | ||
53 | + struct message_queue *queue; 消息队列 | ||
54 | + FILE * logfile; 日志输出文件 | ||
55 | + char result[32]; | ||
56 | + uint32_t handle; | ||
57 | + int session_id; | ||
58 | + int ref; | ||
59 | + bool init; | ||
60 | + bool endless; | ||
61 | + CHECKCALLING_DECL | ||
62 | +}; | ||
63 | +创建消息队列 | ||
64 | + | ||
65 | +4. 调用 M->init()函数 | ||
66 | +设置回调函数 | ||
67 | +skynet_callback(ctx, l , _launch); | ||
68 | +ctx->cb 回调函数 _launch | ||
69 | +ctx->cb_ud snlua结构 | ||
70 | + | ||
71 | +"REG" 注册 | ||
72 | +发送第一个消息 | ||
73 | +skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY, 0, tmp, sz); | ||
74 | + | ||
75 | +调用 _launch 函数 | ||
76 | +skynet_callback(context, NULL, NULL); | ||
77 | +将回调函数清空 | ||
78 | +接着调用 _init 函数 | ||
79 | + | ||
80 | +将 skynet_context 的指针地址 存入 lua 环境中 (通过轻量用户数据来实现) | ||
81 | +加载常用库 | ||
82 | +将 LUA_PATH LUA_CPATH LUA_SERVICE LUA_PRELOAD 写入全局table中 | ||
83 | +通过 loader.lua 加载所有的lua库 | ||
84 | + | ||
85 | +5. 将次级消息队列压入全局消息队列 | ||
86 | +skynet_globalmq_push | ||
87 | + | ||
88 | +6. 在服务主模块中调用 | ||
89 | +skynet.start 设置回调函数 | ||
90 | +c.callback 将 _cb作为键,值lua 回调函数 写入lua环境中 | ||
91 | +skynet_callback | ||
92 | + | ||
93 | +lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); | ||
94 | +lua_State *gL = lua_tothread(L,-1); | ||
95 | + | ||
96 | +cb_ud 保存 主线程,主线程不会被回收,生命周期同vm | ||
97 | + | ||
98 | +主线程 栈上永远保存着两个值 | ||
99 | +traceback | ||
100 | +回调函数 | ||
101 | + | ||
102 | +调用 回调函数 | ||
103 | +r = lua_pcall(L, 5, 0, 1); | ||
104 | +栈上第一个函数作为 错误处理函数 | ||
105 | + | ||
106 | +主线程 调用回调函数 回调函数分配线程去处理 每条消息 | ||
107 | + | ||
108 | +7. 错误处理 | ||
109 | +在 发生错误的 服务 处理 | ||
110 | + | ||
111 | +回调函数发生在主线程 | ||
112 | + | ||
113 | + | ||
114 | + |
@@ -0,0 +1,53 @@ | @@ -0,0 +1,53 @@ | ||
1 | +Role.magics = { | ||
2 | + talentPoint = { | ||
3 | + timestr = "talentLastTime", -- timestamps | ||
4 | + limit = function (role) | ||
5 | + return globalCsv.talentPointLimit | ||
6 | + end, | ||
7 | + recover = "talentRecoveryTime", -- globaldefine | ||
8 | + collectIn = "in_talentPoint", | ||
9 | + collectOut = "out_talentPoint", | ||
10 | + cycle = true, -- 回复满,是否复用时间 | ||
11 | + },-- 天赋点 | ||
12 | +} | ||
13 | + | ||
14 | +方法: | ||
15 | +1. produce 生产 | ||
16 | + | ||
17 | +2. consume 消费 | ||
18 | + | ||
19 | +3. onRecoverTimer 定时更新 | ||
20 | + | ||
21 | +4. onRecoverLogin 登录回复 | ||
22 | + | ||
23 | +使用前准备: | ||
24 | + | ||
25 | +在 timestamp 类中添加 talentLastTime ,上次修改时间 | ||
26 | + | ||
27 | +在 globaldefine 中添加 talentPointLimit ,添加上限值,如上,limit中是function 可以根据role的属性来调整上限值;例如可根据vip调整上限值 | ||
28 | + | ||
29 | +在 globaldefine 中添加 talentRecoveryTime ,每(多少)个单位时间回复该值,这里默认以分钟为单位 | ||
30 | + | ||
31 | +collectIn 和 out_talentPoint 变量 是日志字段 | ||
32 | + | ||
33 | +cycle 是标志位 意思是 满了后消耗,会接着上次剩余时间回复该值 | ||
34 | + | ||
35 | +==================================================================== | ||
36 | + | ||
37 | +对外提供接口 | ||
38 | + | ||
39 | +role:produce(params) | ||
40 | +role:consume(params) | ||
41 | + | ||
42 | +params 可使用字段 | ||
43 | + | ||
44 | +field | ||
45 | + | ||
46 | +delta | ||
47 | + | ||
48 | +cantOver | ||
49 | + | ||
50 | +notify | ||
51 | + | ||
52 | + | ||
53 | + |
@@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
1 | +#! /bin/sh | ||
2 | + | ||
3 | +pid=`cat skynet.pid` | ||
4 | +run=`ps aux | grep skynet | grep -v grep | grep -c $pid` | ||
5 | + | ||
6 | +if [ $run = 1 ]; then | ||
7 | + echo "服务端正在运行" | ||
8 | + exit 0 | ||
9 | +fi | ||
10 | + | ||
11 | +skynet/skynet src/config | ||
12 | +echo "服务端启动完毕" | ||
0 | \ No newline at end of file | 13 | \ No newline at end of file |
@@ -0,0 +1,211 @@ | @@ -0,0 +1,211 @@ | ||
1 | +XXTEA_KEY = "699D448D6D24f7F941E9F6E99F823E18" | ||
2 | + | ||
3 | +MAX_ROLE_NUM = 1000000 | ||
4 | + | ||
5 | +RESET_TIME = 4 | ||
6 | + | ||
7 | +MAX_QUALITY_LEVEL = 5 | ||
8 | + | ||
9 | +MAX_HERO_STAR = 6 | ||
10 | +MAX_HERO_LVL = 100 | ||
11 | +MAX_EVOL_LVL = 5 -- 最高进化等级 | ||
12 | +MAX_BREAK_LVL = 16 | ||
13 | + | ||
14 | +MAX_VIP = 15 | ||
15 | + | ||
16 | +MAIL_EXPIRE_TIME = 7*86400 | ||
17 | + | ||
18 | +MAX_FRIEND_SEARCH_COUNT = 5-- 最大搜索数目 | ||
19 | + | ||
20 | +MAX_YZ_LEVEL = 10 | ||
21 | + | ||
22 | +HOUR_ZONE = 9 -- 时区 | ||
23 | + | ||
24 | +carbonType = { | ||
25 | + Normal = 10000, | ||
26 | + Special = 20000, | ||
27 | + Tower = 30000, | ||
28 | + Trial = 40000, | ||
29 | + Worldboss = 50000, | ||
30 | + Yz = 60000, | ||
31 | + Practice = 70000, | ||
32 | +} | ||
33 | + | ||
34 | +FinishType = { | ||
35 | + AllKill = 1, --全部清光 | ||
36 | + BossKill = 2, --干光boss | ||
37 | +} | ||
38 | + | ||
39 | +StageType = { | ||
40 | + Common = 1, -- 普通 | ||
41 | + Arrange = 2, -- 传送门 | ||
42 | + WeakArrange = 3, -- 脆弱的传送门 | ||
43 | + Supply = 4, -- 补给点 | ||
44 | + Power = 5, -- 采集点 | ||
45 | + Select = 6, -- 抉择点 | ||
46 | + Cross = 7, -- 穿越点 | ||
47 | + RandSelect = 8, -- 随机selet | ||
48 | + OnceArrange = 9, -- 一次性召唤点 | ||
49 | + | ||
50 | +} | ||
51 | + | ||
52 | +AiType = { | ||
53 | + Warn = 1, -- 警戒型 | ||
54 | + Occupy = 2, -- 占领型 | ||
55 | + Special = 3, -- 特殊型 | ||
56 | + Eat = 4, -- 吞噬特殊型 | ||
57 | +} | ||
58 | + | ||
59 | +MonsterType = { | ||
60 | + Boss = 1, -- Boss | ||
61 | + Assault = 2, -- 突击怪 | ||
62 | + Maker = 3, -- 制造者 | ||
63 | + Dregs = 4, -- 渣滓 | ||
64 | + Eat = 5, -- 吞噬者 | ||
65 | + Ghost = 6, -- 隐藏boss | ||
66 | +} | ||
67 | + | ||
68 | +ItemType = { | ||
69 | + RandGiftClose = 1, -- 随机道具 不打开 | ||
70 | + RandGiftOpen = 2, -- 随机道具 打开 | ||
71 | + Material1 = 3, -- 油 | ||
72 | + Material2 = 4, -- 魔力 | ||
73 | + Material3 = 5, -- 食材 | ||
74 | + Material4 = 6, -- 调料 | ||
75 | + HeroSkin = 7, -- 食灵皮肤券 | ||
76 | + PackageStudy = 8, -- 套餐研究素材 | ||
77 | + Hero = 9, -- 武将 | ||
78 | + QuickProp = 10, -- 快速道具 | ||
79 | + BuildDraw = 11, -- 建造图纸 | ||
80 | + Diamond = 12, -- 钻石 | ||
81 | + Medel = 13, -- 勋章 | ||
82 | + EnergyItem = 14, -- 次元能量,固定消耗道具 | ||
83 | + Seed = 15, -- 种子 | ||
84 | + QuickPlant = 16, -- 快速种植 | ||
85 | + JobPerfer = 17, -- 职业偏向 | ||
86 | + LoveItem = 18, -- 好感度材料 | ||
87 | + HeadFarme = 19, -- 头像框 | ||
88 | + HeadIcon = 20, -- 头像 | ||
89 | + HomeSkin = 21, -- 主城皮肤 | ||
90 | + Equip = 22, -- 装备 | ||
91 | + Proof = 23, -- 交易凭证 | ||
92 | + Dress = 24, -- 时装 | ||
93 | + Build = 25, -- 建筑材料 | ||
94 | + HeroBag = 26, -- 食灵背包扩展凭证 | ||
95 | + EquipBag = 27, -- 武器背包扩展凭证 | ||
96 | + GiftBag = 28, -- 礼物背包扩展凭证 | ||
97 | + TeamBag = 29, -- 套餐扩展凭证 | ||
98 | + HeroBuild = 30, -- 烹饪扩展凭证 | ||
99 | + EquipBuild = 31, -- 采购扩展凭证 | ||
100 | + PvpCoin = 32, --pvp积分, 商城购买 | ||
101 | + ChooseBox = 33, --玩家选择盒子, | ||
102 | + DrawCoin = 34, --抽奖券, | ||
103 | + ActivityCoin = 35, --活动积分 | ||
104 | + DinerBox = 36, --便当盒 | ||
105 | + Fitment = 37, --家具 | ||
106 | + DinerCar = 38, --餐车 | ||
107 | + Accessory = 39, --配件 | ||
108 | + DinerCarNum = 40, --餐车数量扩展 | ||
109 | + DinerBoxNum = 41, --便当盒数量扩展 | ||
110 | + AccMaterial = 42, --升级消耗材料 | ||
111 | + DinerCoin = 43, --金币银币 | ||
112 | + PangCi = 44, --胖次 | ||
113 | + SkillUpM = 45, --技能升级材料 | ||
114 | +} | ||
115 | + | ||
116 | +ROUND = {[5]=true,[6]=true,[9]=true,[13]=true,[14]=true,} | ||
117 | + | ||
118 | +-- 属性枚举 | ||
119 | +AttsEnum = { | ||
120 | + hp = 1, -- 血量 | ||
121 | + atk = 2, -- 攻击 | ||
122 | + phyDef = 3, -- 物理防御 | ||
123 | + hit = 4, -- 命中 | ||
124 | + miss = 5, -- 闪避 | ||
125 | + crit = 6, -- 暴击 | ||
126 | + atkSpeed = 7, -- 攻击速度 | ||
127 | + critHurt = 8, -- 暴伤 | ||
128 | +} | ||
129 | + | ||
130 | +AttsEnumEx = { | ||
131 | + [1] = "hp", -- 血量 | ||
132 | + [2] = "atk", -- 攻击 | ||
133 | + [3] = "phyDef", -- 物理防御 | ||
134 | + [4] = "hit", -- 命中 | ||
135 | + [5] = "miss", -- 闪避 | ||
136 | + [6] = "crit", -- 暴击 | ||
137 | + [7] = "atkSpeed", -- 攻击速度 | ||
138 | + [8] = "critHurt", -- 暴伤 | ||
139 | +} | ||
140 | + | ||
141 | +-- 物品起始id | ||
142 | +ItemStartId = { | ||
143 | + hero = 1000, -- 英雄 | ||
144 | + equip = 2000, -- 装备 | ||
145 | +} | ||
146 | + | ||
147 | +HeroType = { | ||
148 | + legend = 1, -- 传说英雄 | ||
149 | + diamond = 2, -- 钻石英雄 | ||
150 | + normal = 3, -- 免费英雄 | ||
151 | + lowStar = 4, -- 低星英雄 | ||
152 | + monster = 5, -- 怪物 | ||
153 | + npc = 6, -- npc | ||
154 | + beauty = 7, -- 女神 | ||
155 | +} | ||
156 | + | ||
157 | +ExploreType = { | ||
158 | + Common = 1, -- 普通 | ||
159 | + Born = 2, -- 出生点 | ||
160 | + Score = 3, -- 积分点 | ||
161 | + Cure = 4, -- 治疗点 | ||
162 | + Item = 5, -- 道具店 | ||
163 | + Cross = 6, -- 传送门 | ||
164 | + Enemy = 7, -- 战斗点 | ||
165 | + Event = 8, -- 抉择点 | ||
166 | +} | ||
167 | + | ||
168 | +ExploreItemType = { | ||
169 | + Bomb = 1, -- 炸弹 | ||
170 | + Weak = 2, -- 虚弱 | ||
171 | + Slow = 3, -- 迟缓 | ||
172 | + Invincible = 4, -- 无敌 | ||
173 | + Forbiden = 5, -- 封印 | ||
174 | + Victory = 6, -- 必胜 | ||
175 | + Dice1 = 7, -- 固定点数1 | ||
176 | + Dice2 = 8, -- 固定点数2 | ||
177 | + Dice3 = 9, -- 固定点数3 | ||
178 | + Dice4 = 10, -- 固定点数4 | ||
179 | + Dice5 = 11, -- 固定点数5 | ||
180 | + Dice6 = 12, -- 固定点数6 | ||
181 | + DiceAll = 13, -- 任意点数 | ||
182 | + Fight = 14, -- 决斗卡 | ||
183 | +} | ||
184 | + | ||
185 | +RoomEvent = { | ||
186 | + move = 1, | ||
187 | + useItem = 2, | ||
188 | + playerOnStage = 3, | ||
189 | + playerStart = 4, | ||
190 | + playerEnd = 5, | ||
191 | + playerExit = 6, | ||
192 | + playerDice = 7, | ||
193 | + playerFight = 8, | ||
194 | + eventCross = 9, | ||
195 | + eventFight = 10, | ||
196 | + eventScore = 11, | ||
197 | + eventItem = 12, | ||
198 | + eventCure = 13, | ||
199 | + addScore = 14, | ||
200 | + updateMap = 15, | ||
201 | + addMainList = 16, | ||
202 | + addWaitList = 17, | ||
203 | + playerRevive= 18, | ||
204 | + playerTalk = 19, | ||
205 | + addHealth = 20, | ||
206 | +} | ||
207 | + | ||
208 | +BANTYPE = { | ||
209 | + default = 0, | ||
210 | + heartWarning = 1, | ||
211 | +} | ||
0 | \ No newline at end of file | 212 | \ No newline at end of file |
@@ -0,0 +1,260 @@ | @@ -0,0 +1,260 @@ | ||
1 | +-- 协议号 | ||
2 | +actionCodes = { | ||
3 | + Sys_heartBeat = 1, | ||
4 | + Sys_errorMsg = 3, | ||
5 | + Sys_innerErrorMsg = 4, | ||
6 | + Sys_commonNotice = 5, | ||
7 | + Sys_maintainNotice = 6, | ||
8 | + Sys_kickdown = 7, | ||
9 | + Sys_runningHorse = 8, | ||
10 | + | ||
11 | + Gm_clientRequest = 20, | ||
12 | + Gm_receiveResponse = 21, | ||
13 | + | ||
14 | + Role_notifyNewEvent = 100, | ||
15 | + Role_queryLoginRpc = 101, | ||
16 | + Role_createRpc = 102, | ||
17 | + Role_loginRpc = 103, | ||
18 | + Role_updateProperty = 105, | ||
19 | + Role_updateProperties = 106, | ||
20 | + Role_changeFormationRpc = 107, | ||
21 | + Role_setCrownRpc = 108, | ||
22 | + Role_formationPosRpc = 109, | ||
23 | + Role_entrustRpc = 110, | ||
24 | + Role_finishEntrustRpc = 111, | ||
25 | + Role_updateStoryBook = 112, | ||
26 | + Role_finishTalkRpc = 113, | ||
27 | + Role_updateHeroBook = 114, | ||
28 | + Role_guideRpc = 115, | ||
29 | + Role_changeAutoSateRpc = 116, | ||
30 | + Role_signRpc = 117, | ||
31 | + Role_dailyTaskRpc = 118, | ||
32 | + Role_mainTaskRpc = 119, | ||
33 | + Role_missionRpc = 120, | ||
34 | + Role_formationQuickRpc = 121, | ||
35 | + Role_changeNameRpc = 122, | ||
36 | + Role_changeHomeBgRpc = 123, | ||
37 | + Role_changeHeadIconRpc = 124, | ||
38 | + Role_changeHeadFrameRpc = 125, | ||
39 | + Role_cookNotesRpc = 126, | ||
40 | + Role_cookAddFavoritesRpc = 127, | ||
41 | + Role_cookDelFavoritesRpc = 128, | ||
42 | + Role_drawCodeRpc = 129, | ||
43 | + Role_chat = 130, | ||
44 | + Role_signGiftRpc = 131, | ||
45 | + Role_changeToRealUserRpc = 132, | ||
46 | + Role_cafeSignRpc = 133, | ||
47 | + Role_syncTimeRpc = 134, | ||
48 | + Role_getLevelRankRpc = 135, | ||
49 | + Role_deletAccountRpc = 136, | ||
50 | + Role_getItemRankRpc = 137, | ||
51 | + | ||
52 | + Hero_loadInfos = 201, | ||
53 | + Hero_updateProperty = 202, | ||
54 | + Hero_decomposeRpc = 203, | ||
55 | + Hero_qualityRpc = 204, | ||
56 | + Hero_strengthRpc = 205, | ||
57 | + Hero_lockRpc = 206, | ||
58 | + Hero_researchRpc = 207, | ||
59 | + Hero_finishResearchRpc = 208, | ||
60 | + Hero_treatRpc = 209, | ||
61 | + Hero_finishTreatRpc = 210, | ||
62 | + Hero_loveItemRpc = 211, | ||
63 | + Hero_finishLoveTaskRpc = 212, | ||
64 | + Hero_changeDressRpc = 213, | ||
65 | + Hero_quickTreatRpc = 214, | ||
66 | + Hero_skillUpRpc = 215, | ||
67 | + Hero_changeNameRpc = 216, | ||
68 | + Hero_likeHeroRpc = 217, | ||
69 | + Hero_commentHeroRpc = 218, | ||
70 | + Hero_getCommentsRpc = 219, | ||
71 | + Hero_likeCommentRpc = 220, | ||
72 | + | ||
73 | + | ||
74 | + | ||
75 | + Item_updateProperty = 301, | ||
76 | + | ||
77 | + Store_produceRpc = 401, | ||
78 | + Store_finishBuildRpc = 402, | ||
79 | + Store_diamondBuyRpc = 403, | ||
80 | + Store_itemBuyRpc = 404, | ||
81 | + Store_rechargeRpc = 405, | ||
82 | + Store_ayncPurchaseRpc = 406, | ||
83 | + Store_purchaseOrderResult = 407, | ||
84 | + Store_iapPurchaseRpc = 408, | ||
85 | + Store_iapCancelPurchase = 409, | ||
86 | + Store_restorePurchaseRpc = 410, | ||
87 | + Store_googlePurchaseRpc = 411, | ||
88 | + Store_googleCancelPurchase = 412, | ||
89 | + Store_samsungPurchaseRpc = 413, | ||
90 | + Store_samsungCancelPurchase = 414, | ||
91 | + | ||
92 | + Trade_getInfoRpc = 450, | ||
93 | + Trade_sellRpc = 451, | ||
94 | + Trade_buyRpc = 452, | ||
95 | + Trade_giveUpRpc = 453, | ||
96 | + Trade_cleanBuyRpc = 454, | ||
97 | + | ||
98 | + Carbon_arrangeCarbonRpc = 500, | ||
99 | + Carbon_moveRpc = 501, | ||
100 | + Carbon_actionEndRpc = 502, | ||
101 | + Carbon_beginGameRpc = 503, | ||
102 | + Carbon_endGameRpc = 504, | ||
103 | + Carbon_cancelMoveRpc = 505, | ||
104 | + Carbon_updateProperty = 506, | ||
105 | + Carbon_endCarbonRpc = 507, | ||
106 | + Carbon_exitTeamRpc = 508, | ||
107 | + Carbon_supplyRpc = 509, | ||
108 | + Map_updateProperty = 510, | ||
109 | + Carbon_changePosRpc = 511, | ||
110 | + Carbon_generateBoss = 512, | ||
111 | + Carbon_givpUpBoss = 513, | ||
112 | + Carbon_drawBossAward = 514, | ||
113 | + Carbon_SelectRpc = 515, | ||
114 | + | ||
115 | + Friend_updateProperty = 550, | ||
116 | + Friend_listRpc = 551, | ||
117 | + Friend_searchRpc = 552, | ||
118 | + Friend_randomRpc = 553, | ||
119 | + Friend_deleteRpc = 554, | ||
120 | + Friend_applyRpc = 555, | ||
121 | + Friend_applyListRpc = 556, | ||
122 | + Friend_handleApplyRpc = 557, | ||
123 | + Friend_report = 558, | ||
124 | + | ||
125 | + Farm_updateProperty = 600, | ||
126 | + Farm_drawMaterial = 601, | ||
127 | + Farm_levelUpBuilding = 602, | ||
128 | + Farm_changeShowMedal = 603, | ||
129 | + Farm_plantRpc = 604, | ||
130 | + Farm_useItemRpc = 605, | ||
131 | + Farm_getPlantRpc = 606, | ||
132 | + Farm_farmInfoRpc = 607, | ||
133 | + Farm_changeFarmerRpc = 608, | ||
134 | + | ||
135 | + Tower_updateProperty = 630, | ||
136 | + Tower_battleBeginRpc = 631, | ||
137 | + Tower_battleEndRpc = 632, | ||
138 | + Tower_resetRpc = 633, | ||
139 | + Tower_cureRpc = 634, | ||
140 | + Tower_changeFormatRpc = 635, | ||
141 | + Tower_getRankRpc = 636, | ||
142 | + Tower_formatQuickRpc = 637, | ||
143 | + Tower_formationPosRpc = 638, | ||
144 | + Tower_breakRpc = 639, | ||
145 | + | ||
146 | + Email_listRpc = 650, | ||
147 | + Email_drawAttachRpc = 651, | ||
148 | + Email_checkRpc = 652, | ||
149 | + Email_delRpc = 653, | ||
150 | + Email_drawAllAttachRpc = 654, | ||
151 | + | ||
152 | + Pvp_updateProperty = 670, | ||
153 | + Pvp_battleBeginRpc = 671, | ||
154 | + Pvp_battleEndRpc = 672, | ||
155 | + Pvp_resetRpc = 673, | ||
156 | + Pvp_enterPvpRpc = 674, | ||
157 | + Pvp_getRankRpc = 675, | ||
158 | + Pvp_changeFormatRpc = 676, | ||
159 | + Pvp_formatQuickRpc = 677, | ||
160 | + Pvp_formationPosRpc = 678, | ||
161 | + Pvp_skillOrderRpc = 679, | ||
162 | + Pvp_shopBuyRpc = 680, | ||
163 | + | ||
164 | + Equip_updateProperty = 750, | ||
165 | + Equip_wearEquipRpc = 751, | ||
166 | + Equip_repairEquipRpc = 752, | ||
167 | + Equip_forgeEquipAttrsRpc = 753, | ||
168 | + Equip_replaceEquipAttrRpc = 754, | ||
169 | + Equip_buildEquipRpc = 755, | ||
170 | + Equip_finishBuildEquipRpc = 756, | ||
171 | + Equip_decomposEquipRpc = 757, | ||
172 | + Equip_resetEquipRpc = 758, | ||
173 | + Equip_upEquipRpc = 759, | ||
174 | + | ||
175 | + Activity_getRewardRpc = 801, | ||
176 | + Activity_lotteryGiftRpc = 802, | ||
177 | + Activity_shopBuyRpc = 803, | ||
178 | + Activity_puzzleRewardRpc = 804, | ||
179 | + Activity_inheritRewardRpc = 805, | ||
180 | + Activity_midAutRewardRpc = 806, | ||
181 | + Activity_clearRed = 807, | ||
182 | + Activity_halloweenMoveRpc = 808, | ||
183 | + Activity_halloweenResetRpc = 809, | ||
184 | + Activity_halloweenRewardRpc = 810, | ||
185 | + Activity_traditionalRewardRpc = 811, | ||
186 | + Activity_africanRpc = 812, | ||
187 | + Activity_oldBackRpc = 813, | ||
188 | + Activity_orderRewardRpc = 814, | ||
189 | + Activity_cookHeroRpc = 815, | ||
190 | + | ||
191 | + Moon_arrangeCarbonRpc = 850, | ||
192 | + Moon_moveRpc = 851, | ||
193 | + Moon_actionEndRpc = 852, | ||
194 | + Moon_beginGameRpc = 853, | ||
195 | + Moon_endGameRpc = 854, | ||
196 | + Moon_cancelMoveRpc = 855, | ||
197 | + Moon_updateProperty = 856, | ||
198 | + Moon_endCarbonRpc = 857, | ||
199 | + Moon_exitTeamRpc = 858, | ||
200 | + Moon_supplyRpc = 859, | ||
201 | + Moon_changePosRpc = 860, | ||
202 | + Moon_generateBoss = 861, | ||
203 | + Moon_givpUpBoss = 862, | ||
204 | + Moon_drawBossAward = 863, | ||
205 | + Moon_SelectRpc = 864, | ||
206 | + | ||
207 | + Paradise_arrangeCarbonRpc = 875, | ||
208 | + Paradise_moveRpc = 876, | ||
209 | + Paradise_actionEndRpc = 877, | ||
210 | + Paradise_beginGameRpc = 878, | ||
211 | + Paradise_endGameRpc = 879, | ||
212 | + Paradise_cancelMoveRpc = 880, | ||
213 | + Paradise_updateProperty = 881, | ||
214 | + Paradise_endCarbonRpc = 882, | ||
215 | + Paradise_exitTeamRpc = 883, | ||
216 | + Paradise_supplyRpc = 884, | ||
217 | + Paradise_changePosRpc = 885, | ||
218 | + Paradise_SelectRpc = 886, | ||
219 | + Paradise_buyCountRpc = 887, | ||
220 | + | ||
221 | + Diner_loadRpc = 900, | ||
222 | + Diner_updateProperty = 901, | ||
223 | + Diner_updateProperties = 902, | ||
224 | + Diner_accessoryUpdateProperty = 903, | ||
225 | + Diner_itemUpdateProperty = 904, | ||
226 | + Diner_wearAccessoryRpc = 905, | ||
227 | + Diner_upLevelAccessoryRpc = 906, | ||
228 | + Diner_supplyMaterialRpc = 907, | ||
229 | + Diner_changeCarRpc = 908, | ||
230 | + Diner_recycleAccessoryRpc = 909, | ||
231 | + Diner_heroToBoxRpc = 910, | ||
232 | + Diner_saveBoxFitmentRpc = 911, | ||
233 | + Diner_recycleFitmentsRpc = 912, | ||
234 | + Diner_sellRpc = 913, | ||
235 | + Diner_finishSellRpc = 914, | ||
236 | + Diner_shopBuyRpc = 915, | ||
237 | + Diner_drawRewardRpc = 916, | ||
238 | + Diner_talentUpdateProperty = 917, | ||
239 | + | ||
240 | + Diner_likeOtherBoxRpc = 918, | ||
241 | + Diner_comfortRankRpc = 919, | ||
242 | + Diner_nearLikeMeRpc = 920, | ||
243 | + Diner_friendListRpc = 921, | ||
244 | + Diner_getBoxDataRpc = 922, | ||
245 | + Diner_changeTalkRpc = 923, | ||
246 | + Diner_changeTalkBgRpc = 924, | ||
247 | +} | ||
248 | + | ||
249 | +rpcResponseBegin = 10000 | ||
250 | + | ||
251 | +actionHandlers = {} | ||
252 | +for key, value in pairs(actionCodes) do | ||
253 | + local suffix = string.sub(key, -3, -1) | ||
254 | + local handlerName = string.gsub(key, "_", ".") | ||
255 | + | ||
256 | + if suffix == "Rpc" then | ||
257 | + actionHandlers[value + rpcResponseBegin] = handlerName .. "Response" | ||
258 | + end | ||
259 | + actionHandlers[value] = string.gsub(key, "_", ".") | ||
260 | +end | ||
0 | \ No newline at end of file | 261 | \ No newline at end of file |
@@ -0,0 +1,41 @@ | @@ -0,0 +1,41 @@ | ||
1 | +-- role | ||
2 | +R_FARM_KEY = "role:%d:farm" | ||
3 | +R_TOWER_KEY = "role:%d:tower" | ||
4 | +R_COOKLOG_KEY = "role:%d:cooklog" | ||
5 | +R_TRADELOG_KEY = "role:%d:tradelog" | ||
6 | +R_PVP_KEY = "role:%d:pvp" | ||
7 | +R_DINER_KEY = "role:%d:diner" | ||
8 | +-- rank | ||
9 | +RANK_PVP = "rank:pvp" | ||
10 | +RANK_TRADE = "rank:trade" | ||
11 | +RANK_TOWER = "rank:tower" | ||
12 | +RANK_BOX = "rank:box" -- 盒子舒适度排行榜 | ||
13 | +MAP_LIKE = "map:box:like" --点赞个数 | ||
14 | +RANK_LEVEL = "rank:level" --等级排名 | ||
15 | +RANK_ITEM = "rank:item" --活动物品排行 | ||
16 | +-- 日志 | ||
17 | +NOTE_COOK_KEY = "note:cook:%d" | ||
18 | + | ||
19 | +TRADE_KEY = "trade:%d" | ||
20 | +TRADE_ID_KEY = "tradeIDs" | ||
21 | + | ||
22 | +TASK_ACTIVE = "task:%d:active" -- 记录激活的任务 | ||
23 | +TASK_FINISH = "task:%d:finish" -- 记录完成的任务 | ||
24 | + | ||
25 | +BOSS_SET = "boss:%d:%d" | ||
26 | +BOSS_INFO = "boss:battle" | ||
27 | + | ||
28 | +FRIEND_KEY = "role:%d:friend" --哈希表 | ||
29 | +FRIEND_APPLY_KEY = "role:%d:apply" -- set | ||
30 | +FRIEND_DINER_LIKE_KEY = "role:%d:diner:like" -- list | ||
31 | + | ||
32 | +UNION_SET = "global:union" | ||
33 | +UNION_KEY = "union:%d" | ||
34 | +UNION_ROLE = "union:%d:%d" | ||
35 | +UNION_ROLE_SET = "union:%d:roleList" | ||
36 | +UNION_APPLY_SET = "union:%d:applyList" | ||
37 | + | ||
38 | +PVP_KING = "pvp:king" --王者传奇组 | ||
39 | +PVP_HONOR = "pvp:honor" --荣耀咸鱼组 | ||
40 | +PVP_NOOB = "pvp:noob" --菜鸟组 | ||
41 | +PVP_INFO = "pvp:info" --玩家信息组 | ||
0 | \ No newline at end of file | 42 | \ No newline at end of file |
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +local _M = {} | ||
2 | +local redisproxy = redisproxy | ||
3 | +function _M.clientRequest(agent, data) | ||
4 | + local msg = MsgPack.unpack(data) | ||
5 | + local role = agent.role | ||
6 | + local action = _M[msg.cmd] | ||
7 | + local bin = MsgPack.pack({ cmd = "指令失败" }) | ||
8 | + if not action then | ||
9 | + SendPacket(actionCodes.Gm_receiveResponse, bin) | ||
10 | + return true | ||
11 | + end | ||
12 | + local ret = action(role, msg) | ||
13 | + bin = MsgPack.pack({ cmd = ret }) | ||
14 | + SendPacket(actionCodes.Gm_receiveResponse, bin) | ||
15 | + return true | ||
16 | +end | ||
17 | + | ||
18 | +return _M | ||
0 | \ No newline at end of file | 19 | \ No newline at end of file |
@@ -0,0 +1,65 @@ | @@ -0,0 +1,65 @@ | ||
1 | +local string_format = string.format | ||
2 | +local mcast_util = mcast_util | ||
3 | +local gmFuncs = require "actions.GmAction" | ||
4 | + | ||
5 | +local function proc_online(cmd, roleId, pms) | ||
6 | + local agent = datacenter.get("agent", roleId) | ||
7 | + if agent then | ||
8 | + local ok, result = pcall(skynet.call, agent.serv, "lua", cmd, pms) | ||
9 | + return ok and result or "指令在线失败" | ||
10 | + end | ||
11 | + return "not_online" | ||
12 | +end | ||
13 | + | ||
14 | +local _M = {} | ||
15 | +local __const = {["id"]=1,["pm1"]=1,["pm2"]=1,["pm3"]=1} | ||
16 | + | ||
17 | +local _T = setmetatable({}, {__index = function(_, k) | ||
18 | + return function (pms) | ||
19 | + for k, v in pairs(pms) do | ||
20 | + -- tonum(v, v) 能tonumber 则 转换,不能则返回原来的值 | ||
21 | + pms[k] = __const[k] and tonum(v, v) or v | ||
22 | + end | ||
23 | + -- 群体操作 | ||
24 | + if not pms.id or pms.id == 0 then | ||
25 | + return _M[k] and _M[k](pms) or "指令不存在" | ||
26 | + end | ||
27 | + -- 个体操作 | ||
28 | + if not gmFuncs[k] then return "指令不存在" end | ||
29 | + -- 在线操作 | ||
30 | + local isOn = proc_online(k, pms.id, pms) | ||
31 | + if isOn ~= "not_online" then | ||
32 | + return isOn | ||
33 | + end | ||
34 | + -- 如果_M有直接操作,跳过load角色 | ||
35 | + if _M[k] then return _M[k](pms) end | ||
36 | + -- 离线操作 | ||
37 | + local role = require("models.Role").new({key = string_format("role:%d", pms.id)}) | ||
38 | + local ret = role:load() | ||
39 | + if not ret then | ||
40 | + return "角色不存在" | ||
41 | + end | ||
42 | + role:loadAll() | ||
43 | + return gmFuncs[k](role, pms) | ||
44 | + end | ||
45 | +end}) | ||
46 | + | ||
47 | +-- 在线广播 | ||
48 | +function _M.broadcast(pms) | ||
49 | + local bin = MsgPack.pack({body = pms.pm2}) | ||
50 | + local codes = { | ||
51 | + ["common"] = actionCodes.Sys_commonNotice, | ||
52 | + ["maintain"] = actionCodes.Sys_maintainNotice, | ||
53 | + } | ||
54 | + if not codes[pms.pm1] then return "错误" end | ||
55 | + | ||
56 | + mcast_util.pub_world(codes[pms.pm1], bin) | ||
57 | + return "广播成功" | ||
58 | +end | ||
59 | + | ||
60 | +function _M.online(pms) | ||
61 | + local count = datacenter.get("onlineCount") or 0 | ||
62 | + return count | ||
63 | +end | ||
64 | + | ||
65 | +return _T | ||
0 | \ No newline at end of file | 66 | \ No newline at end of file |
@@ -0,0 +1,319 @@ | @@ -0,0 +1,319 @@ | ||
1 | +local ipairs = ipairs | ||
2 | +local table = table | ||
3 | +local math = math | ||
4 | +local next = next | ||
5 | +local string = string | ||
6 | +local redisproxy = redisproxy | ||
7 | +local MsgPack = MsgPack | ||
8 | +local getRandomName = getRandomName | ||
9 | +local mcast_util = mcast_util | ||
10 | +local string_format = string.format | ||
11 | +local tonumber = tonumber | ||
12 | +local require = require | ||
13 | +local table_insert = table.insert | ||
14 | +local tconcat = table.concat | ||
15 | +local httpc = require("http.httpc") | ||
16 | + | ||
17 | +local WAVE_HERO_NUMS = 150 | ||
18 | +local WAVE_EQUIP_NUMS = 150 | ||
19 | + | ||
20 | +local function validName(name) | ||
21 | + name = string.upper(name) | ||
22 | + local exist = redisproxy:exists(string_format("user:%s", name)) | ||
23 | + if exist then return "existed" end | ||
24 | + | ||
25 | + local SERV = string_format("NAMED%d", math.random(1, 5)) | ||
26 | + local legal = skynet.call(SERV, "lua", "check", name) | ||
27 | + return legal and "ok" or "illegal" | ||
28 | +end | ||
29 | + | ||
30 | +-- 随机玩家名 | ||
31 | +local function randomRoleName() | ||
32 | + -- 过滤已经存在的名字 | ||
33 | + local name | ||
34 | + repeat | ||
35 | + name = getRandomName() | ||
36 | + until validName(name) == "ok" | ||
37 | + return name | ||
38 | +end | ||
39 | + | ||
40 | +local function setRoleName(uid, roleId) | ||
41 | + local result | ||
42 | + local name | ||
43 | + local dbName | ||
44 | + repeat | ||
45 | + name = randomRoleName() | ||
46 | + dbName = string.upper(name) | ||
47 | + result = redisproxy:setnx(string_format("user:%s", dbName), roleId) | ||
48 | + until result == 1 | ||
49 | + redisproxy:set(string_format("uid:%s", uid), dbName) | ||
50 | + return name | ||
51 | +end | ||
52 | + | ||
53 | +local _M = {} | ||
54 | +function _M.loginRpc( agent, data ) | ||
55 | + local msg = MsgPack.unpack(data) | ||
56 | + local response = {} | ||
57 | + | ||
58 | + if msg.version ~= globalCsv.version then | ||
59 | + response.result = "UPDATE_TIP" | ||
60 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response)) | ||
61 | + return true | ||
62 | + end | ||
63 | + | ||
64 | + -- 1. | ||
65 | + local roleId = redisproxy:get(string_format("user:%s", string.upper(msg.name))) | ||
66 | + if not roleId then | ||
67 | + response.result = "NOT_EXIST" | ||
68 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response)) | ||
69 | + return true | ||
70 | + end | ||
71 | + | ||
72 | + roleId = tonumber(roleId) | ||
73 | + | ||
74 | + --维护不能登录 | ||
75 | + local maintain = tonumber(redisproxy:hget("autoincrement_set", "maintain")) | ||
76 | + if maintain and maintain > 0 then | ||
77 | + if tonumber(redisproxy:hget(string_format("role:%d", roleId), "ignoreMaintain")) ~= 1 then | ||
78 | + response.result = "MAINTAIN_TIP" | ||
79 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response)) | ||
80 | + return true | ||
81 | + end | ||
82 | + end | ||
83 | + | ||
84 | + local now = skynet.timex() | ||
85 | + local role = agent.role | ||
86 | + -- 2 | ||
87 | + if not role then | ||
88 | + local roleKey = string_format("role:%d", roleId) | ||
89 | + if not redisproxy:exists(roleKey) then | ||
90 | + response.result = "DB_ERROR" | ||
91 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response)) | ||
92 | + return true | ||
93 | + end | ||
94 | + -- 2a | ||
95 | + role = require("models.Role").new({key = roleKey}) | ||
96 | + role:load() | ||
97 | + role:loadAll() | ||
98 | + else | ||
99 | + role:reloadWhenLogin() | ||
100 | + end | ||
101 | + | ||
102 | + if not msg.isGMlogin then | ||
103 | + local banTime = role:getProperty("banTime") | ||
104 | + if banTime > now then | ||
105 | + response.result = "BAN_TIP" | ||
106 | + response.banTime = banTime | ||
107 | + response.banType = role:getProperty("banType") | ||
108 | + response.roleId = roleId | ||
109 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response)) | ||
110 | + return true | ||
111 | + end | ||
112 | + if banTime ~= 0 then | ||
113 | + -- 清除封号状态 | ||
114 | + role:setBan(0) | ||
115 | + end | ||
116 | + end | ||
117 | + SERV_OPEN = redisproxy:hget("autoincrement_set", "server_start") | ||
118 | + local lastLoginTime = role:getProperty("lastLoginTime") | ||
119 | + | ||
120 | + -- 跨天登陆事件 | ||
121 | + if not role:onCrossDay(now) then | ||
122 | + role:checkActivityStatus(lastLoginTime, now) | ||
123 | + if role:getProperty("carbonDouble") == "" then | ||
124 | + role:initCarbonDouble() | ||
125 | + end | ||
126 | + end | ||
127 | + -- 登陆回复 | ||
128 | + role:onRecoverLogin(now) | ||
129 | + -- 引导是否连续 | ||
130 | + role:checkGuide() | ||
131 | + | ||
132 | + | ||
133 | + role:setProperty("lastLoginTime", now) | ||
134 | + if msg.device and type(msg.device) == "string" then | ||
135 | + role:setProperty("device", msg.device) | ||
136 | + end | ||
137 | + | ||
138 | + response.role = role:data() | ||
139 | + response.result = "SUCCESS" | ||
140 | + response.serverTime = now | ||
141 | + | ||
142 | + -- 需要加载模块数据 | ||
143 | + local modules = { | ||
144 | + "carbons","maps", | ||
145 | + } | ||
146 | + | ||
147 | + local heroIds = {} | ||
148 | + for heroId, _ in pairs(role.heros) do | ||
149 | + table.insert(heroIds, heroId) | ||
150 | + end | ||
151 | + local heroWave = math.ceil(#heroIds / WAVE_HERO_NUMS) | ||
152 | + | ||
153 | + local equipIds = {} | ||
154 | + for equipId, _ in pairs(role.equips) do | ||
155 | + table.insert(equipIds, equipId) | ||
156 | + end | ||
157 | + local equipWave = math.ceil(#equipIds / WAVE_EQUIP_NUMS) | ||
158 | + | ||
159 | + if #heroIds <= 50 then | ||
160 | + heroWave = 0 | ||
161 | + table_insert(modules, "heros") | ||
162 | + end | ||
163 | + if #equipIds <= 50 then | ||
164 | + equipWave = 0 | ||
165 | + table_insert(modules, "equips") | ||
166 | + end | ||
167 | + | ||
168 | + for _, name in ipairs(modules) do | ||
169 | + response[name] = {} | ||
170 | + for id, unit in pairs(role[name]) do | ||
171 | + response[name][id] = unit:data() | ||
172 | + end | ||
173 | + end | ||
174 | + response.wave = 1 + heroWave + equipWave | ||
175 | + | ||
176 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(response)) | ||
177 | + | ||
178 | + local heroIndex = 1 | ||
179 | + for index = 2, 1 + heroWave do | ||
180 | + local heroResponse = {heros = {}} | ||
181 | + for i = heroIndex, heroIndex + WAVE_HERO_NUMS do | ||
182 | + local heroId = heroIds[i] | ||
183 | + if not heroId then | ||
184 | + break | ||
185 | + end | ||
186 | + local hero = role.heros[heroId] | ||
187 | + table_insert(heroResponse.heros, hero:data()) | ||
188 | + heroIndex = heroIndex + 1 | ||
189 | + end | ||
190 | + heroResponse.heroWave = index | ||
191 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(heroResponse)) | ||
192 | + end | ||
193 | + local equipIndex = 1 | ||
194 | + for index = 2 + heroWave, 1 + heroWave + equipWave do | ||
195 | + local equipResponse = {equips = {}} | ||
196 | + for i = equipIndex, equipIndex + WAVE_EQUIP_NUMS do | ||
197 | + local equipId = equipIds[i] | ||
198 | + if not equipId then | ||
199 | + break | ||
200 | + end | ||
201 | + local equip = role.equips[equipId] | ||
202 | + table_insert(equipResponse.equips, equip:data()) | ||
203 | + equipIndex = equipIndex + 1 | ||
204 | + end | ||
205 | + equipResponse.equipWave = index | ||
206 | + SendPacket(actionCodes.Role_loginRpc, MsgPack.pack(equipResponse)) | ||
207 | + end | ||
208 | + | ||
209 | + -- role:log("login", { ip = agent.ip, diamond = role:getProperty("diamond"), reDiamond = role:getProperty("reDiamond")}) | ||
210 | + | ||
211 | + datacenter.set("agent", roleId, { | ||
212 | + serv = skynet.self(), | ||
213 | + fd = agent.client_fd, | ||
214 | + gate_serv = agent.gate_serv, | ||
215 | + }) | ||
216 | + agent.role = role | ||
217 | + role.lessmsg = false | ||
218 | + | ||
219 | + start_agent_timer() | ||
220 | + -- 注册全服广播 | ||
221 | + local channel = math.randomInt(1, 1) | ||
222 | + local w_channel = datacenter.get( ("MC_W_CHANNEL" .. channel) ) | ||
223 | + if w_channel then | ||
224 | + mcast_util.sub_world(w_channel) | ||
225 | + end | ||
226 | + return true | ||
227 | +end | ||
228 | + | ||
229 | +function _M.createRpc(agent, data) | ||
230 | + local msg = MsgPack.unpack(data) | ||
231 | + local response = {} | ||
232 | + | ||
233 | + -- 再次检查uid | ||
234 | + local uid = tostring(msg.uid) | ||
235 | + local user = redisproxy:get(string_format("uid:%s", uid)) | ||
236 | + if user then | ||
237 | + response.result = "SUCCESS" | ||
238 | + response.roleName = user | ||
239 | + SendPacket(actionCodes.Role_createRpc, MsgPack.pack(response)) | ||
240 | + return true | ||
241 | + end | ||
242 | + | ||
243 | + local roleId = getNextRoleId() | ||
244 | + if not roleId then | ||
245 | + response.result = "DB_FULL" | ||
246 | + SendPacket(actionCodes.Role_createRpc, MsgPack.pack(response)) | ||
247 | + return true | ||
248 | + end | ||
249 | + local roleName = setRoleName(msg.uid, roleId) | ||
250 | + | ||
251 | + local newRole = require("models.Role").new({ | ||
252 | + key = string_format("role:%d", roleId), | ||
253 | + id = roleId, | ||
254 | + uid = tostring(msg.uid), | ||
255 | + subId = msg.subId or 0, | ||
256 | + name = roleName, | ||
257 | + uname = msg.uname or "", | ||
258 | + device = tostring(msg.device) | ||
259 | + }) | ||
260 | + | ||
261 | + if newRole:create() then | ||
262 | + --更新USER表 | ||
263 | + response.result = "SUCCESS" | ||
264 | + response.roleId = roleId | ||
265 | + response.roleName = string.upper(roleName) | ||
266 | + else | ||
267 | + response.result = "DB_ERROR" | ||
268 | + SendPacket(actionCodes.Role_createRpc, MsgPack.pack(response)) | ||
269 | + return true | ||
270 | + end | ||
271 | + | ||
272 | + -- 给角色自动加载当前副本数据 | ||
273 | + newRole:addCarbon({carbonId = 10101, status = 0, starNum = 0}) | ||
274 | + -- 给角色自动加载农场信息 | ||
275 | + newRole:addFarm() | ||
276 | + -- 给角色自动加载爬塔信息 | ||
277 | + newRole:addTower() | ||
278 | + -- 给角色增加pvpInfo | ||
279 | + newRole:addPvpInfo() | ||
280 | + --给新角色增加diner | ||
281 | + newRole:addDiner() | ||
282 | + -- 关闭 锁定新食灵 | ||
283 | + local autoStatus = newRole:getProperty("autoStatus") | ||
284 | + newRole:setProperty("autoStatus", autoStatus:setv(3,1)) | ||
285 | + | ||
286 | + for index, hero in ipairs(globalCsv["birthHero"]) do | ||
287 | + hero.notNotify = true | ||
288 | + hero.desc = "birth_award" | ||
289 | + local heroId = newRole:awardHero(hero.type, hero) | ||
290 | + if index == 1 then | ||
291 | + local newHero = newRole.heros[heroId] | ||
292 | + newHero:setProperty("lock", 1) | ||
293 | + newHero:setProperty("formation", 1) | ||
294 | + newRole:setProperty("crown", heroId) | ||
295 | + newRole:setProperty("formationJson", json.encode({["1"] = {list = {["1"] = heroId}, pos = {["1"] = heroId}, lock = {}}})) | ||
296 | + end | ||
297 | + end | ||
298 | + -- 出生道具 | ||
299 | + local ucode = getActionCode(newRole) | ||
300 | + for id, num in pairs(globalCsv["birthItem"]:toNumMap()) do | ||
301 | + newRole:awardItemCsv(id, {count = num, desc = "birth_award", notNotify = true, ucode = ucode}) | ||
302 | + end | ||
303 | + | ||
304 | + -- 欢迎邮件 | ||
305 | + -- redisproxy:insertEmail({roleId = roleId, emailId = 1}) | ||
306 | + -- redisproxy:insertEmail({roleId = roleId, emailId = 2}) | ||
307 | + | ||
308 | + newRole:log("create", { ip = agent.ip, ucode = ucode}) | ||
309 | + | ||
310 | + SendPacket(actionCodes.Role_createRpc, MsgPack.pack(response)) | ||
311 | + return true | ||
312 | +end | ||
313 | + | ||
314 | +function _M.syncTimeRpc(agent, data) | ||
315 | + SendPacket(actionCodes.Role_syncTimeRpc, MsgPack.pack({nowTime = skynet.timex()})) | ||
316 | + return true | ||
317 | +end | ||
318 | + | ||
319 | +return _M | ||
0 | \ No newline at end of file | 320 | \ No newline at end of file |
@@ -0,0 +1,286 @@ | @@ -0,0 +1,286 @@ | ||
1 | +require "ProtocolCode" | ||
2 | +require "shared.init" | ||
3 | +require "utils.init" | ||
4 | +require "GlobalVar" | ||
5 | +require "RedisKeys" | ||
6 | +require "skynet.manager" | ||
7 | + | ||
8 | +local harbor = require "skynet.harbor" | ||
9 | +local queue = require "skynet.queue" | ||
10 | +local netpack = require "skynet.netpack" | ||
11 | +local socket = require "skynet.socket" | ||
12 | +local sharedata = require "skynet.sharedata" | ||
13 | +local xxtea = require "xxtea" | ||
14 | + | ||
15 | +skynet = require "skynet" | ||
16 | +redisproxy = require "shared.redisproxy" | ||
17 | +datacenter = require "skynet.datacenter" | ||
18 | +mcast_util = require "services/mcast_util" | ||
19 | +globalCsv = require "csvdata/GlobalDefine" | ||
20 | + | ||
21 | +local CMD = {} | ||
22 | +local agentInfo = {} -- { client_fd, role, gate_serv, open_timer} | ||
23 | + | ||
24 | +local agent_util, cs | ||
25 | + | ||
26 | +--- {{{ 定时器相关 | ||
27 | +local function handle_timeout() | ||
28 | + if not agentInfo.open_timer then return end | ||
29 | + | ||
30 | + if not agentInfo.role then | ||
31 | + skynet.timeout(100, handle_timeout) | ||
32 | + return | ||
33 | + end | ||
34 | + | ||
35 | + agent_util:update(agentInfo) | ||
36 | + skynet.timeout(100, handle_timeout) | ||
37 | +end | ||
38 | + | ||
39 | +function start_agent_timer() | ||
40 | + agentInfo.open_timer = true | ||
41 | + skynet.timeout(150, handle_timeout) | ||
42 | +end | ||
43 | + | ||
44 | +function cancel_agent_timer() | ||
45 | + agentInfo.open_timer = false | ||
46 | +end | ||
47 | +---- 定时器相关 }}} | ||
48 | + | ||
49 | +function SendPacket(actionCode, bin, client_fd) | ||
50 | + if #bin > 0 then bin = xxtea.encrypt(bin, XXTEA_KEY) end | ||
51 | + | ||
52 | + local handlerName = actionHandlers[actionCode] | ||
53 | + if string.sub(handlerName, -3, -1) == "Rpc" then | ||
54 | + actionCode = actionCode + rpcResponseBegin | ||
55 | + end | ||
56 | + | ||
57 | + local client_fd = client_fd or agentInfo.client_fd | ||
58 | + local head = string.pack("H", actionCode) | ||
59 | + return socket.write(client_fd, netpack.pack(head .. bin)) | ||
60 | +end | ||
61 | + | ||
62 | +function rpcAgent(roleId, funcName, ...) | ||
63 | + local agent = datacenter.get("agent", roleId) | ||
64 | + if agent then | ||
65 | + return skynet.call(agent.serv, "lua", funcName, ...) | ||
66 | + end | ||
67 | +end | ||
68 | + | ||
69 | +function rpcParter(serv, func, ...) | ||
70 | + if serv then | ||
71 | + local ok, result = pcall(skynet.call, serv, "role", func, ...) | ||
72 | + if ok then | ||
73 | + return result | ||
74 | + end | ||
75 | + end | ||
76 | +end | ||
77 | + | ||
78 | +local string_format = string.format | ||
79 | +local table_unpack = table.unpack | ||
80 | +function rpcRole(roleId, funcName, ...) | ||
81 | + local fields = ... | ||
82 | + local agent = datacenter.get("agent", roleId) | ||
83 | + if agent and agent.serv then | ||
84 | + if funcName == "getProperties" then | ||
85 | + return true, skynet.call(agent.serv, "role", funcName, fields) | ||
86 | + else | ||
87 | + return true, skynet.call(agent.serv, "role", funcName, ...) | ||
88 | + end | ||
89 | + else | ||
90 | + local rediskey = string_format("role:%d", roleId) | ||
91 | + if funcName == "setProperty" then | ||
92 | + return false, redisproxy:hset(rediskey, ...) | ||
93 | + elseif funcName == "getProperty" then | ||
94 | + return false, redisproxy:hget(rediskey, ...) | ||
95 | + elseif funcName == "getProperties" then | ||
96 | + local sRole = require("models.Role") | ||
97 | + local returnValue = redisproxy:hmget(rediskey, table_unpack(...)) | ||
98 | + local ret = {} | ||
99 | + for index, key in ipairs(fields) do | ||
100 | + local typ = sRole.schema[key][1] | ||
101 | + local def = sRole.schema[key][2] | ||
102 | + if typ == "number" then | ||
103 | + ret[key] = tonumber(returnValue[index] or def) | ||
104 | + else | ||
105 | + ret[key] = returnValue[index] | ||
106 | + end | ||
107 | + end | ||
108 | + return false, ret | ||
109 | + elseif funcName == "setProperties" then | ||
110 | + local result = {} | ||
111 | + for k,v in pairs(fields) do | ||
112 | + result[#result+1] = k | ||
113 | + result[#result+1] = v | ||
114 | + end | ||
115 | + return false, redisproxy:hmset(rediskey, table_unpack(result)) | ||
116 | + end | ||
117 | + end | ||
118 | +end | ||
119 | + | ||
120 | +function rpcUnion(funcName, ...) | ||
121 | + local serv = agentInfo.userv | ||
122 | + if not serv then | ||
123 | + local consortiaId = agentInfo.role:getProperty("consortiaId") | ||
124 | + if consortiaId == 0 then return true,1000 end | ||
125 | + local union = datacenter.get("union", consortiaId) | ||
126 | + if not union or not union.serv then | ||
127 | + skynet.error("rpcUnion error: union serv addr not exist", funcName, consortiaId) | ||
128 | + return | ||
129 | + end | ||
130 | + serv = union.serv | ||
131 | + end | ||
132 | + return skynet.call(serv, "lua", funcName, ...) | ||
133 | +end | ||
134 | + | ||
135 | +function rpcOtherUnion(id, funcName, ...) | ||
136 | + local union = datacenter.get("union", id) | ||
137 | + if union and union.serv then | ||
138 | + return skynet.call(union.serv, "lua", funcName, ...) | ||
139 | + end | ||
140 | +end | ||
141 | + | ||
142 | +skynet.register_protocol { | ||
143 | + name = "client", | ||
144 | + id = skynet.PTYPE_CLIENT, | ||
145 | + unpack = function (msg, sz) | ||
146 | + local data = skynet.tostring(msg, sz) | ||
147 | + local cmd = string.unpack("H", string.sub(data, 1, 2)) | ||
148 | + return cmd, string.sub(data, 3) | ||
149 | + end, | ||
150 | + dispatch = function(session, address, cmd, data) | ||
151 | + cs(function() | ||
152 | + if cmd == actionCodes.Sys_heartBeat then | ||
153 | + agent_util:heart_beat(agentInfo) | ||
154 | + return | ||
155 | + end | ||
156 | + local actionName = actionHandlers[cmd] | ||
157 | + if not actionName then | ||
158 | + print("actionName not exist", actionName) | ||
159 | + return | ||
160 | + end | ||
161 | + local modName, funcName = actionName:match("(%w+)%.(%w+)") | ||
162 | + | ||
163 | + local ok, action = pcall(require, "actions." .. modName .. "Action") | ||
164 | + if not ok then | ||
165 | + print("require module name error", action, modName) | ||
166 | + return | ||
167 | + end | ||
168 | + | ||
169 | + local method = action[funcName] | ||
170 | + | ||
171 | + if type(method) ~= "function" then | ||
172 | + print("ERROR_SERVER_INVALID_ACTION", modName, funcName) | ||
173 | + return | ||
174 | + end | ||
175 | + | ||
176 | + if #data > 0 then data = xxtea.decrypt(data, XXTEA_KEY) end | ||
177 | + local result = method(agentInfo, data) | ||
178 | + if not result then | ||
179 | + SendPacket(actionCodes.Sys_innerErrorMsg, MsgPack.pack({id = cmd})) | ||
180 | + end | ||
181 | + end) | ||
182 | + end | ||
183 | +} | ||
184 | + | ||
185 | +skynet.register_protocol { | ||
186 | + name = "role", | ||
187 | + id = 12, | ||
188 | + pack = skynet.pack, | ||
189 | + unpack = skynet.unpack, | ||
190 | + dispatch = function(session, address, submethod, ...) | ||
191 | + local result | ||
192 | + if not agentInfo.role then | ||
193 | + result = "__OFFLINE__" | ||
194 | + else | ||
195 | + result = agentInfo.role[submethod](agentInfo.role, ...) | ||
196 | + end | ||
197 | + | ||
198 | + skynet.ret(skynet.pack(result)) | ||
199 | + end, | ||
200 | +} | ||
201 | + | ||
202 | +-- function CMD.start(gate, fd, ip) | ||
203 | +function CMD.start(session, source, gate, fd, ip) | ||
204 | + ignoreHeartbeat = false | ||
205 | + | ||
206 | + agentInfo.client_fd = fd | ||
207 | + agentInfo.gate_serv = gate | ||
208 | + agentInfo.ip = ip | ||
209 | + | ||
210 | + agent_util:reset() | ||
211 | + math.randomInit() | ||
212 | + | ||
213 | + -- 这里将消息伪装成 watchdog 发出,这样就由 A->B->C->B->A 变成 A->B->C->A | ||
214 | + skynet.redirect(gate, source, "lua", session, skynet.pack("forward", fd, 0, skynet.self())) | ||
215 | +end | ||
216 | + | ||
217 | +function CMD.close() | ||
218 | + cancel_agent_timer() | ||
219 | + mcast_util.usub_world() | ||
220 | + mcast_util.usub_union() | ||
221 | + | ||
222 | + local role = agentInfo.role | ||
223 | + if not role then return end | ||
224 | + role:log("logout", {online = skynet.timex()-role:getProperty("lastLoginTime")}) | ||
225 | + role:onOfflineEvent() | ||
226 | +end | ||
227 | + | ||
228 | +function CMD.exit() | ||
229 | + if agentInfo.role then | ||
230 | + -- role:log("logout", {online = skynet.timex()-role:getProperty("lastLoginTime")}) | ||
231 | + datacenter.set("agent", agentInfo.role:getProperty("id"), nil) | ||
232 | + end | ||
233 | + skynet.exit() | ||
234 | +end | ||
235 | + | ||
236 | +function CMD.subUnion(consortiaId, union) | ||
237 | + mcast_util.sub_union(consortiaId, union.chan) | ||
238 | + agentInfo.userv = union.serv | ||
239 | +end | ||
240 | + | ||
241 | +function CMD:usubUnion() | ||
242 | + mcast_util.usub_union() | ||
243 | + agentInfo.userv = nil | ||
244 | +end | ||
245 | + | ||
246 | +local function routeGM(cmd, params) | ||
247 | + if type(params) ~= "table" or not agentInfo.role then | ||
248 | + return "指令失败" | ||
249 | + end | ||
250 | + local _M = require "actions.GmAction" | ||
251 | + return _M[cmd](agentInfo.role, params) | ||
252 | +end | ||
253 | + | ||
254 | +skynet.start(function() | ||
255 | + skynet.dispatch("lua", function(session, source, command, ...) | ||
256 | + local f = CMD[command] | ||
257 | + if f then | ||
258 | + if command == "exit" then | ||
259 | + f(...) | ||
260 | + elseif command == "start" then | ||
261 | + f(session, source, ...) | ||
262 | + else | ||
263 | + skynet.ret(skynet.pack(f(...))) | ||
264 | + end | ||
265 | + else | ||
266 | + skynet.ret(skynet.pack(routeGM(command, ...))) | ||
267 | + end | ||
268 | + end) | ||
269 | + | ||
270 | + redisd = harbor.queryname("REDIS") | ||
271 | + if tonumber(skynet.getenv "logd") == 1 then | ||
272 | + logd = harbor.queryname("LOGD") | ||
273 | + end | ||
274 | + | ||
275 | + cs = queue() | ||
276 | + | ||
277 | + -- csv | ||
278 | + csvdb = sharedata.query("csvdata") | ||
279 | + -- 错误码特殊处理 | ||
280 | + -- todo | ||
281 | + -- for key, value in pairs(csvdb["sys_codesCsv"]) do | ||
282 | + -- _G[string.upper(value.varname)] = key | ||
283 | + -- end | ||
284 | + | ||
285 | + agent_util = require "services/agent_util" | ||
286 | +end) |
@@ -0,0 +1,17 @@ | @@ -0,0 +1,17 @@ | ||
1 | +root = "./" | ||
2 | +thread = 8 | ||
3 | +logger = "server.log" | ||
4 | +harbor = 0 | ||
5 | +start = "main" -- main script | ||
6 | +bootstrap = "snlua bootstrap" -- The service for bootstrap | ||
7 | +logd = 0 -- 是否开启日志 | ||
8 | +servId = 1 | ||
9 | +baseId = 0 | ||
10 | +codeurl = "127.0.0.1:8686" | ||
11 | + | ||
12 | +lua_path = root .."skynet/lualib/?.lua;"..root.."src/?.lua;"..root.."tools/?.lua" | ||
13 | +luaservice = root.."skynet/service/?.lua;"..root.."src/?.lua" | ||
14 | +lualoader = "skynet/lualib/loader.lua" | ||
15 | +preload = "./src/preload.lua" -- run preload.lua before every lua service run | ||
16 | +cpath = root.."skynet/cservice/?.so" | ||
17 | +lua_cpath = "skynet/luaclib/?.so" | ||
0 | \ No newline at end of file | 18 | \ No newline at end of file |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +Subproject commit 3aabf5c43c36e6da7f63f237301b4772f0129e88 |
@@ -0,0 +1,29 @@ | @@ -0,0 +1,29 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | + | ||
3 | +local max_client = 64 | ||
4 | + | ||
5 | +skynet.start(function() | ||
6 | + print("Server start") | ||
7 | + skynet.newservice("console") | ||
8 | + skynet.newservice("debug_console", 3001) | ||
9 | + | ||
10 | + local ngxd = skynet.newservice("services/ngxd", 3002) | ||
11 | + local watchdog = skynet.newservice("services/watchdog", max_client) | ||
12 | + skynet.call(watchdog, "lua", "start", { | ||
13 | + port = 3003, | ||
14 | + maxclient = max_client, | ||
15 | + ngxd = ngxd, | ||
16 | + | ||
17 | + redishost = "127.0.0.1", | ||
18 | + redisport = 6379, | ||
19 | + redisdb = 4, | ||
20 | + auth = nil, | ||
21 | + | ||
22 | + mongohost = "127.0.0.1", | ||
23 | + mongoport = nil, | ||
24 | + mongouser = nil, | ||
25 | + mongopswd = nil, | ||
26 | + }) | ||
27 | + | ||
28 | + skynet.exit() | ||
29 | +end) |
@@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
1 | +local Role = class("Role", require("shared.ModelBase")) | ||
2 | + | ||
3 | +function Role:ctor( properties ) | ||
4 | + Role.super.ctor(self, properties) | ||
5 | + | ||
6 | + self.heros = {} | ||
7 | + self.ignoreHeartbeat = false | ||
8 | + | ||
9 | +end | ||
10 | + | ||
11 | +Role.schema = { | ||
12 | + key = {"string"}, | ||
13 | + id = {"number"}, | ||
14 | + uid = {"string", ""}, | ||
15 | + sid = {"number", 0}, | ||
16 | + name = {"string", ""}, | ||
17 | +} | ||
18 | + | ||
19 | +Role.fields = { | ||
20 | + id = true, | ||
21 | + uid = true, | ||
22 | + sid = true, | ||
23 | + name = true, | ||
24 | +} | ||
25 | + | ||
26 | +function Role:data() | ||
27 | + return { | ||
28 | + id = self:getProperty("id"), | ||
29 | + } | ||
30 | +end | ||
31 | + | ||
32 | +return Role | ||
0 | \ No newline at end of file | 33 | \ No newline at end of file |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +local _M = {} | ||
2 | + | ||
3 | +_M["rankDetails"] = { | ||
4 | + file = "src/rdsscripts/rankDetails.lua", | ||
5 | + sha1 = nil, | ||
6 | +} | ||
7 | +_M["insertEmail"] = { | ||
8 | + file = "src/rdsscripts/insertEmail.lua", | ||
9 | + sha1 = nil, | ||
10 | +} | ||
11 | +_M["refreshAssist"] = { | ||
12 | + file = "src/rdsscripts/refreshAssist.lua", | ||
13 | + sha1 = nil, | ||
14 | +} | ||
15 | +_M["assistInfo"] = { | ||
16 | + file = "src/rdsscripts/assistInfo.lua", | ||
17 | + sha1 = nil, | ||
18 | +} | ||
19 | + | ||
20 | +return _M | ||
0 | \ No newline at end of file | 21 | \ No newline at end of file |
@@ -0,0 +1,55 @@ | @@ -0,0 +1,55 @@ | ||
1 | +local roleId = tonumber(KEYS[1]) | ||
2 | + | ||
3 | +local formationJson = redis.call("hget", string.format("role:%d", roleId), "pveFormationJson") | ||
4 | + | ||
5 | +local formation = cjson.decode(formationJson) | ||
6 | + | ||
7 | +local function formatAttrEx(str) | ||
8 | + local tb = {} | ||
9 | + for k, v in str:gmatch("([%d.]+)=([%d.]+)") do | ||
10 | + tb[#tb+1] = {tonumber(k), tonumber(v)} | ||
11 | + end | ||
12 | + return tb | ||
13 | +end | ||
14 | + | ||
15 | +local function formatEquips(str) | ||
16 | + local tb = {} | ||
17 | + for k, v in str:gmatch("([%d.]+)=([%d.]+)") do | ||
18 | + tb[tonumber(k)] = tonumber(v) | ||
19 | + end | ||
20 | + return tb | ||
21 | +end | ||
22 | + | ||
23 | +local heroFields = {"type", "level", "star", "evolveCount", "wake", "breakLevel", "equips"} | ||
24 | +local equipFields = {"type", "level", "evolCount", "attrEx"} | ||
25 | +for _, hero in ipairs(formation.heros) do | ||
26 | + if hero.leader then | ||
27 | + local heroInfo = redis.call("hmget", string.format("hero:%d:%d", roleId, hero.id), unpack(heroFields)) | ||
28 | + local equipstr = heroInfo[7] | ||
29 | + local equips = {} | ||
30 | + for part, equipId in equipstr:gmatch("(%d+)=(%d+)") do | ||
31 | + part, equipId = tonumber(part), tonumber(equipId) | ||
32 | + if equipId ~= 0 then | ||
33 | + local equipInfo = redis.call("hmget", string.format("equip:%d:%d", roleId, equipId), unpack(equipFields)) | ||
34 | + equips[equipId] = {} | ||
35 | + for index, value in ipairs(equipInfo) do | ||
36 | + equips[equipId][equipFields[index]] = index ~= 4 and tonumber(value) or formatAttrEx(equipInfo[4] or "") | ||
37 | + end | ||
38 | + end | ||
39 | + end | ||
40 | + return cmsgpack.pack { | ||
41 | + type = tonumber(heroInfo[1]), | ||
42 | + level = tonumber(heroInfo[2]), | ||
43 | + star = tonumber(heroInfo[3]), | ||
44 | + evolveCount = tonumber(heroInfo[4]), | ||
45 | + wake = tonumber(heroInfo[5]), | ||
46 | + breakLevel = tonumber(heroInfo[6]), | ||
47 | + equips = formatEquips(heroInfo[7]), | ||
48 | + equipDtls = equips, | ||
49 | + } | ||
50 | + end | ||
51 | +end | ||
52 | + | ||
53 | +return cmsgpack.pack {} | ||
54 | + | ||
55 | + |
@@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
1 | +local con1 = KEYS[4] or "" | ||
2 | +local con2 = KEYS[5] or "" | ||
3 | +local con3 = KEYS[6] or "" | ||
4 | +local att1 = KEYS[7] or "" | ||
5 | +local att2 = KEYS[8] or "" | ||
6 | +local att3 = KEYS[9] or "" | ||
7 | +local title = KEYS[10] or "" | ||
8 | +local content = KEYS[11] or "" | ||
9 | +local attachments = KEYS[12] or "" | ||
10 | + | ||
11 | +-- local roleInfo = redis.call("HGET", string.format("role:%d", KEYS[1]), "delete") | ||
12 | + | ||
13 | +-- if tonumber(roleInfo) == 1 then return end | ||
14 | + | ||
15 | +local id = redis.call("HINCRBY", string.format("role:%d:autoincr", KEYS[1]), "email", 1) | ||
16 | +redis.call("LPUSH", string.format("role:%d:emailIds", KEYS[1]), id) | ||
17 | +local deleteIds = redis.call("LRANGE", string.format("role:%d:emailIds", KEYS[1]), 50, -1) | ||
18 | +for _, deleteId in ipairs(deleteIds) do | ||
19 | + redis.call("DEL", string.format("email:%d:%d", KEYS[1], deleteId)) | ||
20 | +end | ||
21 | + | ||
22 | +redis.call("LTRIM", string.format("role:%d:emailIds", KEYS[1]), 0, 49) | ||
23 | +redis.call("HMSET", string.format("email:%d:%d", KEYS[1], id), "id", tostring(id), "emailId", KEYS[2], | ||
24 | + "status", "0", "createtime", KEYS[3], | ||
25 | + "con1", con1, "con2", con2, "con3", con3, | ||
26 | + "att1", att1, "att2", att2, "att3", att3, | ||
27 | + "title", title, "content", content, "attachments", attachments) |
@@ -0,0 +1,30 @@ | @@ -0,0 +1,30 @@ | ||
1 | +local field = KEYS[1] | ||
2 | +local roleId = tonumber(KEYS[2]) | ||
3 | + | ||
4 | +local formationJson = redis.call("hget", string.format("role:%d", roleId), field .. "FormationJson") | ||
5 | + | ||
6 | +local formation = cjson.decode(formationJson) | ||
7 | + | ||
8 | +local response = {formation = {}, heros = {}} | ||
9 | + | ||
10 | +if formation.heros then | ||
11 | + for _, hero in ipairs(formation.heros) do | ||
12 | + table.insert(response.formation, hero.id) | ||
13 | + end | ||
14 | +end | ||
15 | + | ||
16 | +local heroIds = redis.call("smembers", string.format("role:%d:heroIds", roleId)) | ||
17 | + | ||
18 | +local heroFields = {"type", "level", "star", "evolveCount", "wake", "breakLevel"} | ||
19 | +for _, heroId in ipairs(heroIds) do | ||
20 | + local heroId = tonumber(heroId) | ||
21 | + local heroInfo = redis.call("hmget", string.format("hero:%d:%d", roleId, heroId), unpack(heroFields)) | ||
22 | + | ||
23 | + local tb = {} | ||
24 | + for k, v in ipairs(heroInfo) do | ||
25 | + tb[heroFields[k]] = tonumber(v) | ||
26 | + end | ||
27 | + response.heros[heroId] = tb | ||
28 | +end | ||
29 | + | ||
30 | +return cmsgpack.pack(response) | ||
0 | \ No newline at end of file | 31 | \ No newline at end of file |
@@ -0,0 +1,58 @@ | @@ -0,0 +1,58 @@ | ||
1 | +local roleId = tonumber(KEYS[1]) | ||
2 | + | ||
3 | +local friendKey = "role:%d:friend" | ||
4 | +local assistKey = "role:%d:assist" | ||
5 | + | ||
6 | +local function formatTable(tbl) | ||
7 | + local t = {} | ||
8 | + for _, id in ipairs(tbl) do | ||
9 | + t[tonumber(id)] = 1 | ||
10 | + end | ||
11 | + return t | ||
12 | +end | ||
13 | + | ||
14 | +local friendIds = redis.call("smembers", friendKey:format(roleId)) | ||
15 | +local assistIds = formatTable(redis.call("smembers", assistKey:format(roleId))) | ||
16 | + | ||
17 | +local heroFields = {"type", "level", "star", "evolveCount", "wake", "breakLevel", "battleValue", "dress"} | ||
18 | +local function getLeader(id, formation) | ||
19 | + for _, hero in ipairs(formation.heros) do | ||
20 | + if hero.leader then | ||
21 | + local heroInfo = redis.call("hmget", string.format("hero:%d:%d", id, hero.id), unpack(heroFields)) | ||
22 | + return { | ||
23 | + type = tonumber(heroInfo[1]), | ||
24 | + level = tonumber(heroInfo[2]), | ||
25 | + star = tonumber(heroInfo[3]), | ||
26 | + evolveCount = tonumber(heroInfo[4]), | ||
27 | + wake = tonumber(heroInfo[5]), | ||
28 | + breakLevel = tonumber(heroInfo[6]), | ||
29 | + battleValue = tonumber(heroInfo[7]), | ||
30 | + dress = tonumber(heroInfo[8]), | ||
31 | + } | ||
32 | + end | ||
33 | + end | ||
34 | +end | ||
35 | + | ||
36 | +local response = {} | ||
37 | +for _, id in ipairs(friendIds) do | ||
38 | + id = tonumber(id) | ||
39 | + local dtls = redis.call("hmget", string.format("role:%d", id), | ||
40 | + "name", "level", "vip", "pveFormationJson") | ||
41 | + local formation = cjson.decode(dtls[4]) | ||
42 | + local leader = getLeader(id, formation) | ||
43 | + if leader then | ||
44 | + table.insert(response, { | ||
45 | + roleId = id, | ||
46 | + name = dtls[1], | ||
47 | + level = tonumber(dtls[2]), | ||
48 | + vip = tonumber(dtls[3]), | ||
49 | + leader = leader, | ||
50 | + used = assistIds[id] or 0, | ||
51 | + }) | ||
52 | + end | ||
53 | +end | ||
54 | + | ||
55 | +return cmsgpack.pack(response) | ||
56 | + | ||
57 | + | ||
58 | + |
@@ -0,0 +1,208 @@ | @@ -0,0 +1,208 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +local socket = require "skynet.socket" | ||
3 | +local redisproxy = require "shared.redisproxy" | ||
4 | +local netpack = require "skynet.netpack" | ||
5 | +local xxtea = require "xxtea" | ||
6 | +local deque = require "deque" | ||
7 | +local datacenter = require "skynet.datacenter" | ||
8 | + | ||
9 | +local pcall = pcall | ||
10 | +local string_format = string.format | ||
11 | + | ||
12 | +local poold | ||
13 | + | ||
14 | +-- agent过期时间 10分钟 | ||
15 | +local AGENT_EXPIRE_TIME = 300 | ||
16 | + | ||
17 | +local _M = { | ||
18 | + -- fd -> uid | ||
19 | + f2u = {}, | ||
20 | + -- uid -> 32 << fd | agent | ||
21 | + u2f = {}, | ||
22 | + -- fd -> ip | ||
23 | + f2i = {}, | ||
24 | + -- fd -> expire | ||
25 | + f2e = {}, | ||
26 | + online = 0, | ||
27 | +} | ||
28 | + | ||
29 | +local function get_f(pack) | ||
30 | + return (pack >> 32) & 0x7fffffff | ||
31 | +end | ||
32 | + | ||
33 | +local function get_a(pack) | ||
34 | + return pack & 0xffffffff | ||
35 | +end | ||
36 | + | ||
37 | +local function set_pack(fd, agent) | ||
38 | + return (fd << 32) | agent | ||
39 | +end | ||
40 | + | ||
41 | +function _M:init(obj, serice) | ||
42 | + self.factory = deque.clone(obj) | ||
43 | + poold = serice | ||
44 | +end | ||
45 | + | ||
46 | +-- @desc: agent退出 | ||
47 | +function _M:exit_agent(fd) | ||
48 | + self.f2e[fd] = nil | ||
49 | + local uid = self.f2u[fd] | ||
50 | + if not uid then return end | ||
51 | + | ||
52 | + local pack = self.u2f[uid] | ||
53 | + if not pack then | ||
54 | + self.f2u[fd] = nil | ||
55 | + return | ||
56 | + end | ||
57 | + | ||
58 | + local agent = get_a(pack) | ||
59 | + | ||
60 | + pcall(skynet.send, agent, "lua", "exit") | ||
61 | + pcall(skynet.send, poold, "lua", "feed") | ||
62 | + | ||
63 | + self.f2u[fd] = nil | ||
64 | + self.u2f[uid] = nil | ||
65 | +end | ||
66 | + | ||
67 | +-- @desc: 客户端连入 | ||
68 | +function _M:socket_open(fd, addr) | ||
69 | + self.f2i[fd] = addr | ||
70 | +end | ||
71 | + | ||
72 | +-- @desc: 网络关闭 | ||
73 | +function _M:socket_close(fd) | ||
74 | + self.f2i[fd] = nil | ||
75 | + local uid = self.f2u[fd] | ||
76 | + if not uid then return end | ||
77 | + self.f2e[fd] = skynet.timex() + AGENT_EXPIRE_TIME | ||
78 | + | ||
79 | + if not self.u2f[uid] then | ||
80 | + self.f2u[fd] = nil | ||
81 | + return | ||
82 | + end | ||
83 | + local agent = get_a(self.u2f[uid]) | ||
84 | + local ok, _ = pcall(skynet.call, agent, "lua", "close") | ||
85 | + if not ok then self:exit_agent(fd) end | ||
86 | +end | ||
87 | + | ||
88 | +-- @desc: 网络出错 | ||
89 | +function _M:socket_error(fd) | ||
90 | + self.f2i[fd] = nil | ||
91 | + local uid = self.f2u[fd] | ||
92 | + if not uid then return end | ||
93 | + | ||
94 | + if not self.u2f[uid] then | ||
95 | + self.f2u[fd] = nil | ||
96 | + return | ||
97 | + end | ||
98 | + local agent = get_a(self.u2f[uid]) | ||
99 | + -- 无论失败否,应该让逻辑走下去,保证agent状态以及索引正确 | ||
100 | + pcall(skynet.call, agent, "lua", "close") | ||
101 | + self:exit_agent(fd) | ||
102 | +end | ||
103 | + | ||
104 | +local function table_nums(t) | ||
105 | + local count = 0 | ||
106 | + for k, v in pairs(t) do | ||
107 | + count = count + 1 | ||
108 | + end | ||
109 | + return count | ||
110 | +end | ||
111 | + | ||
112 | +local next_check_time = 0 | ||
113 | +local next_log_time = 0 | ||
114 | +local CHECK_AGENT_STATUS_INTERVAL = 100 -- 检查agent状态的定时间隔 | ||
115 | +-- @desc: 检查agent状态,若过期,则让agent退出;并定时打日志统计在线人数 | ||
116 | +function _M:check_agent_status() | ||
117 | + local now = skynet.timex() | ||
118 | + if now >= next_check_time then | ||
119 | + next_check_time = now + CHECK_AGENT_STATUS_INTERVAL | ||
120 | + for fd, expire in pairs(self.f2e) do | ||
121 | + if expire < now then | ||
122 | + self:exit_agent(fd) | ||
123 | + end | ||
124 | + end | ||
125 | + end | ||
126 | + | ||
127 | + if now >= next_log_time and now % 60 == 0 and logd then | ||
128 | + next_log_time = now + 60 | ||
129 | + local count = table_nums(self.u2f) | ||
130 | + datacenter.set("onlineCount", count) | ||
131 | + pcall(skynet.send, logd, "lua", "log", "online", {count = count}) | ||
132 | + end | ||
133 | +end | ||
134 | + | ||
135 | +local function query_agent_response(fd, response) | ||
136 | + local head = string.pack("H", actionCodes.Role_queryLoginRpc + rpcResponseBegin) | ||
137 | + | ||
138 | + local bin = MsgPack.pack(response) | ||
139 | + if #bin > 0 then bin = xxtea.encrypt(bin, XXTEA_KEY) end | ||
140 | + socket.write(fd, netpack.pack(head .. bin)) | ||
141 | +end | ||
142 | + | ||
143 | +-- @desc: 玩家登陆第一个包,queryLogin,watchdog为客户端分配一个agent,并告诉gate分配成功,之后的消息直接走agent | ||
144 | +function _M:query_agent(fd, uid) | ||
145 | + local pack = self.u2f[uid] | ||
146 | + if pack then | ||
147 | + local f = get_f(pack) | ||
148 | + if fd == f then | ||
149 | + skynet.error(string.format("%s same fd %d", uid, fd)) | ||
150 | + return | ||
151 | + end | ||
152 | + | ||
153 | + -- self.f2u[f] 肯定存在;self.f2e[f]不存在,则说明在线,则需要踢下线 | ||
154 | + if not self.f2e[f] then | ||
155 | + local head = string.pack("H", actionCodes.Sys_kickdown) | ||
156 | + -- local bin = MsgPack.pack({body = "该账号已登上其他机器"}) | ||
157 | + local bin = MsgPack.pack({body = "既にこのアカウントを利用している端末があります。"}) | ||
158 | + if #bin > 0 then bin = xxtea.encrypt(bin, XXTEA_KEY) end | ||
159 | + socket.write(f, netpack.pack(head .. bin)) | ||
160 | + skynet.timeout(10, function () | ||
161 | + skynet.call(gate_serv, "lua", "kick", f) | ||
162 | + end) | ||
163 | + end | ||
164 | + | ||
165 | + local agent = get_a(pack) | ||
166 | + local ok = pcall(skynet.call, agent, "lua", "start", gate_serv, fd, self.f2i[fd]) | ||
167 | + if not ok then | ||
168 | + query_agent_response(fd, {ret = "INNER_ERROR"}) | ||
169 | + return | ||
170 | + end | ||
171 | + | ||
172 | + self.f2e[f] = nil | ||
173 | + self.f2u[f] = nil | ||
174 | + self.u2f[uid] = set_pack(fd, agent) | ||
175 | + else | ||
176 | + -- 该uid未存储,则说明至少超过10分钟未登陆,由agent池服务pop出一个agent | ||
177 | + local agent = self.factory:pop() | ||
178 | + if not agent then | ||
179 | + -- 服务器满 | ||
180 | + query_agent_response(fd, {ret = "RET_SERVER_FULL"}) | ||
181 | + return | ||
182 | + end | ||
183 | + | ||
184 | + local ok = pcall(skynet.call, agent, "lua", "start", gate_serv, fd, self.f2i[fd]) | ||
185 | + if not ok then | ||
186 | + self.factory:push(agent) | ||
187 | + query_agent_response(fd, {ret = "INNER_ERROR"}) | ||
188 | + return | ||
189 | + end | ||
190 | + | ||
191 | + self.u2f[uid] = set_pack(fd, agent) | ||
192 | + end | ||
193 | + | ||
194 | + self.f2u[fd] = uid | ||
195 | + | ||
196 | + local response = {} | ||
197 | + | ||
198 | + local user = redisproxy:get(string_format("uid:%s", uid)) | ||
199 | + if user then | ||
200 | + response.ret = "RET_HAS_EXISTED" | ||
201 | + response.name = user | ||
202 | + else | ||
203 | + response.ret = "RET_NOT_EXIST" | ||
204 | + end | ||
205 | + query_agent_response(fd, response) | ||
206 | +end | ||
207 | + | ||
208 | +return _M | ||
0 | \ No newline at end of file | 209 | \ No newline at end of file |
@@ -0,0 +1,109 @@ | @@ -0,0 +1,109 @@ | ||
1 | + | ||
2 | +local _M = { } | ||
3 | + | ||
4 | +-- 超时次数 | ||
5 | +local heartTimeoutCount = 0 | ||
6 | +-- 加速次数 | ||
7 | +local heartQuickCount = 0 | ||
8 | +-- 上次检查心跳时间 | ||
9 | +local lastHeartCheckTime = 0 | ||
10 | +-- 下次进入定时检查的时间 | ||
11 | +local nextCheckTime = 0 | ||
12 | +-- 心跳误差允许范围 | ||
13 | +local HEART_BEAT_ERROR_LIMIT = 1 | ||
14 | +-- 最大超时次数 | ||
15 | +local HEART_TIMEOUT_COUNT_MAX = 20 | ||
16 | +-- 最大加速次数 | ||
17 | +local HEART_QUICK_COUNT_MAX = 5 | ||
18 | +-- 心跳定时间隔 | ||
19 | +local HEART_TIMER_INTERVAL = 5 | ||
20 | + | ||
21 | +local function check_heart_beat(agent, now) | ||
22 | + -- 充值等操作不检查心跳 | ||
23 | + local role = agent.role | ||
24 | + if role.ignoreHeartbeat then | ||
25 | + lastHeartCheckTime = now - HEART_TIMER_INTERVAL | ||
26 | + heartTimeoutCount = 0 | ||
27 | + return | ||
28 | + end | ||
29 | + if lastHeartCheckTime - now > HEART_TIMER_INTERVAL or | ||
30 | + now - lastHeartCheckTime > HEART_TIMER_INTERVAL then | ||
31 | + heartTimeoutCount = heartTimeoutCount + 1 | ||
32 | + if heartTimeoutCount >= HEART_TIMEOUT_COUNT_MAX then | ||
33 | + skynet.error("timeout! then agent will shut down by self", agent.client_fd, role:getProperty("name"), role:getProperty("id")) | ||
34 | + skynet.call(agent.gate_serv, "lua", "kick", agent.client_fd) | ||
35 | + heartTimeoutCount = 0 | ||
36 | + end | ||
37 | + else | ||
38 | + heartTimeoutCount = 0 | ||
39 | + end | ||
40 | +end | ||
41 | + | ||
42 | +local PointDataMark = {} | ||
43 | +local resetTimeStr = string.format("%02d00", RESET_TIME) | ||
44 | + | ||
45 | +local function check_daily_reset(agent, now) | ||
46 | + local date = os.date("*t", now) | ||
47 | + local timeStr = string.format("%02d%02d", date.hour, date.min) | ||
48 | + local dataStr = date.year .. string.format("%02d", date.month) .. string.format("%02d", date.day) | ||
49 | + | ||
50 | + local function timeEffect(checkTimeStr) | ||
51 | + if timeStr ~= checkTimeStr then | ||
52 | + return false | ||
53 | + end | ||
54 | + if PointDataMark[dataStr] and PointDataMark[dataStr][checkTimeStr] then | ||
55 | + return false | ||
56 | + end | ||
57 | + PointDataMark[dataStr] = PointDataMark[dataStr] or {} | ||
58 | + PointDataMark[dataStr][checkTimeStr] = true | ||
59 | + return true | ||
60 | + end | ||
61 | + | ||
62 | + if timeEffect(resetTimeStr) then | ||
63 | + -- 刷新每日数据 | ||
64 | + local role = agent.role | ||
65 | + if role then | ||
66 | + role:onCrossDay(now, true) | ||
67 | + end | ||
68 | + end | ||
69 | +end | ||
70 | + | ||
71 | +function _M:update(agent) | ||
72 | + local now = skynet.timex() | ||
73 | + local role = agent.role | ||
74 | + if now >= nextCheckTime then | ||
75 | + pcall(check_heart_beat, agent, now) | ||
76 | + nextCheckTime = now + HEART_TIMER_INTERVAL | ||
77 | + end | ||
78 | + pcall(check_daily_reset, agent, now) | ||
79 | + pcall(role.onRecoverTimer, role, now) | ||
80 | +end | ||
81 | + | ||
82 | +function _M:heart_beat(agent) | ||
83 | + local now = skynet.timex() | ||
84 | + if now == lastHeartCheckTime then | ||
85 | + return | ||
86 | + end | ||
87 | + if now - lastHeartCheckTime <= HEART_TIMER_INTERVAL - HEART_BEAT_ERROR_LIMIT then | ||
88 | + heartQuickCount = heartQuickCount + 1 | ||
89 | + if heartQuickCount == HEART_QUICK_COUNT_MAX then | ||
90 | + -- 将错误写入日志 | ||
91 | + local role = agent.role | ||
92 | + skynet.error("Warning, heart beating is too quick, shut down the agent", agent.client_fd, role:getProperty("name"), role:getProperty("id")) | ||
93 | + -- skynet.call(agent.gate_serv, "lua", "kick", agent.client_fd) | ||
94 | + role:warningHeartTooQuick() | ||
95 | + heartQuickCount = 0 | ||
96 | + end | ||
97 | + else | ||
98 | + heartQuickCount = 0 | ||
99 | + end | ||
100 | + lastHeartCheckTime = now | ||
101 | +end | ||
102 | + | ||
103 | +function _M:reset() | ||
104 | + heartTimeoutCount = 0 | ||
105 | + heartQuickCount = 0 | ||
106 | + lastHeartCheckTime = skynet.timex() | ||
107 | +end | ||
108 | + | ||
109 | +return _M |
@@ -0,0 +1,56 @@ | @@ -0,0 +1,56 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +require "skynet.manager" | ||
3 | +local crab = require "crab.c" | ||
4 | + | ||
5 | +local table_insert = table.insert | ||
6 | +local table_unpack = table.unpack | ||
7 | +local mode, id, dict = ... | ||
8 | + | ||
9 | +local function toutf8(name) | ||
10 | + local t = {} | ||
11 | + for _, v in utf8.codes(name) do | ||
12 | + table_insert(t, v) | ||
13 | + end | ||
14 | + return t | ||
15 | +end | ||
16 | + | ||
17 | +if mode == "sub" then | ||
18 | + local CMD = {} | ||
19 | + dict = tonumber(dict) | ||
20 | + | ||
21 | + function CMD.check(name) | ||
22 | + if name:find("%c") then | ||
23 | + return false | ||
24 | + end | ||
25 | + local utftb = toutf8(name) | ||
26 | + if crab.filter(dict, utftb) then | ||
27 | + return false, utf8.char(table_unpack(utftb)) | ||
28 | + end | ||
29 | + return true | ||
30 | + end | ||
31 | + | ||
32 | + skynet.start(function() | ||
33 | + skynet.dispatch("lua", function(_, _, command, ...) | ||
34 | + local f = CMD[command] | ||
35 | + skynet.ret(skynet.pack(f(...))) | ||
36 | + end) | ||
37 | + | ||
38 | + skynet.register(string.format("CHATED%d", id)) | ||
39 | + end) | ||
40 | +else | ||
41 | + skynet.start(function() | ||
42 | + local ok, forbidNames = pcall(require, "csvdata.forbid_chat") | ||
43 | + if not ok then forbidNames = {} end | ||
44 | + | ||
45 | + local words = {} | ||
46 | + for _, data in ipairs(forbidNames) do | ||
47 | + local ok, utftb = pcall(toutf8, data.name) | ||
48 | + if ok then table.insert(words, utftb) end | ||
49 | + end | ||
50 | + local d = crab.open(words) | ||
51 | + | ||
52 | + for i = 1, 5 do | ||
53 | + skynet.newservice(SERVICE_NAME, "sub", i, d) | ||
54 | + end | ||
55 | + end) | ||
56 | +end |
@@ -0,0 +1,64 @@ | @@ -0,0 +1,64 @@ | ||
1 | +local sharedata = require "skynet.sharedata" | ||
2 | +local skynet = require "skynet" | ||
3 | +local lfs = require "lfs" | ||
4 | +local redisproxy = require "shared.redisproxy" | ||
5 | +require "shared.init" | ||
6 | +require "utils.init" | ||
7 | +require "csvdata.init" | ||
8 | +require "skynet.manager" | ||
9 | +require "RedisKeys" | ||
10 | + | ||
11 | +local csvdb = {} | ||
12 | + | ||
13 | +local function formatFileName(filename) | ||
14 | + filename = string.trim(filename) | ||
15 | + local basename = filename:match("([^/]+)%.lua$") | ||
16 | + if not basename then return end | ||
17 | + local loadname = filename:match("^src/([^.]+)%.lua$") | ||
18 | + loadname = loadname:gsub('/', '.') | ||
19 | + return basename, loadname | ||
20 | +end | ||
21 | + | ||
22 | +local function travCsv(rootPath, pathes) | ||
23 | + pathes = pathes or {} | ||
24 | + local modified = false | ||
25 | + local ok, files, iter = pcall(lfs.dir, rootPath) | ||
26 | + if not ok then return modified end | ||
27 | + for entry in files, iter do | ||
28 | + -- 过滤 . 开始的字符串包括 . .. .git .开头的文件名 | ||
29 | + if string.byte(entry, 1) ~= 46 then | ||
30 | + local pathfile = rootPath .. '/' .. entry | ||
31 | + local attrs = lfs.attributes(pathfile) | ||
32 | + if attrs.mode == 'directory' then | ||
33 | + modified = travCsv(pathfile, pathes) or modified | ||
34 | + else | ||
35 | + local basename, loadname = formatFileName(pathfile) | ||
36 | + if basename and tonum(pathes[loadname]) < attrs.modification then | ||
37 | + modified = true | ||
38 | + if basename == "init" then | ||
39 | + require(loadname) | ||
40 | + else | ||
41 | + csvdb[basename .. "Csv"] = require(loadname) | ||
42 | + end | ||
43 | + pathes[loadname] = attrs.modification | ||
44 | + end | ||
45 | + end | ||
46 | + end | ||
47 | + end | ||
48 | + return modified | ||
49 | +end | ||
50 | + | ||
51 | +-- 每分钟检查是否有更改 | ||
52 | +local file2timeMap = {} | ||
53 | +local function handle_timeout() | ||
54 | + if travCsv("src/csvdata", file2timeMap) then | ||
55 | + sharedata.update("csvdata", csvdb) | ||
56 | + end | ||
57 | + skynet.timeout(100*5, handle_timeout) | ||
58 | +end | ||
59 | + | ||
60 | +skynet.start(function () | ||
61 | + travCsv("src/csvdata", file2timeMap) | ||
62 | + sharedata.new("csvdata", csvdb) | ||
63 | + -- handle_timeout() | ||
64 | +end) |
@@ -0,0 +1,54 @@ | @@ -0,0 +1,54 @@ | ||
1 | +require "csvdata.init" | ||
2 | +require "shared.init" | ||
3 | +require "utils.init" | ||
4 | +require "GlobalVar" | ||
5 | +require "RedisKeys" | ||
6 | +require "ProtocolCode" | ||
7 | +require "skynet.manager" | ||
8 | +local harbor = require "skynet.harbor" | ||
9 | + | ||
10 | +skynet = require "skynet" | ||
11 | + | ||
12 | +redisproxy = require("shared.redisproxy") | ||
13 | +globalCsv = require "csvdata/GlobalDefine" | ||
14 | + | ||
15 | +SendPacket = function ( ... ) end | ||
16 | + | ||
17 | +local function initRedisDb( ... ) | ||
18 | + local servId = tonumber(skynet.getenv("servId")) | ||
19 | + if servId then | ||
20 | + redisproxy:hsetnx("autoincrement_set", "role", servId * MAX_ROLE_NUM) | ||
21 | + redisproxy:hsetnx("autoincrement_set", "union", servId * MAX_ROLE_NUM) | ||
22 | + redisproxy:hsetnx("autoincrement_set", "trade", servId * MAX_ROLE_NUM * 100) | ||
23 | + redisproxy:hsetnx("autoincrement_set", "email", 0) | ||
24 | + redisproxy:hsetnx("autoincrement_set", "emailTimestamp", 0) | ||
25 | + redisproxy:hsetnx("autoincrement_set", "delay_email", 0) | ||
26 | + end | ||
27 | +end | ||
28 | + | ||
29 | +local steps = { | ||
30 | + [1] = { | ||
31 | + handler = initRedisDb, | ||
32 | + desc = "initialize redis database " | ||
33 | + } | ||
34 | +} | ||
35 | + | ||
36 | +skynet.start(function () | ||
37 | + redisd = harbor.queryname("REDIS") | ||
38 | + | ||
39 | + redisproxy = require("shared.redisproxy") | ||
40 | + | ||
41 | + local new = redisproxy:hsetnx("autoincrement_set", "server_start", os.date("%Y%m%d", skynet.timex())) | ||
42 | + if not new then | ||
43 | + print("server has been initialized...") | ||
44 | + skynet.exit() | ||
45 | + return | ||
46 | + end | ||
47 | + | ||
48 | + for _, action in ipairs(steps) do | ||
49 | + print(action.desc .. "start ...") | ||
50 | + action.handler() | ||
51 | + print(action.desc .. "finished ...") | ||
52 | + end | ||
53 | + skynet.exit() | ||
54 | +end) | ||
0 | \ No newline at end of file | 55 | \ No newline at end of file |
@@ -0,0 +1,109 @@ | @@ -0,0 +1,109 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +local harbor = require "skynet.harbor" | ||
3 | +local json = require("shared.json") | ||
4 | +local redisproxy = require("shared.redisproxy") | ||
5 | + | ||
6 | +require "shared.init" | ||
7 | +require "utils.init" | ||
8 | +require "RedisKeys" | ||
9 | +require "skynet.manager" | ||
10 | + | ||
11 | +local ipairs = ipairs | ||
12 | +local table_insert = table.insert | ||
13 | +local tarr2tab = table.array2Table | ||
14 | +local string_format = string.format | ||
15 | + | ||
16 | +local pointDataMark = {} | ||
17 | +local utils = {} | ||
18 | + | ||
19 | +local CHECK_MAIL_STATUS_INTERVAL = 60 | ||
20 | + | ||
21 | +local function mailQuene() | ||
22 | + local delayEmail = tonum(redisproxy:hget("autoincrement_set", "delay_email")) | ||
23 | + if delayEmail == 0 then | ||
24 | + return | ||
25 | + end | ||
26 | + local begin = math.max(delayEmail - 100, 1) | ||
27 | + local mails = redisproxy:pipelining(function (red) | ||
28 | + for id = begin, delayEmail do | ||
29 | + red:hgetall(string.format("delayEmail:%s", id)) | ||
30 | + end | ||
31 | + end) | ||
32 | + if not mails then | ||
33 | + return | ||
34 | + end | ||
35 | + local now = skynet.timex() | ||
36 | + local mailList = {} | ||
37 | + for index, data in ipairs(mails) do | ||
38 | + if next(data) then | ||
39 | + local email = tarr2tab(data) | ||
40 | + if tonum(email.startTime) <= now then | ||
41 | + table_insert(mailList, email) | ||
42 | + | ||
43 | + if #mailList > 100 then | ||
44 | + break | ||
45 | + end | ||
46 | + end | ||
47 | + end | ||
48 | + end | ||
49 | + if #mailList == 0 then | ||
50 | + return | ||
51 | + end | ||
52 | + redisproxy:pipelining(function (red) | ||
53 | + for _, email in ipairs(mailList) do | ||
54 | + red:del(string_format("delayEmail:%s", email.id)) | ||
55 | + end | ||
56 | + end) | ||
57 | + table.sort(mailList, function(a, b) | ||
58 | + return tonum(a.id) < tonum(b.id) | ||
59 | + end) | ||
60 | + for _, email in ipairs(mailList) do | ||
61 | + local gid = redisproxy:hincrby("autoincrement_set", "email", 1) | ||
62 | + if email.mid then | ||
63 | + redisproxy:hmset(string_format("globalEmail:%s", gid), | ||
64 | + "id", gid, | ||
65 | + "createtime", email.endTime, | ||
66 | + "title", email.title, | ||
67 | + "content", email.content, | ||
68 | + "attachments", email.attachments, | ||
69 | + "endtime", email.endTime, | ||
70 | + "mid", email.mid, | ||
71 | + "timestamp", email.startTime | ||
72 | + ) | ||
73 | + else | ||
74 | + redisproxy:hmset(string_format("globalEmail:%s", gid), | ||
75 | + "id", gid, | ||
76 | + "createtime", email.endTime, | ||
77 | + "title", email.title, | ||
78 | + "content", email.content, | ||
79 | + "attachments", email.attachments, | ||
80 | + "endtime", email.endTime, | ||
81 | + "timestamp", email.startTime | ||
82 | + ) | ||
83 | + end | ||
84 | + end | ||
85 | + redisproxy:hset("autoincrement_set", "emailTimestamp", now) | ||
86 | +end | ||
87 | + | ||
88 | +-- @desc: 定时邮件队列检测 | ||
89 | +local function check_mail_queue() | ||
90 | + pcall(mailQuene) | ||
91 | + skynet.timeout(CHECK_MAIL_STATUS_INTERVAL, check_mail_queue) | ||
92 | +end | ||
93 | + | ||
94 | +local CMD = {} | ||
95 | +function CMD.start() | ||
96 | + check_mail_queue() | ||
97 | +end | ||
98 | + | ||
99 | +local function __init__() | ||
100 | + skynet.dispatch("lua", function(_, _, command, ...) | ||
101 | + if CMD[command] then | ||
102 | + skynet.ret(skynet.pack(CMD[command](...))) | ||
103 | + end | ||
104 | + end) | ||
105 | + redisd = harbor.queryname("REDIS") | ||
106 | + skynet.register("GLOBALD") | ||
107 | +end | ||
108 | + | ||
109 | +skynet.start(__init__) |
@@ -0,0 +1,118 @@ | @@ -0,0 +1,118 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +local mongo = require "mongo" | ||
3 | +local queue = require "skynet.queue" | ||
4 | +local bson = require "bson" | ||
5 | +local socketdriver = require "socketdriver" | ||
6 | + | ||
7 | +local serverId = tonumber(skynet.getenv("servId")) | ||
8 | + | ||
9 | +require "shared.init" | ||
10 | +require "skynet.manager" | ||
11 | + | ||
12 | +local table_insert = table.insert | ||
13 | +local pairs = pairs | ||
14 | +local ipairs = ipairs | ||
15 | +local string_format = string.format | ||
16 | + | ||
17 | +local CMD, cache, client, cs = {}, {} | ||
18 | +local auto = {} | ||
19 | + | ||
20 | +local rsyslog_fd | ||
21 | + | ||
22 | +-- function getNextSequence(collect) | ||
23 | +-- local index = auto[collect] | ||
24 | +-- if not index then | ||
25 | +-- local res = client.counters:findAndModify({ | ||
26 | +-- query = {}, | ||
27 | +-- update = {["$inc"] = {[collect] = 1}}, | ||
28 | +-- upsert = true, | ||
29 | +-- new = true, | ||
30 | +-- }) | ||
31 | +-- index = res.value[collect] | ||
32 | +-- else | ||
33 | +-- index = index + 1 | ||
34 | +-- end | ||
35 | +-- auto[collect] = index | ||
36 | +-- return index | ||
37 | +-- end | ||
38 | + | ||
39 | +local dateTypes = {"timestamp", "createTime", "lastLoginTime"} | ||
40 | +local stringTypes = {"s1", "s2", "s3"} | ||
41 | +local intTypes = {"int1", "int2", "int3", "int4"} | ||
42 | +function CMD.log(collect, doc) | ||
43 | + -- write to rsyslog | ||
44 | + local now = skynet.timex() | ||
45 | + doc["timestamp"] = now | ||
46 | + doc["server"] = serverId | ||
47 | + | ||
48 | + for _, field in pairs(stringTypes) do | ||
49 | + if doc[field] then | ||
50 | + doc[field] = tostring(doc[field]) | ||
51 | + end | ||
52 | + end | ||
53 | + for _, field in pairs(intTypes) do | ||
54 | + if doc[field] then | ||
55 | + doc[field] = tonumber(doc[field]) | ||
56 | + end | ||
57 | + end | ||
58 | + for _, field in pairs(dateTypes) do | ||
59 | + if doc[field] then | ||
60 | + doc[field .. "_t"] = tonumber(os.date("%Y%m%d%H%M%S", doc[field])) | ||
61 | + end | ||
62 | + end | ||
63 | + -- Local-6 + Info | ||
64 | + socketdriver.send(rsyslog_fd, string.format("<182>%s: %s\n", collect, json.encode(doc))) | ||
65 | + | ||
66 | + -- if not cache[collect] then cache[collect] = {} end | ||
67 | + -- doc["timestamp"] = now | ||
68 | + -- doc["_id"] = getNextSequence(collect) | ||
69 | + -- table_insert(cache[collect], doc) | ||
70 | +end | ||
71 | + | ||
72 | +function CMD.open(conf) | ||
73 | + rsyslog_fd = socketdriver.connect("127.0.0.1", 514) | ||
74 | + socketdriver.start(rsyslog_fd) | ||
75 | + | ||
76 | + -- local db = mongo.client { | ||
77 | + -- host = conf.mongohost, | ||
78 | + -- port = conf.mongoport or 27017, | ||
79 | + -- username = conf.mongouser, | ||
80 | + -- password = conf.mongopswd, | ||
81 | + -- } | ||
82 | + | ||
83 | + -- assert(db, "mongo connect error") | ||
84 | + | ||
85 | + -- local servId = skynet.getenv "servId" | ||
86 | + -- client = db["s"..servId] | ||
87 | +end | ||
88 | + | ||
89 | +-- local function __loop__() | ||
90 | +-- while true do | ||
91 | +-- cs(function () | ||
92 | +-- for col, docs in pairs(cache) do | ||
93 | +-- client[col]:batch_insert(docs) | ||
94 | +-- client.counters:update({}, {["$set"]={[col]=auto[col]}}) | ||
95 | +-- end | ||
96 | +-- cache = {} | ||
97 | +-- end) | ||
98 | +-- skynet.sleep(200) | ||
99 | +-- end | ||
100 | +-- end | ||
101 | + | ||
102 | +local function __init__() | ||
103 | + skynet.dispatch("lua", function (_, _, command, ...) | ||
104 | + local f = CMD[command] | ||
105 | + if command == "open" then | ||
106 | + skynet.ret(skynet.pack(f(...))) | ||
107 | + else | ||
108 | + local collect, doc = ... | ||
109 | + cs(function() f(collect, doc) end) | ||
110 | + end | ||
111 | + end) | ||
112 | + cs = queue() | ||
113 | + | ||
114 | + -- skynet.fork(__loop__) | ||
115 | + skynet.register "LOGD" | ||
116 | +end | ||
117 | + | ||
118 | +skynet.start(__init__) |
@@ -0,0 +1,109 @@ | @@ -0,0 +1,109 @@ | ||
1 | + | ||
2 | +local _M = {} | ||
3 | + | ||
4 | +local mc = require "skynet.multicast" | ||
5 | +local datacenter = require "skynet.datacenter" | ||
6 | +local skynet = require "skynet" | ||
7 | + | ||
8 | +local chan_w, chan_g | ||
9 | +local chan_ws = {} | ||
10 | + | ||
11 | +function _M.sub_world(w_channel) | ||
12 | + chan_w = mc.new { | ||
13 | + channel = w_channel, | ||
14 | + dispatch = function (channel, source, ...) | ||
15 | + if select("#", ...) == 0 then | ||
16 | + return | ||
17 | + end | ||
18 | + local actionCode, bin = ... | ||
19 | + if SendPacket then | ||
20 | + SendPacket(actionCode, bin) | ||
21 | + end | ||
22 | + end | ||
23 | + } | ||
24 | + chan_w:subscribe() | ||
25 | +end | ||
26 | + | ||
27 | +function _M.usub_world() | ||
28 | + if chan_w then | ||
29 | + chan_w:unsubscribe() | ||
30 | + chan_w = nil | ||
31 | + end | ||
32 | +end | ||
33 | + | ||
34 | +function _M.sub_worlds(w_channels) | ||
35 | + for _, cell in pairs(w_channels) do | ||
36 | + local chan = mc.new { | ||
37 | + channel = cell, | ||
38 | + dispatch = function (channel, source, ...) | ||
39 | + if select("#", ...) == 0 then | ||
40 | + return | ||
41 | + end | ||
42 | + local actionCode, bin = ... | ||
43 | + if SendPacket then | ||
44 | + SendPacket(actionCode, bin) | ||
45 | + end | ||
46 | + end | ||
47 | + } | ||
48 | + chan:subscribe() | ||
49 | + table.insert(chan_ws, chan) | ||
50 | + end | ||
51 | +end | ||
52 | + | ||
53 | +function _M.pub_world(actionCode, bin) | ||
54 | + if not bin then | ||
55 | + return | ||
56 | + end | ||
57 | + if #chan_ws > 0 then | ||
58 | + for _, chan in pairs(chan_ws) do | ||
59 | + chan:publish(actionCode, bin) | ||
60 | + end | ||
61 | + return | ||
62 | + end | ||
63 | + if not bin or not chan_w then return end | ||
64 | + chan_w:publish(actionCode, bin) | ||
65 | +end | ||
66 | + | ||
67 | +function _M.sub_union(gid, chan) | ||
68 | + chan_g = mc.new { | ||
69 | + channel = chan, | ||
70 | + dispatch = function (channel, source, ...) | ||
71 | + if select("#", ...) == 0 then | ||
72 | + return | ||
73 | + end | ||
74 | + local actionCode, bin = ... | ||
75 | + if SendPacket then | ||
76 | + SendPacket(actionCode, bin) | ||
77 | + end | ||
78 | + end | ||
79 | + } | ||
80 | + chan_g:subscribe() | ||
81 | +end | ||
82 | + | ||
83 | +function _M.usub_union() | ||
84 | + if chan_g then | ||
85 | + chan_g:unsubscribe() | ||
86 | + chan_g = nil | ||
87 | + end | ||
88 | +end | ||
89 | + | ||
90 | +function _M.pub_union(actionCode, bin) | ||
91 | + if not bin or not chan_g then return end | ||
92 | + chan_g:publish(actionCode, bin) | ||
93 | +end | ||
94 | + | ||
95 | +function _M.pub_person(fromFd, toRoleId, actionCode, bin) | ||
96 | + if 0 == redisproxy:exists(string.format("role:%d", toRoleId)) then | ||
97 | + return SYS_ERROR_CHAT_NOT_EXIST | ||
98 | + end | ||
99 | + -- 若在线,实时发送聊天信息 | ||
100 | + local agent = datacenter.get("agent", toRoleId) | ||
101 | + if agent then | ||
102 | + SendPacket(actionCode, bin, agent.fd) | ||
103 | + SendPacket(actionCode, bin, fromFd) | ||
104 | + return | ||
105 | + end | ||
106 | + return SYS_ERROR_CHAT_NOT_ONLINE | ||
107 | +end | ||
108 | + | ||
109 | +return _M | ||
0 | \ No newline at end of file | 110 | \ No newline at end of file |
@@ -0,0 +1,56 @@ | @@ -0,0 +1,56 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +require "skynet.manager" | ||
3 | +local crab = require "crab.c" | ||
4 | + | ||
5 | +local table_insert = table.insert | ||
6 | +local table_unpack = table.unpack | ||
7 | +local mode, id, dict = ... | ||
8 | + | ||
9 | +local function toutf8(name) | ||
10 | + local t = {} | ||
11 | + for _, v in utf8.codes(name) do | ||
12 | + table_insert(t, v) | ||
13 | + end | ||
14 | + return t | ||
15 | +end | ||
16 | + | ||
17 | +if mode == "sub" then | ||
18 | + local CMD = {} | ||
19 | + dict = tonumber(dict) | ||
20 | + | ||
21 | + function CMD.check(name) | ||
22 | + if name:find("%c") then | ||
23 | + return false | ||
24 | + end | ||
25 | + local utftb = toutf8(name) | ||
26 | + if crab.filter(dict, utftb) then | ||
27 | + return false, utf8.char(table_unpack(utftb)) | ||
28 | + end | ||
29 | + return true | ||
30 | + end | ||
31 | + | ||
32 | + skynet.start(function() | ||
33 | + skynet.dispatch("lua", function(_, _, command, ...) | ||
34 | + local f = CMD[command] | ||
35 | + skynet.ret(skynet.pack(f(...))) | ||
36 | + end) | ||
37 | + | ||
38 | + skynet.register(string.format("NAMED%d", id)) | ||
39 | + end) | ||
40 | +else | ||
41 | + skynet.start(function() | ||
42 | + local ok, forbidNames = pcall(require, "csvdata.name_forbid") | ||
43 | + if not ok then forbidNames = {} end | ||
44 | + | ||
45 | + local words = {} | ||
46 | + for _, data in ipairs(forbidNames) do | ||
47 | + local ok, utftb = pcall(toutf8, data.name) | ||
48 | + if ok then table.insert(words, utftb) end | ||
49 | + end | ||
50 | + local d = crab.open(words) | ||
51 | + | ||
52 | + for i = 1, 5 do | ||
53 | + skynet.newservice(SERVICE_NAME, "sub", i, d) | ||
54 | + end | ||
55 | + end) | ||
56 | +end |
@@ -0,0 +1,104 @@ | @@ -0,0 +1,104 @@ | ||
1 | +skynet = require "skynet" | ||
2 | +redisproxy = require "shared.redisproxy" | ||
3 | +netpack = require "skynet.netpack" | ||
4 | +datacenter = require "skynet.datacenter" | ||
5 | +sharedata = require "skynet.sharedata" | ||
6 | +mcast_util = require "services/mcast_util" | ||
7 | +globalCsv = require "csvdata/GlobalDefine" | ||
8 | + | ||
9 | +local socket = require "skynet.socket" | ||
10 | +local harbor = require "skynet.harbor" | ||
11 | + | ||
12 | +require "shared.init" | ||
13 | +require "utils.init" | ||
14 | +require "ProtocolCode" | ||
15 | +require "skynet.manager" | ||
16 | +require "RedisKeys" | ||
17 | +require "GlobalVar" | ||
18 | + | ||
19 | +local port = tonumber(...) | ||
20 | +local SEP = "$end$" | ||
21 | +local actions = {} | ||
22 | + | ||
23 | +SendPacket = function(...) end | ||
24 | +rpcAgent = function(...) end | ||
25 | +rpcParter = function(...) end | ||
26 | +rpcRole = function(...) end | ||
27 | +rpcUnion = function(...) end | ||
28 | +rpcOtherUnion = function(...) end | ||
29 | + | ||
30 | +skynet.register_protocol { | ||
31 | + name = "role", | ||
32 | + id = 12, | ||
33 | + pack = skynet.pack, | ||
34 | + unpack = skynet.unpack, | ||
35 | +} | ||
36 | + | ||
37 | +local function process(request) | ||
38 | + local req = json.decode(request) | ||
39 | + if req["handle"] == "checkStatus" then | ||
40 | + return "success" | ||
41 | + end | ||
42 | + | ||
43 | + local modName, funcName = req["handle"]:match("([%w_]+)%.([%w_]+)") | ||
44 | + if not modName or not funcName then | ||
45 | + return table.concat({"handle格式错误", modName, funcName}, "\t") | ||
46 | + end | ||
47 | + local action = require("actions." .. modName .. "Action") | ||
48 | + return action[funcName](req) | ||
49 | +end | ||
50 | + | ||
51 | +local function main_loop(stdin, send) | ||
52 | + socket.lock(stdin) | ||
53 | + while true do | ||
54 | + local request = socket.readline(stdin, SEP) | ||
55 | + if not request then | ||
56 | + break | ||
57 | + end | ||
58 | + | ||
59 | + local response = process(request) | ||
60 | + if response then send(response) end | ||
61 | + end | ||
62 | + socket.unlock(stdin) | ||
63 | +end | ||
64 | + | ||
65 | +local function start() | ||
66 | + local listen_socket = socket.listen("0.0.0.0", port) | ||
67 | + print("Start nginx proxy at port: ", port) | ||
68 | + | ||
69 | + redisd = harbor.queryname("REDIS") | ||
70 | + | ||
71 | + csvdb = sharedata.query("csvdata") | ||
72 | + | ||
73 | + socket.start(listen_socket, function(id, addr) | ||
74 | + local function send(...) | ||
75 | + local t = { ... } | ||
76 | + socket.write(id, table.concat(t, "\t")) | ||
77 | + socket.write(id, SEP) | ||
78 | + end | ||
79 | + socket.start(id) | ||
80 | + skynet.fork(main_loop, id, send) | ||
81 | + end) | ||
82 | + | ||
83 | + -- 注册全服广播 | ||
84 | + local channels = {} | ||
85 | + for i = 1, 10 do | ||
86 | + local channel = datacenter.get("MC_W_CHANNEL" .. i) | ||
87 | + if channel then | ||
88 | + table.insert(channels, channel) | ||
89 | + end | ||
90 | + end | ||
91 | + if #channels > 0 then | ||
92 | + mcast_util.sub_worlds(channels) | ||
93 | + end | ||
94 | +end | ||
95 | + | ||
96 | +local function __init__() | ||
97 | + skynet.dispatch("lua", function(_,_, command, ...) | ||
98 | + if command == "start" then | ||
99 | + skynet.ret(skynet.pack(start(...))) | ||
100 | + end | ||
101 | + end) | ||
102 | +end | ||
103 | + | ||
104 | +skynet.start(__init__) |
@@ -0,0 +1,45 @@ | @@ -0,0 +1,45 @@ | ||
1 | +--[[ | ||
2 | + deque 为了减少锁竞争,尽量保证 a服务 push;b服务 pop | ||
3 | +]] | ||
4 | +local skynet = require "skynet" | ||
5 | +local deque = require "deque" | ||
6 | + | ||
7 | +local CMD = {} | ||
8 | +local factory | ||
9 | + | ||
10 | +local dead = 0 | ||
11 | +-- agent死亡,通知poold补充,当累计到5个agent时,马上生成5个agent放入poold中 | ||
12 | +-- 当然这里也可以写得更复杂,参考redis落地规则 | ||
13 | +function CMD.feed() | ||
14 | + dead = dead + 1 | ||
15 | + if dead == 5 then | ||
16 | + dead = 0 | ||
17 | + for i=1, 5 do | ||
18 | + factory:push(skynet.newservice("agent")) | ||
19 | + end | ||
20 | + end | ||
21 | +end | ||
22 | + | ||
23 | +-- 系统启动,生成count个agent放入双端队列中 | ||
24 | +function CMD.start(count) | ||
25 | + factory, addr = deque.new(count) | ||
26 | + | ||
27 | + for i=1, count do | ||
28 | + factory:push(skynet.newservice("agent")) | ||
29 | + end | ||
30 | + | ||
31 | + return addr | ||
32 | +end | ||
33 | + | ||
34 | +local function __init__() | ||
35 | + skynet.dispatch("lua", function (_, _, command, ...) | ||
36 | + local f = CMD[command] | ||
37 | + if command == "start" then | ||
38 | + skynet.ret(skynet.pack(f(...))) | ||
39 | + return | ||
40 | + end | ||
41 | + f(...) | ||
42 | + end) | ||
43 | +end | ||
44 | + | ||
45 | +skynet.start(__init__) |
@@ -0,0 +1,39 @@ | @@ -0,0 +1,39 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +require "skynet.manager" | ||
3 | +local redis = require "skynet.db.redis" | ||
4 | + | ||
5 | +local db | ||
6 | + | ||
7 | +local command = {} | ||
8 | + | ||
9 | +function command.open(conf) | ||
10 | + db = redis.connect({ | ||
11 | + host = conf.redishost, | ||
12 | + port = conf.redisport, | ||
13 | + db = conf.redisdb or 0, | ||
14 | + auth = conf.auth, | ||
15 | + }) | ||
16 | + | ||
17 | + --[[ | ||
18 | + local csvdata = require("csvdata.world_boss_battle") | ||
19 | + for i=1, #csvdata do | ||
20 | + for j=1, #csvdata[i] do | ||
21 | + db:del(string.format("boss:%d:%d", i, j)) | ||
22 | + end | ||
23 | + end | ||
24 | + --]] | ||
25 | + db:del("rtpvp") | ||
26 | + | ||
27 | +end | ||
28 | + | ||
29 | +skynet.start(function() | ||
30 | + skynet.dispatch("lua", function(session, address, cmd, ...) | ||
31 | + if cmd == "open" then | ||
32 | + local f = command[string.lower(cmd)] | ||
33 | + skynet.ret(skynet.pack(f(...))) | ||
34 | + else | ||
35 | + skynet.ret(skynet.pack(db[string.lower(cmd)](db, ...))) | ||
36 | + end | ||
37 | + end) | ||
38 | + skynet.register "REDIS" | ||
39 | +end) | ||
0 | \ No newline at end of file | 40 | \ No newline at end of file |
@@ -0,0 +1,143 @@ | @@ -0,0 +1,143 @@ | ||
1 | +require "shared.init" | ||
2 | +require "utils.init" | ||
3 | +require "ProtocolCode" | ||
4 | +require "GlobalVar" | ||
5 | +require "RedisKeys" | ||
6 | +require "skynet.manager" | ||
7 | + | ||
8 | +local sharedata = require "sharedata" | ||
9 | +local redisproxy = require "shared.redisproxy" | ||
10 | +local datacenter = require "datacenter" | ||
11 | +local queue = require "skynet.queue" | ||
12 | + | ||
13 | +skynet = require "skynet" | ||
14 | +globalCsv = require "csvdata.GlobalDefine" | ||
15 | + | ||
16 | +local table_pack = table.pack | ||
17 | +local table_unpack = table.unpack | ||
18 | +local string_format = string.format | ||
19 | +local pairs = pairs | ||
20 | +local ipairs = ipairs | ||
21 | +local tonumber = tonumber | ||
22 | + | ||
23 | +-- 维护在线状态 | ||
24 | + | ||
25 | +local unionInfo, CMD, cs = {}, {} | ||
26 | + | ||
27 | +skynet.register_protocol { | ||
28 | + name = "role", | ||
29 | + id = 12, | ||
30 | + pack = skynet.pack, | ||
31 | + unpack = skynet.unpack, | ||
32 | +} | ||
33 | + | ||
34 | +local function handle_timeout() | ||
35 | + local now = skynet.timex() | ||
36 | + unionInfo:onTimer(now) | ||
37 | + skynet.timeout(100, handle_timeout) | ||
38 | +end | ||
39 | + | ||
40 | +--[[ | ||
41 | +getProperties({"field1", "field2", ...}) | ||
42 | +return: {field1 = value1, field2 = value2, ...} | ||
43 | +------- | ||
44 | +setProperties({field1 = value1, field2 = value2, ...}) | ||
45 | +]] | ||
46 | +function rpcRole(roleId, funcName, ...) | ||
47 | + if not unionInfo.members[roleId] then return end | ||
48 | + local serv = unionInfo.members[roleId].serv | ||
49 | + if serv then | ||
50 | + if funcName == "getProperties" then | ||
51 | + return skynet.call(serv, "role", funcName, table_unpack(...)) | ||
52 | + else | ||
53 | + return skynet.call(serv, "role", funcName, ...) | ||
54 | + end | ||
55 | + else | ||
56 | + local rediskey = string_format("role:%d", roleId) | ||
57 | + if funcName == "getProperty" then | ||
58 | + return redisproxy:hget(rediskey, ...) | ||
59 | + elseif funcName == "setProperty" then | ||
60 | + return redisproxy:hset(rediskey, ...) | ||
61 | + elseif funcName == "getProperties" then | ||
62 | + local sRole = require "models.Role" | ||
63 | + local fields = table_pack(...) | ||
64 | + local rets = redisproxy:hmget(rediskey, table_unpack(fields, 1, fields.n)) | ||
65 | + local result = {} | ||
66 | + for i=1, fields.n do | ||
67 | + local typ = sRole.schema[fields[i]][1] | ||
68 | + local def = sRole.schema[fields[i]][2] | ||
69 | + if typ == "number" then | ||
70 | + result[fields[i]] = tonumber(rets[i] or def) | ||
71 | + else | ||
72 | + result[fields[i]] = rets[i] | ||
73 | + end | ||
74 | + end | ||
75 | + return result | ||
76 | + elseif funcName == "setProperties" then | ||
77 | + local fields = ... | ||
78 | + local params = {} | ||
79 | + for field, value in pairs(fields) do | ||
80 | + params[#params+1] = field | ||
81 | + params[#params+1] = value | ||
82 | + end | ||
83 | + return redisproxy:hmset(rediskey, table_unpack(params)) | ||
84 | + end | ||
85 | + end | ||
86 | +end | ||
87 | + | ||
88 | +-- 加载联盟数据 | ||
89 | +function CMD.load(unionId) | ||
90 | + unionInfo = require("models.Union").new({key = UNION_KEY:format(unionId)}) | ||
91 | + unionInfo:load() | ||
92 | + unionInfo:loadMembers() | ||
93 | +end | ||
94 | + | ||
95 | +-- 创建一个联盟 | ||
96 | +function CMD.new(roleId) | ||
97 | + local unionId = redisproxy:hincrby("autoincrement_set", "union", 1) | ||
98 | + unionInfo = require("models.Union").new({ | ||
99 | + key = UNION_KEY:format(unionId), | ||
100 | + master = roleId | ||
101 | + }) | ||
102 | + unionInfo:create() | ||
103 | + unionInfo:addMember(roleId, true) | ||
104 | + redisproxy:sadd(UNION_SET, unionId) | ||
105 | + return unionId | ||
106 | +end | ||
107 | + | ||
108 | +-- 登录/登出 需要向联盟服务报备 | ||
109 | +function CMD.sign(cmd, roleId, serv) | ||
110 | + if not unionInfo.members[roleId] then return end | ||
111 | + if cmd == "login" then | ||
112 | + unionInfo.members[roleId].serv = serv | ||
113 | + elseif cmd == "logout" then | ||
114 | + unionInfo.members[roleId].serv = nil | ||
115 | + end | ||
116 | +end | ||
117 | + | ||
118 | +local function __init__() | ||
119 | + skynet.dispatch("lua", function(_, _, command, ...) | ||
120 | + cs(function (...) | ||
121 | + if CMD[command] then | ||
122 | + skynet.ret(skynet.pack(CMD[command](...))) | ||
123 | + return | ||
124 | + else | ||
125 | + if unionInfo and unionInfo[submethod] then | ||
126 | + if command == 'dismiss' then | ||
127 | + unionInfo:dismiss(...) | ||
128 | + return | ||
129 | + end | ||
130 | + local result = unionInfo[submethod](unionInfo, ...) | ||
131 | + skynet.ret(skynet.pack(result)) | ||
132 | + return | ||
133 | + end | ||
134 | + end | ||
135 | + skynet.error("uniond commond error cmd=", command) | ||
136 | + end) | ||
137 | + end) | ||
138 | + skynet.register("UNIOND") | ||
139 | + csvdb = sharedata.query("csvdata") | ||
140 | + cs = queue() | ||
141 | +end | ||
142 | + | ||
143 | +skynet.start(__init__) | ||
0 | \ No newline at end of file | 144 | \ No newline at end of file |
@@ -0,0 +1,125 @@ | @@ -0,0 +1,125 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +local redisproxy = require "shared.redisproxy" | ||
3 | +local socket = require "skynet.socket" | ||
4 | +local netpack = require "skynet.netpack" | ||
5 | +local datacenter = require "skynet.datacenter" | ||
6 | +local snax = require "skynet.snax" | ||
7 | +local agent_ctrl = require "services.agent_ctrl" | ||
8 | +local xxtea = require "xxtea" | ||
9 | +local mc = require "skynet.multicast" | ||
10 | + | ||
11 | +require "ProtocolCode" | ||
12 | +require "GlobalVar" | ||
13 | +require "shared.init" | ||
14 | +require "utils.init" | ||
15 | + | ||
16 | +local CMD, SOCKET = {}, {} | ||
17 | +local globald | ||
18 | + | ||
19 | +local pool_size = tonumber(...) | ||
20 | + | ||
21 | +function SOCKET.open(fd, addr) | ||
22 | + skynet.call(gate_serv, "lua", "accept" , fd) | ||
23 | + agent_ctrl:socket_open(fd, addr) | ||
24 | +end | ||
25 | + | ||
26 | +function SOCKET.close(fd) | ||
27 | + print("socket close", fd) | ||
28 | + agent_ctrl:socket_close(fd) | ||
29 | +end | ||
30 | + | ||
31 | +function SOCKET.error(fd, msg) | ||
32 | + print("socket error",fd, msg) | ||
33 | + agent_ctrl:socket_error(fd) | ||
34 | +end | ||
35 | + | ||
36 | +function SOCKET.data(fd, msg) | ||
37 | + local cmd = string.unpack("H", string.sub(msg, 1, 2)) | ||
38 | + if cmd == actionCodes.Role_queryLoginRpc then | ||
39 | + local data = MsgPack.unpack(xxtea.decrypt(string.sub(msg, 3), XXTEA_KEY)) | ||
40 | + -- TODO: 先检测uid的合法性 | ||
41 | + if not data or not data.uid then return end | ||
42 | + agent_ctrl:query_agent(fd, data.uid) | ||
43 | + end | ||
44 | +end | ||
45 | + | ||
46 | +local use_logd = tonumber(skynet.getenv "logd") | ||
47 | + | ||
48 | +-- @desc: agent状态定时检测 | ||
49 | +function check_agent_status() | ||
50 | + pcall(agent_ctrl.check_agent_status, agent_ctrl) | ||
51 | + skynet.timeout(1, check_agent_status) | ||
52 | +end | ||
53 | + | ||
54 | +-- 创建world以及union channel 用于广播 | ||
55 | +function create_mutilcast() | ||
56 | + for i = 1, 10 do | ||
57 | + local chan_w = mc:new() | ||
58 | + datacenter.set("MC_W_CHANNEL" .. i, chan_w.channel) | ||
59 | + end | ||
60 | +end | ||
61 | + | ||
62 | +function CMD.start(conf) | ||
63 | + skynet.call(gate_serv, "lua", "open" , conf) | ||
64 | + skynet.call(redisd, "lua", "open", conf) | ||
65 | + | ||
66 | + if use_logd == 1 then | ||
67 | + skynet.call(logd, "lua", "open", conf) | ||
68 | + end | ||
69 | + -- 开启agent状态检测定时器 | ||
70 | + check_agent_status() | ||
71 | + -- 创建广播服务 | ||
72 | + create_mutilcast() | ||
73 | + | ||
74 | + skynet.call(conf.ngxd, "lua", "start") | ||
75 | + | ||
76 | + -- roomServer = skynet.newservice("services/roomServer") | ||
77 | + -- skynet.call(roomServer, "lua", "start") | ||
78 | + | ||
79 | + globald = skynet.newservice("services/globald") | ||
80 | + skynet.call(globald, "lua", "start") | ||
81 | + | ||
82 | + skynet.newservice("services/dbseed") | ||
83 | + | ||
84 | +end | ||
85 | + | ||
86 | +function CMD.forceClose(fd) | ||
87 | + agent_ctrl:exit_agent(fd) | ||
88 | +end | ||
89 | + | ||
90 | +skynet.start(function() | ||
91 | + skynet.dispatch("lua", function(session, source, cmd, subcmd, ...) | ||
92 | + if cmd == "socket" then | ||
93 | + local f = SOCKET[subcmd] | ||
94 | + f(...) | ||
95 | + -- socket api don't need return | ||
96 | + else | ||
97 | + local f = assert(CMD[cmd]) | ||
98 | + skynet.ret(skynet.pack(f(subcmd, ...))) | ||
99 | + end | ||
100 | + end) | ||
101 | + -- 数据库服务 | ||
102 | + redisd = skynet.newservice("services/redisd") | ||
103 | + | ||
104 | + -- load all csv data | ||
105 | + skynet.newservice("services/csvdatad") | ||
106 | + print("launch csvdatad ...") | ||
107 | + | ||
108 | + -- 日志服务 | ||
109 | + if use_logd == 1 then | ||
110 | + logd = skynet.newservice("services/logd") | ||
111 | + end | ||
112 | + | ||
113 | + local poold = skynet.newservice("services/poold") | ||
114 | + local obj = skynet.call(poold, "lua", "start", pool_size) | ||
115 | + | ||
116 | + agent_ctrl:init(obj, poold) | ||
117 | + | ||
118 | + print(string.format("launch %d agent at the beginning", pool_size)) | ||
119 | + | ||
120 | + -- 全局工具函数 | ||
121 | + skynet.newservice("services/named") | ||
122 | + skynet.newservice("services/chated") | ||
123 | + -- 网关服务 | ||
124 | + gate_serv = skynet.newservice("gate") | ||
125 | +end) |
@@ -0,0 +1,240 @@ | @@ -0,0 +1,240 @@ | ||
1 | +local ModelBase = class("ModelBase") | ||
2 | +ModelBase.key = "key" | ||
3 | +ModelBase.schema = { | ||
4 | + key = {"string"} | ||
5 | +} | ||
6 | +ModelBase.fields = {} -- 数据库字段 field, update 是否立即更新 | ||
7 | + | ||
8 | +local string_format = string.format | ||
9 | +local table_insert = table.insert | ||
10 | +local table_unpack = table.unpack | ||
11 | +local assert = assert | ||
12 | +local next = next | ||
13 | +local ipairs = ipairs | ||
14 | +local pairs = pairs | ||
15 | +local tostring = tostring | ||
16 | +local tonumber = tonumber | ||
17 | +local redisproxy = redisproxy | ||
18 | + | ||
19 | +local function filterProperties(properties, filter) | ||
20 | + for i, field in ipairs(filter) do | ||
21 | + properties[field] = nil | ||
22 | + end | ||
23 | +end | ||
24 | + | ||
25 | +function ModelBase:ctor(properties) | ||
26 | + self.isModelBase_ = true | ||
27 | + -- self.dirtyFields = {} | ||
28 | + | ||
29 | + if type(properties) ~= "table" then properties = {} end | ||
30 | + self:setProperties(properties, true) --缺少的域将设置默认值 | ||
31 | +end | ||
32 | + | ||
33 | +--[[-- | ||
34 | + | ||
35 | +返回对象的 ID 值。 | ||
36 | + | ||
37 | +**Returns:** | ||
38 | + | ||
39 | +- ID 值 | ||
40 | + | ||
41 | +]] | ||
42 | +function ModelBase:getKey() | ||
43 | + local id = self[self.class.key .. "_"] | ||
44 | + assert(id ~= nil, string_format("%s [%s:getKey()] Invalid key", tostring(self), self.class.__cname)) | ||
45 | + return id | ||
46 | +end | ||
47 | + | ||
48 | +function ModelBase:load(properties) | ||
49 | + if not self:isValidKey() then | ||
50 | + print(string_format("%s [%s:id] should be set before load", tostring(self), self.class.__cname)) | ||
51 | + return false | ||
52 | + end | ||
53 | + | ||
54 | + if not properties then | ||
55 | + properties = redisproxy:hgetall(self:getKey()) | ||
56 | + end | ||
57 | + | ||
58 | + if not next(properties) then return false end | ||
59 | + | ||
60 | + self:setProperties(properties, true) | ||
61 | + | ||
62 | + self:onLoad() | ||
63 | + | ||
64 | + return true | ||
65 | +end | ||
66 | + | ||
67 | +--创建model对应的redis数据, 必须已经设置了ID | ||
68 | +function ModelBase:create() | ||
69 | + if not self:isValidKey() then | ||
70 | + print(string_format("%s [%s:key] should be set before create", tostring(self), self.class.__cname)) | ||
71 | + return nil | ||
72 | + end | ||
73 | + | ||
74 | + --将所有的域都置为dirty, 存储到redis | ||
75 | + -- for fieldName, update in pairs(self.class.fields) do | ||
76 | + -- self.dirtyFields[fieldName] = true | ||
77 | + -- end | ||
78 | + self:save() | ||
79 | + self:onCreate() | ||
80 | + | ||
81 | + return self | ||
82 | +end | ||
83 | + | ||
84 | +function ModelBase:save() | ||
85 | + local redisProperties = self:getProperties() | ||
86 | + | ||
87 | + local params = {} | ||
88 | + for fieldName, value in pairs(redisProperties) do | ||
89 | + -- if self.dirtyFields[fieldName] then | ||
90 | + local propname = fieldName .. "_" | ||
91 | + table_insert(params, fieldName) | ||
92 | + table_insert(params, self[propname]) | ||
93 | + -- end | ||
94 | + end | ||
95 | + if next(params) then | ||
96 | + redisproxy:hmset(self:getKey(), table_unpack(params)) | ||
97 | + end | ||
98 | +end | ||
99 | + | ||
100 | +--[[-- | ||
101 | + | ||
102 | +确定对象是否设置了有效的 key。 | ||
103 | + | ||
104 | +]] | ||
105 | +function ModelBase:isValidKey() | ||
106 | + local propname = self.class.key .. "_" | ||
107 | + local key = self[propname] | ||
108 | + return type(key) == "string" and key ~= "" | ||
109 | +end | ||
110 | + | ||
111 | +--[[-- | ||
112 | + | ||
113 | +修改对象的属性。 | ||
114 | +NOTE: 如果properties缺少schema中的域, 将用默认值来填充 | ||
115 | + | ||
116 | +**Parameters:** | ||
117 | + | ||
118 | +- properties: 包含属性值的数组 | ||
119 | + | ||
120 | +]] | ||
121 | +function ModelBase:setProperties(properties, notWrite) | ||
122 | + assert(type(properties) == "table", "Invalid properties") | ||
123 | + -- string_format("%s [%s:setProperties()] Invalid properties", tostring(self), self.class.__cname)) | ||
124 | + | ||
125 | + local params = {} | ||
126 | + for field, schema in pairs(self.class.schema) do | ||
127 | + local typ, def = table_unpack(schema) | ||
128 | + local propname = field .. "_" | ||
129 | + | ||
130 | + local val = properties[field] or def | ||
131 | + if val ~= nil then | ||
132 | + if typ == "number" then val = tonumber(val) end | ||
133 | + assert(type(val) == typ, | ||
134 | + string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s", | ||
135 | + tostring(self), self.class.__cname, field, typ, type(val))) | ||
136 | + self[propname] = val | ||
137 | + table_insert(params, field) | ||
138 | + table_insert(params, val) | ||
139 | + end | ||
140 | + end | ||
141 | + if not notWrite and next(params) then | ||
142 | + redisproxy:hmset(self:getKey(), table_unpack(params)) | ||
143 | + end | ||
144 | +end | ||
145 | + | ||
146 | +--[[-- | ||
147 | + | ||
148 | +取得对象的属性值。 | ||
149 | + | ||
150 | +**Parameters:** | ||
151 | + | ||
152 | +- fields: 要取得哪些属性的值,如果未指定该参数,则返回 fields 中设定的属性 | ||
153 | +- filter: 要从结果中过滤掉哪些属性,如果未指定则不过滤 | ||
154 | + | ||
155 | +**Returns:** | ||
156 | + | ||
157 | +- 包含属性值的数组 | ||
158 | + | ||
159 | +]] | ||
160 | +function ModelBase:getProperties(fields, filter) | ||
161 | + local schema = self.class.schema | ||
162 | + if type(fields) ~= "table" then fields = table.keys(self.class.fields) end | ||
163 | + | ||
164 | + local properties = {} | ||
165 | + for i, field in ipairs(fields) do | ||
166 | + local propname = field .. "_" | ||
167 | + local typ = schema[field][1] | ||
168 | + local val = self[propname] | ||
169 | + assert(type(val) == typ, | ||
170 | + string_format("%s [%s:getProperties()] Type mismatch, %s expected %s, actual is %s", | ||
171 | + tostring(self), self.class.__cname, field, typ, type(val))) | ||
172 | + properties[field] = val | ||
173 | + end | ||
174 | + | ||
175 | + if type(filter) == "table" then | ||
176 | + filterProperties(properties, filter) | ||
177 | + end | ||
178 | + | ||
179 | + return properties | ||
180 | +end | ||
181 | + | ||
182 | +function ModelBase:getProperty(property) | ||
183 | + if type(property) ~= "string" then return nil end | ||
184 | + if not self.class.schema[property] then return nil end | ||
185 | + return self:getProperties({property})[property] | ||
186 | +end | ||
187 | + | ||
188 | +function ModelBase:setProperty(property, value, update) | ||
189 | + if not self.class.schema[property] then | ||
190 | + print(string_format("%s [%s:setProperty()] Invalid property : %s", | ||
191 | + tostring(self), self.class.__cname, property)) | ||
192 | + return | ||
193 | + end | ||
194 | + | ||
195 | + local typ, def = table_unpack(self.class.schema[property]) | ||
196 | + local propname = property .. "_" | ||
197 | + | ||
198 | + if typ == "number" then value = tonumber(value) end | ||
199 | + assert(type(value) == typ, | ||
200 | + string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s", | ||
201 | + tostring(self), self.class.__cname, property, typ, type(value))) | ||
202 | + self[propname] = value | ||
203 | + | ||
204 | + if self.class.fields[property] or update then | ||
205 | + redisproxy:hset(self:getKey(), property, value) | ||
206 | + else | ||
207 | + -- self.dirtyFields[property] = true -- record the fields been modified | ||
208 | + end | ||
209 | +end | ||
210 | + | ||
211 | +function ModelBase:incrProperty(property, value, update) | ||
212 | + if not self.class.schema[property] then | ||
213 | + print(string_format("%s [%s:setProperty()] Invalid property : %s", | ||
214 | + tostring(self), self.class.__cname, property)) | ||
215 | + return | ||
216 | + end | ||
217 | + | ||
218 | + local typ, def = table_unpack(self.class.schema[property]) | ||
219 | + local propname = property .. "_" | ||
220 | + | ||
221 | + if typ == "number" then value = tonumber(value) end | ||
222 | + assert(type(value) == typ, | ||
223 | + string_format("%s [%s:setProperties()] Type mismatch, %s expected %s, actual is %s", | ||
224 | + tostring(self), self.class.__cname, property, typ, type(value))) | ||
225 | + self[propname] = self[propname] + value | ||
226 | + | ||
227 | + if self.class.fields[property] or update then | ||
228 | + return redisproxy:hincrby(self:getKey(), property, value) | ||
229 | + else | ||
230 | + -- self.dirtyFields[property] = true -- record the fields been modified | ||
231 | + end | ||
232 | +end | ||
233 | + | ||
234 | +function ModelBase:onLoad() | ||
235 | +end | ||
236 | + | ||
237 | +function ModelBase:onCreate() | ||
238 | +end | ||
239 | + | ||
240 | +return ModelBase | ||
0 | \ No newline at end of file | 241 | \ No newline at end of file |
@@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
1 | +local crypto = {} | ||
2 | + | ||
3 | +function crypto.encryptXXTEA(plaintext, key) | ||
4 | + return CCCrypto:encryptXXTEALua(plaintext, string.len(plaintext), key, string.len(key)) | ||
5 | +end | ||
6 | + | ||
7 | +function crypto.decryptXXTEA(ciphertext, key) | ||
8 | + return CCCrypto:decryptXXTEALua(ciphertext, string.len(ciphertext), key, string.len(key)) | ||
9 | +end | ||
10 | + | ||
11 | +function crypto.encodeBase64(plaintext) | ||
12 | + return CCCrypto:encodeBase64Lua(plaintext, string.len(plaintext)) | ||
13 | +end | ||
14 | + | ||
15 | +function crypto.decodeBase64(ciphertext) | ||
16 | + return CCCrypto:decodeBase64Lua(ciphertext) | ||
17 | +end | ||
18 | + | ||
19 | +function crypto.md5(input, isRawOutput) | ||
20 | + if type(isRawOutput) ~= "boolean" then isRawOutput = false end | ||
21 | + return CCCrypto:MD5Lua(input, isRawOutput) | ||
22 | +end | ||
23 | + | ||
24 | +return crypto |
@@ -0,0 +1,327 @@ | @@ -0,0 +1,327 @@ | ||
1 | +--[[ | ||
2 | + | ||
3 | +Copyright (c) 2011-2012 qeeplay.com | ||
4 | + | ||
5 | +http://dualface.github.com/quick-cocos2d-x/ | ||
6 | + | ||
7 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
8 | +of this software and associated documentation files (the "Software"), to deal | ||
9 | +in the Software without restriction, including without limitation the rights | ||
10 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
11 | +copies of the Software, and to permit persons to whom the Software is | ||
12 | +furnished to do so, subject to the following conditions: | ||
13 | + | ||
14 | +The above copyright notice and this permission notice shall be included in | ||
15 | +all copies or substantial portions of the Software. | ||
16 | + | ||
17 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
20 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
22 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
23 | +THE SOFTWARE. | ||
24 | + | ||
25 | +]] | ||
26 | + | ||
27 | +--[[-- | ||
28 | + | ||
29 | +Debug functions. | ||
30 | + | ||
31 | +## Functions ## | ||
32 | + | ||
33 | +- echo | ||
34 | +- echoInfo | ||
35 | +- echoError | ||
36 | +- printf | ||
37 | + | ||
38 | +]] | ||
39 | +local skynet = require "skynet" | ||
40 | +if ngx and ngx.say then | ||
41 | + echo_ = ngx.say | ||
42 | +elseif CCLuaLog then | ||
43 | + echo_ = CCLuaLog | ||
44 | +end | ||
45 | +if not echo_ then echo_ = print end | ||
46 | +if not echof_ then echof_ = skynet.error end | ||
47 | + | ||
48 | +io.output():setvbuf('no') | ||
49 | + | ||
50 | +function echo(...) | ||
51 | + local arr = {} | ||
52 | + for i, a in ipairs({...}) do | ||
53 | + arr[#arr + 1] = tostring(a) | ||
54 | + end | ||
55 | + | ||
56 | + echo_(table.concat(arr, "\t")) | ||
57 | +end | ||
58 | + | ||
59 | +function echof( ... ) | ||
60 | + local arr = {} | ||
61 | + for i, a in ipairs({...}) do | ||
62 | + arr[#arr + 1] = tostring(a) | ||
63 | + end | ||
64 | + | ||
65 | + echof_(table.concat(arr, "\t")) | ||
66 | +end | ||
67 | + | ||
68 | +if CCLuaLog then print = echo end | ||
69 | + | ||
70 | +--[[-- | ||
71 | + | ||
72 | +Output a formatted string. | ||
73 | + | ||
74 | +Depends on the platform, output to console or log file. @see echo(). | ||
75 | + | ||
76 | +@param string format | ||
77 | +@param mixed ... | ||
78 | + | ||
79 | +@see echo | ||
80 | + | ||
81 | +]] | ||
82 | +function printf(fmt, ...) | ||
83 | + echo(string.format(tostring(fmt), ...)) | ||
84 | +end | ||
85 | + | ||
86 | +function echoError(fmt, ...) | ||
87 | + echo(string.format("[ERR] %s%s", string.format(tostring(fmt), ...), debug.traceback("", 2))) | ||
88 | +end | ||
89 | + | ||
90 | +function echoInfo(fmt, ...) | ||
91 | + echo("[INFO] " .. string.format(tostring(fmt), ...)) | ||
92 | +end | ||
93 | + | ||
94 | +function echoLog(tag, fmt, ...) | ||
95 | + echo(string.format("[%s] %s", string.upper(tostring(tag)), string.format(tostring(fmt), ...))) | ||
96 | +end | ||
97 | + | ||
98 | +function getPackageName(moduleName) | ||
99 | + local packageName = "" | ||
100 | + local pos = string.find(string.reverse(moduleName), "%.") | ||
101 | + if pos then | ||
102 | + packageName = string.sub(moduleName, 1, string.len(moduleName) - pos + 1) | ||
103 | + end | ||
104 | + return packageName | ||
105 | +end | ||
106 | + | ||
107 | +--[[-- | ||
108 | + | ||
109 | +Dumps information about a variable. | ||
110 | + | ||
111 | +@param mixed object | ||
112 | +@param string label | ||
113 | +@param bool isReturnContents | ||
114 | +@param int nesting | ||
115 | +@return nil|string | ||
116 | + | ||
117 | +]] | ||
118 | +function dump(object, label, isReturnContents, nesting) | ||
119 | + if type(nesting) ~= "number" then nesting = 99 end | ||
120 | + | ||
121 | + local lookupTable = {} | ||
122 | + local result = {} | ||
123 | + | ||
124 | + local function _v(v) | ||
125 | + if type(v) == "string" then | ||
126 | + v = "\"" .. v .. "\"" | ||
127 | + end | ||
128 | + return tostring(v) | ||
129 | + end | ||
130 | + | ||
131 | + local traceback = string.split(debug.traceback("", 2), "\n") | ||
132 | + echo("dump from: " .. string.trim(traceback[3])) | ||
133 | + | ||
134 | + local function _dump(object, label, indent, nest, keylen) | ||
135 | + label = label or "<var>" | ||
136 | + spc = "" | ||
137 | + if type(keylen) == "number" then | ||
138 | + spc = string.rep(" ", keylen - string.len(_v(label))) | ||
139 | + end | ||
140 | + if type(object) ~= "table" then | ||
141 | + result[#result +1 ] = string.format("%s%s%s = %s", indent, _v(label), spc, _v(object)) | ||
142 | + elseif lookupTable[object] then | ||
143 | + result[#result +1 ] = string.format("%s%s%s = *REF*", indent, label, spc) | ||
144 | + else | ||
145 | + lookupTable[object] = true | ||
146 | + if nest > nesting then | ||
147 | + result[#result +1 ] = string.format("%s%s = *MAX NESTING*", indent, label) | ||
148 | + else | ||
149 | + result[#result +1 ] = string.format("%s%s = {", indent, _v(label)) | ||
150 | + local indent2 = indent.." " | ||
151 | + local keys = {} | ||
152 | + local keylen = 0 | ||
153 | + local values = {} | ||
154 | + for k, v in pairs(object) do | ||
155 | + keys[#keys + 1] = k | ||
156 | + local vk = _v(k) | ||
157 | + local vkl = string.len(vk) | ||
158 | + if vkl > keylen then keylen = vkl end | ||
159 | + values[k] = v | ||
160 | + end | ||
161 | + table.sort(keys, function(a, b) | ||
162 | + if type(a) == "number" and type(b) == "number" then | ||
163 | + return a < b | ||
164 | + else | ||
165 | + return tostring(a) < tostring(b) | ||
166 | + end | ||
167 | + end) | ||
168 | + for i, k in ipairs(keys) do | ||
169 | + _dump(values[k], k, indent2, nest + 1, keylen) | ||
170 | + end | ||
171 | + result[#result +1] = string.format("%s}", indent) | ||
172 | + end | ||
173 | + end | ||
174 | + end | ||
175 | + _dump(object, label, "- ", 1) | ||
176 | + | ||
177 | + if isReturnContents then | ||
178 | + return table.concat(result, "\n") | ||
179 | + end | ||
180 | + | ||
181 | + for i, line in ipairs(result) do | ||
182 | + echo(line) | ||
183 | + end | ||
184 | +end | ||
185 | + | ||
186 | + | ||
187 | +--[[-- | ||
188 | + | ||
189 | +Dumps information about a variable into file. | ||
190 | + | ||
191 | +@param mixed object | ||
192 | +@param string label | ||
193 | +@param bool isReturnContents | ||
194 | +@param int nesting | ||
195 | +@return nil|string | ||
196 | + | ||
197 | +]] | ||
198 | +function dump2file(object, label, isReturnContents, nesting) | ||
199 | + if type(nesting) ~= "number" then nesting = 99 end | ||
200 | + | ||
201 | + local lookupTable = {} | ||
202 | + local result = {} | ||
203 | + | ||
204 | + local function _v(v) | ||
205 | + if type(v) == "string" then | ||
206 | + v = "\"" .. v .. "\"" | ||
207 | + end | ||
208 | + return tostring(v) | ||
209 | + end | ||
210 | + | ||
211 | + local traceback = string.split(debug.traceback("", 2), "\n") | ||
212 | + echo("dump from: " .. string.trim(traceback[3])) | ||
213 | + | ||
214 | + local function _dump(object, label, indent, nest, keylen) | ||
215 | + label = label or "<var>" | ||
216 | + spc = "" | ||
217 | + if type(keylen) == "number" then | ||
218 | + spc = string.rep(" ", keylen - string.len(_v(label))) | ||
219 | + end | ||
220 | + if type(object) ~= "table" then | ||
221 | + result[#result +1 ] = string.format("%s%s%s = %s", indent, _v(label), spc, _v(object)) | ||
222 | + elseif lookupTable[object] then | ||
223 | + result[#result +1 ] = string.format("%s%s%s = *REF*", indent, label, spc) | ||
224 | + else | ||
225 | + lookupTable[object] = true | ||
226 | + if nest > nesting then | ||
227 | + result[#result +1 ] = string.format("%s%s = *MAX NESTING*", indent, label) | ||
228 | + else | ||
229 | + result[#result +1 ] = string.format("%s%s = {", indent, _v(label)) | ||
230 | + local indent2 = indent.." " | ||
231 | + local keys = {} | ||
232 | + local keylen = 0 | ||
233 | + local values = {} | ||
234 | + for k, v in pairs(object) do | ||
235 | + keys[#keys + 1] = k | ||
236 | + local vk = _v(k) | ||
237 | + local vkl = string.len(vk) | ||
238 | + if vkl > keylen then keylen = vkl end | ||
239 | + values[k] = v | ||
240 | + end | ||
241 | + table.sort(keys, function(a, b) | ||
242 | + if type(a) == "number" and type(b) == "number" then | ||
243 | + return a < b | ||
244 | + else | ||
245 | + return tostring(a) < tostring(b) | ||
246 | + end | ||
247 | + end) | ||
248 | + for i, k in ipairs(keys) do | ||
249 | + _dump(values[k], k, indent2, nest + 1, keylen) | ||
250 | + end | ||
251 | + result[#result +1] = string.format("%s}", indent) | ||
252 | + end | ||
253 | + end | ||
254 | + end | ||
255 | + _dump(object, label, "- ", 1) | ||
256 | + | ||
257 | + if isReturnContents then | ||
258 | + return table.concat(result, "\n") | ||
259 | + end | ||
260 | + | ||
261 | + for i, line in ipairs(result) do | ||
262 | + echof(line) | ||
263 | + end | ||
264 | +end | ||
265 | + | ||
266 | +--[[-- | ||
267 | + | ||
268 | +Outputs or returns a parsable string representation of a variable. | ||
269 | + | ||
270 | +@param mixed object | ||
271 | +@param string label | ||
272 | +@return string | ||
273 | + | ||
274 | +]] | ||
275 | +function vardump(object, label) | ||
276 | + local lookupTable = {} | ||
277 | + local result = {} | ||
278 | + | ||
279 | + local function _v(v) | ||
280 | + if type(v) == "string" then | ||
281 | + v = "\"" .. v .. "\"" | ||
282 | + end | ||
283 | + return tostring(v) | ||
284 | + end | ||
285 | + | ||
286 | + local function _vardump(object, label, indent, nest) | ||
287 | + label = label or "<var>" | ||
288 | + local postfix = "" | ||
289 | + if nest > 1 then postfix = "," end | ||
290 | + if type(object) ~= "table" then | ||
291 | + if type(label) == "string" then | ||
292 | + result[#result +1] = string.format("%s%s = %s%s", indent, label, _v(object), postfix) | ||
293 | + else | ||
294 | + result[#result +1] = string.format("%s%s%s", indent, _v(object), postfix) | ||
295 | + end | ||
296 | + elseif not lookupTable[object] then | ||
297 | + lookupTable[object] = true | ||
298 | + | ||
299 | + if type(label) == "string" then | ||
300 | + result[#result +1 ] = string.format("%s%s = {", indent, label) | ||
301 | + else | ||
302 | + result[#result +1 ] = string.format("%s{", indent) | ||
303 | + end | ||
304 | + local indent2 = indent .. " " | ||
305 | + local keys = {} | ||
306 | + local values = {} | ||
307 | + for k, v in pairs(object) do | ||
308 | + keys[#keys + 1] = k | ||
309 | + values[k] = v | ||
310 | + end | ||
311 | + table.sort(keys, function(a, b) | ||
312 | + if type(a) == "number" and type(b) == "number" then | ||
313 | + return a < b | ||
314 | + else | ||
315 | + return tostring(a) < tostring(b) | ||
316 | + end | ||
317 | + end) | ||
318 | + for i, k in ipairs(keys) do | ||
319 | + _vardump(values[k], k, indent2, nest + 1) | ||
320 | + end | ||
321 | + result[#result +1] = string.format("%s}%s", indent, postfix) | ||
322 | + end | ||
323 | + end | ||
324 | + _vardump(object, label, "", 1) | ||
325 | + | ||
326 | + return table.concat(result, "\n") | ||
327 | +end |
@@ -0,0 +1,846 @@ | @@ -0,0 +1,846 @@ | ||
1 | +--[[ | ||
2 | + | ||
3 | +Copyright (c) 2011-2012 qeeplay.com | ||
4 | + | ||
5 | +http://dualface.github.com/quick-cocos2d-x/ | ||
6 | + | ||
7 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
8 | +of this software and associated documentation files (the "Software"), to deal | ||
9 | +in the Software without restriction, including without limitation the rights | ||
10 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
11 | +copies of the Software, and to permit persons to whom the Software is | ||
12 | +furnished to do so, subject to the following conditions: | ||
13 | + | ||
14 | +The above copyright notice and this permission notice shall be included in | ||
15 | +all copies or substantial portions of the Software. | ||
16 | + | ||
17 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
20 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
22 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
23 | +THE SOFTWARE. | ||
24 | + | ||
25 | +]] | ||
26 | + | ||
27 | +--[[-- | ||
28 | + | ||
29 | +Convert to number. | ||
30 | + | ||
31 | +@param mixed v | ||
32 | +@return number | ||
33 | + | ||
34 | +]] | ||
35 | +function tonum(v, default) | ||
36 | + default = default or 0 | ||
37 | + return tonumber(v) or default | ||
38 | +end | ||
39 | + | ||
40 | +--[[-- | ||
41 | + | ||
42 | +Convert to integer. | ||
43 | + | ||
44 | +@param mixed v | ||
45 | +@return number(integer) | ||
46 | + | ||
47 | +]] | ||
48 | +function toint(v) | ||
49 | + return math.round(tonumber(v)) | ||
50 | +end | ||
51 | + | ||
52 | +--[[-- | ||
53 | + | ||
54 | +Convert to boolean. | ||
55 | + | ||
56 | +@param mixed v | ||
57 | +@return boolean | ||
58 | + | ||
59 | +]] | ||
60 | +function tobool(v) | ||
61 | + return (v ~= nil and v ~= false) | ||
62 | +end | ||
63 | + | ||
64 | +--[[-- | ||
65 | + | ||
66 | +Convert to table. | ||
67 | + | ||
68 | +@param mixed v | ||
69 | +@return table | ||
70 | + | ||
71 | +]] | ||
72 | +function totable(v) | ||
73 | + if type(v) ~= "table" then v = {} end | ||
74 | + return v | ||
75 | +end | ||
76 | + | ||
77 | +--[[-- | ||
78 | + | ||
79 | +Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string). string.format() alias. | ||
80 | + | ||
81 | +@param string format | ||
82 | +@param mixed ... | ||
83 | +@return string | ||
84 | + | ||
85 | +]] | ||
86 | +function format(...) | ||
87 | + return string.format(...) | ||
88 | +end | ||
89 | + | ||
90 | +--[[-- | ||
91 | + | ||
92 | +Creating a copy of an table with fully replicated properties. | ||
93 | + | ||
94 | +**Usage:** | ||
95 | + | ||
96 | + -- Creating a reference of an table: | ||
97 | + local t1 = {a = 1, b = 2} | ||
98 | + local t2 = t1 | ||
99 | + t2.b = 3 -- t1 = {a = 1, b = 3} <-- t1.b changed | ||
100 | + | ||
101 | + -- Createing a copy of an table: | ||
102 | + local t1 = {a = 1, b = 2} | ||
103 | + local t2 = clone(t1) | ||
104 | + t2.b = 3 -- t1 = {a = 1, b = 2} <-- t1.b no change | ||
105 | + | ||
106 | + | ||
107 | +@param mixed object | ||
108 | +@return mixed | ||
109 | + | ||
110 | +]] | ||
111 | +function clone(object) | ||
112 | + local lookup_table = {} | ||
113 | + local function _copy(object) | ||
114 | + if type(object) ~= "table" then | ||
115 | + return object | ||
116 | + elseif lookup_table[object] then | ||
117 | + return lookup_table[object] | ||
118 | + end | ||
119 | + local new_table = {} | ||
120 | + lookup_table[object] = new_table | ||
121 | + for key, value in pairs(object) do | ||
122 | + new_table[_copy(key)] = _copy(value) | ||
123 | + end | ||
124 | + return setmetatable(new_table, getmetatable(object)) | ||
125 | + end | ||
126 | + return _copy(object) | ||
127 | +end | ||
128 | + | ||
129 | +--[[-- | ||
130 | + | ||
131 | +Create an class. | ||
132 | + | ||
133 | +**Usage:** | ||
134 | + | ||
135 | + local Shape = class("Shape") | ||
136 | + | ||
137 | + -- base class | ||
138 | + function Shape:ctor(shapeName) | ||
139 | + self.shapeName = shapeName | ||
140 | + printf("Shape:ctor(%s)", self.shapeName) | ||
141 | + end | ||
142 | + | ||
143 | + function Shape:draw() | ||
144 | + printf("draw %s", self.shapeName) | ||
145 | + end | ||
146 | + | ||
147 | + -- | ||
148 | + | ||
149 | + local Circle = class("Circle", Shape) | ||
150 | + | ||
151 | + function Circle:ctor() | ||
152 | + Circle.super.ctor(self, "circle") -- call super-class method | ||
153 | + self.radius = 100 | ||
154 | + end | ||
155 | + | ||
156 | + function Circle:setRadius(radius) | ||
157 | + self.radius = radius | ||
158 | + end | ||
159 | + | ||
160 | + function Circle:draw() -- overrideing super-class method | ||
161 | + printf("draw %s, raidus = %0.2f", self.shapeName, self.raidus) | ||
162 | + end | ||
163 | + | ||
164 | + -- | ||
165 | + | ||
166 | + local Rectangle = class("Rectangle", Shape) | ||
167 | + | ||
168 | + function Rectangle:ctor() | ||
169 | + Rectangle.super.ctor(self, "rectangle") | ||
170 | + end | ||
171 | + | ||
172 | + -- | ||
173 | + | ||
174 | + local circle = Circle.new() -- output: Shape:ctor(circle) | ||
175 | + circle:setRaidus(200) | ||
176 | + circle:draw() -- output: draw circle, radius = 200.00 | ||
177 | + | ||
178 | + local rectangle = Rectangle.new() -- output: Shape:ctor(rectangle) | ||
179 | + rectangle:draw() -- output: draw rectangle | ||
180 | + | ||
181 | + | ||
182 | +@param string classname | ||
183 | +@param table|function super-class | ||
184 | +@return table | ||
185 | + | ||
186 | +]] | ||
187 | +function class(classname, super) | ||
188 | + local superType = type(super) | ||
189 | + local cls | ||
190 | + | ||
191 | + if superType ~= "function" and superType ~= "table" then | ||
192 | + superType = nil | ||
193 | + super = nil | ||
194 | + end | ||
195 | + | ||
196 | + if superType == "function" or (super and super.__ctype == 1) then | ||
197 | + -- inherited from native C++ Object | ||
198 | + cls = {} | ||
199 | + | ||
200 | + if superType == "table" then | ||
201 | + -- copy fields from super | ||
202 | + for k,v in pairs(super) do cls[k] = v end | ||
203 | + cls.__create = super.__create | ||
204 | + cls.super = super | ||
205 | + else | ||
206 | + cls.__create = super | ||
207 | + cls.ctor = function() end | ||
208 | + end | ||
209 | + | ||
210 | + cls.__cname = classname | ||
211 | + cls.__ctype = 1 | ||
212 | + | ||
213 | + function cls.new(...) | ||
214 | + local instance = cls.__create(...) | ||
215 | + -- copy fields from class to native object | ||
216 | + for k,v in pairs(cls) do instance[k] = v end | ||
217 | + instance.class = cls | ||
218 | + instance:ctor(...) | ||
219 | + return instance | ||
220 | + end | ||
221 | + | ||
222 | + else | ||
223 | + -- inherited from Lua Object | ||
224 | + if super then | ||
225 | + cls = clone(super) | ||
226 | + cls.super = super | ||
227 | + else | ||
228 | + cls = {ctor = function() end} | ||
229 | + end | ||
230 | + | ||
231 | + cls.__cname = classname | ||
232 | + cls.__ctype = 2 -- lua | ||
233 | + cls.__index = cls | ||
234 | + | ||
235 | + function cls.new(...) | ||
236 | + local instance = setmetatable({}, cls) | ||
237 | + instance.class = cls | ||
238 | + instance:ctor(...) | ||
239 | + return instance | ||
240 | + end | ||
241 | + end | ||
242 | + | ||
243 | + return cls | ||
244 | +end | ||
245 | + | ||
246 | +--[[-- | ||
247 | + | ||
248 | +]] | ||
249 | +function import(moduleName, currentModuleName) | ||
250 | + local currentModuleNameParts | ||
251 | + local moduleFullName = moduleName | ||
252 | + local offset = 1 | ||
253 | + | ||
254 | + while true do | ||
255 | + if string.byte(moduleName, offset) ~= 46 then -- . | ||
256 | + moduleFullName = string.sub(moduleName, offset) | ||
257 | + if currentModuleNameParts and #currentModuleNameParts > 0 then | ||
258 | + moduleFullName = table.concat(currentModuleNameParts, ".") .. "." .. moduleFullName | ||
259 | + end | ||
260 | + break | ||
261 | + end | ||
262 | + offset = offset + 1 | ||
263 | + | ||
264 | + if not currentModuleNameParts then | ||
265 | + if not currentModuleName then | ||
266 | + local n,v = debug.getlocal(3, 1) | ||
267 | + currentModuleName = v | ||
268 | + end | ||
269 | + | ||
270 | + currentModuleNameParts = string.split(currentModuleName, ".") | ||
271 | + end | ||
272 | + table.remove(currentModuleNameParts, #currentModuleNameParts) | ||
273 | + end | ||
274 | + | ||
275 | + return require(moduleFullName) | ||
276 | +end | ||
277 | + | ||
278 | +--[[-- | ||
279 | + | ||
280 | +]] | ||
281 | +function handler(target, method) | ||
282 | + return function(...) return method(target, ...) end | ||
283 | +end | ||
284 | + | ||
285 | +--[[-- | ||
286 | + | ||
287 | +]] | ||
288 | +function handlerObject(object) | ||
289 | + return function(event, ...) | ||
290 | + if object[event] then | ||
291 | + return object[event](object, ...) | ||
292 | + end | ||
293 | + end | ||
294 | +end | ||
295 | + | ||
296 | +--[[-- | ||
297 | + | ||
298 | +Returns a associative table containing the matching values. | ||
299 | + | ||
300 | +@param table arr | ||
301 | +@param table names | ||
302 | +@return array | ||
303 | + | ||
304 | +]] | ||
305 | +function export(arr, names) | ||
306 | + local args = {} | ||
307 | + for k, def in pairs(names) do | ||
308 | + if type(k) == "number" then | ||
309 | + args[def] = arr[def] | ||
310 | + else | ||
311 | + args[k] = arr[k] or def | ||
312 | + end | ||
313 | + end | ||
314 | + return args | ||
315 | +end | ||
316 | + | ||
317 | +--[[-- | ||
318 | + | ||
319 | +hecks if the given key or index exists in the table. | ||
320 | + | ||
321 | +@param table arr | ||
322 | +@param mixed key | ||
323 | +@return boolean | ||
324 | + | ||
325 | +]] | ||
326 | +function isset(arr, key) | ||
327 | + return type(arr) == "table" and arr[key] ~= nil | ||
328 | +end | ||
329 | + | ||
330 | +--[[-- | ||
331 | + | ||
332 | +Rounds a float. | ||
333 | + | ||
334 | +@param number num | ||
335 | +@return number(integer) | ||
336 | + | ||
337 | +]] | ||
338 | +function math.round(num) | ||
339 | + return math.floor(num + 0.5) | ||
340 | +end | ||
341 | + | ||
342 | +--[[-- | ||
343 | + | ||
344 | +Checks whether a file exists. | ||
345 | + | ||
346 | +@param string path | ||
347 | +@return boolean | ||
348 | + | ||
349 | +]] | ||
350 | +function io.exists(path) | ||
351 | + local file = io.open(path, "r") | ||
352 | + if file then | ||
353 | + io.close(file) | ||
354 | + return true | ||
355 | + end | ||
356 | + return false | ||
357 | +end | ||
358 | + | ||
359 | +--[[-- | ||
360 | + | ||
361 | +Reads entire file into a string, or return FALSE on failure. | ||
362 | + | ||
363 | +@param string path | ||
364 | +@return string | ||
365 | + | ||
366 | +]] | ||
367 | +function io.readfile(path) | ||
368 | + local file = io.open(path, "r") | ||
369 | + if file then | ||
370 | + local content = file:read("*a") | ||
371 | + io.close(file) | ||
372 | + return content | ||
373 | + end | ||
374 | + return nil | ||
375 | +end | ||
376 | + | ||
377 | +--[[-- | ||
378 | + | ||
379 | +Write a string to a file, or return FALSE on failure. | ||
380 | + | ||
381 | +@param string path | ||
382 | +@param string content | ||
383 | +@param string mode | ||
384 | +@return boolean | ||
385 | + | ||
386 | +### Note: | ||
387 | +The mode string can be any of the following: | ||
388 | + "r": read mode | ||
389 | + "w": write mode; | ||
390 | + "a": append mode; | ||
391 | + "r+": update mode, all previous data is preserved; | ||
392 | + "w+": update mode, all previous data is erased; (the default); | ||
393 | + "a+": append update mode, previous data is preserved, writing is only allowed at the end of file. | ||
394 | + | ||
395 | +]] | ||
396 | +function io.writefile(path, content, mode) | ||
397 | + mode = mode or "w+" | ||
398 | + local file = io.open(path, mode) | ||
399 | + if file then | ||
400 | + if file:write(content) == nil then return false end | ||
401 | + io.close(file) | ||
402 | + return true | ||
403 | + else | ||
404 | + return false | ||
405 | + end | ||
406 | +end | ||
407 | + | ||
408 | +--[[-- | ||
409 | + | ||
410 | +Returns information about a file path. | ||
411 | + | ||
412 | +**Usage:** | ||
413 | + | ||
414 | + local path = "/var/app/test/abc.png" | ||
415 | + local pathinfo = io.pathinfo(path) | ||
416 | + -- pathinfo.dirname = "/var/app/test/" | ||
417 | + -- pathinfo.filename = "abc.png" | ||
418 | + -- pathinfo.basename = "abc" | ||
419 | + -- pathinfo.extname = ".png" | ||
420 | + | ||
421 | + | ||
422 | +@param string path | ||
423 | +@return table | ||
424 | + | ||
425 | +]] | ||
426 | +function io.pathinfo(path) | ||
427 | + local pos = string.len(path) | ||
428 | + local extpos = pos + 1 | ||
429 | + while pos > 0 do | ||
430 | + local b = string.byte(path, pos) | ||
431 | + if b == 46 then -- 46 = char "." | ||
432 | + extpos = pos | ||
433 | + elseif b == 47 then -- 47 = char "/" | ||
434 | + break | ||
435 | + end | ||
436 | + pos = pos - 1 | ||
437 | + end | ||
438 | + | ||
439 | + local dirname = string.sub(path, 1, pos) | ||
440 | + local filename = string.sub(path, pos + 1) | ||
441 | + extpos = extpos - pos | ||
442 | + local basename = string.sub(filename, 1, extpos - 1) | ||
443 | + local extname = string.sub(filename, extpos) | ||
444 | + return { | ||
445 | + dirname = dirname, | ||
446 | + filename = filename, | ||
447 | + basename = basename, | ||
448 | + extname = extname | ||
449 | + } | ||
450 | +end | ||
451 | + | ||
452 | +--[[-- | ||
453 | + | ||
454 | +Gets file size, or return FALSE on failure. | ||
455 | + | ||
456 | +@param string path | ||
457 | +@return number(integer) | ||
458 | + | ||
459 | +]] | ||
460 | +function io.filesize(path) | ||
461 | + local size = false | ||
462 | + local file = io.open(path, "r") | ||
463 | + if file then | ||
464 | + local current = file:seek() | ||
465 | + size = file:seek("end") | ||
466 | + file:seek("set", current) | ||
467 | + io.close(file) | ||
468 | + end | ||
469 | + return size | ||
470 | +end | ||
471 | + | ||
472 | +--[[-- | ||
473 | + | ||
474 | +Count all elements in an table. | ||
475 | + | ||
476 | +@param table t | ||
477 | +@return number(integer) | ||
478 | + | ||
479 | +]] | ||
480 | +function table.nums(t) | ||
481 | + local count = 0 | ||
482 | + for k, v in pairs(t) do | ||
483 | + count = count + 1 | ||
484 | + end | ||
485 | + return count | ||
486 | +end | ||
487 | + | ||
488 | +--[[-- | ||
489 | + | ||
490 | +Return all the keys or a subset of the keys of an table. | ||
491 | + | ||
492 | +**Usage:** | ||
493 | + | ||
494 | + local t = {a = 1, b = 2, c = 3} | ||
495 | + local keys = table.keys(t) | ||
496 | + -- keys = {"a", "b", "c"} | ||
497 | + | ||
498 | + | ||
499 | +@param table t | ||
500 | +@return table | ||
501 | + | ||
502 | +]] | ||
503 | +function table.keys(t) | ||
504 | + local keys = {} | ||
505 | + for k, v in pairs(t) do | ||
506 | + keys[#keys + 1] = k | ||
507 | + end | ||
508 | + return keys | ||
509 | +end | ||
510 | + | ||
511 | +--[[-- | ||
512 | + | ||
513 | +Return all the values of an table. | ||
514 | + | ||
515 | +**Usage:** | ||
516 | + | ||
517 | + local t = {a = "1", b = "2", c = "3"} | ||
518 | + local values = table.values(t) | ||
519 | + -- values = {1, 2, 3} | ||
520 | + | ||
521 | + | ||
522 | +@param table t | ||
523 | +@return table | ||
524 | + | ||
525 | +]] | ||
526 | +function table.values(t) | ||
527 | + local values = {} | ||
528 | + for k, v in pairs(t) do | ||
529 | + values[#values + 1] = v | ||
530 | + end | ||
531 | + return values | ||
532 | +end | ||
533 | + | ||
534 | +--[[-- | ||
535 | + | ||
536 | +Merge tables. | ||
537 | + | ||
538 | +**Usage:** | ||
539 | + | ||
540 | + local dest = {a = 1, b = 2} | ||
541 | + local src = {c = 3, d = 4} | ||
542 | + table.merge(dest, src) | ||
543 | + -- dest = {a = 1, b = 2, c = 3, d = 4} | ||
544 | + | ||
545 | + | ||
546 | +@param table dest | ||
547 | +@param table src | ||
548 | + | ||
549 | +]] | ||
550 | +function table.merge(dest, src) | ||
551 | + for k, v in pairs(src) do | ||
552 | + dest[k] = v | ||
553 | + end | ||
554 | +end | ||
555 | + | ||
556 | + | ||
557 | +--[[-- | ||
558 | + | ||
559 | +insert list. | ||
560 | + | ||
561 | +**Usage:** | ||
562 | + | ||
563 | + local dest = {1, 2, 3} | ||
564 | + local src = {4, 5, 6} | ||
565 | + table.insertTo(dest, src) | ||
566 | + -- dest = {1, 2, 3, 4, 5, 6} | ||
567 | + dest = {1, 2, 3} | ||
568 | + table.insertTo(dest, src, 5) | ||
569 | + -- dest = {1, 2, 3, nil, 4, 5, 6} | ||
570 | + | ||
571 | + | ||
572 | +@param table dest | ||
573 | +@param table src | ||
574 | +@param table begin insert position for dest | ||
575 | +]] | ||
576 | +function table.insertTo(dest, src, begin) | ||
577 | + begin = tonumber(begin) | ||
578 | + if begin == nil then | ||
579 | + begin = #dest + 1 | ||
580 | + end | ||
581 | + | ||
582 | + local len = #src | ||
583 | + for i = 0, len - 1 do | ||
584 | + dest[i + begin] = src[i + 1] | ||
585 | + end | ||
586 | +end | ||
587 | + | ||
588 | +function table.maxkey(tb) | ||
589 | + local max = 0 | ||
590 | + for k, v in pairs(tb) do | ||
591 | + if k > max then | ||
592 | + max = k | ||
593 | + end | ||
594 | + end | ||
595 | + return max | ||
596 | +end | ||
597 | + | ||
598 | +function table.minkey(tb) | ||
599 | + local min = math.huge | ||
600 | + for k, v in pairs(tb) do | ||
601 | + if k < min then | ||
602 | + min = k | ||
603 | + end | ||
604 | + end | ||
605 | + return min | ||
606 | +end | ||
607 | + | ||
608 | +--[[-- | ||
609 | + | ||
610 | +Convert special characters to HTML entities. | ||
611 | + | ||
612 | +The translations performed are: | ||
613 | + | ||
614 | +- '&' (ampersand) becomes '&' | ||
615 | +- '"' (double quote) becomes '"' | ||
616 | +- "'" (single quote) becomes ''' | ||
617 | +- '<' (less than) becomes '<' | ||
618 | +- '>' (greater than) becomes '>' | ||
619 | + | ||
620 | +@param string input | ||
621 | +@return string | ||
622 | + | ||
623 | +]] | ||
624 | +function string.htmlspecialchars(input) | ||
625 | + for k, v in pairs(string._htmlspecialchars_set) do | ||
626 | + input = string.gsub(input, k, v) | ||
627 | + end | ||
628 | + return input | ||
629 | +end | ||
630 | +string._htmlspecialchars_set = {} | ||
631 | +string._htmlspecialchars_set["&"] = "&" | ||
632 | +string._htmlspecialchars_set["\""] = """ | ||
633 | +string._htmlspecialchars_set["'"] = "'" | ||
634 | +string._htmlspecialchars_set["<"] = "<" | ||
635 | +string._htmlspecialchars_set[">"] = ">" | ||
636 | + | ||
637 | +--[[-- | ||
638 | + | ||
639 | +Inserts HTML line breaks before all newlines in a string. | ||
640 | + | ||
641 | +Returns string with '<br />' inserted before all newlines (\n). | ||
642 | + | ||
643 | +@param string input | ||
644 | +@return string | ||
645 | + | ||
646 | +]] | ||
647 | +function string.nl2br(input) | ||
648 | + return string.gsub(input, "\n", "<br />") | ||
649 | +end | ||
650 | + | ||
651 | +--[[-- | ||
652 | + | ||
653 | +Returns a HTML entities formatted version of string. | ||
654 | + | ||
655 | +@param string input | ||
656 | +@return string | ||
657 | + | ||
658 | +]] | ||
659 | +function string.text2html(input) | ||
660 | + input = string.gsub(input, "\t", " ") | ||
661 | + input = string.htmlspecialchars(input) | ||
662 | + input = string.gsub(input, " ", " ") | ||
663 | + input = string.nl2br(input) | ||
664 | + return input | ||
665 | +end | ||
666 | + | ||
667 | +--[[-- | ||
668 | + | ||
669 | +Split a string by string. | ||
670 | + | ||
671 | +@param string str | ||
672 | +@param string delimiter | ||
673 | +@return table | ||
674 | + | ||
675 | +]] | ||
676 | +function string.split(str, delimiter) | ||
677 | + if (delimiter=='') then return false end | ||
678 | + local pos,arr = 0, {} | ||
679 | + -- for each divider found | ||
680 | + for st,sp in function() return string.find(str, delimiter, pos, true) end do | ||
681 | + table.insert(arr, string.sub(str, pos, st - 1)) | ||
682 | + pos = sp + 1 | ||
683 | + end | ||
684 | + table.insert(arr, string.sub(str, pos)) | ||
685 | + return arr | ||
686 | +end | ||
687 | + | ||
688 | +--[[-- | ||
689 | + | ||
690 | +Strip whitespace (or other characters) from the beginning of a string. | ||
691 | + | ||
692 | +@param string str | ||
693 | +@return string | ||
694 | + | ||
695 | +]] | ||
696 | +function string.ltrim(str) | ||
697 | + return string.gsub(str, "^[ \t\n\r]+", "") | ||
698 | +end | ||
699 | + | ||
700 | +--[[-- | ||
701 | + | ||
702 | +Strip whitespace (or other characters) from the end of a string. | ||
703 | + | ||
704 | +@param string str | ||
705 | +@return string | ||
706 | + | ||
707 | +]] | ||
708 | +function string.rtrim(str) | ||
709 | + return string.gsub(str, "[ \t\n\r]+$", "") | ||
710 | +end | ||
711 | + | ||
712 | +--[[-- | ||
713 | + | ||
714 | +Strip whitespace (or other characters) from the beginning and end of a string. | ||
715 | + | ||
716 | +@param string str | ||
717 | +@return string | ||
718 | + | ||
719 | +]] | ||
720 | +function string.trim(str) | ||
721 | + str = string.gsub(str, "^[ \t\n\r]+", "") | ||
722 | + return string.gsub(str, "[ \t\n\r]+$", "") | ||
723 | +end | ||
724 | + | ||
725 | +--[[-- | ||
726 | + | ||
727 | +Make a string's first character uppercase. | ||
728 | + | ||
729 | +@param string str | ||
730 | +@return string | ||
731 | + | ||
732 | +]] | ||
733 | +function string.ucfirst(str) | ||
734 | + return string.upper(string.sub(str, 1, 1)) .. string.sub(str, 2) | ||
735 | +end | ||
736 | + | ||
737 | +--[[-- | ||
738 | + | ||
739 | +@param string str | ||
740 | +@return string | ||
741 | + | ||
742 | +]] | ||
743 | +function string.urlencodeChar(char) | ||
744 | + return "%" .. string.format("%02X", string.byte(c)) | ||
745 | +end | ||
746 | + | ||
747 | +--[[-- | ||
748 | + | ||
749 | +URL-encodes string. | ||
750 | + | ||
751 | +@param string str | ||
752 | +@return string | ||
753 | + | ||
754 | +]] | ||
755 | +function string.urlencode(str) | ||
756 | + -- convert line endings | ||
757 | + str = string.gsub(tostring(str), "\n", "\r\n") | ||
758 | + -- escape all characters but alphanumeric, '.' and '-' | ||
759 | + str = string.gsub(str, "([^%w%.%- ])", string.urlencodeChar) | ||
760 | + -- convert spaces to "+" symbols | ||
761 | + return string.gsub(str, " ", "+") | ||
762 | +end | ||
763 | + | ||
764 | +--[[-- | ||
765 | + | ||
766 | +Get UTF8 string length. | ||
767 | + | ||
768 | +@param string str | ||
769 | +@return int | ||
770 | + | ||
771 | +]] | ||
772 | +function string.utf8len(str) | ||
773 | + local len = #str | ||
774 | + local left = len | ||
775 | + local cnt = 0 | ||
776 | + local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc} | ||
777 | + while left ~= 0 do | ||
778 | + local tmp = string.byte(str, -left) | ||
779 | + local i = #arr | ||
780 | + while arr[i] do | ||
781 | + if tmp >= arr[i] then | ||
782 | + left = left - i | ||
783 | + break | ||
784 | + end | ||
785 | + i = i - 1 | ||
786 | + end | ||
787 | + cnt = cnt + 1 | ||
788 | + end | ||
789 | + return cnt | ||
790 | +end | ||
791 | + | ||
792 | +--[[-- | ||
793 | + | ||
794 | +Return formatted string with a comma (",") between every group of thousands. | ||
795 | + | ||
796 | +**Usage:** | ||
797 | + | ||
798 | + local value = math.comma("232423.234") -- value = "232,423.234" | ||
799 | + | ||
800 | + | ||
801 | +@param number num | ||
802 | +@return string | ||
803 | + | ||
804 | +]] | ||
805 | +function string.formatNumberThousands(num) | ||
806 | + local formatted = tostring(tonumber(num)) | ||
807 | + while true do | ||
808 | + formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') | ||
809 | + if k == 0 then break end | ||
810 | + end | ||
811 | + return formatted | ||
812 | +end | ||
813 | + | ||
814 | +function table.find(t, item) | ||
815 | + return table.keyOfItem(t, item) ~= nil | ||
816 | +end | ||
817 | + | ||
818 | +function table.keyOfItem(t, item) | ||
819 | + for k,v in pairs(t) do | ||
820 | + if v == item then return k end | ||
821 | + end | ||
822 | + return nil | ||
823 | +end | ||
824 | + | ||
825 | +function table.removeItem(list, item, removeAll) | ||
826 | + local rmCount = 0 | ||
827 | + for i = 1, #list do | ||
828 | + if list[i - rmCount] == item then | ||
829 | + table.remove(list, i - rmCount) | ||
830 | + if removeAll then | ||
831 | + rmCount = rmCount + 1 | ||
832 | + else | ||
833 | + break | ||
834 | + end | ||
835 | + end | ||
836 | + end | ||
837 | +end | ||
838 | + | ||
839 | +function table.array2Table(arr) | ||
840 | + local ret = {} | ||
841 | + for i=1, #arr, 2 do | ||
842 | + ret[arr[i]] = arr[i+1] | ||
843 | + end | ||
844 | + return ret | ||
845 | +end | ||
846 | + |
@@ -0,0 +1,431 @@ | @@ -0,0 +1,431 @@ | ||
1 | +----------------------------------------------------------------------------- | ||
2 | +-- JSON4Lua: JSON encoding / decoding support for the Lua language. | ||
3 | +-- json Module. | ||
4 | +-- Author: Craig Mason-Jones | ||
5 | +-- Homepage: http://github.com/craigmj/json4lua/ | ||
6 | +-- Version: 1.0.0 | ||
7 | +-- This module is released under the MIT License (MIT). | ||
8 | +-- Please see LICENCE.txt for details. | ||
9 | +-- | ||
10 | +-- USAGE: | ||
11 | +-- This module exposes two functions: | ||
12 | +-- json.encode(o) | ||
13 | +-- Returns the table / string / boolean / number / nil / json.null value as a JSON-encoded string. | ||
14 | +-- json.decode(json_string) | ||
15 | +-- Returns a Lua object populated with the data encoded in the JSON string json_string. | ||
16 | +-- | ||
17 | +-- REQUIREMENTS: | ||
18 | +-- compat-5.1 if using Lua 5.0 | ||
19 | +-- | ||
20 | +-- CHANGELOG | ||
21 | +-- 0.9.20 Introduction of local Lua functions for private functions (removed _ function prefix). | ||
22 | +-- Fixed Lua 5.1 compatibility issues. | ||
23 | +-- Introduced json.null to have null values in associative arrays. | ||
24 | +-- json.encode() performance improvement (more than 50%) through table.concat rather than .. | ||
25 | +-- Introduced decode ability to ignore /**/ comments in the JSON string. | ||
26 | +-- 0.9.10 Fix to array encoding / decoding to correctly manage nil/null values in arrays. | ||
27 | +----------------------------------------------------------------------------- | ||
28 | + | ||
29 | +----------------------------------------------------------------------------- | ||
30 | +-- Imports and dependencies | ||
31 | +----------------------------------------------------------------------------- | ||
32 | +local math = require('math') | ||
33 | +local string = require("string") | ||
34 | +local table = require("table") | ||
35 | + | ||
36 | +----------------------------------------------------------------------------- | ||
37 | +-- Module declaration | ||
38 | +----------------------------------------------------------------------------- | ||
39 | +local json = {} -- Public namespace | ||
40 | +local json_private = {} -- Private namespace | ||
41 | + | ||
42 | +-- Public functions | ||
43 | + | ||
44 | +-- Private functions | ||
45 | +local decode_scanArray | ||
46 | +local decode_scanComment | ||
47 | +local decode_scanConstant | ||
48 | +local decode_scanNumber | ||
49 | +local decode_scanObject | ||
50 | +local decode_scanString | ||
51 | +local decode_scanWhitespace | ||
52 | +local encodeString | ||
53 | +local isArray | ||
54 | +local isEncodable | ||
55 | + | ||
56 | +table.getn = function (t) | ||
57 | + if t.n then | ||
58 | + return t.n | ||
59 | + else | ||
60 | + local n = 0 | ||
61 | + for i in pairs(t) do | ||
62 | + if type(i) == "number" then | ||
63 | + n = math.max(n, i) | ||
64 | + end | ||
65 | + end | ||
66 | + return n | ||
67 | + end | ||
68 | +end | ||
69 | + | ||
70 | +----------------------------------------------------------------------------- | ||
71 | +-- PUBLIC FUNCTIONS | ||
72 | +----------------------------------------------------------------------------- | ||
73 | +--- Encodes an arbitrary Lua object / variable. | ||
74 | +-- @param v The Lua object / variable to be JSON encoded. | ||
75 | +-- @return String containing the JSON encoding in internal Lua string format (i.e. not unicode) | ||
76 | +function json.encode (v) | ||
77 | + -- Handle nil values | ||
78 | + if v==nil then | ||
79 | + return "null" | ||
80 | + end | ||
81 | + | ||
82 | + local vtype = type(v) | ||
83 | + | ||
84 | + -- Handle strings | ||
85 | + if vtype=='string' then | ||
86 | + return '"' .. json_private.encodeString(v) .. '"' -- Need to handle encoding in string | ||
87 | + end | ||
88 | + | ||
89 | + -- Handle booleans | ||
90 | + if vtype=='number' or vtype=='boolean' then | ||
91 | + return tostring(v) | ||
92 | + end | ||
93 | + | ||
94 | + -- Handle tables | ||
95 | + if vtype=='table' then | ||
96 | + local rval = {} | ||
97 | + -- Consider arrays separately | ||
98 | + local bArray, maxCount = isArray(v) | ||
99 | + if bArray then | ||
100 | + for i = 1,maxCount do | ||
101 | + table.insert(rval, json.encode(v[i])) | ||
102 | + end | ||
103 | + else -- An object, not an array | ||
104 | + for i,j in pairs(v) do | ||
105 | + if isEncodable(i) and isEncodable(j) then | ||
106 | + table.insert(rval, '"' .. json_private.encodeString(i) .. '":' .. json.encode(j)) | ||
107 | + end | ||
108 | + end | ||
109 | + end | ||
110 | + if bArray then | ||
111 | + return '[' .. table.concat(rval,',') ..']' | ||
112 | + else | ||
113 | + return '{' .. table.concat(rval,',') .. '}' | ||
114 | + end | ||
115 | + end | ||
116 | + | ||
117 | + -- Handle null values | ||
118 | + if vtype=='function' and v==null then | ||
119 | + return 'null' | ||
120 | + end | ||
121 | + | ||
122 | + assert(false,'encode attempt to encode unsupported type ' .. vtype .. ':' .. tostring(v)) | ||
123 | +end | ||
124 | + | ||
125 | + | ||
126 | +--- Decodes a JSON string and returns the decoded value as a Lua data structure / value. | ||
127 | +-- @param s The string to scan. | ||
128 | +-- @param [startPos] Optional starting position where the JSON string is located. Defaults to 1. | ||
129 | +-- @param Lua object, number The object that was scanned, as a Lua table / string / number / boolean or nil, | ||
130 | +-- and the position of the first character after | ||
131 | +-- the scanned JSON object. | ||
132 | +function json.decode(s, startPos) | ||
133 | + startPos = startPos and startPos or 1 | ||
134 | + startPos = decode_scanWhitespace(s,startPos) | ||
135 | + assert(startPos<=string.len(s), 'Unterminated JSON encoded object found at position in [' .. s .. ']') | ||
136 | + local curChar = string.sub(s,startPos,startPos) | ||
137 | + -- Object | ||
138 | + if curChar=='{' then | ||
139 | + return decode_scanObject(s,startPos) | ||
140 | + end | ||
141 | + -- Array | ||
142 | + if curChar=='[' then | ||
143 | + return decode_scanArray(s,startPos) | ||
144 | + end | ||
145 | + -- Number | ||
146 | + if string.find("+-0123456789.e", curChar, 1, true) then | ||
147 | + return decode_scanNumber(s,startPos) | ||
148 | + end | ||
149 | + -- String | ||
150 | + if curChar==[["]] or curChar==[[']] then | ||
151 | + return decode_scanString(s,startPos) | ||
152 | + end | ||
153 | + if string.sub(s,startPos,startPos+1)=='/*' then | ||
154 | + return decode(s, decode_scanComment(s,startPos)) | ||
155 | + end | ||
156 | + -- Otherwise, it must be a constant | ||
157 | + return decode_scanConstant(s,startPos) | ||
158 | +end | ||
159 | + | ||
160 | +--- The null function allows one to specify a null value in an associative array (which is otherwise | ||
161 | +-- discarded if you set the value with 'nil' in Lua. Simply set t = { first=json.null } | ||
162 | +function null() | ||
163 | + return null -- so json.null() will also return null ;-) | ||
164 | +end | ||
165 | +----------------------------------------------------------------------------- | ||
166 | +-- Internal, PRIVATE functions. | ||
167 | +-- Following a Python-like convention, I have prefixed all these 'PRIVATE' | ||
168 | +-- functions with an underscore. | ||
169 | +----------------------------------------------------------------------------- | ||
170 | + | ||
171 | +--- Scans an array from JSON into a Lua object | ||
172 | +-- startPos begins at the start of the array. | ||
173 | +-- Returns the array and the next starting position | ||
174 | +-- @param s The string being scanned. | ||
175 | +-- @param startPos The starting position for the scan. | ||
176 | +-- @return table, int The scanned array as a table, and the position of the next character to scan. | ||
177 | +function decode_scanArray(s,startPos) | ||
178 | + local array = {} -- The return value | ||
179 | + local stringLen = string.len(s) | ||
180 | + assert(string.sub(s,startPos,startPos)=='[','decode_scanArray called but array does not start at position ' .. startPos .. ' in string:\n'..s ) | ||
181 | + startPos = startPos + 1 | ||
182 | + -- Infinite loop for array elements | ||
183 | + repeat | ||
184 | + startPos = decode_scanWhitespace(s,startPos) | ||
185 | + assert(startPos<=stringLen,'JSON String ended unexpectedly scanning array.') | ||
186 | + local curChar = string.sub(s,startPos,startPos) | ||
187 | + if (curChar==']') then | ||
188 | + return array, startPos+1 | ||
189 | + end | ||
190 | + if (curChar==',') then | ||
191 | + startPos = decode_scanWhitespace(s,startPos+1) | ||
192 | + end | ||
193 | + assert(startPos<=stringLen, 'JSON String ended unexpectedly scanning array.') | ||
194 | + object, startPos = json.decode(s,startPos) | ||
195 | + table.insert(array,object) | ||
196 | + until false | ||
197 | +end | ||
198 | + | ||
199 | +--- Scans a comment and discards the comment. | ||
200 | +-- Returns the position of the next character following the comment. | ||
201 | +-- @param string s The JSON string to scan. | ||
202 | +-- @param int startPos The starting position of the comment | ||
203 | +function decode_scanComment(s, startPos) | ||
204 | + assert( string.sub(s,startPos,startPos+1)=='/*', "decode_scanComment called but comment does not start at position " .. startPos) | ||
205 | + local endPos = string.find(s,'*/',startPos+2) | ||
206 | + assert(endPos~=nil, "Unterminated comment in string at " .. startPos) | ||
207 | + return endPos+2 | ||
208 | +end | ||
209 | + | ||
210 | +--- Scans for given constants: true, false or null | ||
211 | +-- Returns the appropriate Lua type, and the position of the next character to read. | ||
212 | +-- @param s The string being scanned. | ||
213 | +-- @param startPos The position in the string at which to start scanning. | ||
214 | +-- @return object, int The object (true, false or nil) and the position at which the next character should be | ||
215 | +-- scanned. | ||
216 | +function decode_scanConstant(s, startPos) | ||
217 | + local consts = { ["true"] = true, ["false"] = false, ["null"] = nil } | ||
218 | + local constNames = {"true","false","null"} | ||
219 | + | ||
220 | + for i,k in pairs(constNames) do | ||
221 | + if string.sub(s,startPos, startPos + string.len(k) -1 )==k then | ||
222 | + return consts[k], startPos + string.len(k) | ||
223 | + end | ||
224 | + end | ||
225 | + assert(nil, 'Failed to scan constant from string ' .. s .. ' at starting position ' .. startPos) | ||
226 | +end | ||
227 | + | ||
228 | +--- Scans a number from the JSON encoded string. | ||
229 | +-- (in fact, also is able to scan numeric +- eqns, which is not | ||
230 | +-- in the JSON spec.) | ||
231 | +-- Returns the number, and the position of the next character | ||
232 | +-- after the number. | ||
233 | +-- @param s The string being scanned. | ||
234 | +-- @param startPos The position at which to start scanning. | ||
235 | +-- @return number, int The extracted number and the position of the next character to scan. | ||
236 | +function decode_scanNumber(s,startPos) | ||
237 | + local endPos = startPos+1 | ||
238 | + local stringLen = string.len(s) | ||
239 | + local acceptableChars = "+-0123456789.e" | ||
240 | + while (string.find(acceptableChars, string.sub(s,endPos,endPos), 1, true) | ||
241 | + and endPos<=stringLen | ||
242 | + ) do | ||
243 | + endPos = endPos + 1 | ||
244 | + end | ||
245 | + local stringValue = 'return ' .. string.sub(s,startPos, endPos-1) | ||
246 | + local stringEval = load(stringValue) | ||
247 | + assert(stringEval, 'Failed to scan number [ ' .. stringValue .. '] in JSON string at position ' .. startPos .. ' : ' .. endPos) | ||
248 | + return stringEval(), endPos | ||
249 | +end | ||
250 | + | ||
251 | +--- Scans a JSON object into a Lua object. | ||
252 | +-- startPos begins at the start of the object. | ||
253 | +-- Returns the object and the next starting position. | ||
254 | +-- @param s The string being scanned. | ||
255 | +-- @param startPos The starting position of the scan. | ||
256 | +-- @return table, int The scanned object as a table and the position of the next character to scan. | ||
257 | +function decode_scanObject(s,startPos) | ||
258 | + local object = {} | ||
259 | + local stringLen = string.len(s) | ||
260 | + local key, value | ||
261 | + assert(string.sub(s,startPos,startPos)=='{','decode_scanObject called but object does not start at position ' .. startPos .. ' in string:\n' .. s) | ||
262 | + startPos = startPos + 1 | ||
263 | + repeat | ||
264 | + startPos = decode_scanWhitespace(s,startPos) | ||
265 | + assert(startPos<=stringLen, 'JSON string ended unexpectedly while scanning object.') | ||
266 | + local curChar = string.sub(s,startPos,startPos) | ||
267 | + if (curChar=='}') then | ||
268 | + return object,startPos+1 | ||
269 | + end | ||
270 | + if (curChar==',') then | ||
271 | + startPos = decode_scanWhitespace(s,startPos+1) | ||
272 | + end | ||
273 | + assert(startPos<=stringLen, 'JSON string ended unexpectedly scanning object.') | ||
274 | + -- Scan the key | ||
275 | + key, startPos = json.decode(s,startPos) | ||
276 | + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) | ||
277 | + startPos = decode_scanWhitespace(s,startPos) | ||
278 | + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) | ||
279 | + assert(string.sub(s,startPos,startPos)==':','JSON object key-value assignment mal-formed at ' .. startPos) | ||
280 | + startPos = decode_scanWhitespace(s,startPos+1) | ||
281 | + assert(startPos<=stringLen, 'JSON string ended unexpectedly searching for value of key ' .. key) | ||
282 | + value, startPos = json.decode(s,startPos) | ||
283 | + object[key]=value | ||
284 | + until false -- infinite loop while key-value pairs are found | ||
285 | +end | ||
286 | + | ||
287 | +-- START SoniEx2 | ||
288 | +-- Initialize some things used by decode_scanString | ||
289 | +-- You know, for efficiency | ||
290 | +local escapeSequences = { | ||
291 | + ["\\t"] = "\t", | ||
292 | + ["\\f"] = "\f", | ||
293 | + ["\\r"] = "\r", | ||
294 | + ["\\n"] = "\n", | ||
295 | + ["\\b"] = "\b" | ||
296 | +} | ||
297 | +setmetatable(escapeSequences, {__index = function(t,k) | ||
298 | + -- skip "\" aka strip escape | ||
299 | + return string.sub(k,2) | ||
300 | +end}) | ||
301 | +-- END SoniEx2 | ||
302 | + | ||
303 | +--- Scans a JSON string from the opening inverted comma or single quote to the | ||
304 | +-- end of the string. | ||
305 | +-- Returns the string extracted as a Lua string, | ||
306 | +-- and the position of the next non-string character | ||
307 | +-- (after the closing inverted comma or single quote). | ||
308 | +-- @param s The string being scanned. | ||
309 | +-- @param startPos The starting position of the scan. | ||
310 | +-- @return string, int The extracted string as a Lua string, and the next character to parse. | ||
311 | +function decode_scanString(s,startPos) | ||
312 | + assert(startPos, 'decode_scanString(..) called without start position') | ||
313 | + local startChar = string.sub(s,startPos,startPos) | ||
314 | + -- START SoniEx2 | ||
315 | + -- PS: I don't think single quotes are valid JSON | ||
316 | + assert(startChar == [["]] or startChar == [[']],'decode_scanString called for a non-string') | ||
317 | + --assert(startPos, "String decoding failed: missing closing " .. startChar .. " for string at position " .. oldStart) | ||
318 | + local t = {} | ||
319 | + local i,j = startPos,startPos | ||
320 | + while string.find(s, startChar, j+1) ~= j+1 do | ||
321 | + local oldj = j | ||
322 | + i,j = string.find(s, "\\.", j+1) | ||
323 | + local x,y = string.find(s, startChar, oldj+1) | ||
324 | + if not i or x < i then | ||
325 | + i,j = x,y-1 | ||
326 | + end | ||
327 | + table.insert(t, string.sub(s, oldj+1, i-1)) | ||
328 | + if string.sub(s, i, j) == "\\u" then | ||
329 | + local a = string.sub(s,j+1,j+4) | ||
330 | + j = j + 4 | ||
331 | + local n = tonumber(a, 16) | ||
332 | + assert(n, "String decoding failed: bad Unicode escape " .. a .. " at position " .. i .. " : " .. j) | ||
333 | + -- math.floor(x/2^y) == lazy right shift | ||
334 | + -- a % 2^b == bitwise_and(a, (2^b)-1) | ||
335 | + -- 64 = 2^6 | ||
336 | + -- 4096 = 2^12 (or 2^6 * 2^6) | ||
337 | + local x | ||
338 | + if n < 0x80 then | ||
339 | + x = string.char(n % 0x80) | ||
340 | + elseif n < 0x800 then | ||
341 | + -- [110x xxxx] [10xx xxxx] | ||
342 | + x = string.char(0xC0 + (math.floor(n/64) % 0x20), 0x80 + (n % 0x40)) | ||
343 | + else | ||
344 | + -- [1110 xxxx] [10xx xxxx] [10xx xxxx] | ||
345 | + x = string.char(0xE0 + (math.floor(n/4096) % 0x10), 0x80 + (math.floor(n/64) % 0x40), 0x80 + (n % 0x40)) | ||
346 | + end | ||
347 | + table.insert(t, x) | ||
348 | + else | ||
349 | + table.insert(t, escapeSequences[string.sub(s, i, j)]) | ||
350 | + end | ||
351 | + end | ||
352 | + table.insert(t,string.sub(j, j+1)) | ||
353 | + assert(string.find(s, startChar, j+1), "String decoding failed: missing closing " .. startChar .. " at position " .. j .. "(for string at position " .. startPos .. ")") | ||
354 | + return table.concat(t,""), j+2 | ||
355 | + -- END SoniEx2 | ||
356 | +end | ||
357 | + | ||
358 | +--- Scans a JSON string skipping all whitespace from the current start position. | ||
359 | +-- Returns the position of the first non-whitespace character, or nil if the whole end of string is reached. | ||
360 | +-- @param s The string being scanned | ||
361 | +-- @param startPos The starting position where we should begin removing whitespace. | ||
362 | +-- @return int The first position where non-whitespace was encountered, or string.len(s)+1 if the end of string | ||
363 | +-- was reached. | ||
364 | +function decode_scanWhitespace(s,startPos) | ||
365 | + local whitespace=" \n\r\t" | ||
366 | + local stringLen = string.len(s) | ||
367 | + while ( string.find(whitespace, string.sub(s,startPos,startPos), 1, true) and startPos <= stringLen) do | ||
368 | + startPos = startPos + 1 | ||
369 | + end | ||
370 | + return startPos | ||
371 | +end | ||
372 | + | ||
373 | +--- Encodes a string to be JSON-compatible. | ||
374 | +-- This just involves back-quoting inverted commas, back-quotes and newlines, I think ;-) | ||
375 | +-- @param s The string to return as a JSON encoded (i.e. backquoted string) | ||
376 | +-- @return The string appropriately escaped. | ||
377 | + | ||
378 | +local escapeList = { | ||
379 | + ['"'] = '\\"', | ||
380 | + ['\\'] = '\\\\', | ||
381 | + ['/'] = '\\/', | ||
382 | + ['\b'] = '\\b', | ||
383 | + ['\f'] = '\\f', | ||
384 | + ['\n'] = '\\n', | ||
385 | + ['\r'] = '\\r', | ||
386 | + ['\t'] = '\\t' | ||
387 | +} | ||
388 | + | ||
389 | +function json_private.encodeString(s) | ||
390 | + local s = tostring(s) | ||
391 | + return s:gsub(".", function(c) return escapeList[c] end) -- SoniEx2: 5.0 compat | ||
392 | +end | ||
393 | + | ||
394 | +-- Determines whether the given Lua type is an array or a table / dictionary. | ||
395 | +-- We consider any table an array if it has indexes 1..n for its n items, and no | ||
396 | +-- other data in the table. | ||
397 | +-- I think this method is currently a little 'flaky', but can't think of a good way around it yet... | ||
398 | +-- @param t The table to evaluate as an array | ||
399 | +-- @return boolean, number True if the table can be represented as an array, false otherwise. If true, | ||
400 | +-- the second returned value is the maximum | ||
401 | +-- number of indexed elements in the array. | ||
402 | +function isArray(t) | ||
403 | + -- Next we count all the elements, ensuring that any non-indexed elements are not-encodable | ||
404 | + -- (with the possible exception of 'n') | ||
405 | + local maxIndex = 0 | ||
406 | + for k,v in pairs(t) do | ||
407 | + if (type(k)=='number' and math.floor(k)==k and 1<=k) then -- k,v is an indexed pair | ||
408 | + if (not isEncodable(v)) then return false end -- All array elements must be encodable | ||
409 | + maxIndex = math.max(maxIndex,k) | ||
410 | + else | ||
411 | + if (k=='n') then | ||
412 | + if v ~= table.getn(t) then return false end -- False if n does not hold the number of elements | ||
413 | + else -- Else of (k=='n') | ||
414 | + if isEncodable(v) then return false end | ||
415 | + end -- End of (k~='n') | ||
416 | + end -- End of k,v not an indexed pair | ||
417 | + end -- End of loop across all pairs | ||
418 | + return true, maxIndex | ||
419 | +end | ||
420 | + | ||
421 | +--- Determines whether the given Lua object / table / variable can be JSON encoded. The only | ||
422 | +-- types that are JSON encodable are: string, boolean, number, nil, table and json.null. | ||
423 | +-- In this implementation, all other types are ignored. | ||
424 | +-- @param o The object to examine. | ||
425 | +-- @return boolean True if the object should be JSON encoded, false if it should be ignored. | ||
426 | +function isEncodable(o) | ||
427 | + local t = type(o) | ||
428 | + return (t=='string' or t=='boolean' or t=='number' or t=='nil' or t=='table') or (t=='function' and o==null) | ||
429 | +end | ||
430 | + | ||
431 | +return json | ||
0 | \ No newline at end of file | 432 | \ No newline at end of file |
@@ -0,0 +1,72 @@ | @@ -0,0 +1,72 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +local harbor = require "skynet.harbor" | ||
3 | + | ||
4 | +local table_insert = table.insert | ||
5 | + | ||
6 | +local redisproxy = {} | ||
7 | + | ||
8 | +setmetatable(redisproxy, { __index = function(t, k) | ||
9 | + local cmd = string.upper(k) | ||
10 | + local f = function (self, ...) | ||
11 | + local ok, result = pcall(skynet.call, redisd, "lua", cmd, ...) | ||
12 | + if not ok then | ||
13 | + skynet.error(cmd, ..., "\n", debug.traceback(coroutine.running(), nil)) | ||
14 | + return | ||
15 | + end | ||
16 | + return result | ||
17 | + end | ||
18 | + t[k] = f | ||
19 | + return f | ||
20 | +end}) | ||
21 | + | ||
22 | +function redisproxy:runScripts(name, ...) | ||
23 | + local RedisScripts = require("rdsscripts/RedisScripts") | ||
24 | + | ||
25 | + if not RedisScripts[name].sha1 then | ||
26 | + local content = io.readfile(RedisScripts[name].file) | ||
27 | + RedisScripts[name].sha1 = self:script("LOAD", content) | ||
28 | + end | ||
29 | + | ||
30 | + -- 不存在脚本(系统问题或者需要刷新脚本) | ||
31 | + local existScript = self:script("EXISTS", RedisScripts[name].sha1) | ||
32 | + if existScript[1] == 0 then | ||
33 | + local content = io.readfile(RedisScripts[name].file) | ||
34 | + RedisScripts[name].sha1 = self:script("LOAD", content) | ||
35 | + end | ||
36 | + | ||
37 | + return self:evalsha(RedisScripts[name].sha1, ...) | ||
38 | +end | ||
39 | + | ||
40 | +local meta = {__index = function (tab, name) return function (_, ...) tab[#tab+1]={name, ...} end end} | ||
41 | +function redisproxy:pipelining(block) | ||
42 | + local ops = setmetatable({{"multi"}}, meta) | ||
43 | + block(ops) | ||
44 | + if #ops == 1 then return end | ||
45 | + ops[#ops+1]={"exec"} | ||
46 | + return self:pipeline(ops) | ||
47 | +end | ||
48 | + | ||
49 | +function redisproxy:insertEmail(params) | ||
50 | + local pms = { | ||
51 | + roleId = params.roleId, | ||
52 | + emailId = params.emailId, | ||
53 | + createtime = params.createtime or skynet.timex(), | ||
54 | + con1 = params.con1 or "", | ||
55 | + con2 = params.con2 or "", | ||
56 | + con3 = params.con3 or "", | ||
57 | + att1 = params.att1 or "", | ||
58 | + att2 = params.att2 or "", | ||
59 | + att3 = params.att3 or "", | ||
60 | + title = params.title or "", | ||
61 | + content = params.content or "", | ||
62 | + attachments = params.attachments or "", | ||
63 | + } | ||
64 | + self:runScripts("insertEmail", 12, | ||
65 | + pms.roleId, pms.emailId, pms.createtime, | ||
66 | + pms.con1, pms.con2, pms.con3, | ||
67 | + pms.att1, pms.att2, pms.att3, | ||
68 | + pms.title, pms.content, pms.attachments) | ||
69 | + return true | ||
70 | +end | ||
71 | + | ||
72 | +return redisproxy | ||
0 | \ No newline at end of file | 73 | \ No newline at end of file |
@@ -0,0 +1,313 @@ | @@ -0,0 +1,313 @@ | ||
1 | +local skynet = require "skynet" | ||
2 | +local cjson = require "shared.json" | ||
3 | +local md5 = require("md5") | ||
4 | +local httpc = require("http.httpc") | ||
5 | + | ||
6 | +local servId = tonumber(skynet.getenv("servId")) | ||
7 | +function getRandomName() | ||
8 | + local nameCsv = csvdb["name_combCsv"] | ||
9 | + local part1 = nameCsv[1][math.random(1, #nameCsv[1])].name | ||
10 | + local part2 = nameCsv[2][math.random(1, #nameCsv[2])].name | ||
11 | + local part3 = nameCsv[3][math.random(1, #nameCsv[3])].name | ||
12 | + | ||
13 | + return table.concat({part1, part2, part3}) | ||
14 | +end | ||
15 | + | ||
16 | +function getActionCode(role) | ||
17 | + if not role.uniqueCount then | ||
18 | + role.uniqueCount = 0 | ||
19 | + end | ||
20 | + local action = {role:getProperty("id"), skynet.timex(), role.uniqueCount} | ||
21 | + role.uniqueCount = role.uniqueCount + 1 | ||
22 | + return table.concat(action, "_") | ||
23 | +end | ||
24 | + | ||
25 | +-- begin 数据库自增字段 | ||
26 | +function getNextRoleId() | ||
27 | + local roleId = redisproxy:hget("autoincrement_set", "role") | ||
28 | + if roleId - servId * MAX_ROLE_NUM >= MAX_ROLE_NUM - 1 then | ||
29 | + return | ||
30 | + end | ||
31 | + return redisproxy:hincrby("autoincrement_set", "role", 1) | ||
32 | +end | ||
33 | + | ||
34 | +function getNextTradeId() | ||
35 | + return redisproxy:hincrby("autoincrement_set", "trade", 1) | ||
36 | +end | ||
37 | + | ||
38 | +-- end 数据库自增字段 | ||
39 | + | ||
40 | +-- 查找上限 | ||
41 | +-- [10, 20, 30, 40] 查找15, 返回指向10的元素 | ||
42 | +function lowerBoundSeach(data, searchKey) | ||
43 | + -- 先排序 | ||
44 | + local lastKey = nil | ||
45 | + local keys = table.keys(data) | ||
46 | + table.sort(keys) | ||
47 | + for _, key in ipairs(keys) do | ||
48 | + if key > searchKey then | ||
49 | + break | ||
50 | + end | ||
51 | + lastKey = key | ||
52 | + end | ||
53 | + | ||
54 | + return lastKey and data[lastKey] or nil | ||
55 | +end | ||
56 | + | ||
57 | +-- 将201402021800或者20140202的格式转化成unixtime | ||
58 | +function toUnixtime(timeStr) | ||
59 | + local strLength = string.len(timeStr) | ||
60 | + if strLength ~= 8 and strLength ~= 10 then return end | ||
61 | + local year = string.sub(timeStr, 1, 4) | ||
62 | + local month = string.sub(timeStr, 5, 6) | ||
63 | + local day = string.sub(timeStr, 7, 8) | ||
64 | + local hour, minute = 0, 0 | ||
65 | + if strLength == 10 then | ||
66 | + hour = string.sub(timeStr, 9, 10) | ||
67 | + --minute = string.sub(timeStr, 11, 12) | ||
68 | + end | ||
69 | + return os.time{year=year, month=month, day=day, hour=hour, min=minute, sec=0} | ||
70 | +end | ||
71 | + | ||
72 | +-- 判断时间点是不是当天 | ||
73 | +function isToday(curTimestamp) | ||
74 | + local curTm = os.date("*t", curTimestamp) | ||
75 | + local nowTm = os.date("*t", skynet.timex()) | ||
76 | + return curTm.year == nowTm.year and curTm.month == nowTm.month and curTm.day == nowTm.day | ||
77 | +end | ||
78 | + | ||
79 | +function withTimeHead(value, now) | ||
80 | + now = now or skynet.timex() | ||
81 | + return (2000000000-now) .. "." .. value | ||
82 | +end | ||
83 | + | ||
84 | +function cutTimeHead(value) | ||
85 | + return tonumber(value:match("%d+%.(%d+)")) | ||
86 | +end | ||
87 | + | ||
88 | +-- 判断是不是同一个星期 | ||
89 | +function isCrossWeek(target, now) | ||
90 | + now = now or skynet.timex() | ||
91 | + return specMonday(target) ~= specMonday(now - RESET_TIME * 3600) | ||
92 | +end | ||
93 | + | ||
94 | +-- 两个时间相隔多少星期 | ||
95 | +function crossWeek(target, now) | ||
96 | + now = now or skynet.timex() | ||
97 | + return math.floor((specMonday(target)-specMonday(now))/604800) | ||
98 | +end | ||
99 | + | ||
100 | +-- 判断是不是同一个月 | ||
101 | +function isCrossMonth(target, now) | ||
102 | + now = now or skynet.timex() | ||
103 | + local tarTm = os.date("*t", target) | ||
104 | + local nowTm = os.date("*t", now - RESET_TIME * 3600) | ||
105 | + if tarTm.year == nowTm.year and tarTm.month == nowTm.month then | ||
106 | + return false | ||
107 | + else | ||
108 | + return true | ||
109 | + end | ||
110 | +end | ||
111 | + | ||
112 | +function isCrossDay(lastTime, now) | ||
113 | + if lastTime == 0 then return true end | ||
114 | + now = now or skynet.timex() | ||
115 | + local today4h = specTime({hour = RESET_TIME}, now - RESET_TIME * 3600) | ||
116 | + return lastTime < today4h and now >= today4h | ||
117 | +end | ||
118 | + | ||
119 | +function crossDay(target, now) | ||
120 | + now = now or skynet.timex() | ||
121 | + | ||
122 | + local tarTime = specTime({hour = RESET_TIME}, target) | ||
123 | + --local nowTime = specTime({hour = RESET_TIME}, now) --当前时间有没有过一天而不是当前天四点 | ||
124 | + return math.floor((now - tarTime)/ 86400) | ||
125 | +end | ||
126 | + | ||
127 | +function crossWeekFromOpen(now) | ||
128 | + now = now or skynet.timex() | ||
129 | + local openTime = os.time{ | ||
130 | + year = tonum(SERV_OPEN:sub(1,4)), | ||
131 | + month = tonum(SERV_OPEN:sub(5,6)), | ||
132 | + day = tonum(SERV_OPEN:sub(7,8)), | ||
133 | + } | ||
134 | + return crossWeek(openTime, now) | ||
135 | +end | ||
136 | + | ||
137 | +function crossDayFromOpen(now) | ||
138 | + now = now or skynet.timex() | ||
139 | + local openTime = os.time{ | ||
140 | + year = tonum(SERV_OPEN:sub(1,4)), | ||
141 | + month = tonum(SERV_OPEN:sub(5,6)), | ||
142 | + day = tonum(SERV_OPEN:sub(7,8)), | ||
143 | + hour = RESET_TIME, | ||
144 | + } | ||
145 | + return crossDay(openTime, now) | ||
146 | +end | ||
147 | + | ||
148 | +-- 30*86400 = 2592000 | ||
149 | +function monthLater(now) | ||
150 | + now = now or skynet.timex() | ||
151 | + now = now - RESET_TIME * 3600 -- 排除0-4点钟 影响 | ||
152 | + return specTime({hour = RESET_TIME}, now) + 2592000 | ||
153 | +end | ||
154 | +-- 一天以后 | ||
155 | +function dayLater(now) | ||
156 | + now = now or skynet.timex() | ||
157 | + now = now - RESET_TIME * 3600 -- 排除0-4点钟 影响 | ||
158 | + return specTime({hour = RESET_TIME}, now) + 86400 | ||
159 | +end | ||
160 | +-- 到下一个时间点的秒数差和下一个时间点的unixtime | ||
161 | +function diffTime(params) | ||
162 | + params = params or {} | ||
163 | + local currentTime = skynet.timex() | ||
164 | + | ||
165 | + local curTm = os.date("*t", currentTime) | ||
166 | + local nextYear = params.year or curTm.year | ||
167 | + local nextMonth = params.month or curTm.month | ||
168 | + local nextDay = params.day or curTm.day + 1 | ||
169 | + local nextHour = params.hour or 0 | ||
170 | + local nextMinute = params.min or 0 | ||
171 | + local nextSecond = params.sec or 0 | ||
172 | + | ||
173 | + local nextUnixTime = os.time({ year = nextYear, month = nextMonth, day = nextDay, hour = nextHour, min = nextMinute, sec = nextSecond}) | ||
174 | + return os.difftime(nextUnixTime, currentTime), nextUnixTime | ||
175 | +end | ||
176 | + | ||
177 | +-- 取今天特殊时刻时间戳 | ||
178 | +function specTime(pms, now) | ||
179 | + now = now or skynet.timex() | ||
180 | + local tm = os.date("*t", now) | ||
181 | + local year = pms.year or tm.year | ||
182 | + local month = pms.month or tm.month | ||
183 | + local day = pms.day or tm.day | ||
184 | + local hour = pms.hour or 0 | ||
185 | + local min = pms.min or 0 | ||
186 | + local sec = pms.sec or 0 | ||
187 | + return os.time({year = year, month = month, day = day, hour = hour, min = min, sec = sec}) | ||
188 | +end | ||
189 | + | ||
190 | +function specMonday(now) | ||
191 | + now = now or skynet.timex() | ||
192 | + local tm = os.date("*t", now) | ||
193 | + local wday = (tm.wday + 6) % 7 | ||
194 | + if wday == 0 then wday = 7 end | ||
195 | + | ||
196 | + local time = os.time({year = tm.year, month = tm.month, day = tm.day}) | ||
197 | + return time - (wday - 1) * 86400 | ||
198 | +end | ||
199 | + | ||
200 | +function isSpecTime(startHour, endHour, specday) | ||
201 | + local tm = os.date("*t", skynet.timex()) | ||
202 | + if specday then | ||
203 | + local day = (tm.wday+6)%7 | ||
204 | + if day == 0 then day = 7 end | ||
205 | + return specday == day and tm.hour >= startHour and tm.hour < endHour | ||
206 | + end | ||
207 | + return tm.hour >= startHour and tm.hour < endHour | ||
208 | +end | ||
209 | + | ||
210 | +function getSecond(timeStr) | ||
211 | + timeStr = timeStr or "0000" | ||
212 | + local hour = tonumber(string.sub(timeStr, 1, 2)) | ||
213 | + local min = tonumber(string.sub(timeStr, 3, 4)) | ||
214 | + return hour * 3600 + min * 60 | ||
215 | +end | ||
216 | + | ||
217 | +function urlencode(str) | ||
218 | + if (str) then | ||
219 | + str = string.gsub (str, "\n", "\r\n") | ||
220 | + str = string.gsub (str, "([^%w ])", | ||
221 | + function (c) return string.format ("%%%02X", string.byte(c)) end) | ||
222 | + str = string.gsub (str, " ", "+") | ||
223 | + end | ||
224 | + return str | ||
225 | +end | ||
226 | + | ||
227 | +function isnan(value) | ||
228 | + return value ~= value | ||
229 | +end | ||
230 | + | ||
231 | +-- 推送通知 | ||
232 | +local serverid = skynet.getenv "serverid" | ||
233 | +local secretKey = "467C2221D3A20FE69D23A33E8940C2C5" | ||
234 | + | ||
235 | +-- 推送该服务器的所有用户 | ||
236 | +-- tag都是交集处理 | ||
237 | +function notifyClients(msg, otherTags) | ||
238 | + local tags = { serverid } | ||
239 | + for _, tag in ipairs(otherTags or {}) do | ||
240 | + table.insert(tags, tag) | ||
241 | + end | ||
242 | + | ||
243 | + local content = { | ||
244 | + ["appid"] = "1000013239", | ||
245 | + ["audience"] = { | ||
246 | + [otherTags and "tag_and" or "tag"] = tags, | ||
247 | + }, | ||
248 | + -- ["audience"] = "all", | ||
249 | + ["notification"] = { | ||
250 | + ["alert"] = msg, | ||
251 | + }, | ||
252 | + ["options"] = { | ||
253 | + ["ttl"] = 60 * 120 | ||
254 | + } | ||
255 | + } | ||
256 | + | ||
257 | + local contentJson = cjson.encode(content) | ||
258 | + local header = { | ||
259 | + ["content-type"] = "application/x-www-form-urlencoded", | ||
260 | + ["X-MJPUSH-SIGNATURE"] = md5.sumhexa(urlencode(contentJson .. "&" .. secretKey)) | ||
261 | + } | ||
262 | + | ||
263 | + local status, body = httpc.request("POST", "push.mjyun.com", "/api/push", {}, header, contentJson) | ||
264 | + if tonumber(status) ~= 200 then | ||
265 | + skynet.error(status, body) | ||
266 | + end | ||
267 | +end | ||
268 | + | ||
269 | +-- { uid, msg, scheduleTime} | ||
270 | +function notifyClient(params) | ||
271 | + params = params or {} | ||
272 | + params.key = "zhaolugame20170831" | ||
273 | + | ||
274 | + local status, body = httpc.get(skynet.getenv("codeurl"), | ||
275 | + "/mipush/notify_user?" .. httpGetFormatData(params), {}, {}) | ||
276 | + if tonumber(status) ~= 200 then | ||
277 | + skynet.error(status, body) | ||
278 | + return | ||
279 | + end | ||
280 | + | ||
281 | + return body | ||
282 | +end | ||
283 | + | ||
284 | +-- { uid, msgId} | ||
285 | +function deleteNotify(params) | ||
286 | + params = params or {} | ||
287 | + params.key = "zhaolugame20170831" | ||
288 | + | ||
289 | + local status, body = httpc.get(skynet.getenv("codeurl"), | ||
290 | + "/mipush/delete_notify?" .. httpGetFormatData(params), {}, {}) | ||
291 | + if tonumber(status) ~= 200 then | ||
292 | + skynet.error(status, body) | ||
293 | + return | ||
294 | + end | ||
295 | + | ||
296 | + return body | ||
297 | +end | ||
298 | + | ||
299 | +--http get数据 | ||
300 | +function httpGetFormatData(params) | ||
301 | + local function escape(s) | ||
302 | + return (string.gsub(s, "([^A-Za-z0-9_])", function(c) | ||
303 | + return string.format("%%%02X", string.byte(c)) | ||
304 | + end)) | ||
305 | + end | ||
306 | + | ||
307 | + local body = {} | ||
308 | + for k, v in pairs(params) do | ||
309 | + table.insert(body, string.format("%s=%s", escape(k), escape(v))) | ||
310 | + end | ||
311 | + | ||
312 | + return table.concat(body, "&") | ||
313 | +end | ||
0 | \ No newline at end of file | 314 | \ No newline at end of file |
@@ -0,0 +1,42 @@ | @@ -0,0 +1,42 @@ | ||
1 | +-- 初始化 | ||
2 | +function math.randomInit(seed) | ||
3 | + seed = seed or skynet.timex() | ||
4 | + math.randomseed(tonumber(tostring(seed):reverse():sub(1,6))) | ||
5 | +end | ||
6 | + | ||
7 | +-- 随机浮点数 | ||
8 | +function math.randomFloat(lower, greater) | ||
9 | + return math.min(lower, greater) + math.random() * math.abs(greater - lower); | ||
10 | +end | ||
11 | + | ||
12 | +function math.randomInt(lower, greater) | ||
13 | + return math.random(math.min(lower, greater), math.max(lower, greater)) | ||
14 | +end | ||
15 | + | ||
16 | +-- 根据权重值从数据集合里面随机出 | ||
17 | +-- @param dataset 数据集合 | ||
18 | +-- @param field 权重域 | ||
19 | +function math.randWeight(dataset, field) | ||
20 | + if not dataset then return nil end | ||
21 | + | ||
22 | + field = field or "weight" | ||
23 | + | ||
24 | + -- 计算权值总和 | ||
25 | + local weightSum = 0 | ||
26 | + for key, value in pairs(dataset) do | ||
27 | + weightSum = weightSum + tonumber(value[field]) | ||
28 | + end | ||
29 | + | ||
30 | + local randWeight = math.randomFloat(0, weightSum) | ||
31 | + for key, value in pairs(dataset) do | ||
32 | + if tonumber(value[field]) > 0 then | ||
33 | + if randWeight > tonumber(value[field]) then | ||
34 | + randWeight = randWeight - tonumber(value[field]) | ||
35 | + else | ||
36 | + return key | ||
37 | + end | ||
38 | + end | ||
39 | + end | ||
40 | + | ||
41 | + return nil | ||
42 | +end | ||
0 | \ No newline at end of file | 43 | \ No newline at end of file |
@@ -0,0 +1,334 @@ | @@ -0,0 +1,334 @@ | ||
1 | +local ipairs = ipairs | ||
2 | +local pairs = pairs | ||
3 | +local table_insert = table.insert | ||
4 | +local table_concat = table.concat | ||
5 | +local table_remove = table.remove | ||
6 | +local string_format = string.format | ||
7 | +local type = type | ||
8 | +local tonumber = tonumber | ||
9 | +local next = next | ||
10 | + | ||
11 | +local strh = require "strh" | ||
12 | + | ||
13 | +function string.setv(str, k, v, delimiter) | ||
14 | + delimiter = delimiter or " " | ||
15 | + -- 若存在则替换,若无则append | ||
16 | + return strh.modify(str, {[tonumber(k)]=tonumber(v)}, false, delimiter) | ||
17 | +end | ||
18 | + | ||
19 | +function string.msetv(str, vs, delimiter) | ||
20 | + delimiter = delimiter or " " | ||
21 | + if not next(vs) then return str end | ||
22 | + return strh.modify(str, vs, false, delimiter) | ||
23 | +end | ||
24 | + | ||
25 | +function string.incrv(str, k, delta, delimiter) | ||
26 | + delimiter = delimiter or " " | ||
27 | + return strh.modify(str, {[tonumber(k)]=tonumber(delta)}, true, delimiter) | ||
28 | +end | ||
29 | + | ||
30 | +function string.mincrv(str, ds, delimiter) | ||
31 | + delimiter = delimiter or " " | ||
32 | + if not next(ds) then return str end | ||
33 | + return strh.modify(str, ds, true, delimiter) | ||
34 | +end | ||
35 | + | ||
36 | +function string.delk(str, k, delimiter) | ||
37 | + delimiter = delimiter or " " | ||
38 | + return strh.modify(str, {[tonumber(k)]=""}, false, delimiter) | ||
39 | +end | ||
40 | + | ||
41 | +function string.mdelk(str, ks, delimiter) | ||
42 | + delimiter = delimiter or " " | ||
43 | + local mod = {} | ||
44 | + for _, k in ipairs(ks) do | ||
45 | + mod[k] = "" | ||
46 | + end | ||
47 | + return strh.modify(str, mod, false, delimiter) | ||
48 | +end | ||
49 | + | ||
50 | +function string.getv(str, k, default, delimiter) | ||
51 | + default = default or -1 | ||
52 | + delimiter = delimiter or " " | ||
53 | + return strh.getv(str, k, default, delimiter) | ||
54 | +end | ||
55 | + | ||
56 | +function string.toArray(str, toNum, delimiter) | ||
57 | + delimiter = delimiter or " " | ||
58 | + return strh.toarray(str, tobool(toNum), delimiter) | ||
59 | +end | ||
60 | + | ||
61 | +function string.value(str, index, default, notNum, delimiter) | ||
62 | + delimiter = delimiter or " " | ||
63 | + local bfind, value = strh.value(str, index, delimiter) | ||
64 | + if not notNum then | ||
65 | + value = tonumber(value) | ||
66 | + end | ||
67 | + return bfind and value or (default or value) | ||
68 | +end | ||
69 | + | ||
70 | +function string.setv_dk(str, k1, k2, v, delimiter) | ||
71 | + delimiter = delimiter or " " | ||
72 | + local mod = {[k1]={[k2]=v}} | ||
73 | + return strh.moddk(str, mod, false, delimiter) | ||
74 | +end | ||
75 | + | ||
76 | +-- {{k1,k2,v}} | ||
77 | +function string.msetv_dk(str, vs, delimiter) | ||
78 | + delimiter = delimiter or " " | ||
79 | + if not next(vs) then return str end | ||
80 | + local mod = {} | ||
81 | + for _, t in ipairs(vs) do | ||
82 | + local k1 = tonumber(t[1]) | ||
83 | + local k2 = tonumber(t[2]) | ||
84 | + local v = tonumber(t[3]) | ||
85 | + if mod[k1] then | ||
86 | + mod[k1][k2] = v | ||
87 | + else | ||
88 | + mod[k1] = {[k2] = v} | ||
89 | + end | ||
90 | + end | ||
91 | + return strh.moddk(str, mod, false, delimiter) | ||
92 | +end | ||
93 | + | ||
94 | +function string.incrv_dk(str, k1, k2, delta, delimiter) | ||
95 | + delimiter = delimiter or " " | ||
96 | + local mod = {[tonumber(k1)]={[tonumber(k2)]=tonumber(delta)}} | ||
97 | + return strh.moddk(str, mod, true, delimiter) | ||
98 | +end | ||
99 | + | ||
100 | +function string.mincrv_dk(str, ds, delimiter) | ||
101 | + delimiter = delimiter or " " | ||
102 | + if not next(ds) then return str end | ||
103 | + local mod = {} | ||
104 | + for _, t in ipairs(ds) do | ||
105 | + local k1 = tonumber(t[1]) | ||
106 | + local k2 = tonumber(t[2]) | ||
107 | + local delta = tonumber(t[3]) | ||
108 | + if mod[k1] then | ||
109 | + mod[k1][k2] = delta | ||
110 | + else | ||
111 | + mod[k1] = {[k2] = delta} | ||
112 | + end | ||
113 | + end | ||
114 | + return strh.moddk(str, mod, true, delimiter) | ||
115 | +end | ||
116 | + | ||
117 | +function string.delk_dk(str, k1, k2, delimiter) | ||
118 | + delimiter = delimiter or " " | ||
119 | + local mod = {[tonumber(k1)]={[tonumber(k2)]=""}} | ||
120 | + return strh.moddk(str, mod, false, delimiter) | ||
121 | +end | ||
122 | + | ||
123 | +function string.mdelk_dk(str, ks, delimiter) | ||
124 | + delimiter = delimiter or " " | ||
125 | + local mod = {} | ||
126 | + for _, t in ipairs(ks) do | ||
127 | + local k1 = tonumber(t[1]) | ||
128 | + local k2 = tonumber(t[2]) | ||
129 | + if mod[k1] then | ||
130 | + mod[k1][k2] = "" | ||
131 | + else | ||
132 | + mod[k1] = {[k2] = ""} | ||
133 | + end | ||
134 | + end | ||
135 | + return strh.moddk(str, mod, false, delimiter) | ||
136 | +end | ||
137 | + | ||
138 | +function string.getv_dk(str, k1, k2, default, delimiter) | ||
139 | + default = default or -1 | ||
140 | + delimiter = delimiter or " " | ||
141 | + return strh.getvdk(str, k1, k2, default, delimiter) | ||
142 | +end | ||
143 | + | ||
144 | +function string.toNumMap(str, delimiter) | ||
145 | + delimiter = delimiter or " " | ||
146 | + return strh.tonummap(str, delimiter) | ||
147 | +end | ||
148 | + | ||
149 | +--[[ | ||
150 | +from: 1=2=3 | ||
151 | +to: {"1","2","3"} | ||
152 | +]] | ||
153 | +function string.split2(str, pattern) | ||
154 | + pattern = pattern or "[%d.=]+" | ||
155 | + local tb = {} | ||
156 | + for v in str:gmatch(pattern) do | ||
157 | + table_insert(tb, v) | ||
158 | + end | ||
159 | + return tb | ||
160 | +end | ||
161 | + | ||
162 | +--[[ | ||
163 | +from: 1=2 3=4 | ||
164 | +to: {["1"]="2",["3"]="4"} | ||
165 | +]] | ||
166 | +function string.tomap(str) | ||
167 | + local tb = {} | ||
168 | + for k, v in str:gmatch("([%d.]+)=([%d.]+)") do | ||
169 | + tb[k] = v | ||
170 | + end | ||
171 | + return tb | ||
172 | +end | ||
173 | + | ||
174 | +--[[ | ||
175 | +from: 1=2 3=4 5=6.1 | ||
176 | +to: {{1,2},{3,4},{5,6.1}} | ||
177 | +]] | ||
178 | +function string.toAttrMap(str, pattern) | ||
179 | + pattern = pattern or "([%d.]+)=([%d.]+)" | ||
180 | + local tb = {} | ||
181 | + for k, v in str:gmatch(pattern) do | ||
182 | + tb[#tb+1] = {tonum(k), tonum(v)} | ||
183 | + end | ||
184 | + return tb | ||
185 | +end | ||
186 | + | ||
187 | +--[[ | ||
188 | +from: "1=2=3 4=5=6" | ||
189 | +to: {{"1", "2", "3"}, {"4", "5", "6"}} | ||
190 | +]] | ||
191 | +function string.toTableArray(str, toNum, delimiter, pattern) | ||
192 | + pattern = pattern or "[%d.=]+" | ||
193 | + delimiter = delimiter or "=" | ||
194 | + local tb = {} | ||
195 | + for v in str:gmatch(pattern) do | ||
196 | + tb[#tb+1] = v:toArray(toNum, delimiter) | ||
197 | + end | ||
198 | + return tb | ||
199 | +end | ||
200 | + | ||
201 | +function string.toTableArraySec(str, delimiter) | ||
202 | + delimiter = delimiter or " " | ||
203 | + local array = {} | ||
204 | + local tempArray = string.split(string.trim(str), delimiter) | ||
205 | + for _, value in ipairs(tempArray) do | ||
206 | + local trimValue = string.trim(value) | ||
207 | + if trimValue ~= "" then | ||
208 | + value = string.split(trimValue, "=") | ||
209 | + table_insert(array, value) | ||
210 | + end | ||
211 | + end | ||
212 | + | ||
213 | + return array | ||
214 | +end | ||
215 | + | ||
216 | +--[[ | ||
217 | +from: "x1=x2=x3=x4 y1=y2=y3=y4" | ||
218 | +to: {[x1]={x2,x3,x4},[y1]={y2,y3,y4}} | ||
219 | +]] | ||
220 | +function string.toAttArray(str) | ||
221 | + local tb = {} | ||
222 | + for v1, vt in str:gmatch("([%d.]+)=([%d.=]+)") do | ||
223 | + tb[tonum(v1)] = vt:toArray(true) | ||
224 | + end | ||
225 | + return tb | ||
226 | +end | ||
227 | + | ||
228 | +--[[ | ||
229 | +x=x...=x x=x...=x | ||
230 | +最后一个x为权值 返回权值之外的值 | ||
231 | +]] | ||
232 | +function string.randWeight(str, bMulti) | ||
233 | + if not str or str == "" then return nil end | ||
234 | + | ||
235 | + local tab, sum = {}, 0 | ||
236 | + for index, vstr in ipairs(str:toArray()) do | ||
237 | + local tmp = vstr:toArray(true, "=") | ||
238 | + sum = sum + tmp[#tmp] | ||
239 | + tab[index] = tmp | ||
240 | + end | ||
241 | + | ||
242 | + local weight = math.randomFloat(0, sum) | ||
243 | + | ||
244 | + for _, v in ipairs(tab) do | ||
245 | + local val = v[#v] | ||
246 | + if val < weight then | ||
247 | + weight = weight - val | ||
248 | + else | ||
249 | + if bMulti then | ||
250 | + table_remove(v) | ||
251 | + return v | ||
252 | + else | ||
253 | + return v[1] | ||
254 | + end | ||
255 | + end | ||
256 | + end | ||
257 | +end | ||
258 | + | ||
259 | +function string.randLine(str) | ||
260 | + local num = str:nums("%d+") | ||
261 | + local index = math.random(num) | ||
262 | + return tonumber(str:value(index)) | ||
263 | +end | ||
264 | + | ||
265 | +--[[ | ||
266 | +结构类似如: | ||
267 | +v1;v2;v3; | ||
268 | +]] | ||
269 | +function string.sismember(str, value, delimiter) | ||
270 | + delimiter = delimiter or ";" | ||
271 | + for _, v in ipairs(str:toArray(true, delimiter)) do | ||
272 | + if v == val then | ||
273 | + return true | ||
274 | + end | ||
275 | + end | ||
276 | +end | ||
277 | + | ||
278 | +function string.sadd(str, value, delimiter) | ||
279 | + delimiter = delimiter or ";" | ||
280 | + if str:sismember(value) then | ||
281 | + return str | ||
282 | + end | ||
283 | + return table_concat({str, value, delimiter}) | ||
284 | +end | ||
285 | + | ||
286 | +function string.srem(str, value, delimiter) | ||
287 | + delimiter = delimiter or ";" | ||
288 | + local form = table_concat({"([^", delimiter, "]+)", delimiter}) | ||
289 | + local str = str:gsub(form, {[tostring(value)]=""}) | ||
290 | + return str | ||
291 | +end | ||
292 | + | ||
293 | +--[[ | ||
294 | +from: 1 2 3 4 5 | ||
295 | +to: 数字的个数 这里是5 | ||
296 | +]] | ||
297 | +function string.nums(str, format) | ||
298 | + format = format or "%d+" | ||
299 | + local count = 0 | ||
300 | + for _ in str:gmatch(format) do | ||
301 | + count = count + 1 | ||
302 | + end | ||
303 | + return count | ||
304 | +end | ||
305 | + | ||
306 | +-- getbit setbit 支持负索引 | ||
307 | +function string.getbit(str, pos) | ||
308 | + local len = #str | ||
309 | + if pos > len or len+pos < 0 then return 48 end | ||
310 | + if pos < 0 and len+pos >= 0 then pos = len+pos+1 end | ||
311 | + return str:byte(pos) | ||
312 | +end | ||
313 | + | ||
314 | +function string.bitcnt(str) | ||
315 | + local cnt = 0 | ||
316 | + for i=1, #str do | ||
317 | + cnt = cnt + str:byte(i) - 48 | ||
318 | + end | ||
319 | + return cnt | ||
320 | +end | ||
321 | + | ||
322 | +function string.setbit(str, pos, yes) | ||
323 | + yes = yes or "1" | ||
324 | + local len = #str | ||
325 | + if pos < 0 then | ||
326 | + if len+pos < 0 then return str end | ||
327 | + pos = len+pos+1 | ||
328 | + end | ||
329 | + if len < pos then | ||
330 | + return str .. string.rep("0", pos-len-1) .. yes | ||
331 | + else | ||
332 | + return str:sub(1, pos-1) .. yes .. str:sub(pos+1, -1) | ||
333 | + end | ||
334 | +end | ||
0 | \ No newline at end of file | 335 | \ No newline at end of file |