From 1d138b17c3872a603a2489516253656990fb829f Mon Sep 17 00:00:00 2001 From: genxium Date: Wed, 1 Feb 2023 17:43:15 +0800 Subject: [PATCH] Fixes for UDP p2p packets handling in frontend input buffer. --- battle_srv/models/room.go | 7 +++-- frontend/assets/scenes/login.fire | 2 +- frontend/assets/scenes/offline_map.fire | 2 +- frontend/assets/scripts/Map.js | 40 +++++++++++++++++-------- frontend/assets/scripts/WsSessionMgr.js | 9 ++++-- frontend/settings/project.json | 2 +- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/battle_srv/models/room.go b/battle_srv/models/room.go index 9fcff3e..dba6c06 100644 --- a/battle_srv/models/room.go +++ b/battle_srv/models/room.go @@ -825,6 +825,7 @@ func (pR *Room) OnDismissed() { dilutedServerFps := float64(58.0) // Don't set this value too small, otherwise we might miss force confirmation needs for slow tickers! pR.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(serverFps) / dilutedServerFps) pR.BattleDurationFrames = int32(60 * serverFps) + //pR.BattleDurationFrames = int32(20 * serverFps) pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1) pR.InputFrameUpsyncDelayTolerance = battle.ConvertToNoDelayInputFrameId(pR.NstDelayFrames) - 1 // this value should be strictly smaller than (NstDelayFrames >> InputScaleFrames), otherwise "type#1 forceConfirmation" might become a lag avalanche pR.MaxChasingRenderFramesPerUpdate = 9 // Don't set this value too high to avoid exhausting frontend CPU within a single frame, roughly as the "turn-around frames to recover" is empirically OK @@ -1187,11 +1188,11 @@ func (pR *Room) markConfirmationIfApplicable(inputFrameUpsyncBatch []*pb.InputFr if false == fromUDP { /* - [WARNING] We have to distinguish whether or not the incoming batch is from UDP here, otherwise "pR.LatestPlayerUpsyncedInputFrameId - pR.LastAllConfirmedInputFrameId" might become unexpectedly large in case of "UDP packet loss + slow ws session"! + [WARNING] We have to distinguish whether or not the incoming batch is from UDP here, otherwise "pR.LatestPlayerUpsyncedInputFrameId - pR.LastAllConfirmedInputFrameId" might become unexpectedly large in case of "UDP packet loss + slow ws session"! - Moreover, only ws session upsyncs should advance "player.LastReceivedInputFrameId" & "pR.LatestPlayerUpsyncedInputFrameId". + Moreover, only ws session upsyncs should advance "player.LastReceivedInputFrameId" & "pR.LatestPlayerUpsyncedInputFrameId". - Kindly note that the updates of "player.LastReceivedInputFrameId" could be discrete before and after reconnection. + Kindly note that the updates of "player.LastReceivedInputFrameId" could be discrete before and after reconnection. */ player.LastReceivedInputFrameId = clientInputFrameId if clientInputFrameId > pR.LatestPlayerUpsyncedInputFrameId { diff --git a/frontend/assets/scenes/login.fire b/frontend/assets/scenes/login.fire index 6ee6c32..8b3a75e 100644 --- a/frontend/assets/scenes/login.fire +++ b/frontend/assets/scenes/login.fire @@ -461,7 +461,7 @@ "array": [ 0, 0, - 209.73151519075364, + 216.2814178905528, 0, 0, 0, diff --git a/frontend/assets/scenes/offline_map.fire b/frontend/assets/scenes/offline_map.fire index d7ae87a..75e44fa 100644 --- a/frontend/assets/scenes/offline_map.fire +++ b/frontend/assets/scenes/offline_map.fire @@ -547,7 +547,7 @@ "array": [ 0, 0, - 209.57814771583418, + 216.2814178905528, 0, 0, 0, diff --git a/frontend/assets/scripts/Map.js b/frontend/assets/scripts/Map.js index 664719f..4e580e6 100644 --- a/frontend/assets/scripts/Map.js +++ b/frontend/assets/scripts/Map.js @@ -46,7 +46,7 @@ window.onUdpMessage = (args) => { const peerJoinIndex = req.joinIndex; if (peerJoinIndex == self.selfPlayerInfo.JoinIndex) return; const batch = req.inputFrameUpsyncBatch; - //self.onPeerInputFrameUpsync(peerJoinIndex, batch, true); // By now calling "self.onPeerInputFrameUpsync" from "onUdpMessage" seems to be overriding local inputs unexpectedly -- even after the local input has already arrived at the peer side via backend udp tunnel! Root cause remains to be investigated. + self.onPeerInputFrameUpsync(peerJoinIndex, batch, true); } } }; @@ -173,7 +173,7 @@ cc.Class({ const prefabbedInputList = new Array(self.playerRichInfoDict.size).fill(0); // the returned "gopkgs.NewInputFrameDownsync.InputList" is immutable, thus we can only modify the values in "prefabbedInputList" - for (let k in prefabbedInputList) { + for (let k = 0; k < window.boundRoomCapacity; ++k) { if (null != existingInputFrame) { // When "null != existingInputFrame", it implies that "true == canConfirmSelf" here, we just have to assign "prefabbedInputList[(joinIndex-1)]" specifically and copy all others prefabbedInputList[k] = existingInputFrame.InputList[k]; @@ -893,7 +893,7 @@ cc.Class({ throw `Failed to dump input cache (maybe recentInputCache too small)! inputFrameDownsync.inputFrameId=${inputFrameDownsync.inputFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`; } } - //self._markConfirmationIfApplicable(); + self._markConfirmationIfApplicable(); if (null == firstPredictedYetIncorrectInputFrameId) return; const renderFrameId1 = gopkgs.ConvertToFirstUsedRenderFrameId(firstPredictedYetIncorrectInputFrameId) - 1; @@ -939,16 +939,18 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu for (let k in batch) { const inputFrame = batch[k]; // could be either "pb.InputFrameDownsync" or "pb.InputFrameUpsync", depending on "fromUDP" const inputFrameId = inputFrame.inputFrameId; - if (inputFrameId < renderedInputFrameIdUpper) { - // Avoid obfuscating already rendered history + if (inputFrameId <= renderedInputFrameIdUpper) { + // [WARNING] Avoid obfuscating already rendered history, even at "inputFrameId == renderedInputFrameIdUpper", due to the use of "INPUT_SCALE_FRAMES" some previous render frames might already be rendered with "inputFrameId"! continue; } if (inputFrameId <= self.lastAllConfirmedInputFrameId) { // [WARNING] Don't reject it by "inputFrameId <= self.lastIndividuallyConfirmedInputFrameId[peerJoinIndex-1]", the arrival of UDP packets might not reserve their sending order! continue; } - const existingInputFrame = self.getOrPrefabInputFrameUpsync(inputFrameId, false); // Make sure that inputFrame exists locally - if (0 < (existingInputFrame.ConfirmedList & (1 << (peerJoinIndex - 1)))) { + const peerJoinIndexMask = (1 << (peerJoinIndex - 1)); + self.getOrPrefabInputFrameUpsync(inputFrameId, false); // Make sure that inputFrame exists locally + const existingInputFrame = self.recentInputCache.GetByFrameId(inputFrameId); + if (0 < (existingInputFrame.ConfirmedList & peerJoinIndexMask)) { continue; } const peerEncodedInput = (true == fromUDP ? inputFrame.encoded : inputFrame.inputList[peerJoinIndex - 1]); @@ -958,13 +960,11 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu } effCnt += 1; // the returned "gopkgs.NewInputFrameDownsync.InputList" is immutable, thus we can only modify the values in "newInputList" and "newConfirmedList"! - let newInputList = new Array(self.playerRichInfoDict.size).fill(0); - for (let i in existingInputFrame.InputList) { - newInputList[i] = existingInputFrame.InputList[i]; - } + let newInputList = existingInputFrame.InputList.slice(); newInputList[peerJoinIndex - 1] = peerEncodedInput; - let newConfirmedList = (existingInputFrame.confirmedList | (1 << (peerJoinIndex - 1))); + let newConfirmedList = (existingInputFrame.ConfirmedList | peerJoinIndex); const newInputFrameDownsyncLocal = gopkgs.NewInputFrameDownsync(inputFrameId, newInputList, newConfirmedList); + console.log(`Updated encoded input of peerJoinIndex=${peerJoinIndex} to ${peerEncodedInput} for inputFrameId=${inputFrameId}/renderedInputFrameIdUpper=${renderedInputFrameIdUpper} from ${JSON.stringify(inputFrame)}; newInputFrameDownsyncLocal=${self.gopkgsInputFrameDownsyncStr(newInputFrameDownsyncLocal)}; existingInputFrame=${self.gopkgsInputFrameDownsyncStr(existingInputFrame)}`); self.recentInputCache.SetByFrameId(newInputFrameDownsyncLocal, inputFrameId); } if (0 < effCnt) { @@ -988,7 +988,6 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu if (ALL_BATTLE_STATES.IN_BATTLE != self.battleState) { return; } - self._stringifyRdfIdToActuallyUsedInput(); window.closeWSConnection(constants.RET_CODE.BATTLE_STOPPED, ""); self.battleState = ALL_BATTLE_STATES.IN_SETTLEMENT; self.countdownNanos = null; @@ -1465,6 +1464,21 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame return s.join(','); }, + gopkgsInputFrameDownsyncStr(inputFrameDownsync) { + if (null == inputFrameDownsync) return "{}"; + const self = this; + let s = []; + s.push(`InputFrameId:${inputFrameDownsync.InputFrameId}`); + let ss = []; + for (let k = 0; k < window.boundRoomCapacity; ++k) { + ss.push(`"${inputFrameDownsync.InputList[k]}"`); + } + s.push(`InputList:[${ss.join(',')}]`); + s.push(`ConfirmedList:${inputFrameDownsync.ConfirmedList}`); + + return `{${s.join(',')}}`; + }, + _stringifyRdfIdToActuallyUsedInput() { const self = this; let s = []; diff --git a/frontend/assets/scripts/WsSessionMgr.js b/frontend/assets/scripts/WsSessionMgr.js index fecc456..da7a2e2 100644 --- a/frontend/assets/scripts/WsSessionMgr.js +++ b/frontend/assets/scripts/WsSessionMgr.js @@ -250,7 +250,8 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { console.warn(`The WS clientSession is closed: evt=${JSON.stringify(evt)}, evt.code=${evt.code}`); if (cc.sys.isNative) { if (mapIns.frameDataLoggingEnabled) { - console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`); + console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()} +`); } DelayNoMore.UdpSession.closeUdpSession(); } @@ -260,7 +261,8 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { case constants.RET_CODE.BATTLE_STOPPED: // deliberately do nothing if (mapIns.frameDataLoggingEnabled) { - console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`); + console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()} +`); } break; case constants.RET_CODE.PLAYER_NOT_ADDABLE_TO_ROOM: @@ -277,7 +279,8 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) { case constants.RET_CODE.PLAYER_CHEATING: case 1006: // Peer(i.e. the backend) gone unexpectedly, but not working for "cc.sys.isNative" if (mapIns.frameDataLoggingEnabled) { - console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`); + console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()} +`); } window.clearLocalStorageAndBackToLoginScene(true); break; diff --git a/frontend/settings/project.json b/frontend/settings/project.json index 63cab17..e14a34c 100644 --- a/frontend/settings/project.json +++ b/frontend/settings/project.json @@ -71,7 +71,7 @@ "shelter_z_reducer", "shelter" ], - "last-module-event-record-time": 1674632533161, + "last-module-event-record-time": 1675240036576, "simulator-orientation": false, "simulator-resolution": { "height": 640,