Fixes for frontend chaser.

This commit is contained in:
genxium 2022-12-22 11:35:18 +08:00
parent 3c15e21652
commit 14bb6fa1ea
6 changed files with 41 additions and 47 deletions

View File

@ -21,6 +21,7 @@
"IS_BOT_ACC": 3017, "IS_BOT_ACC": 3017,
"ACTIVE_WATCHDOG": 3018, "ACTIVE_WATCHDOG": 3018,
"BATTLE_STOPPED": 3019, "BATTLE_STOPPED": 3019,
"CLIENT_MISMATCHED_RENDER_FRAME": 3020,
"__comment__": "OTHERS", "__comment__": "OTHERS",
"INVALID_TOKEN": 2001, "INVALID_TOKEN": 2001,

View File

@ -380,11 +380,10 @@ func (pR *Room) InputsBufferString(allDetails bool) string {
break break
} }
f := tmp.(*InputFrameDownsync) f := tmp.(*InputFrameDownsync)
//s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, &inputList: %p, confirmedList: %v}", f.InputFrameId, f.InputList, &(f.InputList), f.ConfirmedList)) s = append(s, fmt.Sprintf("{\"inputFrameId\":%d,\"inputList\":%v,\"confirmedList\":\"%d\"}", f.InputFrameId, f.InputList, f.ConfirmedList))
s = append(s, fmt.Sprintf("{inputFrameId: %v, inputList: %v, confirmedList: %v}", f.InputFrameId, f.InputList, f.ConfirmedList))
} }
return strings.Join(s, "; ") return strings.Join(s, "\n")
} else { } else {
return fmt.Sprintf("{renderFrameId: %d, stInputFrameId: %d, edInputFrameId: %d, lastAllConfirmedInputFrameIdWithChange: %d, lastAllConfirmedInputFrameId: %d}", pR.RenderFrameId, pR.InputsBuffer.StFrameId, pR.InputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId) return fmt.Sprintf("{renderFrameId: %d, stInputFrameId: %d, edInputFrameId: %d, lastAllConfirmedInputFrameIdWithChange: %d, lastAllConfirmedInputFrameId: %d}", pR.RenderFrameId, pR.InputsBuffer.StFrameId, pR.InputsBuffer.EdFrameId, pR.LastAllConfirmedInputFrameIdWithChange, pR.LastAllConfirmedInputFrameId)
} }
@ -777,7 +776,7 @@ func (pR *Room) OnDismissed() {
pR.InputFrameUpsyncDelayTolerance = (pR.NstDelayFrames >> pR.InputScaleFrames) - 1 // this value should be strictly smaller than (NstDelayFrames >> InputScaleFrames), otherwise "type#1 forceConfirmation" might become a lag avalanche pR.InputFrameUpsyncDelayTolerance = (pR.NstDelayFrames >> pR.InputScaleFrames) - 1 // this value should be strictly smaller than (NstDelayFrames >> InputScaleFrames), otherwise "type#1 forceConfirmation" might become a lag avalanche
pR.MaxChasingRenderFramesPerUpdate = 12 // Don't set this value too high to avoid exhausting frontend CPU within a single frame pR.MaxChasingRenderFramesPerUpdate = 12 // Don't set this value too high to avoid exhausting frontend CPU within a single frame
pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work! pR.BackendDynamicsEnabled = true // [WARNING] When "false", recovery upon reconnection wouldn't work!
pR.ForceAllResyncOnAnyActiveSlowTicker = true // See tradeoff discussion in "downsyncToAllPlayers" pR.ForceAllResyncOnAnyActiveSlowTicker = true // See tradeoff discussion in "downsyncToAllPlayers"
punchSkillId := int32(1) punchSkillId := int32(1)
pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0) pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0)

View File

