BaiZhanChengShenDB.lua 21 KB

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