BaiZhanChengShenDB.lua 20 KB

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