BaiZhanChengShenCS.lua 23 KB

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