Fixes for UDP p2p packets handling in frontend input buffer.

This commit is contained in:
genxium 2023-02-01 17:43:15 +08:00
parent 851678e2f3
commit 1d138b17c3
6 changed files with 40 additions and 22 deletions

View File

@ -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! 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.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(serverFps) / dilutedServerFps)
pR.BattleDurationFrames = int32(60 * serverFps) pR.BattleDurationFrames = int32(60 * serverFps)
//pR.BattleDurationFrames = int32(20 * serverFps)
pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1) 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.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 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 { 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 player.LastReceivedInputFrameId = clientInputFrameId
if clientInputFrameId > pR.LatestPlayerUpsyncedInputFrameId { if clientInputFrameId > pR.LatestPlayerUpsyncedInputFrameId {

View File

@ -461,7 +461,7 @@
"array": [ "array": [
0, 0,
0, 0,
209.73151519075364, 216.2814178905528,
0, 0,
0, 0,
0, 0,

View File

@ -547,7 +547,7 @@
"array": [ "array": [
0, 0,
0, 0,
209.57814771583418, 216.2814178905528,
0, 0,
0, 0,
0, 0,

View File

@ -46,7 +46,7 @@ window.onUdpMessage = (args) => {
const peerJoinIndex = req.joinIndex; const peerJoinIndex = req.joinIndex;
if (peerJoinIndex == self.selfPlayerInfo.JoinIndex) return; if (peerJoinIndex == self.selfPlayerInfo.JoinIndex) return;
const batch = req.inputFrameUpsyncBatch; 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); 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" // 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) { 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 // 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]; 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)}`; 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; if (null == firstPredictedYetIncorrectInputFrameId) return;
const renderFrameId1 = gopkgs.ConvertToFirstUsedRenderFrameId(firstPredictedYetIncorrectInputFrameId) - 1; const renderFrameId1 = gopkgs.ConvertToFirstUsedRenderFrameId(firstPredictedYetIncorrectInputFrameId) - 1;
@ -939,16 +939,18 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
for (let k in batch) { for (let k in batch) {
const inputFrame = batch[k]; // could be either "pb.InputFrameDownsync" or "pb.InputFrameUpsync", depending on "fromUDP" const inputFrame = batch[k]; // could be either "pb.InputFrameDownsync" or "pb.InputFrameUpsync", depending on "fromUDP"
const inputFrameId = inputFrame.inputFrameId; const inputFrameId = inputFrame.inputFrameId;
if (inputFrameId < renderedInputFrameIdUpper) { if (inputFrameId <= renderedInputFrameIdUpper) {
// Avoid obfuscating already rendered history // [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; continue;
} }
if (inputFrameId <= self.lastAllConfirmedInputFrameId) { 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! // [WARNING] Don't reject it by "inputFrameId <= self.lastIndividuallyConfirmedInputFrameId[peerJoinIndex-1]", the arrival of UDP packets might not reserve their sending order!
continue; continue;
} }
const existingInputFrame = self.getOrPrefabInputFrameUpsync(inputFrameId, false); // Make sure that inputFrame exists locally const peerJoinIndexMask = (1 << (peerJoinIndex - 1));
if (0 < (existingInputFrame.ConfirmedList & (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; continue;
} }
const peerEncodedInput = (true == fromUDP ? inputFrame.encoded : inputFrame.inputList[peerJoinIndex - 1]); 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; effCnt += 1;
// the returned "gopkgs.NewInputFrameDownsync.InputList" is immutable, thus we can only modify the values in "newInputList" and "newConfirmedList"! // 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); let newInputList = existingInputFrame.InputList.slice();
for (let i in existingInputFrame.InputList) {
newInputList[i] = existingInputFrame.InputList[i];
}
newInputList[peerJoinIndex - 1] = peerEncodedInput; newInputList[peerJoinIndex - 1] = peerEncodedInput;
let newConfirmedList = (existingInputFrame.confirmedList | (1 << (peerJoinIndex - 1))); let newConfirmedList = (existingInputFrame.ConfirmedList | peerJoinIndex);
const newInputFrameDownsyncLocal = gopkgs.NewInputFrameDownsync(inputFrameId, newInputList, newConfirmedList); 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); self.recentInputCache.SetByFrameId(newInputFrameDownsyncLocal, inputFrameId);
} }
if (0 < effCnt) { if (0 < effCnt) {
@ -988,7 +988,6 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
if (ALL_BATTLE_STATES.IN_BATTLE != self.battleState) { if (ALL_BATTLE_STATES.IN_BATTLE != self.battleState) {
return; return;
} }
self._stringifyRdfIdToActuallyUsedInput();
window.closeWSConnection(constants.RET_CODE.BATTLE_STOPPED, ""); window.closeWSConnection(constants.RET_CODE.BATTLE_STOPPED, "");
self.battleState = ALL_BATTLE_STATES.IN_SETTLEMENT; self.battleState = ALL_BATTLE_STATES.IN_SETTLEMENT;
self.countdownNanos = null; self.countdownNanos = null;
@ -1465,6 +1464,21 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
return s.join(','); 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() { _stringifyRdfIdToActuallyUsedInput() {
const self = this; const self = this;
let s = []; let s = [];

View File

@ -250,7 +250,8 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
console.warn(`The WS clientSession is closed: evt=${JSON.stringify(evt)}, evt.code=${evt.code}`); console.warn(`The WS clientSession is closed: evt=${JSON.stringify(evt)}, evt.code=${evt.code}`);
if (cc.sys.isNative) { if (cc.sys.isNative) {
if (mapIns.frameDataLoggingEnabled) { if (mapIns.frameDataLoggingEnabled) {
console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`); console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}
`);
} }
DelayNoMore.UdpSession.closeUdpSession(); DelayNoMore.UdpSession.closeUdpSession();
} }
@ -260,7 +261,8 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
case constants.RET_CODE.BATTLE_STOPPED: case constants.RET_CODE.BATTLE_STOPPED:
// deliberately do nothing // deliberately do nothing
if (mapIns.frameDataLoggingEnabled) { if (mapIns.frameDataLoggingEnabled) {
console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`); console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}
`);
} }
break; break;
case constants.RET_CODE.PLAYER_NOT_ADDABLE_TO_ROOM: 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 constants.RET_CODE.PLAYER_CHEATING:
case 1006: // Peer(i.e. the backend) gone unexpectedly, but not working for "cc.sys.isNative" case 1006: // Peer(i.e. the backend) gone unexpectedly, but not working for "cc.sys.isNative"
if (mapIns.frameDataLoggingEnabled) { if (mapIns.frameDataLoggingEnabled) {
console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}`); console.warn(`${mapIns._stringifyRdfIdToActuallyUsedInput()}
`);
} }
window.clearLocalStorageAndBackToLoginScene(true); window.clearLocalStorageAndBackToLoginScene(true);
break; break;

View File

@ -71,7 +71,7 @@
"shelter_z_reducer", "shelter_z_reducer",
"shelter" "shelter"
], ],
"last-module-event-record-time": 1674632533161, "last-module-event-record-time": 1675240036576,
"simulator-orientation": false, "simulator-orientation": false,
"simulator-resolution": { "simulator-resolution": {
"height": 640, "height": 640,