BaiZhanChengShenCS.lua 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. -- 百战成神(跨服 CS)
  2. --
  3. -- 职责:
  4. -- 1) 活动周期: Timer.oneMin/onHour -> timedStageHandle 开/关轮、发奖
  5. -- 2) 玩家数据: 积分、排行、匹配、REGISTER/UPDATE_SHOW
  6. -- 3) 协议: 处理普通服 LW_* , 回 WL_* 到对应 logic fd
  7. -- 4) 断连补偿: 逻辑服 LW_HELLO -> onLogicServerConnect 补 ACT_START/待发奖
  8. --
  9. -- 对外导出:
  10. -- oneMin, onHour, onLogicServerConnect, syncActStateToAllConnected
  11. -- N2C_* (InnerHandler 注册)
  12. local InnerMsg = require("core.InnerMsg")
  13. local Util = require("common.Util")
  14. local Timer = require("core.Timer")
  15. local MiddleManager = require("middle.MiddleManager")
  16. local CommonDB = require("common.CommonDB")
  17. local BaiZhanChengShenDB = require("baiZhanChengShen.BaiZhanChengShenDB")
  18. local BaiZhanChengShenDefine = require("baiZhanChengShen.BaiZhanChengShenDefine")
  19. local BzcsLog = require("baiZhanChengShen.BaiZhanChengShenLog")
  20. ------------------------------------ 活动周期调度 ------------------------------------
  21. local wDay
  22. -- 本轮发奖 Timer 已调度(进程内防重; rewardIssued 在调度前落库防 oneMin 重复触发)
  23. local rewardIssueInProgress = false
  24. -- 上一 tick 是否处于可挑战窗口(用于 0:10 到点补广播, 跨服重启后首次 tick 也会触发一次)
  25. local prevIsRunning = false
  26. -- 刷新缓存的当前星期
  27. local function updateWDay()
  28. wDay = Util.getWeekDay()
  29. end
  30. -- 今日 0 点时间戳
  31. local function getTodayStartTime()
  32. return Util.getDayStartTime(os.time())
  33. end
  34. -- 将 baseTime 对齐到所在周开放首日 0:10 (getWeekDay: 1=周日, 7=周六)
  35. local function alignRoundStart(baseTime)
  36. local startW = BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[1]
  37. local endW = BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[2]
  38. local w = Util.getWeekDay(baseTime)
  39. local sub
  40. if startW <= endW then
  41. if w >= startW and w <= endW then
  42. sub = w - startW
  43. elseif w < startW then
  44. sub = w + 7 - startW
  45. else
  46. sub = w - startW
  47. end
  48. elseif w >= startW then
  49. sub = w - startW
  50. else
  51. sub = w + 7 - startW
  52. end
  53. local dayStart = Util.getDayStartTime(baseTime)
  54. return dayStart - sub * 86400 + BaiZhanChengShenDefine.BZCS_START_SEC
  55. end
  56. -- 由开局时间推算结束日(周日) 23:00 时间戳
  57. local function calcRoundEnd(startTime)
  58. local openStart = alignRoundStart(startTime)
  59. local endDayOffset = BaiZhanChengShenDefine.GetOpenEndDayOffset()
  60. local endDayStart = Util.getDayStartTime(openStart) + endDayOffset * 86400
  61. return endDayStart + BaiZhanChengShenDefine.BZCS_END_SEC
  62. end
  63. -- 当前是否在周六~周日开放日
  64. local function isInOpenWday()
  65. if not wDay then updateWDay() end
  66. local startW = BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[1]
  67. local endW = BaiZhanChengShenDefine.BZCS_OPEN_WDAY_AREA[2]
  68. if startW <= endW then
  69. return wDay >= startW and wDay <= endW
  70. end
  71. return wDay >= startW or wDay <= endW
  72. end
  73. -- 本轮在 DB 中仍有效(未过 endTime 且处于开放周), 含 startTime 前待开启时段
  74. local function hasActiveRound(now)
  75. now = now or os.time()
  76. local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
  77. if startTime <= 0 or endTime <= 0 then
  78. return false
  79. end
  80. if now >= endTime then
  81. return false
  82. end
  83. return isInOpenWday()
  84. end
  85. -- 活动是否处于可挑战窗口(已过本轮 startTime)
  86. local function isRunning()
  87. local now = os.time()
  88. local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
  89. if startTime == 0 or endTime == 0 then
  90. return false
  91. end
  92. if now < startTime or now > endTime then
  93. return false
  94. end
  95. return isInOpenWday()
  96. end
  97. -- 首轮开启前置: 跨服基准开服天数达到阈值(最老服)
  98. local function isFirstRoundOpenEligible()
  99. local openDay = CommonDB.getServerOpenDay() or 0
  100. return openDay >= BaiZhanChengShenDefine.BZCS_OPEN_SVR_DAY
  101. end
  102. -- 严格判断当前是否可开启新一轮:
  103. -- 周末开放日 + 已过本轮周六 0:10 + 未超过本轮周日 23:00
  104. -- 首轮还需满足跨服开服天数>=45; 非首轮需满足周期间隔且上轮已结束
  105. local function canOpenNewRoundNow(now, lastReset, startTime, endTime)
  106. now = now or os.time()
  107. if not isInOpenWday() then
  108. return false
  109. end
  110. local roundStart = alignRoundStart(now)
  111. if now < roundStart then
  112. return false
  113. end
  114. local roundEnd = calcRoundEnd(roundStart)
  115. if now > roundEnd then
  116. return false
  117. end
  118. if lastReset == 0 then
  119. if not isFirstRoundOpenEligible() then
  120. return false
  121. end
  122. return true
  123. end
  124. if Util.diffDay(lastReset) < BaiZhanChengShenDefine.BZCS_CYCLE_DAYS then
  125. return false
  126. end
  127. if startTime > 0 and now < endTime then
  128. return false
  129. end
  130. return true
  131. end
  132. -- 汇总本次 WL 广播已通知的逻辑服 serverId(MiddleManager_GetAllFD 返回 SVRINDEX_2_FD, key 即 serverId)
  133. local function formatNotifyServerIds(svrIndex2Fd)
  134. local seen, serverIds = {}, {}
  135. for serverId, _ in pairs(svrIndex2Fd or {}) do
  136. if serverId and not seen[serverId] then
  137. seen[serverId] = true
  138. serverIds[#serverIds + 1] = serverId
  139. end
  140. end
  141. -- 合服扩展连接(仅 MERGE 表有登记时补充)
  142. for serverId, _ in pairs(SVRINDEX_2_FD_MERGE or {}) do
  143. if serverId and not seen[serverId] then
  144. seen[serverId] = true
  145. serverIds[#serverIds + 1] = serverId
  146. end
  147. end
  148. table.sort(serverIds)
  149. return table.concat(serverIds, ",")
  150. end
  151. -- 向单个逻辑服 fd 推送当前活动状态(重连/跨服重启后补偿, 避免错过广播)
  152. local function sendActStateToFd(fd)
  153. if not fd then return end
  154. local serverId = MiddleManager.FD_2_SVRINDEX[fd] or 0
  155. local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
  156. if hasActiveRound() then
  157. local msgData = InnerMsg.wl.WL_BZCS_ACT_START
  158. msgData.startTime = startTime
  159. msgData.endTime = endTime
  160. InnerMsg.sendMsg(fd, msgData)
  161. BzcsLog.logAction("act_open", string.format(
  162. "tag=sync_fd serverId=%s fd=%s start=%s end=%s running=%s",
  163. serverId, fd, startTime, endTime, isRunning() and 1 or 0
  164. ))
  165. else
  166. local msgData = InnerMsg.wl.WL_BZCS_ACT_END
  167. InnerMsg.sendMsg(fd, msgData)
  168. BzcsLog.logAction("act_close", string.format(
  169. "tag=sync_fd serverId=%s fd=%s start=%s end=%s running=0",
  170. serverId, fd, startTime, endTime
  171. ))
  172. end
  173. end
  174. -- 逻辑服连上跨服时: 同步活动状态 + 补发断连期间缓存的周期奖励
  175. function onLogicServerConnect(fd)
  176. if _G.is_middle ~= true or not fd then return end
  177. sendActStateToFd(fd)
  178. local serverId = MiddleManager.FD_2_SVRINDEX[fd]
  179. if not serverId then return end
  180. local pending = BaiZhanChengShenDB.TakePendingRewards(serverId)
  181. if #pending > 0 then
  182. local msgData = InnerMsg.wl.WL_BZCS_ISSUE_REWARD
  183. msgData.rewardList = pending
  184. InnerMsg.sendMsg(fd, msgData)
  185. BzcsLog.logAction("reward_reissue", string.format("serverId=%s cnt=%s", serverId, #pending))
  186. end
  187. end
  188. -- 跨服启动后向已连接逻辑服同步活动状态
  189. function syncActStateToAllConnected()
  190. if _G.is_middle ~= true then return end
  191. local fdList = MiddleManager.MiddleManager_GetAllFD()
  192. for _, fd in pairs(fdList) do
  193. sendActStateToFd(fd)
  194. end
  195. end
  196. -- 广播活动开启, 普通服写入 KEY_BZCS_START_TIME; tag 用于区分开轮/时间修正等场景
  197. function ActOpen(startTime, tag)
  198. tag = tag or "broadcast"
  199. local msgData = InnerMsg.wl.WL_BZCS_ACT_START
  200. msgData.startTime = startTime or os.time()
  201. local _, endTime = BaiZhanChengShenDB.GetActivityTimes()
  202. msgData.endTime = endTime
  203. local svrIndex2Fd = MiddleManager.MiddleManager_GetAllFD()
  204. local fdCnt = 0
  205. for serverId, fd in pairs(svrIndex2Fd) do
  206. InnerMsg.sendMsg(fd, msgData)
  207. fdCnt = fdCnt + 1
  208. end
  209. BzcsLog.logAction("act_open", string.format(
  210. "tag=%s start=%s end=%s fdCnt=%s running=%s servers=%s",
  211. tag, msgData.startTime, msgData.endTime or 0, fdCnt, isRunning() and 1 or 0,
  212. formatNotifyServerIds(svrIndex2Fd)
  213. ))
  214. end
  215. -- 广播活动结束; tag 用于区分正常结算/放弃上轮等场景
  216. function ActEnd(tag)
  217. tag = tag or "broadcast"
  218. local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
  219. local msgData = InnerMsg.wl.WL_BZCS_ACT_END
  220. local svrIndex2Fd = MiddleManager.MiddleManager_GetAllFD()
  221. local fdCnt = 0
  222. for serverId, fd in pairs(svrIndex2Fd) do
  223. InnerMsg.sendMsg(fd, msgData)
  224. fdCnt = fdCnt + 1
  225. end
  226. BzcsLog.logAction("act_close", string.format(
  227. "tag=%s start=%s end=%s fdCnt=%s rewardIssued=%s servers=%s",
  228. tag, startTime or 0, endTime or 0, fdCnt, BaiZhanChengShenDB.IsRewardIssued() and 1 or 0,
  229. formatNotifyServerIds(svrIndex2Fd)
  230. ))
  231. end
  232. -- 按 serverId 汇总待发奖 {{uuid,rank},...}
  233. local function groupRewardsByServer(playerList)
  234. local byServer = {}
  235. for _, info in ipairs(playerList) do
  236. local uuid, rank, serverId = info[1], info[2], info[3]
  237. if serverId then
  238. local list = byServer[serverId]
  239. if not list then
  240. list = {}
  241. byServer[serverId] = list
  242. end
  243. list[#list + 1] = {uuid, rank}
  244. end
  245. end
  246. return byServer
  247. end
  248. -- 向指定逻辑服批量下发排名奖励(每服一次 WL); 未连接则整批入 pending
  249. local function issueRewardBatch(serverId, rewardList)
  250. if not rewardList or #rewardList == 0 then return end
  251. local fd = MiddleManager.getFDBySvrIndex(serverId)
  252. if fd then
  253. local msgData = InnerMsg.wl.WL_BZCS_ISSUE_REWARD
  254. msgData.rewardList = rewardList
  255. InnerMsg.sendMsg(fd, msgData)
  256. BzcsLog.logAction("reward_issue_send", string.format("serverId=%s cnt=%s", serverId, #rewardList))
  257. else
  258. BaiZhanChengShenDB.AddPendingRewards(serverId, rewardList)
  259. BzcsLog.logAction("reward_issue_offline", string.format("serverId=%s cnt=%s", serverId, #rewardList))
  260. end
  261. end
  262. -- 活动结束发奖: 按逻辑服批量 WL_BZCS_ISSUE_REWARD(服与服之间 2s 节流)
  263. function IssueRewardManager()
  264. if BaiZhanChengShenDB.IsRewardIssued() or rewardIssueInProgress then
  265. return
  266. end
  267. local rewardPlayers = BaiZhanChengShenDB.GetAllPlayersForReward()
  268. local byServer = groupRewardsByServer(rewardPlayers)
  269. local serverList = {}
  270. for serverId, rewardList in pairs(byServer) do
  271. serverList[#serverList + 1] = {serverId, rewardList}
  272. end
  273. if #serverList == 0 then
  274. BaiZhanChengShenDB.SetRewardIssued(true)
  275. return
  276. end
  277. rewardIssueInProgress = true
  278. -- 先落库标记, 避免异步 Timer 完成前 timedStageHandle/oneMin 重复调度
  279. BaiZhanChengShenDB.SetRewardIssued(true)
  280. BzcsLog.logAction("reward_issue", string.format("playerCnt=%s serverCnt=%s", #rewardPlayers, #serverList))
  281. local delay = 0
  282. local onlineCnt, pendingCnt = 0, 0
  283. for _, entry in ipairs(serverList) do
  284. local serverId, rewardList = entry[1], entry[2]
  285. local fd = MiddleManager.getFDBySvrIndex(serverId)
  286. if not fd then
  287. BaiZhanChengShenDB.AddPendingRewards(serverId, rewardList)
  288. pendingCnt = pendingCnt + 1
  289. else
  290. delay = delay + 2
  291. onlineCnt = onlineCnt + 1
  292. Timer.addLater(delay, issueRewardBatch, serverId, rewardList)
  293. end
  294. end
  295. rewardIssueInProgress = false
  296. BzcsLog.logAction("reward_issue_scheduled", string.format(
  297. "onlineCnt=%s pendingCnt=%s delaySec=%s", onlineCnt, pendingCnt, delay
  298. ))
  299. end
  300. -- 开启新周期: 重置 DB 并 ActOpen
  301. local function newRoundHandle(now)
  302. now = now or os.time()
  303. local startTime = alignRoundStart(now)
  304. local endTime = calcRoundEnd(startTime)
  305. BaiZhanChengShenDB.ResetForNewRound(startTime, endTime)
  306. ActOpen(startTime, "new_round")
  307. end
  308. -- 结束当前周期: 发奖 + ActEnd
  309. local function endRoundHandle()
  310. if BaiZhanChengShenDB.IsRewardIssued() or rewardIssueInProgress then
  311. return
  312. end
  313. BzcsLog.logAction("act_settle", "begin_issue_reward")
  314. IssueRewardManager()
  315. ActEnd("end_round")
  316. end
  317. -- 放弃上轮未完成的周期发奖(满21天开新轮时不再补发)
  318. local function abandonUnissuedRewards()
  319. if BaiZhanChengShenDB.IsRewardIssued() then
  320. return
  321. end
  322. BzcsLog.logAction("reward_abandon", "new_round_skip")
  323. BaiZhanChengShenDB.SetRewardIssued(true)
  324. BaiZhanChengShenDB.ClearAllPendingRewards()
  325. ActEnd("abandon_new_round")
  326. end
  327. -- 是否应开启新轮; 条件满足则执行 newRoundHandle 并广播
  328. local function tryOpenNewRound(now, lastReset, startTime, endTime)
  329. if not canOpenNewRoundNow(now, lastReset, startTime, endTime) then
  330. return false
  331. end
  332. if lastReset > 0 and not BaiZhanChengShenDB.IsRewardIssued() then
  333. abandonUnissuedRewards()
  334. end
  335. newRoundHandle(now)
  336. return true
  337. end
  338. -- 周期阶段机 (oneMin/onHour 调用):
  339. -- 1) 严格满足开新轮条件(含首轮45天门槛、周末窗口 0:10~23:00、间隔/结束检查) -> newRoundHandle + ActOpen
  340. -- 2) 已过 endTime 且未发奖 -> endRoundHandle
  341. -- 3) 刚进入可挑战窗口(如周六0:10) -> ActOpen 补偿逻辑服
  342. local function timedStageHandle()
  343. local now = os.time()
  344. local lastReset = BaiZhanChengShenDB.GetLastResetTime()
  345. local startTime, endTime = BaiZhanChengShenDB.GetActivityTimes()
  346. local running = isRunning()
  347. if tryOpenNewRound(now, lastReset, startTime, endTime) then
  348. prevIsRunning = isRunning()
  349. return
  350. end
  351. if startTime > 0 and now >= endTime and not BaiZhanChengShenDB.IsRewardIssued() then
  352. endRoundHandle()
  353. prevIsRunning = false
  354. return
  355. end
  356. if running and not prevIsRunning then
  357. ActOpen(startTime, "window_open")
  358. end
  359. prevIsRunning = running
  360. end
  361. -- Timer 每分钟(跳过整点)检查阶段
  362. function oneMin()
  363. if _G.is_middle ~= true then return end
  364. if Util.getMin() == 0 then return end
  365. timedStageHandle()
  366. end
  367. -- Timer 每小时检查阶段, 0 点刷新星期
  368. function onHour(hour)
  369. if _G.is_middle ~= true then return end
  370. if hour == 0 or not wDay then
  371. updateWDay()
  372. end
  373. timedStageHandle()
  374. end
  375. ------------------------------------ N2C (普通服 LW -> 本模块 -> WL 回包) ------------------------------------
  376. -- msg 中带 sourceServerId/playerUuid, 通过 MiddleManager.getFDBySvrIndex 回包
  377. -- 向逻辑服 fd 发送 WL 协议
  378. local function sendWL(fd, msgData)
  379. if not fd then return false end
  380. InnerMsg.sendMsg(fd, msgData)
  381. return true
  382. end
  383. -- 业务失败时回 WL_BZCS_TIPS
  384. local function errTips(sourceServerId, playerUuid, errCode)
  385. BzcsLog.logAction("err_tips", string.format("serverId=%s uuid=%s err=%s", sourceServerId or 0, playerUuid or "", errCode or 0))
  386. local msgData = InnerMsg.wl.WL_BZCS_TIPS
  387. msgData.playerUuid = playerUuid
  388. msgData.errCode = errCode or 0
  389. local fd = MiddleManager.getFDBySvrIndex(sourceServerId)
  390. if not fd then return end
  391. sendWL(fd, msgData)
  392. end
  393. -- LW_BZCS_MATCH -> WL_BZCS_MATCH (±500步进扩大, 窗口内随机匹配最多3人; refreshRanks 非空时仅刷新展示)
  394. function N2C_Match(msg)
  395. local fd = MiddleManager.getFDBySvrIndex(msg.sourceServerId)
  396. if not isRunning() then
  397. return errTips(msg.sourceServerId, msg.playerUuid, BaiZhanChengShenDefine.BZCS_ERR_NOT_OPEN)
  398. end
  399. local pinfo = BaiZhanChengShenDB.GetPlayer(msg.playerUuid)
  400. local myScore = pinfo and pinfo.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE
  401. local refreshRanks = msg.refreshRanks
  402. local opponents
  403. if refreshRanks and #refreshRanks > 0 then
  404. opponents = BaiZhanChengShenDB.GetMatchOpponentsByRanks(refreshRanks)
  405. if #opponents < #refreshRanks then
  406. opponents = BaiZhanChengShenDB.GetMatchOpponents(msg.playerUuid, myScore, {})
  407. end
  408. else
  409. opponents = BaiZhanChengShenDB.GetMatchOpponents(msg.playerUuid, myScore, {})
  410. end
  411. local msgData = InnerMsg.wl.WL_BZCS_MATCH
  412. msgData.playerUuid = msg.playerUuid
  413. msgData.myScore = myScore
  414. msgData.myRank = BaiZhanChengShenDB.GetRankByUuid(msg.playerUuid)
  415. msgData.opponentList = opponents
  416. sendWL(fd, msgData)
  417. end
  418. -- LW_BZCS_RANK_LIST -> WL_BZCS_RANK_LIST
  419. function N2C_RankList(msg)
  420. local fd = MiddleManager.getFDBySvrIndex(msg.sourceServerId)
  421. local msgData = InnerMsg.wl.WL_BZCS_RANK_LIST
  422. msgData.playerUuid = msg.playerUuid
  423. msgData.rankList = BaiZhanChengShenDB.GetRankList(BaiZhanChengShenDefine.BZCS_RANK_MAX)
  424. msgData.myRankInfo = BaiZhanChengShenDB.BuildPlayerRankInfo(msg.playerUuid)
  425. sendWL(fd, msgData)
  426. end
  427. -- LW_BZCS_OPPONENT_INFO -> WL_BZCS_OPPONENT_INFO
  428. function N2C_OpponentInfo(msg)
  429. local fd = MiddleManager.getFDBySvrIndex(msg.sourceServerId)
  430. local target = BaiZhanChengShenDB.GetPlayerByRank(msg.targetRank)
  431. local msgData = InnerMsg.wl.WL_BZCS_OPPONENT_INFO
  432. msgData.playerUuid = msg.playerUuid
  433. msgData.res = target and 0 or -1
  434. msgData.targetInfo = BaiZhanChengShenDB.BuildOpponentInfoSnapshot(target) or {}
  435. sendWL(fd, msgData)
  436. end
  437. -- LW_BZCS_OPPONENT_LINEUP -> WL_BZCS_OPPONENT_LINEUP (含机器人 showInfo)
  438. function N2C_OpponentLineup(msg)
  439. local fd = MiddleManager.getFDBySvrIndex(msg.sourceServerId)
  440. local target = BaiZhanChengShenDB.GetPlayerByRank(msg.targetRank)
  441. local msgData = InnerMsg.wl.WL_BZCS_OPPONENT_LINEUP
  442. msgData.playerUuid = msg.playerUuid
  443. msgData.targetRank = msg.targetRank or 0
  444. msgData.showInfo = target and target.showInfo or {}
  445. msgData.isRobot = target and target.isRobot or 0
  446. sendWL(fd, msgData)
  447. end
  448. -- LW_BZCS_CAN_FIGHT -> WL_BZCS_CAN_FIGHT (按开战时全服名次锁定对手, 扣次在 NS C2N_CanFight)
  449. function N2C_CanFight(msg)
  450. local fd = MiddleManager.getFDBySvrIndex(msg.sourceServerId)
  451. if not isRunning() then
  452. return errTips(msg.sourceServerId, msg.playerUuid, BaiZhanChengShenDefine.BZCS_ERR_NOT_OPEN)
  453. end
  454. local targetRank = msg.targetRank
  455. if not targetRank or targetRank < 1 then
  456. return errTips(msg.sourceServerId, msg.playerUuid, BaiZhanChengShenDefine.BZCS_ERR_TARGET_INVALID)
  457. end
  458. local target = BaiZhanChengShenDB.GetPlayerByRank(targetRank)
  459. if not target then
  460. return errTips(msg.sourceServerId, msg.playerUuid, BaiZhanChengShenDefine.BZCS_ERR_TARGET_INVALID)
  461. end
  462. if target.uuid == msg.playerUuid then
  463. return errTips(msg.sourceServerId, msg.playerUuid, BaiZhanChengShenDefine.BZCS_ERR_TARGET_INVALID)
  464. end
  465. local msgData = InnerMsg.wl.WL_BZCS_CAN_FIGHT
  466. msgData.playerUuid = msg.playerUuid
  467. msgData.targetRank = targetRank
  468. msgData.defUuid = target.uuid
  469. msgData.defServerId = target.serverId or 0
  470. local si = target.showInfo or {}
  471. msgData.defName = si.name or ""
  472. msgData.defScore = target.score or BaiZhanChengShenDefine.BZCS_INIT_SCORE
  473. msgData.isRobot = target.isRobot or 0
  474. msgData.res = 0
  475. sendWL(fd, msgData)
  476. end
  477. -- LW_BZCS_REGISTER 首次挑战注册跨服玩家(保留已有积分/firstJoinTime)
  478. function N2C_Register(msg)
  479. local pinfo = msg.playerInfo
  480. if not pinfo or not pinfo.uuid then return end
  481. local old = BaiZhanChengShenDB.GetPlayer(pinfo.uuid)
  482. if old then
  483. pinfo.score = old.score
  484. pinfo.scoreTime = old.scoreTime
  485. if (old.firstJoinTime or 0) > 0 then
  486. pinfo.firstJoinTime = old.firstJoinTime
  487. end
  488. else
  489. pinfo.score = BaiZhanChengShenDefine.BZCS_INIT_SCORE
  490. pinfo.firstJoinTime = pinfo.firstJoinTime or os.time()
  491. pinfo.scoreTime = os.time()
  492. end
  493. pinfo.isRobot = 0
  494. BaiZhanChengShenDB.UpsertPlayer(pinfo.uuid, pinfo)
  495. BzcsLog.logAction("register", string.format("uuid=%s serverId=%s score=%s firstJoin=%s", pinfo.uuid, pinfo.serverId or 0, pinfo.score or 0, pinfo.firstJoinTime or 0))
  496. end
  497. -- LW_BZCS_UPDATE_SHOW 增量合并展示数据
  498. function N2C_UpdateShow(msg)
  499. local pinfo = BaiZhanChengShenDB.GetPlayer(msg.playerUuid)
  500. if not pinfo or not msg.showInfo then return end
  501. pinfo.showInfo = pinfo.showInfo or {}
  502. BaiZhanChengShenDefine.MergeShowInfo(pinfo.showInfo, msg.showInfo)
  503. BaiZhanChengShenDB.UpsertPlayer(msg.playerUuid, pinfo)
  504. BzcsLog.logAction("update_show", string.format("uuid=%s type=%s race=%s", msg.playerUuid, msg.updateType or 0, msg.race or 0))
  505. end
  506. -- REGISTER 尚未落库时, 用 FIGHT_END 包字段建最小攻方记录(兜底乱序/丢包)
  507. local function ensureAtkRegistered(msg)
  508. local atkUuid = msg and msg.atkUuid
  509. if not atkUuid or atkUuid == "" then return end
  510. if BaiZhanChengShenDB.GetPlayer(atkUuid) then return end
  511. local pinfo = {
  512. uuid = atkUuid,
  513. serverId = msg.atkServerId or 0,
  514. score = BaiZhanChengShenDefine.BZCS_INIT_SCORE,
  515. firstJoinTime = os.time(),
  516. scoreTime = os.time(),
  517. isRobot = 0,
  518. showInfo = { name = msg.atkName or "" },
  519. }
  520. BaiZhanChengShenDB.UpsertPlayer(atkUuid, pinfo)
  521. BzcsLog.logAction("register_fallback", string.format("uuid=%s serverId=%s", atkUuid, pinfo.serverId))
  522. end
  523. -- LW_BZCS_FIGHT_END 整场结算: 攻守加减分, WL 通知攻方; 真人守方另发 WL_BZCS_DEF_NOTIFY
  524. function N2C_FightEnd(msg)
  525. local atkUuid = msg.atkUuid
  526. local defUuid = msg.defUuid
  527. local atkWin = msg.atkWin == 1
  528. local atkDelta = atkWin and BaiZhanChengShenDefine.BZCS_ATK_WIN_SCORE or BaiZhanChengShenDefine.BZCS_ATK_LOSE_SCORE
  529. local defDelta = atkWin and BaiZhanChengShenDefine.BZCS_DEF_LOSE_SCORE or BaiZhanChengShenDefine.BZCS_DEF_WIN_SCORE
  530. ensureAtkRegistered(msg)
  531. local atkScore = BaiZhanChengShenDB.UpdateScore(atkUuid, atkDelta)
  532. local defScore = BaiZhanChengShenDB.UpdateScore(defUuid, defDelta)
  533. if not atkScore then
  534. BzcsLog.logAction("fight_end_atk_score_fail", string.format(
  535. "atk=%s atkSvr=%s delta=%s", atkUuid or "", msg.atkServerId or 0, atkDelta
  536. ))
  537. atkScore = BaiZhanChengShenDefine.BZCS_INIT_SCORE + atkDelta
  538. end
  539. BzcsLog.logAction("fight_end", string.format(
  540. "atk=%s def=%s atkWin=%s atkDelta=%s defDelta=%s atkScore=%s defScore=%s atkSvr=%s defSvr=%s",
  541. atkUuid or "", defUuid or "", msg.atkWin or 0, atkDelta, defDelta, atkScore, defScore or -1,
  542. msg.atkServerId or 0, msg.defServerId or 0
  543. ))
  544. local atkFd = MiddleManager.getFDBySvrIndex(msg.atkServerId)
  545. local wlAtk = InnerMsg.wl.WL_BZCS_FIGHT_END
  546. wlAtk.playerUuid = atkUuid
  547. wlAtk.atkWin = msg.atkWin or 0
  548. wlAtk.scoreChange = atkDelta
  549. wlAtk.myScore = atkScore
  550. wlAtk.defName = msg.defName or ""
  551. wlAtk.defServerId = msg.defServerId or 0
  552. wlAtk.raceResults = msg.raceResults or {}
  553. if not sendWL(atkFd, wlAtk) then
  554. BzcsLog.logAction("fight_end_wl_fail", string.format("atk=%s atkSvr=%s", atkUuid or "", msg.atkServerId or 0))
  555. end
  556. local defInfo = BaiZhanChengShenDB.GetPlayer(defUuid)
  557. if defInfo and defInfo.isRobot ~= 1 and defInfo.serverId and defScore then
  558. local defFd = MiddleManager.getFDBySvrIndex(defInfo.serverId)
  559. if defFd then
  560. local wlDef = InnerMsg.wl.WL_BZCS_DEF_NOTIFY
  561. wlDef.playerUuid = defUuid
  562. wlDef.atkName = msg.atkName or ""
  563. wlDef.atkServerId = msg.atkServerId or 0
  564. wlDef.atkWin = msg.atkWin == 1 and 0 or 1
  565. wlDef.scoreChange = defDelta
  566. wlDef.myScore = defScore
  567. wlDef.raceResults = msg.raceResults or {}
  568. sendWL(defFd, wlDef)
  569. end
  570. elseif defInfo and defInfo.isRobot ~= 1 and not defScore then
  571. BzcsLog.logAction("fight_end_def_score_fail", string.format(
  572. "def=%s defSvr=%s delta=%s", defUuid or "", defInfo.serverId or 0, defDelta
  573. ))
  574. end
  575. end