Commit 314bc5df00ab866ab762f4e22f3ceec4681cc52e

Authored by zhengshouren
1 parent 7cbc0876

提交服务器初始代码

.gitmodules
1 1 [submodule "skynet"]
2 2 path = skynet
3 3 url = git@120.26.43.151:common/skynet.git
  4 +[submodule "src/csvdata"]
  5 + path = src/csvdata
  6 + url = git@120.26.43.151:wasteland/csvdata.git
... ...
README.md 0 → 100644
docs/call 的调用流程分析.txt 0 → 100644
... ... @@ -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 +====================================================================
... ...
docs/skynet变量含义.txt 0 → 100644
... ... @@ -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 +====================================================================
... ...
docs/skynet消息队列.txt 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +session, type
  2 +
  3 +session 是保证 call 操作中 A->B->A 过程中,处理能衔接起来
  4 +
  5 +type 操作 是为了 找打相应的 pack,unpack 以及 dispatch 函数
  6 +
  7 +skynet 含有两级队列 全局队列和服务私有队列
  8 +全局队列里面包含服务私有队列的头指针
  9 +
  10 +服务私有队列 一次只被处理一个
0 11 \ No newline at end of file
... ...
docs/stm分析.txt 0 → 100644
... ... @@ -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 +
... ...
docs/stringhelper.txt 0 → 100644
... ... @@ -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 +
... ...
docs/任务设计详解.txt 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +成就系统设计:
  2 +
  3 +Role:missionInit
  4 +读取配表,将missionCsv按类型生成self.missions
  5 +[type] = {id1, id2, ...} 便于 后面程序索引
  6 +
  7 +Role:checkMissionTask(type, value, count)
  8 +成就暂时有三种类型
  9 +
  10 +1. 匹配型
  11 +2. 累积型
  12 +3. 收集型
... ...
docs/启动服务流程分析.txt 0 → 100644
... ... @@ -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 +
... ...
docs/自动增长类变量实现.txt 0 → 100644
... ... @@ -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 +
... ...
kill.sh 0 → 100755
... ... @@ -0,0 +1,4 @@
  1 +#! /bin/sh
  2 +
  3 +kill -9 `cat skynet.pid` 2>/dev/null
  4 +echo "服务端已经关闭"
0 5 \ No newline at end of file
... ...
run.sh 0 → 100755
... ... @@ -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 13 \ No newline at end of file
... ...
src/GlobalVar.lua 0 → 100644
... ... @@ -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 212 \ No newline at end of file
... ...
src/ProtocolCode.lua 0 → 100644
... ... @@ -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 261 \ No newline at end of file
... ...
src/RedisKeys.lua 0 → 100644
... ... @@ -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 42 \ No newline at end of file
... ...
src/actions/GmAction.lua 0 → 100644
... ... @@ -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 19 \ No newline at end of file
... ...
src/actions/NgxAction.lua 0 → 100644
... ... @@ -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 66 \ No newline at end of file
... ...
src/actions/RoleAction.lua 0 → 100644
... ... @@ -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 320 \ No newline at end of file
... ...
src/agent.lua 0 → 100644
... ... @@ -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)
... ...
src/config 0 → 100644
... ... @@ -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 18 \ No newline at end of file
... ...
... ... @@ -0,0 +1 @@
  1 +Subproject commit 3aabf5c43c36e6da7f63f237301b4772f0129e88
... ...
src/main.lua 0 → 100644
... ... @@ -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)
... ...
src/models/Role.lua 0 → 100644
... ... @@ -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 33 \ No newline at end of file
... ...
src/nodenames.lua 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +center = "127.0.0.1:9000"
  2 +
  3 +node_01 = "127.0.0.1:9898"
0 4 \ No newline at end of file
... ...
src/preload.lua 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +
  2 +local skynet = require "skynet"
  3 +
  4 +skynet.timex = function ()
  5 + return math.floor(skynet.time())
  6 +end
0 7 \ No newline at end of file
... ...
src/rdsscripts/RedisScripts.lua 0 → 100644
... ... @@ -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 21 \ No newline at end of file
... ...
src/rdsscripts/assistInfo.lua 0 → 100644
... ... @@ -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 +
... ...
src/rdsscripts/insertEmail.lua 0 → 100644
... ... @@ -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)
... ...
src/rdsscripts/rankDetails.lua 0 → 100644
... ... @@ -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 31 \ No newline at end of file
... ...
src/rdsscripts/refreshAssist.lua 0 → 100644
... ... @@ -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 +
... ...
src/services/agent_ctrl.lua 0 → 100644
... ... @@ -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 209 \ No newline at end of file
... ...
src/services/agent_util.lua 0 → 100644
... ... @@ -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
... ...
src/services/chated.lua 0 → 100644
... ... @@ -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
... ...
src/services/csvdatad.lua 0 → 100644
... ... @@ -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)
... ...
src/services/dbseed.lua 0 → 100644
... ... @@ -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 55 \ No newline at end of file
... ...
src/services/globald.lua 0 → 100644
... ... @@ -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__)
... ...
src/services/logd.lua 0 → 100644
... ... @@ -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__)
... ...
src/services/mcast_util.lua 0 → 100644
... ... @@ -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 110 \ No newline at end of file
... ...
src/services/named.lua 0 → 100644
... ... @@ -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
... ...
src/services/ngxd.lua 0 → 100644
... ... @@ -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__)
... ...
src/services/poold.lua 0 → 100644
... ... @@ -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__)
... ...
src/services/redisd.lua 0 → 100644
... ... @@ -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 40 \ No newline at end of file
... ...
src/services/uniond.lua 0 → 100644
... ... @@ -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 144 \ No newline at end of file
... ...
src/services/watchdog.lua 0 → 100644
... ... @@ -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)
... ...
src/shared/ModelBase.lua 0 → 100644
... ... @@ -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 241 \ No newline at end of file
... ...
src/shared/crypto.lua 0 → 100644
... ... @@ -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
... ...
src/shared/debug.lua 0 → 100644
... ... @@ -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
... ...
src/shared/functions.lua 0 → 100644
... ... @@ -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 '&amp;'
  615 +- '"' (double quote) becomes '&quot;'
  616 +- "'" (single quote) becomes '&#039;'
  617 +- '<' (less than) becomes '&lt;'
  618 +- '>' (greater than) becomes '&gt;'
  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["&"] = "&amp;"
  632 +string._htmlspecialchars_set["\""] = "&quot;"
  633 +string._htmlspecialchars_set["'"] = "&#039;"
  634 +string._htmlspecialchars_set["<"] = "&lt;"
  635 +string._htmlspecialchars_set[">"] = "&gt;"
  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, " ", "&nbsp;")
  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 +
... ...
src/shared/init.lua 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +require("shared.functions")
  2 +require("shared.debug")
  3 +
  4 +json = require("shared.json")
  5 +MsgPack = require "cmsgpack"
0 6 \ No newline at end of file
... ...
src/shared/json.lua 0 → 100644
... ... @@ -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 432 \ No newline at end of file
... ...
src/shared/redisproxy.lua 0 → 100644
... ... @@ -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 73 \ No newline at end of file
... ...
src/utils/CommonFunc.lua 0 → 100644
... ... @@ -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 314 \ No newline at end of file
... ...
src/utils/MathUtil.lua 0 → 100644
... ... @@ -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 43 \ No newline at end of file
... ...
src/utils/StringUtil.lua 0 → 100644
... ... @@ -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 335 \ No newline at end of file
... ...
src/utils/init.lua 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +require("utils.CommonFunc")
  2 +require("utils.StringUtil")
  3 +require "utils.MathUtil"
0 4 \ No newline at end of file
... ...