local cjson = require "cjson" local skynet = require "skynet" local crypt = require "crypt" function cjson_encode(obj) return cjson.encode(obj) end function cjson_decode(json) return cjson.decode(json) end function skynet_time() return math.ceil(skynet.time()) end function is_robot(uid) return uid < 1000000 end -- 将时间区间转换成秒 -- 年:y year -- 周:w week -- 日:d day -- 时:h hour -- 分:m minute -- 秒:s second function convert_second(t) if is_empty(t) or type(t) ~= "table" then return 0 end local second = t.second or t.s or 0 local minute = t.minute or t.m or 0 local hour = t.hour or t.h or 0 local day = t.day or t.d or 0 local week = t.week or t.w or 0 local year = t.year or t.y or 0 day = day + week * 7 + year * 365 hour = hour + day * 24 minute = minute + hour * 60 second = second + minute * 60 return second end -- 格式化时间 function os_date(fmt, time) -- 计算时区差值 if DIFF_TIME_ZONE == nil then local now = os.time() DIFF_TIME_ZONE = math.ceil(8 * 3600 - os.difftime(now, os.time(os.date("!*t", now)))) print("skynet_time ", string.format("DIFF_TIME_ZONE[%s]", tostring(DIFF_TIME_ZONE))) end time = time + DIFF_TIME_ZONE return os.date(fmt, time) end function date_time(t) return os.date("%Y-%m-%d %H:%M:%S", t) end function jm_reload_module(module_name) -- print("reload config,", module_name)print -- 只针对配置更新 package.loaded[module_name] = nil return require(module_name) end --创建定时器 function create_timeout(ti, func, ...) local active = true local args = {...} skynet.timeout( ti, function() if active then active = false func(table.unpack(args)) end end ) local timer = {} timer.is_timeout = function() return not active end timer.delete = function() local is_active = active active = false return is_active end return timer end -- 创建循环定时器 -- times 执行次数(负数时无限次) -- time 间隔时间(单位0.01秒) -- func 执行函数 -- ... 对应参数 function create_timer(times, time, func, ...) local active = true local isPause = false local args = {...} local run_timer run_timer = function() if times == 0 or not active then active = false return end -- 小于0时不处理;非整数不合法,转成整数 if times > 0 then times = math.floor(times - 1) if times < 0 then times = 0 end end skynet.timeout(time, run_timer) if not isPause then func(table.unpack(args)) end end run_timer() -- 定时器查看与控制 local timer = {} timer.is_timeout = function() return not active end -- 关闭操作 timer.delete = function() local is_active = active active = false return is_active end -- 暂停操作 timer.pause = function() local is_pause = isPause isPause = true return is_pause end -- 继续操作 timer.continue = function() local is_pause = isPause isPause = false return is_pause end return timer end --根据权重筛选,real为true时,总权重不大于0时,不做随机 function key_rand(obj, real) local weight = {} local t = 0 for k, v in ipairs(obj) do t = t + (v.weight or v.w) weight[k] = t end if t <= 0 then if real then return else return math.random(#obj) end end local c = math.random(1, t) for k, v in ipairs(weight) do if c < v then return k end end return #weight end function random_list_by_weight(list, key) if list == nil then return end local subkey = key or "weight" local weights = {} local t = 0 for k, v in ipairs(list) do t = t + v[subkey] weights[k] = t end if t == 0 then return end local c = math.random(0, t) for k, v in ipairs(weights) do if c <= v then return k end end return 1 end -- 一定量的物品随机分配特定份数,返回分配数量列表 function rand_divide(total, count) assert(total >= count) local list = {} local sum = 0 for i = 1, count do local num = math.random(total, total * 10) table.insert(list, num) sum = sum + num end table.sort(list) local left = total local res = {} for k, v in ipairs(list) do local real = math.floor(v * total / sum) real = real > 0 and real or 1 table.insert(res, real) left = left - real end if left > 0 then local index = math.random(count) res[index] = res[index] + left end return res end -- 从一定的元素中选择特定量的元素,返回索引列表 function rand_choice(total, count) assert(total >= count) local list = {} local sum = 0 for i = 1, total do local num = math.random(total * 10) table.insert(list, {index = i, weight = num}) end table.sort( list, function(u, v) return u.weight > v.weight end ) local res = {} for i = 1, count do table.insert(res, list[i].index) end return res end -- 从一定的元素中选择特定量的元素,返回索引列表(有权重) function rand_choice_by_weight(list, count) local randList = table.copy(list) assert(#randList >= count) local indexList = {} for i = 1, count do local index = key_rand(randList) table.remove(randList, index) table.insert(indexList, index) end for i = #indexList, 1, -1 do for j = i - 1, 1, -1 do if indexList[i] >= indexList[j] then indexList[i] = indexList[i] + 1 end end end return indexList end --bool转为整型 function bool_to_int(val) if val then return 1 else return 0 end end --初始化为默认值 function init_inc(old_val, default_val, inc_val) if not old_val then old_val = default_val end return old_val + inc_val end --两者取较大值 function max(val1, val2) if val1 > val2 then return val1 end return val2 end --两者取最小值 function min(val1, val2) if val1 < val2 then return val1 end return val2 end --列表转为发送数据 function empty_to_data(t) if not t or table.empty(t) then return nil end return t end -- 空值判断:nil或常用默认值时返回true,以下情况返回true -- nil: nil -- number: 0 -- string: "" -- boolean: false -- table: {} -- userdata: 0x0(null) -- 注:类似php中以 t 作为条件时为假的情况(除userdata外) function is_empty(t) if not t then return true end local tp = type(t) if tp == "number" and t == 0 then return true end if tp == "string" and t == "" then return true end if tp == "boolean" and t == false then return true end if tp == "table" and not next(t) then return true end if tp == "userdata" then local ok, res = pcall(cjson_encode, t) if ok and res == "null" then return true end end end --列表随机一个值 function list_rand(obj) local index = math.random(1, #obj) return obj[index] end function per_hour_timer(f, ...) while true do local now = os.date("*t", skynet_time()) -- 每1小时进入循环 local t = (60 - now.min) * 60 * 100 skynet.sleep(t) f(...) end end function per_day_timer(f, ...) while true do local now = os.date("*t", skynet_time()) local t = (23 - now.hour) * 3600 * 100 + (60 - now.min) * 60 * 100 skynet.sleep(t) f(...) end end -- 每日备份时钟 function per_day_user_backup_timer(f, ...) while true do local now = os.date("*t", skynet_time()) -- 凌晨3点开始 local t = (23 - now.hour) * 3600 * 100 + (60 - now.min) * 60 * 100 + 3 * 3600 skynet.sleep(t) f(...) end end -- 1 time = 0.01s function per_timer(time, f, ...) while true do skynet.sleep(time) f(...) end end --创建token function token_create(uid, timestamp, password, secret) local s = string.format("%s:%s:%s", uid, timestamp, password) s = crypt.base64encode(crypt.desencode(secret, s)) return s:gsub( "[+/]", function(c) if c == "+" then return "-" else return "_" end end ) end --解析token function token_parse(token, secret) token = token:gsub( "[-_]", function(c) if c == "-" then return "+" else return "/" end end ) local s = crypt.desdecode(secret, crypt.base64decode(token)) local uid, timestamp, password = s:match("([^:]+):([^:]+):(.+)") return uid, timestamp, password end function time_now_str() return os.date("%Y-%m-%d %H:%M:%S", skynet_time()) end function getTimestamp(src) --从日期字符串中截取出年月日时分秒 local _, _, y, m, d, hour, min, sec = string.find(src, "(%d+)-(%d+)-(%d+)%s*(%d+):(%d+):(%d+)") local t = { year = tonumber(y), month = tonumber(m), day = tonumber(d), hour = tonumber(hour), min = tonumber(min), sec = tonumber(sec) } --把日期时间字符串转换成对应的日期时间 return os.time(t) end -- 获取当天秒数 function get_day_timestamp(src) local h = tonumber(string.sub(src, 1, 2)) local m = tonumber(string.sub(src, 4, 5)) local s = tonumber(string.sub(src, 7, 8)) return h * 3600 + m * 60 + s end function is_robot(uid) return tonumber(uid) < 1000000 end function is_include_channel(uid_channel, include_channel, not_include_channel) if not include_channel then return false end if not_include_channel and table.member(not_include_channel, uid_channel) then return false end if table.member(include_channel, 0) then return true elseif table.member(include_channel, uid_channel) then return true end return false end function is_include_version(version, include_version, not_include_version) if not include_version then return false end if not_include_version then for _, v in ipairs(not_include_version) do if string.find(version, v) == 1 then return false end end end for _, v in ipairs(include_version) do if string.find(version, v) == 1 then return true end end return false end function process_slq_inject(sql) local strLower = string.lower(sql) local words = {"and", "exec", "insert", "select", "delete", "update", "truncate"} for _, word in ipairs(words) do if string.find(strLower, word) then return false end end return true end function is_include_channel2(uid_channel, include_channel, not_include_channel) if not_include_channel then for k, v in ipairs(not_include_channel) do if v == uid_channel then return false end end end if include_channel then for k, v in ipairs(include_channel) do if v == uid_channel or v == 0 then -- 0是默认全渠道 return true end end end return false end function is_type(t, values) for _, v in ipairs(values) do if type(v) ~= t then return false end end return true end function is_nil(str) return str == nil or str == "" end -- 将nil或null(userdata类型)转换成空table,本来是有效数据的不做改变 function trans_table(tb) if is_empty(tb) then return {} end return tb end -- 检查用户所属城市是否屏蔽城市 function check_city(city_config, city) if city_config and city then for _, v in ipairs(city_config) do local i = string.find(city, v) if i then return true end end end return false end -- 检查用户渠道是否开放红包功能 -- 也可以被新版本的过滤格式调用 function check_channel(channel_config, channel) return not channel_config or (channel_config.t == 1 and table.include(channel_config.c, channel)) or (channel_config.t == 0 and not table.include(channel_config.c, channel)) end function check_city1(city_config, city) return not city_config or (city_config.t == 1 and check_city(city_config.c, city)) or (city_config.t == 0 and not check_city(city_config.c, city)) end function date_is_same(date1, date2) local time_date1 = string.split(date1, "-") local time_date2 = string.split(date2, "-") return tonumber(time_date1[1]) == tonumber(time_date2[1]) and tonumber(time_date1[2]) == tonumber(time_date2[2]) and tonumber(time_date1[3]) == tonumber(time_date2[3]) end -- 查找两个日期之间的所有日期 -- date2必须大于等于date1 function foreach_day(date1, date2, f) local date = date1 local t = getTimestamp(date .. " 00:00:00") while true do f(date) if date_is_same(date, date2) then break end t = t + 24 * 3600 date = os.date("%Y-%m-%d", t) end end local function split(s, p) local rt = {} string.gsub( s, "[^" .. p .. "]+", function(w) table.insert(rt, w) end ) return rt end local function version_to_number(version, digit) if is_nil(version) then return 0 end local arr = split(version, "%.") local i = 1 if arr[1] == "t" then i = 2 end local verCount = tonumber(arr[i]) * 100 + tonumber(arr[i + 1]) if digit and digit == 3 then if arr[i + 2] then verCount = verCount * 100 + tonumber(arr[i + 2]) else verCount = verCount * 100 + 1 end end return verCount end -- 检查版本是否大于等于指定版本号 function greater_version(version, user_version) local arr = split(version, "%.") local digit = #arr return version_to_number(user_version, digit) >= version_to_number(version, digit) end -- 检查版本是否小于等于指定版本号 function less_version(version, user_version) local arr = split(version, "%.") local digit = #arr return version_to_number(user_version, digit) <= version_to_number(version, digit) end -- 检查旧版本{t=1, v={"2.11"}}格式 -- version获取玩家app_version function check_version(version_config, version) if is_nil(version) then return false end if not version_config then return true end local arr1 = string.split1(version, "%.") local version1 = string.format("%s.%s", arr1[1], arr1[2]) local version2 = nil if arr1[1] == "t" or arr1[1] == "T" then version1 = string.format("%s.%s", arr1[2], arr1[3]) if #arr1 >= 4 then version2 = string.format("%s.%s.%s", arr1[2], arr1[3], arr1[4]) end else if #arr1 >= 3 then version2 = string.format("%s.%s.%s", arr1[1], arr1[2], arr1[3]) end end -- 允许 if version_config.t == 1 then if table.include(version_config.v, version1) then return true end if version2 and table.include(version_config.v, version2) then return true end return false end -- 屏蔽 if version_config.t == 0 then if version2 then if table.include(version_config.v, version1) or table.include(version_config.v, version2) then return false end else if table.include(version_config.v, version1) then return false end end return true end -- 大于等于 if version_config.t == 2 then if #version_config.v == 1 then if greater_version(version_config.v[1], version) then return true end end return false end -- 小于等于 if version_config.t == 3 then if #version_config.v == 1 then if less_version(version_config.v[1], version) then return true end end return false end -- 包含区间 if version_config.t == 4 then if #version_config.v == 2 then if greater_version(version_config.v[1], version) and less_version(version_config.v[2], version) then return true end end return false end -- 不包含区间 if version_config.t == 5 then if #version_config.v == 2 then if greater_version(version_config.v[1], version) and less_version(version_config.v[2], version) then return false end end return true end return false end -- 获取当天结束时间 function get_day_end_time(currTime, days) local daySec = 24 * 3600 local addDay = days or 1 local timeout = math.floor((currTime + 8 * 3600) / daySec) * daySec + daySec * addDay - 8 * 3600 - 1 return timeout end -- 获取当天开始时间 function get_day_start_time(currTime, days) local daySec = 24 * 3600 local addDay = days or 0 local timeout = math.floor((currTime + 8 * 3600) / daySec) * daySec + daySec * addDay - 8 * 3600 return timeout end -- 版本转字符串 function version_to_string(version, points) if is_nil(version) then return "nil" end local arr = split(version, "%.") local i = 1 if arr[1] == "t" then i = 2 end local str = "" local times = points or 2 for j = i, i + times do local temp = nil if arr[j] then temp = string.format("%03d", tonumber(arr[j])) else temp = "000" end str = str .. temp end return str end function pairsByKeys(t) local a = {} for n in pairs(t) do a[#a + 1] = n end table.sort(a) local i = 0 return function() i = i + 1 return a[i], t[a[i]] end end -- 获取当前天数(自1970年) function get_yday(timestamp) return math.floor((timestamp + (8 * 3600)) / (3600 * 24)) end -- 获取周期时间 function get_circle_time(data) if data == nil or data.m == nil or data.hour == nil or data.min == nil then return end local currTime = skynet_time() local now = os.date("*t", skynet_time()) -- 每12小时 if data.m == 0 then return get_circle_time_12h(data) end -- 每天 if data.m == 1 then return get_circle_time_day(data) end -- 每周 if data.m == 2 then return get_circle_time_wday(data) end -- 每月 if data.m == 3 then return get_circle_time_month(data) end -- 每季 if data.m == 4 then return get_circle_time_session(data) end -- 每年 if data.m == 5 then return get_circle_time_year(data) end end -- 时间周期 - 12小时制 function get_circle_time_12h(data) if data == nil or data.m == nil or data.hour == nil or data.min == nil then return end -- 数据验证 if data.m ~= 0 or data.hour < 0 or data.hour > 11 then return end local currTime = skynet_time() local now = os.date("*t", currTime) local dhour = data.hour if now.hour >= 12 then dhour = dhour + 12 end local endTime = os.time({year = now.year, month = now.month, day = now.day, hour = dhour, min = data.min, sec = 0}) if currTime >= endTime then endTime = endTime + 12 * 3600 end return endTime, endTime - currTime end -- 时间周期 - 每日 function get_circle_time_day(data) if data == nil or data.m == nil or data.hour == nil or data.min == nil then return end -- 数据验证 if data.m ~= 1 or data.hour < 0 or data.hour > 23 then return end local currTime = skynet_time() local now = os.date("*t", currTime) local endTime = os.time({year = now.year, month = now.month, day = now.day, hour = data.hour, min = data.min, sec = 0}) if currTime >= endTime then endTime = endTime + 24 * 3600 end return endTime, endTime - currTime end -- 时间周期 - 每周 function get_circle_time_wday(data) if data == nil or data.m == nil or data.day == nil or data.hour == nil or data.min == nil then return end -- 数据验证 if data.m ~= 2 or data.day < 0 or data.day > 7 or data.hour < 0 or data.hour > 23 then return end local currTime = skynet_time() local now = os.date("*t", currTime) local wday = data.day + 1 if wday > 7 then wday = 1 end local endTime = os.time({year = now.year, month = now.month, day = now.day, hour = data.hour, min = data.min, sec = 0}) local diffDays = wday - now.wday if diffDays < 0 then diffDays = diffDays + 7 end endTime = endTime + diffDays * 24 * 3600 if currTime >= endTime then endTime = endTime + 7 * 24 * 3600 end return endTime, endTime - currTime end -- 时间周期 - 每月 function get_circle_time_month(data) if data == nil or data.m == nil or data.day == nil or data.hour == nil or data.min == nil then return end -- 数据验证 if data.m ~= 3 or data.day < 0 or data.day > 31 or data.hour < 0 or data.hour > 23 then return end local currTime = skynet_time() local now = os.date("*t", currTime) local endTime = os.time({year = now.year, month = now.month, day = data.day, hour = data.hour, min = data.min, sec = 0}) if currTime >= endTime then local nMonth = now.month + 1 local nYear = now.year if nMonth > 12 then nMonth = 1 nYear = nYear + 1 end endTime = os.time({year = nYear, month = nMonth, day = data.day, hour = data.hour, min = data.min, sec = 0}) end return endTime, endTime - currTime end -- 时间周期 - 每月 function get_circle_time_session(data) if data == nil or data.m == nil or data.month == nil or data.day == nil or data.hour == nil or data.min == nil then return end -- 数据验证 if data.m ~= 4 or data.month < 0 or data.month > 3 or data.day < 0 or data.day > 31 or data.hour < 0 or data.hour > 23 then return end local currTime = skynet_time() local now = os.date("*t", currTime) local cMonth = math.floor(now.month / 3) * 3 + data.month local endTime = os.time({year = now.year, month = cMonth, day = data.day, hour = data.hour, min = data.min, sec = 0}) if currTime >= endTime then cMonth = cMonth + 3 local nYear = now.year if cMonth > 12 then cMonth = cMonth - 12 nYear = nYear + 1 end endTime = os.time({year = nYear, month = cMonth, day = data.day, hour = data.hour, min = data.min, sec = 0}) end return endTime, endTime - currTime end -- 时间周期 - 每月 function get_circle_time_year(data) if data == nil or data.m == nil or data.day == nil or data.hour == nil or data.min == nil then return end -- 数据验证 if data.m ~= 5 or data.month < 0 or data.month > 12 or data.day < 0 or data.day > 31 or data.hour < 0 or data.hour > 23 then return end local currTime = skynet_time() local now = os.date("*t", currTime) local nTime = os.time({year = now.year, month = data.month, day = data.day, hour = data.hour, min = data.hour, sec = 0}) if currTime >= nTime then nTime = os.time( {year = now.year + 1, month = data.month, day = data.day, hour = data.hour, min = data.min, sec = 0} ) end return nTime, nTime - currTime end -- x的y次方 function g_x_pow_y(x, y) if x == nil or y == nil then return 0 end local count = x if y > 1 then for i = 1, y - 1 do count = count * x end end if x ~= 0 and y == 0 then return 1 end return count end function RandomWithWeight(list, sum) if list == nil or #list == 0 then return 0, 0 end if sum == nil or sum == 0 then sum = 0 for _, aWeight in ipairs(list) do sum = sum + aWeight end end local ret = 1 if sum > 0 then local r = math.random(1, sum) local didSum = 0 for index, q in ipairs(list) do didSum = didSum + q if r <= didSum then ret = index break end end end return ret, sum end -- data: {{3,10},{4,10},{5,10},{6,10},{7,10}} function getRandomWithWeight(data) local weight = {} for k, v in pairs(data) do table.insert(weight, v[2]) end local index = RandomWithWeight(weight) local area = data[index] return area[1] end -- list: {{2,...,10},{3,...,10},{4,...,10}} function getRandomListByWeightAtLast(list) local weight = {} for k, v in ipairs(list) do table.insert(weight, v[#v]) end local index = RandomWithWeight(weight) return list[index], index end -- 获取当前月份天数 function getDayAmount() return os.date("%d", os.time({year = os.date("%Y"), month = os.date("%m") + 1, day = 0})) end function timeout_call(ti, ...) local token = {} local ret skynet.fork( function(...) ret = table.pack(pcall(skynet.call, ...)) skynet.wakeup(token) end, ... ) skynet.sleep(ti, token) if ret then if ret[1] then return table.unpack(ret, 1, ret.n) else error(ret[2]) end else -- timeout return false end end function mysqlEscapeString(str) local escape_map = { ["\0"] = "\\0", ["\b"] = "\\b", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t", ["\26"] = "\\Z", ["\\"] = "\\\\", ["'"] = "\\'", ['"'] = '\\"' } return string.format("%s", string.gsub(str or "", '[\0\b\n\r\t\26\\\'"]', escape_map)) end local function traceback(msg) local errorMsg = "safeRun error" .. tostring(msg) .. "\n" .. debug.traceback() .. "\n" log.error(errorMsg) return errorMsg end function safeRun(func, ...) local args = {...} return xpcall( function() return func(table.unpack(args)) end, traceback ) end -- 针对有非数组的数字下标的table转换成json function cjson_sparse_encode(obj) local cjsonObj = cjson.new() cjsonObj.encode_sparse_array(true) return cjsonObj.encode(obj) end -- 非标准encode,只能用 likeCjsonEncode 来decode function likeCjsonEncode(obj) if type(obj) ~= "table" then return cjson_encode(obj) end local cjson2 = cjson.new() local list = {} local findNumKey findNumKey = function(info, list, link, deep) if type(info) ~= "table" or deep > 100 then return end for k, v in pairs(info) do local tp = type(k) link = link .. "," .. k .. ":" .. tp if type(k) == "number" then table.insert(list, link) end findNumKey(v, list, link, deep + 1) end end findNumKey(obj, list, "start", 0) cjson2.encode_sparse_array(true) local json = cjson2.encode(obj) local data = json if #list > 0 then data = {["json"] = json, ["numKey"] = list} end return cjson_encode(data) end function cleanObjNull(obj) if not obj or type(obj) ~= "table" then return end for k, v in pairs(obj) do if type(v) == "userdata" then obj[k] = nil else cleanObjNull(v) end end end -- 非标准decode,只能decode用 likeCjsonDecode 来encode的string function likeCjsonDecode(json) local data = cjson_decode(json) if type(data) ~= "table" then return data end local cjson2 = cjson.new() cjson2.encode_sparse_array(true) local numKey = data["numKey"] if not numKey then return data end local obj = cjson2.decode(data["json"]) cleanObjNull(obj) local list = {} for k, v in ipairs(numKey) do local link = string.split(v, ",") table.insert(list, link) end table.sort( list, function(a, b) return #a < #b end ) local setNumKey setNumKey = function(obj, v, deep, count) deep = deep + 1 if count < deep or type(obj) ~= "table" then return end local keyObj = string.split(v[deep], ":") local key = keyObj[2] == "number" and tonumber(keyObj[1]) or keyObj[1] .. "" if count == deep then key = key .. "" local numKey = tonumber(key) if obj and obj[key] then local tmp = obj[key] obj[key] = nil obj[numKey] = tmp end return end setNumKey(obj[key], v, deep, count) end for k, v in ipairs(list) do setNumKey(obj, v, 1, #v) end return obj end -- 字符串转小驼峰 function formatSmallHump(name) local data = {} string.gsub( name, "[^_]+", function(w) table.insert(data, w) end ) local result = {} for i, v in ipairs(data) do if v ~= "" then v = string.lower(v) if #result > 0 then v = string.gsub(v, "^%l", string.upper) end table.insert(result, v) end end return table.concat(result) end