Minor update.

This commit is contained in:
genxium 2023-02-01 21:35:00 +08:00
parent d37ebd4c33
commit 5cfcac6cf6
2 changed files with 46 additions and 13 deletions

View File

@ -894,8 +894,12 @@ cc.Class({
} }
} }
self._markConfirmationIfApplicable(); self._markConfirmationIfApplicable();
self._handleIncorrectlyRenderedPrediction(firstPredictedYetIncorrectInputFrameId, batch, false);
},
_handleIncorrectlyRenderedPrediction(firstPredictedYetIncorrectInputFrameId, batch, fromUDP) {
if (null == firstPredictedYetIncorrectInputFrameId) return; if (null == firstPredictedYetIncorrectInputFrameId) return;
const self = this;
const renderFrameId1 = gopkgs.ConvertToFirstUsedRenderFrameId(firstPredictedYetIncorrectInputFrameId) - 1; const renderFrameId1 = gopkgs.ConvertToFirstUsedRenderFrameId(firstPredictedYetIncorrectInputFrameId) - 1;
if (renderFrameId1 >= self.chaserRenderFrameId) return; if (renderFrameId1 >= self.chaserRenderFrameId) return;
@ -911,12 +915,17 @@ cc.Class({
-------------------------------------------------------- --------------------------------------------------------
*/ */
// The actual rollback-and-chase would later be executed in update(dt). // The actual rollback-and-chase would later be executed in update(dt).
console.log(`Mismatched input detected, resetting chaserRenderFrameId: ${self.chaserRenderFrameId}->${renderFrameId1} by firstPredictedYetIncorrectInputFrameId: ${firstPredictedYetIncorrectInputFrameId} console.log(`Mismatched input detected, resetting chaserRenderFrameId: ${self.chaserRenderFrameId}->${renderFrameId1} by
firstPredictedYetIncorrectInputFrameId: ${firstPredictedYetIncorrectInputFrameId}
lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId} lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}
recentInputCache=${self._stringifyRecentInputCache(false)} recentInputCache=${self._stringifyRecentInputCache(false)}
batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inputFrameId}]`); batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inputFrameId}]
fromUDP=${fromUDP}`);
self.chaserRenderFrameId = renderFrameId1; self.chaserRenderFrameId = renderFrameId1;
self.networkDoctor.logRollbackFrames(self.renderFrameId - self.chaserRenderFrameId); let rollbackFrames = (self.renderFrameId - self.chaserRenderFrameId);
if (0 > rollbackFrames)
rollbackFrames = 0;
self.networkDoctor.logRollbackFrames(rollbackFrames);
}, },
onPeerInputFrameUpsync(peerJoinIndex, batch, fromUDP) { onPeerInputFrameUpsync(peerJoinIndex, batch, fromUDP) {
@ -935,12 +944,24 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
let effCnt = 0; let effCnt = 0;
//console.log(`Received peer inputFrameUpsync batch w/ inputFrameId in [${batch[0].inputFrameId}, ${batch[batch.length - 1].inputFrameId}] for prediction assistance`); //console.log(`Received peer inputFrameUpsync batch w/ inputFrameId in [${batch[0].inputFrameId}, ${batch[batch.length - 1].inputFrameId}] for prediction assistance`);
let firstPredictedYetIncorrectInputFrameId = null;
const renderedInputFrameIdUpper = gopkgs.ConvertToDelayedInputFrameId(self.renderFrameId); const renderedInputFrameIdUpper = gopkgs.ConvertToDelayedInputFrameId(self.renderFrameId);
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;
const peerEncodedInput = (true == fromUDP ? inputFrame.encoded : inputFrame.inputList[peerJoinIndex - 1]);
if (inputFrameId <= renderedInputFrameIdUpper) { 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"! // [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"!
// TODO: Shall we update the "chaserRenderFrameId" if the rendered history was wrong? It doesn't seem to impact eventual correctness if we allow the update of "chaserRenderFrameId" upon "inputFrameId <= renderedInputFrameIdUpper" here, however UDP upsync doesn't reserve order from a same sender and there might be multiple other senders, hence it might result in unnecessarily frequent chasing.
const localInputFrame = self.recentInputCache.GetByFrameId(inputFrameId);
if (null != localInputFrame
&&
null == firstPredictedYetIncorrectInputFrameId
&&
localInputFrame.InputList[peerJoinIndex - 1] != peerEncodedInput
) {
firstPredictedYetIncorrectInputFrameId = inputFrameId;
}
continue; continue;
} }
if (inputFrameId <= self.lastAllConfirmedInputFrameId) { if (inputFrameId <= self.lastAllConfirmedInputFrameId) {
@ -953,7 +974,6 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
if (0 < (existingInputFrame.ConfirmedList & peerJoinIndexMask)) { if (0 < (existingInputFrame.ConfirmedList & peerJoinIndexMask)) {
continue; continue;
} }
const peerEncodedInput = (true == fromUDP ? inputFrame.encoded : inputFrame.inputList[peerJoinIndex - 1]);
if (inputFrameId > self.lastIndividuallyConfirmedInputFrameId[peerJoinIndex - 1]) { if (inputFrameId > self.lastIndividuallyConfirmedInputFrameId[peerJoinIndex - 1]) {
self.lastIndividuallyConfirmedInputFrameId[peerJoinIndex - 1] = inputFrameId; self.lastIndividuallyConfirmedInputFrameId[peerJoinIndex - 1] = inputFrameId;
self.lastIndividuallyConfirmedInputList[peerJoinIndex - 1] = peerEncodedInput; self.lastIndividuallyConfirmedInputList[peerJoinIndex - 1] = peerEncodedInput;
@ -964,13 +984,14 @@ batchInputFrameIdRange=[${batch[0].inputFrameId}, ${batch[batch.length - 1].inpu
newInputList[peerJoinIndex - 1] = peerEncodedInput; newInputList[peerJoinIndex - 1] = peerEncodedInput;
let newConfirmedList = (existingInputFrame.ConfirmedList | peerJoinIndex); 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)}`); //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) {
//self._markConfirmationIfApplicable(); //self._markConfirmationIfApplicable();
self.networkDoctor.logPeerInputFrameUpsync(batch[0].inputFrameId, batch[batch.length - 1].inputFrameId); self.networkDoctor.logPeerInputFrameUpsync(batch[0].inputFrameId, batch[batch.length - 1].inputFrameId);
} }
self._handleIncorrectlyRenderedPrediction(firstPredictedYetIncorrectInputFrameId, batch, fromUDP);
}, },
onPlayerAdded(rdf /* pb.RoomDownsyncFrame */ ) { onPlayerAdded(rdf /* pb.RoomDownsyncFrame */ ) {
@ -1115,7 +1136,7 @@ othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!! ++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
self.lastRenderFrameIdTriggeredAt = performance.now(); self.lastRenderFrameIdTriggeredAt = performance.now();
let t3 = performance.now(); let t3 = performance.now();
self.skipRenderFrameFlag = self.networkDoctor.isTooFast(); self.skipRenderFrameFlag = self.networkDoctor.isTooFast(self);
} catch (err) { } catch (err) {
console.error("Error during Map.update", err); console.error("Error during Map.update", err);
self.onBattleStopped(); // TODO: Popup to ask player to refresh browser self.onBattleStopped(); // TODO: Popup to ask player to refresh browser

View File

@ -79,19 +79,31 @@ NetworkDoctor.prototype.logSkippedRenderFrameCnt = function() {
this.skippedRenderFrameCnt += 1; this.skippedRenderFrameCnt += 1;
} }
NetworkDoctor.prototype.isTooFast = function() { NetworkDoctor.prototype.isTooFast = function(mapIns) {
return false;
const [sendingFps, srvDownsyncFps, peerUpsyncFps, rollbackFrames, skippedRenderFrameCnt] = this.stats(); const [sendingFps, srvDownsyncFps, peerUpsyncFps, rollbackFrames, skippedRenderFrameCnt] = this.stats();
if (sendingFps >= this.inputRateThreshold + 2) { if (sendingFps >= this.inputRateThreshold + 3) {
// Don't send too fast // Don't send too fast
console.log(`Sending too fast, sendingFps=${sendingFps}`);
return true; return true;
} else if (sendingFps >= this.inputRateThreshold && srvDownsyncFps >= this.inputRateThreshold) { } else {
const sendingFpsNormal = (sendingFps >= this.inputRateThreshold);
// An outstanding lag within the "inputFrameDownsyncQ" will reduce "srvDownsyncFps", HOWEVER, a constant lag wouldn't impact "srvDownsyncFps"! In native platforms we might use PING value might help as a supplement information to confirm that the "selfPlayer" is not lagged within the time accounted by "inputFrameDownsyncQ". // An outstanding lag within the "inputFrameDownsyncQ" will reduce "srvDownsyncFps", HOWEVER, a constant lag wouldn't impact "srvDownsyncFps"! In native platforms we might use PING value might help as a supplement information to confirm that the "selfPlayer" is not lagged within the time accounted by "inputFrameDownsyncQ".
if (rollbackFrames >= this.rollbackFramesThreshold) { const recvFpsNormal = (srvDownsyncFps >= this.inputRateThreshold || peerUpsyncFps >= this.inputRateThreshold * (window.boundRoomCapacity - 1));
// I got many frames rolled back while none of my peers effectively helped my preciction. Deliberately not using "peerUpsyncThreshold" here because when using UDP p2p upsync broadcasting, we expect to receive effective p2p upsyncs from every other player. if (sendingFpsNormal && recvFpsNormal) {
let selfInputFrameIdFront = gopkgs.ConvertToNoDelayInputFrameId(mapIns.renderFrameId);
let minInputFrameIdFront = Number.MAX_VALUE;
for (let k = 0; k < window.boundRoomCapacity; ++k) {
if (k + 1 == mapIns.selfPlayerInfo.JoinIndex) continue;
if (mapIns.lastIndividuallyConfirmedInputFrameId[k] >= minInputFrameIdFront) continue;
minInputFrameIdFront = mapIns.lastIndividuallyConfirmedInputFrameId[k];
}
if ((selfInputFrameIdFront > minInputFrameIdFront) && ((selfInputFrameIdFront - minInputFrameIdFront) > (mapIns.inputFrameUpsyncDelayTolerance+2))) {
// first comparison condition is to avoid numeric overflow
console.log(`Game logic ticking too fast, selfInputFrameIdFront=${selfInputFrameIdFront}, minInputFrameIdFront=${minInputFrameIdFront}, inputFrameUpsyncDelayTolerance=${mapIns.inputFrameUpsyncDelayTolerance}`);
return true; return true;
} }
} }
}
return false; return false;
}; };