@ -82,6 +82,7 @@ var constants = {
"IS_BOT_ACC": 3017, "IS_BOT_ACC": 3017,
"ACTIVE_WATCHDOG": 3018, "ACTIVE_WATCHDOG": 3018,
"BATTLE_STOPPED": 3019, "BATTLE_STOPPED": 3019,
"CLIENT_MISMATCHED_RENDER_FRAME": 3020,
"__comment__": "OTHERS", "__comment__": "OTHERS",
"INVALID_TOKEN": 2001, "INVALID_TOKEN": 2001,

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
216.05530045313827, 210.4441731196186,
0, 0,
0, 0,
0, 0,

View File

@ -204,7 +204,6 @@ cc.Class({
playerId: self.selfPlayerInfo.id, playerId: self.selfPlayerInfo.id,
act: window.UPSYNC_MSG_ACT_PLAYER_CMD, act: window.UPSYNC_MSG_ACT_PLAYER_CMD,
joinIndex: self.selfPlayerInfo.joinIndex, joinIndex: self.selfPlayerInfo.joinIndex,
ackingFrameId: self.lastAllConfirmedRenderFrameId,
ackingInputFrameId: self.lastAllConfirmedInputFrameId, ackingInputFrameId: self.lastAllConfirmedInputFrameId,
inputFrameUpsyncBatch: inputFrameUpsyncBatch, inputFrameUpsyncBatch: inputFrameUpsyncBatch,
}).finish(); }).finish();
@ -296,10 +295,9 @@ cc.Class({
self.renderFrameId = 0; // After battle started self.renderFrameId = 0; // After battle started
self.bulletBattleLocalIdCounter = 0; self.bulletBattleLocalIdCounter = 0;
self.lastAllConfirmedRenderFrameId = -1;
self.lastAllConfirmedInputFrameId = -1; self.lastAllConfirmedInputFrameId = -1;
self.lastUpsyncInputFrameId = -1; self.lastUpsyncInputFrameId = -1;
self.chaserRenderFrameId = -1; // at any moment, "lastAllConfirmedRenderFrameId <= chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "onInputFrameDownsyncBatch" self.chaserRenderFrameId = -1; // at any moment, "chaserRenderFrameId <= renderFrameId", but "chaserRenderFrameId" would fluctuate according to "onInputFrameDownsyncBatch"
self.recentRenderCache = new RingBuffer(self.renderCacheSize); self.recentRenderCache = new RingBuffer(self.renderCacheSize);
@ -579,6 +577,7 @@ cc.Class({
onRoomDownsyncFrame(rdf, accompaniedInputFrameDownsyncBatch) { onRoomDownsyncFrame(rdf, accompaniedInputFrameDownsyncBatch) {
// This function is also applicable to "re-joining". // This function is also applicable to "re-joining".
const self = window.mapIns; const self = window.mapIns;
self.onInputFrameDownsyncBatch(accompaniedInputFrameDownsyncBatch); // Important to do this step before setting IN_BATTLE
if (!self.recentRenderCache) { if (!self.recentRenderCache) {
return; return;
} }
@ -602,12 +601,11 @@ cc.Class({
const [dumpRenderCacheRet, oldStRenderFrameId, oldEdRenderFrameId] = (shouldForceDumping1 || shouldForceDumping2 || shouldForceResync) ? self.recentRenderCache.setByFrameId(rdf, rdf.id) : [window.RING_BUFF_CONSECUTIVE_SET, null, null]; const [dumpRenderCacheRet, oldStRenderFrameId, oldEdRenderFrameId] = (shouldForceDumping1 || shouldForceDumping2 || shouldForceResync) ? self.recentRenderCache.setByFrameId(rdf, rdf.id) : [window.RING_BUFF_CONSECUTIVE_SET, null, null];
if (window.RING_BUFF_FAILED_TO_SET == dumpRenderCacheRet) { if (window.RING_BUFF_FAILED_TO_SET == dumpRenderCacheRet) {
throw `Failed to dump render cache#1 (maybe recentRenderCache too small)! rdf.id=${rdf.id}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`; throw `Failed to dump render cache#1 (maybe recentRenderCache too small)! rdf.id=${rdf.id}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`;
} }
if (!shouldForceResync && (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START < rdf.id && window.RING_BUFF_CONSECUTIVE_SET == dumpRenderCacheRet)) { if (!shouldForceResync && (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START < rdf.id && window.RING_BUFF_CONSECUTIVE_SET == dumpRenderCacheRet)) {
/* /*
Don't change Don't change
- lastAllConfirmedRenderFrameId, it's updated only in "rollbackAndChase" (except for when RING_BUFF_NON_CONSECUTIVE_SET)
- chaserRenderFrameId, it's updated only in "rollbackAndChase & onInputFrameDownsyncBatch" (except for when RING_BUFF_NON_CONSECUTIVE_SET) - chaserRenderFrameId, it's updated only in "rollbackAndChase & onInputFrameDownsyncBatch" (except for when RING_BUFF_NON_CONSECUTIVE_SET)
*/ */
return dumpRenderCacheRet; return dumpRenderCacheRet;
@ -632,14 +630,12 @@ cc.Class({
console.log('On battle started! renderFrameId=', rdf.id); console.log('On battle started! renderFrameId=', rdf.id);
} else { } else {
self.hideFindingPlayersGUI(rdf); self.hideFindingPlayersGUI(rdf);
self.onInputFrameDownsyncBatch(accompaniedInputFrameDownsyncBatch); // Important to do this step before setting IN_BATTLE console.warn(`Got resync@localRenderFrameId=${self.renderFrameId} -> rdf.id=${rdf.id} & rdf.backendUnconfirmedMask=${rdf.backendUnconfirmedMask}, @lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, @chaserRenderFrameId=${self.chaserRenderFrameId}, @localRecentInputCache=${mapIns._stringifyRecentInputCache(false)}`);
console.warn(`Got resync@localRenderFrameId=${self.renderFrameId} -> rdf.id=${rdf.id} & rdf.backendUnconfirmedMask=${rdf.backendUnconfirmedMask}, @lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, @lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, @chaserRenderFrameId=${self.chaserRenderFrameId}, @localRecentInputCache=${mapIns._stringifyRecentInputCache(false)}`);
} }
self.renderFrameId = rdf.id; self.renderFrameId = rdf.id;
self.lastRenderFrameIdTriggeredAt = performance.now(); self.lastRenderFrameIdTriggeredAt = performance.now();
// In this case it must be true that "rdf.id > chaserRenderFrameId >= lastAllConfirmedRenderFrameId". // In this case it must be true that "rdf.id > chaserRenderFrameId".
self.lastAllConfirmedRenderFrameId = rdf.id;
self.chaserRenderFrameId = rdf.id; self.chaserRenderFrameId = rdf.id;
const candidateLastAllConfirmedInputFrame = self._convertToInputFrameId(rdf.id - 1, self.inputDelayFrames); const candidateLastAllConfirmedInputFrame = self._convertToInputFrameId(rdf.id - 1, self.inputDelayFrames);
if (self.lastAllConfirmedInputFrame < candidateLastAllConfirmedInputFrame) { if (self.lastAllConfirmedInputFrame < candidateLastAllConfirmedInputFrame) {
@ -728,7 +724,7 @@ cc.Class({
for (let k in batch) { for (let k in batch) {
const inputFrameDownsync = batch[k]; const inputFrameDownsync = batch[k];
const inputFrameDownsyncId = inputFrameDownsync.inputFrameId; const inputFrameDownsyncId = inputFrameDownsync.inputFrameId;
if (inputFrameDownsyncId < self.lastAllConfirmedInputFrameId) { if (inputFrameDownsyncId <= self.lastAllConfirmedInputFrameId) {
continue; continue;
} }
// [WARNING] Take all "inputFrameDownsync" from backend as all-confirmed, it'll be later checked by "rollbackAndChase". // [WARNING] Take all "inputFrameDownsync" from backend as all-confirmed, it'll be later checked by "rollbackAndChase".
@ -745,7 +741,7 @@ cc.Class({
inputFrameDownsync.confirmedList = (1 << self.playerRichInfoDict.size) - 1; inputFrameDownsync.confirmedList = (1 << self.playerRichInfoDict.size) - 1;
const [ret, oldStFrameId, oldEdFrameId] = self.recentInputCache.setByFrameId(inputFrameDownsync, inputFrameDownsync.inputFrameId); const [ret, oldStFrameId, oldEdFrameId] = self.recentInputCache.setByFrameId(inputFrameDownsync, inputFrameDownsync.inputFrameId);
if (window.RING_BUFF_FAILED_TO_SET == ret) { if (window.RING_BUFF_FAILED_TO_SET == ret) {
throw `Failed to dump input cache (maybe recentInputCache too small)! inputFrameDownsync.inputFrameId=${inputFrameDownsync.inputFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, 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)}`;
} }
} }
@ -759,8 +755,6 @@ cc.Class({
/* /*
A typical case is as follows. A typical case is as follows.
-------------------------------------------------------- --------------------------------------------------------
[self.lastAllConfirmedRenderFrameId] : 22
<renderFrameId1> : 36 <renderFrameId1> : 36
@ -770,7 +764,8 @@ 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.warn(`Mismatched input detected, resetting chaserRenderFrameId: ${self.chaserRenderFrameId}->${renderFrameId1} by firstPredictedYetIncorrectInputFrameId: ${inputFrameId1}`); console.warn(`Mismatched input detected, resetting chaserRenderFrameId: ${self.chaserRenderFrameId}->${renderFrameId1} by firstPredictedYetIncorrectInputFrameId: ${inputFrameId1}
lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}`);
self.chaserRenderFrameId = renderFrameId1; self.chaserRenderFrameId = renderFrameId1;
}, },
@ -787,7 +782,7 @@ cc.Class({
logBattleStats() { logBattleStats() {
const self = this; const self = this;
let s = []; let s = [];
s.push(`Battle stats: renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastUpsyncInputFrameId=${self.lastUpsyncInputFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, chaserRenderFrameId=${self.chaserRenderFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`); s.push(`Battle stats: renderFrameId=${self.renderFrameId}, lastUpsyncInputFrameId=${self.lastUpsyncInputFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, chaserRenderFrameId=${self.chaserRenderFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`);
for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) { for (let i = self.recentInputCache.stFrameId; i < self.recentInputCache.edFrameId; ++i) {
const inputFrameDownsync = self.recentInputCache.getByFrameId(i); const inputFrameDownsync = self.recentInputCache.getByFrameId(i);
@ -910,11 +905,16 @@ cc.Class({
*/ */
// [WARNING] Don't try to get "prevRdf(i.e. renderFrameId == latest-1)" by "self.recentRenderCache.getByFrameId(...)" here, as the cache might have been updated by asynchronous "onRoomDownsyncFrame(...)" calls! // [WARNING] Don't try to get "prevRdf(i.e. renderFrameId == latest-1)" by "self.recentRenderCache.getByFrameId(...)" here, as the cache might have been updated by asynchronous "onRoomDownsyncFrame(...)" calls!
if (self.othersForcedDownsyncRenderFrameDict.has(rdf.id)) { if (self.othersForcedDownsyncRenderFrameDict.has(rdf.id)) {
const delayedInputFrameId = self._convertToInputFrameId(rdf.id, 0);
const othersForcedDownsyncRenderFrame = self.othersForcedDownsyncRenderFrameDict.get(rdf.id); const othersForcedDownsyncRenderFrame = self.othersForcedDownsyncRenderFrameDict.get(rdf.id);
if (!self.equalRoomDownsyncFrames(othersForcedDownsyncRenderFrame, rdf)) { if (self.lastAllConfirmedInputFrameId >= delayedInputFrameId && !self.equalRoomDownsyncFrames(othersForcedDownsyncRenderFrame, rdf)) {
console.warn(`Mismatched render frame prediction@rdf.id=${rdf.id}, @localRenderFrameId=${self.renderFrameId}, @lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, @lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, @chaserRenderFrameId=${self.chaserRenderFrameId}, @localRecentInputCache=${mapIns._stringifyRecentInputCache(false)}: console.warn(`Mismatched render frame@rdf.id=${rdf.id} w/ inputFrameId=${delayedInputFrameId}, @lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, @chaserRenderFrameId=${self.chaserRenderFrameId}:
rdf=${JSON.stringify(rdf)} rdf=${JSON.stringify(rdf)}
othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame)}`); othersForcedDownsyncRenderFrame=${JSON.stringify(othersForcedDownsyncRenderFrame)}
recentRenderCache=${self._stringifyRecentRenderCache(true)}
recentInputCache=${self._stringifyRecentInputCache(true)}`);
closeWSConnection(constants.RET_CODE.CLIENT_MISMATCHED_RENDER_FRAME, "");
self.onManualRejoinRequired("[DEBUG] CLIENT_MISMATCHED_RENDER_FRAME");
rdf = othersForcedDownsyncRenderFrame; rdf = othersForcedDownsyncRenderFrame;
} }
} }
@ -1524,39 +1524,28 @@ playerColliderPos=${self.stringifyColliderCenterInWorld(playerCollider, halfColl
const self = this; const self = this;
let prevLatestRdf = null, let prevLatestRdf = null,
latestRdf = null; latestRdf = null;
for (let i = renderFrameIdSt; i < renderFrameIdEd; i++) { for (let i = renderFrameIdSt; i < renderFrameIdEd; i++) {
latestRdf = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"; [WARNING] When "true == isChasing", this function can be interruptted by "onRoomDownsyncFrame(rdf)" asynchronously anytime, making this line return "null"! const currRdf = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"; [WARNING] When "true == isChasing" and using Firefox, this function could be interruptted by "onRoomDownsyncFrame(rdf)" asynchronously anytime, making this line return "null"!
if (null == latestRdf) { if (null == currRdf) {
console.warn(`Couldn't find renderFrame for i=${i} to rollback, self.renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, might've been interruptted by onRoomDownsyncFrame`); throw `Couldn't find renderFrame for i=${i} to rollback (are you using Firefox?), self.renderFrameId=${self.renderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, might've been interruptted by onRoomDownsyncFrame`;
return [prevLatestRdf, latestRdf];
} }
const j = self._convertToInputFrameId(i, self.inputDelayFrames); const j = self._convertToInputFrameId(i, self.inputDelayFrames);
const delayedInputFrame = self.getCachedInputFrameDownsyncWithPrediction(j); const delayedInputFrame = self.recentInputCache.getByFrameId(j); // Don't make prediction here, the inputFrameDownsyncs in recentInputCache was already predicted while prefabbing
if (null == delayedInputFrame) { if (null == delayedInputFrame) {
// Shouldn't happen! // Shouldn't happen!
throw `Failed to get cached delayedInputFrame for i=${i}, j=${j}, renderFrameId=${self.renderFrameId}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastUpsyncInputFrameId=${self.lastUpsyncInputFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, chaserRenderFrameId=${self.chaserRenderFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`; throw `Failed to get cached delayedInputFrame for i=${i}, j=${j}, renderFrameId=${self.renderFrameId}, lastUpsyncInputFrameId=${self.lastUpsyncInputFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}, chaserRenderFrameId=${self.chaserRenderFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`;
}
prevLatestRdf = latestRdf;
latestRdf = self.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, prevLatestRdf, collisionSys, collisionSysMap);
if (
self._allConfirmed(delayedInputFrame.confirmedList)
&&
latestRdf.id > self.lastAllConfirmedRenderFrameId
) {
// We got a more up-to-date "all-confirmed-render-frame".
self.lastAllConfirmedRenderFrameId = latestRdf.id;
if (latestRdf.id > self.chaserRenderFrameId) {
// it must be true that "chaserRenderFrameId >= lastAllConfirmedRenderFrameId", regardeless of the "isChasing" param
self.chaserRenderFrameId = latestRdf.id;
}
} }
const nextRdf = self.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRdf, collisionSys, collisionSysMap);
if (true == isChasing) { if (true == isChasing) {
// Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic! // [WARNING] Move the cursor "self.chaserRenderFrameId" when "true == isChasing", keep in mind that "self.chaserRenderFrameId" is not monotonic!
self.chaserRenderFrameId = latestRdf.id; self.chaserRenderFrameId = nextRdf.id;
} else if (nextRdf.id == self.chaserRenderFrameId + 1) {
self.chaserRenderFrameId = nextRdf.id; // To avoid redundant calculation
} }
self.recentRenderCache.setByFrameId(latestRdf, latestRdf.id); self.recentRenderCache.setByFrameId(nextRdf, nextRdf.id);
prevLatestRdf = currRdf;
latestRdf = nextRdf;
} }
return [prevLatestRdf, latestRdf]; return [prevLatestRdf, latestRdf];

View File

@ -193,6 +193,8 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
// [WARNING] The callback "onclose" might be called AFTER the webpage is refreshed with "1001 == evt.code". // [WARNING] The callback "onclose" might be called AFTER the webpage is refreshed with "1001 == evt.code".
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}`);
switch (evt.code) { switch (evt.code) {
case constants.RET_CODE.CLIENT_MISMATCHED_RENDER_FRAME:
break;
case constants.RET_CODE.BATTLE_STOPPED: case constants.RET_CODE.BATTLE_STOPPED:
// deliberately do nothing // deliberately do nothing
break; break;
@ -209,6 +211,8 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
case constants.RET_CODE.PLAYER_NOT_FOUND: case constants.RET_CODE.PLAYER_NOT_FOUND:
case constants.RET_CODE.PLAYER_CHEATING: case constants.RET_CODE.PLAYER_CHEATING:
case 1006: // Peer(i.e. the backend) gone unexpectedly case 1006: // Peer(i.e. the backend) gone unexpectedly
console.warn(`recentRenderCache=${mapIns._stringifyRecentRenderCache(true)}
recentInputCache=${mapIns._stringifyRecentInputCache(true)}`);
window.clearLocalStorageAndBackToLoginScene(true); window.clearLocalStorageAndBackToLoginScene(true);
break; break;
default: default: