BaiZhanChengShenDB.lua 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. -- 百战成神(DB) 跨服数据层
  2. --
  3. -- 运行环境: 仅跨服进程 _G.is_middle == true
  4. -- 存储: Mongo 集合 DB.db_bzcs
  5. --
  6. -- 全服积分榜 rankCache + uuid2rank(与 rankDirty 同步重建); 排序 score 降序 -> scoreTime 升序 -> uuid
  7. -- 客户端展示榜仅前 BZCS_RANK_MAX 名, 与匹配用的全服榜不是同一展示范围
  8. --[=[
  9. BzcsData = {
  10. activityStartTime, activityEndTime, -- 本轮活动起止时间戳
  11. lastResetTime, -- 上次开轮时间(用于 BZCS_CYCLE_DAYS 周期判定)
  12. rewardIssued, -- 本轮是否已发奖
  13. playerList = {
  14. [uuid] = {
  15. uuid, serverId,
  16. score, -- 积分, 默认 BZCS_INIT_SCORE
  17. scoreTime, -- 达到当前积分的时间戳(同分比较用, 越小名次越靠前)
  18. isRobot, -- 1=机器人
  19. firstJoinTime, -- 首次挑战时间, >0 才参与周期发奖
  20. showInfo, -- { name,head,headFrame,body, heroArr={[race]=真人阵容|机器人{monsterOutID,racePower}} }
  21. },
  22. },
  23. serverList = { [serverId] = { uuid, ... } }, -- 按服索引, 发奖 WL 路由用
  24. pendingRewards = { [serverId] = { {uuid, rank}, ... } }, -- 逻辑服断连时待发奖队列
  25. }
  26. ]=]
  27. local LuaMongo = _G.lua_mongo
  28. local DB = require("common.DB")
  29. local Util = require("common.Util")
  30. local CreateRole = require("role.CreateRole")
  31. local RoleDBLogic = require("role.RoleDBLogic")
  32. local BaiZhanChengShenDefine = require("baiZhanChengShen.BaiZhanChengShenDefine")
  33. local BzcsLog = require("baiZhanChengShen.BaiZhanChengShenLog")
  34. local BzcsConfig = require("excel.baiZhanChengShen")
  35. BzcsData = BzcsData or {}
  36. local dbUpdate = {_id = nil}
  37. local dbUpdateField = {}
  38. local rankCache = nil
  39. local uuid2rank = nil
  40. local rankDirty = true
  41. -- 增量更新 Mongo 字段
  42. local function updateValue(key, value)
  43. if not key then return end
  44. if value then
  45. dbUpdateField["$set"] = {[key] = value}
  46. dbUpdateField["$unset"] = nil
  47. else
  48. dbUpdateField["$set"] = nil
  49. dbUpdateField["$unset"] = {[key] = 1}
  50. end
  51. dbUpdate._id = BzcsData._id
  52. LuaMongo.update(DB.db_bzcs, dbUpdate, dbUpdateField)
  53. end
  54. -- 从 db_bzcs 加载或初始化空档
  55. local function loadData()
  56. LuaMongo.find(DB.db_bzcs)
  57. local data = {}
  58. if LuaMongo.next(data) then
  59. BzcsData = data
  60. else
  61. BzcsData.activityStartTime = 0
  62. BzcsData.activityEndTime = 0
  63. BzcsData.lastResetTime = 0
  64. BzcsData.rewardIssued = true
  65. BzcsData.playerList = {}
  66. LuaMongo.insert(DB.db_bzcs, BzcsData)
  67. end
  68. rankDirty = true
  69. end
  70. -- 排行同分比较用时间(达到当前积分时刻)
  71. local function getRankScoreTime(pinfo)
  72. return pinfo.scoreTime or 0
  73. end
  74. -- 按积分降序重建排行缓存; 同分按 scoreTime 升序(先达到该积分者靠前), 仍同则 uuid
  75. local function sortRankCache()
  76. if not rankDirty then return end
  77. rankCache = {}
  78. uuid2rank = {}
  79. for uuid, pinfo in pairs(BzcsData.playerList or {}) do
  80. rankCache[#rankCache + 1] = {
  81. uuid = uuid,
  82. score = pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE,
  83. scoreTime = getRankScoreTime(pinfo),
  84. }
  85. end
  86. table.sort(rankCache, function(a, b)
  87. if a.score ~= b.score then
  88. return a.score > b.score
  89. end
  90. if a.scoreTime ~= b.scoreTime then
  91. return a.scoreTime < b.scoreTime
  92. end
  93. return a.uuid < b.uuid
  94. end)
  95. for rank, entry in ipairs(rankCache) do
  96. uuid2rank[entry.uuid] = rank
  97. end
  98. rankDirty = false
  99. end
  100. -- 保证 rankCache / uuid2rank 已按当前 playerList 重建
  101. local function ensureRankIndex()
  102. sortRankCache()
  103. end
  104. -- 保证 showInfo 结构完整
  105. local function normalizePlayer(pinfo)
  106. if not pinfo then return end
  107. pinfo.showInfo = pinfo.showInfo or {}
  108. pinfo.showInfo.heroArr = pinfo.showInfo.heroArr or {}
  109. end
  110. -- 按 serverId 重建 serverList(仅真人, 机器人不参与发奖路由)
  111. local function rebuildServerList()
  112. BzcsData.serverList = BzcsData.serverList or {}
  113. Util.cleanTable(BzcsData.serverList)
  114. for uuid, pinfo in pairs(BzcsData.playerList or {}) do
  115. if pinfo.isRobot ~= 1 then
  116. local sid = pinfo.serverId
  117. if sid then
  118. BzcsData.serverList[sid] = BzcsData.serverList[sid] or {}
  119. table.insert(BzcsData.serverList[sid], uuid)
  120. end
  121. end
  122. end
  123. end
  124. -- 机器单族仅存 monsterOutID + racePower(由 monsterOut 算出); 展示由 ExpandBzcsRaceShow 用时展开
  125. local function genRobotRaceShow(robotListIdx, race)
  126. local cfg = BzcsConfig.robotList[robotListIdx]
  127. if not cfg or not cfg.monsterOutIDs then return {} end
  128. local monsterOutID = cfg.monsterOutIDs[race]
  129. if not monsterOutID then return {} end
  130. return {
  131. monsterOutID = monsterOutID,
  132. formation = 1,
  133. racePower = BaiZhanChengShenDefine.CalcMonsterOutPower(monsterOutID),
  134. }
  135. end
  136. local function getRobotScore(robotListIdx)
  137. local cfg = BzcsConfig.robotList[robotListIdx]
  138. return (cfg and cfg.score) or BaiZhanChengShenDefine.BZCS_INIT_SCORE
  139. end
  140. -- 生成机器人池: 数量 = robotList 条数, bzcs_robot_i 对应 robotList[i]
  141. function GenerateRobots(cnt)
  142. cnt = cnt or BaiZhanChengShenDefine.GetRobotListCount()
  143. if cnt < 1 then return end
  144. BzcsData.playerList = BzcsData.playerList or {}
  145. local now = os.time()
  146. for i = 1, cnt do
  147. local uuid = "bzcs_robot_" .. i
  148. if not BzcsData.playerList[uuid] then
  149. local heroArr = {}
  150. for _, race in ipairs(BaiZhanChengShenDefine.BZCS_RACE_ORDER) do
  151. heroArr[race] = genRobotRaceShow(i, race)
  152. end
  153. BzcsData.playerList[uuid] = {
  154. uuid = uuid,
  155. isRobot = 1,
  156. score = getRobotScore(i),
  157. scoreTime = now + i,
  158. firstJoinTime = now,
  159. showInfo = {
  160. name = CreateRole.getRandomName(),
  161. head = CreateRole.getRandomHead(),
  162. headFrame = CreateRole.getRandomHeadFrame(),
  163. body = CreateRole.getRandomBody(),
  164. heroArr = heroArr,
  165. },
  166. }
  167. end
  168. end
  169. rankDirty = true
  170. rebuildServerList()
  171. updateValue("playerList", BzcsData.playerList)
  172. BzcsLog.logAction("robot_gen", string.format("cnt=%s robotList=%s", cnt, BaiZhanChengShenDefine.GetRobotListCount()))
  173. end
  174. -- 逻辑服断连期间缓存的周期奖励, 重连后补发
  175. function AddPendingReward(serverId, uuid, rank)
  176. if not serverId or not uuid then return end
  177. AddPendingRewards(serverId, {{uuid, rank}})
  178. end
  179. -- 逻辑服未连接时缓存整批待发奖
  180. function AddPendingRewards(serverId, rewardList)
  181. if not serverId or not rewardList or #rewardList == 0 then return end
  182. BzcsData.pendingRewards = BzcsData.pendingRewards or {}
  183. local list = BzcsData.pendingRewards[serverId]
  184. if not list then
  185. list = {}
  186. BzcsData.pendingRewards[serverId] = list
  187. end
  188. local addCnt, skipCnt = 0, 0
  189. for _, info in ipairs(rewardList) do
  190. local uuid, rank = info[1], info[2]
  191. if uuid and uuid ~= "" and rank and rank > 0 then
  192. list[#list + 1] = {uuid, rank}
  193. addCnt = addCnt + 1
  194. else
  195. skipCnt = skipCnt + 1
  196. BzcsLog.logAction("reward_pending_skip", string.format(
  197. "serverId=%s uuid=%s rank=%s", serverId, uuid or "", rank or 0
  198. ))
  199. end
  200. end
  201. if addCnt < 1 then
  202. return
  203. end
  204. updateValue("pendingRewards." .. serverId, list)
  205. BzcsLog.logAction("reward_pending", string.format(
  206. "serverId=%s add=%s skip=%s total=%s", serverId, addCnt, skipCnt, #list
  207. ))
  208. end
  209. -- 清空全部待发奖缓存(放弃上轮发奖开新轮时用)
  210. function ClearAllPendingRewards()
  211. if not BzcsData.pendingRewards or not next(BzcsData.pendingRewards) then
  212. return
  213. end
  214. BzcsData.pendingRewards = {}
  215. updateValue("pendingRewards", BzcsData.pendingRewards)
  216. BzcsLog.logAction("reward_pending_clear", "all")
  217. end
  218. -- 取出并清空某逻辑服待发奖列表, 返回 {{uuid, rank}, ...}
  219. function TakePendingRewards(serverId)
  220. BzcsData.pendingRewards = BzcsData.pendingRewards or {}
  221. local list = BzcsData.pendingRewards[serverId]
  222. if not list or #list == 0 then
  223. return {}
  224. end
  225. BzcsData.pendingRewards[serverId] = nil
  226. updateValue("pendingRewards." .. serverId, nil)
  227. return list
  228. end
  229. -- 跨服启动: 加载数据, 无玩家则生成机器人池, 并向已连接逻辑服同步活动状态
  230. function initAfterStart()
  231. if _G.is_middle ~= true then return end
  232. loadData()
  233. if not BzcsData.playerList or not next(BzcsData.playerList) then
  234. GenerateRobots()
  235. end
  236. rebuildServerList()
  237. BzcsLog.logAction("db_init", string.format("playerCnt=%s", Util.getTableCount(BzcsData.playerList or {})))
  238. local BaiZhanChengShenCS = require("baiZhanChengShen.BaiZhanChengShenCS")
  239. BaiZhanChengShenCS.syncActStateToAllConnected()
  240. end
  241. --------------------------------------------------------------------------------
  242. -- 活动周期
  243. --------------------------------------------------------------------------------
  244. -- 获取本轮活动起止时间戳
  245. function GetActivityTimes()
  246. return BzcsData.activityStartTime or 0, BzcsData.activityEndTime or 0
  247. end
  248. -- 获取上次开新轮时间(BZCS_CYCLE_DAYS 周期判定)
  249. function GetLastResetTime()
  250. return BzcsData.lastResetTime or 0
  251. end
  252. -- 本轮周期奖励是否已发放
  253. function IsRewardIssued()
  254. return BzcsData.rewardIssued == true
  255. end
  256. -- 设置活动时间并清除发奖标记(开新轮)
  257. function SetActivityTimes(startTime, endTime)
  258. BzcsData.activityStartTime = startTime
  259. BzcsData.activityEndTime = endTime
  260. BzcsData.lastResetTime = startTime
  261. BzcsData.rewardIssued = false
  262. updateValue("activityStartTime", startTime)
  263. updateValue("activityEndTime", endTime)
  264. updateValue("lastResetTime", startTime)
  265. updateValue("rewardIssued", false)
  266. end
  267. -- 标记本轮是否已发奖
  268. function SetRewardIssued(flag)
  269. BzcsData.rewardIssued = flag
  270. updateValue("rewardIssued", flag)
  271. end
  272. -- 按 uuid 查询跨服玩家/机器人
  273. function GetPlayer(uuid)
  274. local pinfo = BzcsData.playerList and BzcsData.playerList[uuid]
  275. if pinfo then
  276. normalizePlayer(pinfo)
  277. end
  278. return pinfo
  279. end
  280. --------------------------------------------------------------------------------
  281. -- 玩家数据
  282. --------------------------------------------------------------------------------
  283. -- 新增或合并玩家跨服数据
  284. function UpsertPlayer(uuid, data)
  285. BzcsData.playerList = BzcsData.playerList or {}
  286. local old = BzcsData.playerList[uuid]
  287. if old then
  288. for k, v in pairs(data) do
  289. old[k] = v
  290. end
  291. data = old
  292. else
  293. BzcsData.playerList[uuid] = data
  294. end
  295. data.uuid = uuid
  296. normalizePlayer(data)
  297. rankDirty = true
  298. rebuildServerList()
  299. updateValue("playerList." .. uuid, data)
  300. return data
  301. end
  302. -- 增减积分并落库(积分变化时刷新 scoreTime 供同分排序)
  303. function UpdateScore(uuid, delta)
  304. local pinfo = GetPlayer(uuid)
  305. if not pinfo then
  306. BzcsLog.logAction("score_miss", string.format("uuid=%s delta=%s", uuid or "", delta or 0))
  307. return
  308. end
  309. pinfo.score = (pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE) + delta
  310. pinfo.scoreTime = os.time()
  311. rankDirty = true
  312. updateValue("playerList." .. uuid .. ".score", pinfo.score)
  313. updateValue("playerList." .. uuid .. ".scoreTime", pinfo.scoreTime)
  314. BzcsLog.logAction("score", string.format("uuid=%s delta=%s score=%s isRobot=%s", uuid, delta, pinfo.score, pinfo.isRobot or 0))
  315. return pinfo.score
  316. end
  317. --------------------------------------------------------------------------------
  318. -- 排行与匹配
  319. --------------------------------------------------------------------------------
  320. -- 查询玩家当前全服名次, 0=未上榜
  321. function GetRankByUuid(uuid)
  322. if not uuid then return 0 end
  323. ensureRankIndex()
  324. return uuid2rank[uuid] or 0
  325. end
  326. -- 按全服名次取玩家(含机器人), rank 从 1 起
  327. function GetPlayerByRank(rank)
  328. if not rank or rank < 1 then return nil end
  329. ensureRankIndex()
  330. local entry = rankCache and rankCache[rank]
  331. if not entry then return nil end
  332. return GetPlayer(entry.uuid)
  333. end
  334. -- 单条排行榜展示结构(榜单与 myRankInfo 共用)
  335. function BuildRankInfoEntry(pinfo, rank)
  336. if not pinfo then return nil end
  337. local si = pinfo.showInfo or {}
  338. return {
  339. rank = rank or 0,
  340. uuid = pinfo.uuid,
  341. name = si.name or "",
  342. head = si.head or 0,
  343. headFrame = si.headFrame or 0,
  344. serverId = pinfo.serverId or 0,
  345. power = BaiZhanChengShenDefine.CalcPlayerPower(si),
  346. score = pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE,
  347. isRobot = pinfo.isRobot,
  348. }
  349. end
  350. -- 指定玩家排行展示(未注册跨服时仅 rank/score 等默认值)
  351. function BuildPlayerRankInfo(uuid)
  352. local rank = GetRankByUuid(uuid)
  353. local pinfo = GetPlayer(uuid)
  354. local info = BuildRankInfoEntry(pinfo, rank)
  355. if info then return info end
  356. return {
  357. rank = rank,
  358. uuid = uuid or "",
  359. name = "",
  360. head = 0,
  361. headFrame = 0,
  362. serverId = 0,
  363. power = 0,
  364. score = BaiZhanChengShenDefine.BZCS_INIT_SCORE,
  365. }
  366. end
  367. -- 对手详情 WL 回包(仅 GC_BZCS_OPPONENT_INFO 所需字段)
  368. function BuildOpponentInfoSnapshot(pinfo)
  369. if not pinfo then return nil end
  370. local si = pinfo.showInfo or {}
  371. return {
  372. name = si.name or "",
  373. head = si.head or 0,
  374. headFrame = si.headFrame or 0,
  375. power = BaiZhanChengShenDefine.CalcPlayerPower(si),
  376. score = pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE,
  377. }
  378. end
  379. -- 客户端展示榜前 limit 名(默认100), 含 rank/uuid/展示字段
  380. function GetRankList(limit)
  381. ensureRankIndex()
  382. limit = limit or BaiZhanChengShenDefine.BZCS_RANK_MAX
  383. local ret = {}
  384. for i = 1, math.min(limit, #(rankCache or {})) do
  385. local entry = rankCache[i]
  386. local pinfo = GetPlayer(entry.uuid)
  387. local info = BuildRankInfoEntry(pinfo, i)
  388. if info then
  389. ret[#ret + 1] = info
  390. end
  391. end
  392. return ret
  393. end
  394. local function getOpponentBody(uuid, pinfo)
  395. local si = pinfo.showInfo or {}
  396. local body = si.body or 0
  397. if body ~= 0 or pinfo.isRobot == 1 then
  398. return body
  399. end
  400. local db = RoleDBLogic.getDb(uuid, "body")
  401. return db and db.body or 0
  402. end
  403. local function makeMatchOpponentEntry(uuid, pinfo)
  404. local si = pinfo.showInfo or {}
  405. local score = pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE
  406. return {
  407. rank = uuid2rank[uuid] or 0,
  408. uuid = uuid,
  409. serverId = pinfo.serverId or 0,
  410. name = si.name,
  411. body = getOpponentBody(uuid, pinfo),
  412. power = BaiZhanChengShenDefine.CalcPlayerPower(si),
  413. score = score,
  414. isRobot = pinfo.isRobot,
  415. }
  416. end
  417. -- 收集当前积分窗口内可匹配候选(已选/排除名单不入列)
  418. local function collectWindowCandidates(minScore, maxScore, excludeTb)
  419. local candidates = {}
  420. for uuid, pinfo in pairs(BzcsData.playerList or {}) do
  421. if not excludeTb[uuid] then
  422. local score = pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE
  423. if score >= minScore and score <= maxScore then
  424. local oppRank = uuid2rank[uuid] or 0
  425. if oppRank > 0 then
  426. candidates[#candidates + 1] = {uuid = uuid, pinfo = pinfo}
  427. end
  428. end
  429. end
  430. end
  431. return candidates
  432. end
  433. -- 从候选中随机抽取至多 need 名加入结果
  434. local function pickRandomFromCandidates(selected, excludeTb, candidates, need)
  435. if need <= 0 or #candidates < 1 then
  436. return
  437. end
  438. table.shuffle(candidates)
  439. for i = 1, #candidates do
  440. if need <= 0 then
  441. break
  442. end
  443. local c = candidates[i]
  444. excludeTb[c.uuid] = true
  445. selected[#selected + 1] = makeMatchOpponentEntry(c.uuid, c.pinfo)
  446. need = need - 1
  447. end
  448. end
  449. -- 步进匹配不足时: 按与己方积分差升序补满(真人与机器人一视同仁, 可超出步进窗口)
  450. local function fillMatchOpponentsFallback(selected, excludeTb, myScore)
  451. local need = BaiZhanChengShenDefine.BZCS_OPPONENT_CNT - #selected
  452. if need <= 0 then
  453. return
  454. end
  455. local candidates = {}
  456. for uuid, pinfo in pairs(BzcsData.playerList or {}) do
  457. if not excludeTb[uuid] then
  458. local oppRank = uuid2rank[uuid] or 0
  459. if oppRank > 0 then
  460. local score = pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE
  461. candidates[#candidates + 1] = {
  462. uuid = uuid,
  463. pinfo = pinfo,
  464. scoreDist = math.abs(score - myScore),
  465. rank = oppRank,
  466. }
  467. end
  468. end
  469. end
  470. table.sort(candidates, function(a, b)
  471. if a.scoreDist ~= b.scoreDist then
  472. return a.scoreDist < b.scoreDist
  473. end
  474. return a.rank < b.rank
  475. end)
  476. for _, c in ipairs(candidates) do
  477. if need <= 0 then
  478. break
  479. end
  480. excludeTb[c.uuid] = true
  481. selected[#selected + 1] = makeMatchOpponentEntry(c.uuid, c.pinfo)
  482. need = need - 1
  483. end
  484. end
  485. -- 按积分±step*500 步进扩大(±500/±1000/±1500...); 每步窗口内随机抽选; 不足3人时按积分差兜底补满
  486. -- 返回 {rank,uuid,serverId,name,power,score,isRobot}[], rank 为全服积分榜名次(匹配标识)
  487. function GetMatchOpponents(myUuid, myScore, excludeTb)
  488. excludeTb = excludeTb or {}
  489. excludeTb[myUuid] = true
  490. ensureRankIndex()
  491. local selected = {}
  492. local need = BaiZhanChengShenDefine.BZCS_OPPONENT_CNT
  493. local step = 1
  494. while need > 0 and step <= BaiZhanChengShenDefine.BZCS_MATCH_MAX_STEP do
  495. local minScore = myScore - step * BaiZhanChengShenDefine.BZCS_MATCH_STEP
  496. local maxScore = myScore + step * BaiZhanChengShenDefine.BZCS_MATCH_STEP
  497. local candidates = collectWindowCandidates(minScore, maxScore, excludeTb)
  498. pickRandomFromCandidates(selected, excludeTb, candidates, need)
  499. need = BaiZhanChengShenDefine.BZCS_OPPONENT_CNT - #selected
  500. step = step + 1
  501. end
  502. fillMatchOpponentsFallback(selected, excludeTb, myScore)
  503. table.sort(selected, function(a, b)
  504. return (a.rank or 0) < (b.rank or 0)
  505. end)
  506. return selected
  507. end
  508. -- 按全服名次列表取对手摘要(仅刷新展示, 不重算匹配)
  509. function GetMatchOpponentsByRanks(ranks)
  510. if not ranks or #ranks == 0 then
  511. return {}
  512. end
  513. ensureRankIndex()
  514. local ret = {}
  515. for _, rank in ipairs(ranks) do
  516. local pinfo = GetPlayerByRank(rank)
  517. if pinfo then
  518. ret[#ret + 1] = makeMatchOpponentEntry(pinfo.uuid, pinfo)
  519. end
  520. end
  521. table.sort(ret, function(a, b)
  522. return (a.rank or 0) < (b.rank or 0)
  523. end)
  524. return ret
  525. end
  526. -- 新周期: 清除真人跨服数据(删号/合服后避免残留), 重置机器人池与发奖标记
  527. function ResetForNewRound(newStart, newEnd)
  528. newStart = newStart or os.time()
  529. local robotIdx = 0
  530. local removedCnt = 0
  531. for uuid, pinfo in pairs(BzcsData.playerList or {}) do
  532. if pinfo.isRobot == 1 then
  533. robotIdx = robotIdx + 1
  534. local listIdx = BaiZhanChengShenDefine.GetRobotListIndex(uuid)
  535. pinfo.score = getRobotScore(listIdx)
  536. pinfo.scoreTime = newStart + robotIdx
  537. pinfo.firstJoinTime = 0
  538. else
  539. BzcsData.playerList[uuid] = nil
  540. removedCnt = removedCnt + 1
  541. end
  542. end
  543. rankDirty = true
  544. BzcsData.activityStartTime = newStart
  545. BzcsData.activityEndTime = newEnd
  546. BzcsData.lastResetTime = newStart
  547. BzcsData.rewardIssued = false
  548. if not BzcsData.playerList or not next(BzcsData.playerList) then
  549. GenerateRobots()
  550. end
  551. rebuildServerList()
  552. dbUpdate._id = BzcsData._id
  553. LuaMongo.update(DB.db_bzcs, dbUpdate, BzcsData)
  554. BzcsLog.logAction("round_reset", string.format("start=%s end=%s robotCnt=%s removedReal=%s", newStart, newEnd, robotIdx, removedCnt))
  555. end
  556. -- 周期发奖列表: 仅 firstJoinTime>0 且非机器人, 返回 {uuid, rank, serverId}
  557. function GetAllPlayersForReward()
  558. ensureRankIndex()
  559. local ret = {}
  560. for rank, entry in ipairs(rankCache or {}) do
  561. if rank > BaiZhanChengShenDefine.BZCS_REWARD_RANK_MAX then
  562. break
  563. end
  564. local pinfo = GetPlayer(entry.uuid)
  565. if pinfo and (pinfo.firstJoinTime or 0) > 0 and pinfo.isRobot ~= 1 then
  566. ret[#ret + 1] = {entry.uuid, rank, pinfo.serverId}
  567. end
  568. end
  569. return ret
  570. end