Completed frontend refactoring for more convenient unit testing.

This commit is contained in:
genxium 2022-11-11 13:27:48 +08:00
parent 3f4e49656a
commit 15a062af10
3 changed files with 65 additions and 53 deletions

View File

@ -1195,7 +1195,7 @@ func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRende
// [WARNING] It's possible that by now "allConfirmedMask != delayedInputFrame.ConfirmedList && delayedInputFrameId <= pR.LastAllConfirmedInputFrameId", we trust "pR.LastAllConfirmedInputFrameId" as the TOP AUTHORITY. // [WARNING] It's possible that by now "allConfirmedMask != delayedInputFrame.ConfirmedList && delayedInputFrameId <= pR.LastAllConfirmedInputFrameId", we trust "pR.LastAllConfirmedInputFrameId" as the TOP AUTHORITY.
atomic.StoreUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask) atomic.StoreUint64(&(delayedInputFrame.ConfirmedList), allConfirmedMask)
} }
nextRenderFrame := pR.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, pR.CollisionSysMap) nextRenderFrame := pR.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, pR.CollisionSysMap)
// Update in the latest player pointers // Update in the latest player pointers
for playerId, playerDownsync := range nextRenderFrame.Players { for playerId, playerDownsync := range nextRenderFrame.Players {
@ -1238,11 +1238,10 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
if nil != delayedInputFrame { if nil != delayedInputFrame {
inputList := delayedInputFrame.InputList inputList := delayedInputFrame.InputList
// Ordered by joinIndex to guarantee determinism? effPushbacks := make([]Vec2D, pR.Capacity) // Guaranteed determinism regardless of traversal order
// Move players according to inputs for playerId, player := range pR.Players {
for joinIndex := 1; joinIndex <= pR.Capacity; joinIndex++ { joinIndex := player.JoinIndex
player := pR.PlayersArr[joinIndex-1] effPushbacks[joinIndex-1].X, effPushbacks[joinIndex-1].Y = float64(0), float64(0)
playerId := player.Id
currPlayerDownsync := currRenderFrame.Players[playerId] currPlayerDownsync := currRenderFrame.Players[playerId]
encodedInput := inputList[joinIndex-1] encodedInput := inputList[joinIndex-1]
if 0 == encodedInput { if 0 == encodedInput {
@ -1252,42 +1251,42 @@ func (pR *Room) applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputF
newVx := (currPlayerDownsync.VirtualGridX + (decodedInput[0] + decodedInput[0]*currPlayerDownsync.Speed)) newVx := (currPlayerDownsync.VirtualGridX + (decodedInput[0] + decodedInput[0]*currPlayerDownsync.Speed))
newVy := (currPlayerDownsync.VirtualGridY + (decodedInput[1] + decodedInput[1]*currPlayerDownsync.Speed)) newVy := (currPlayerDownsync.VirtualGridY + (decodedInput[1] + decodedInput[1]*currPlayerDownsync.Speed))
// Reset playerCollider position from the "virtual grid position" // Reset playerCollider position from the "virtual grid position"
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + currPlayerDownsync.JoinIndex collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
playerCollider := collisionSysMap[collisionPlayerIndex] playerCollider := collisionSysMap[collisionPlayerIndex]
playerCollider.X, playerCollider.Y = pR.virtualGridToPlayerColliderPos(newVx, newVy, player) playerCollider.X, playerCollider.Y = pR.virtualGridToPlayerColliderPos(newVx, newVy, player)
// Update in the collision system
playerCollider.Update() playerCollider.Update()
} }
// handle pushbacks upon collision after all movements treated as simultaneous // handle pushbacks upon collision after all movements treated as simultaneous
for joinIndex := 1; joinIndex <= pR.Capacity; joinIndex++ { for _, player := range pR.Players {
player := pR.PlayersArr[joinIndex-1] joinIndex := player.JoinIndex
playerId := player.Id collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + player.JoinIndex
playerCollider := collisionSysMap[collisionPlayerIndex] playerCollider := collisionSysMap[collisionPlayerIndex]
oldDx, oldDy := float64(0), float64(0) oldDx, oldDy := float64(0), float64(0)
dx, dy := oldDx, oldDy
if collision := playerCollider.Check(oldDx, oldDy); collision != nil { if collision := playerCollider.Check(oldDx, oldDy); collision != nil {
playerShape := playerCollider.Shape.(*resolv.ConvexPolygon) playerShape := playerCollider.Shape.(*resolv.ConvexPolygon)
for _, obj := range collision.Objects { for _, obj := range collision.Objects {
barrierShape := obj.Shape.(*resolv.ConvexPolygon) barrierShape := obj.Shape.(*resolv.ConvexPolygon)
if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped { if overlapped, pushbackX, pushbackY := CalcPushbacks(oldDx, oldDy, playerShape, barrierShape); overlapped {
Logger.Debug(fmt.Sprintf("Collided & overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY)) Logger.Debug(fmt.Sprintf("Collided & overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v, pushbackX=%v, pushbackY=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape), pushbackX, pushbackY))
dx -= pushbackX effPushbacks[joinIndex-1].X += pushbackX
dy -= pushbackY effPushbacks[joinIndex-1].Y += pushbackY
} else { } else {
Logger.Debug(fmt.Sprintf("Collided BUT not overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape))) Logger.Debug(fmt.Sprintf("Collided BUT not overlapped: player.X=%v, player.Y=%v, oldDx=%v, oldDy=%v, playerShape=%v, toCheckBarrier=%v", playerCollider.X, playerCollider.Y, oldDx, oldDy, ConvexPolygonStr(playerShape), ConvexPolygonStr(barrierShape)))
} }
} }
} }
}
playerCollider.X += dx for playerId, player := range pR.Players {
playerCollider.Y += dy joinIndex := player.JoinIndex
collisionPlayerIndex := COLLISION_PLAYER_INDEX_PREFIX + joinIndex
// Update again in "collision space" playerCollider := collisionSysMap[collisionPlayerIndex]
playerCollider.Update()
// Update "virtual grid position" // Update "virtual grid position"
newVx, newVy := pR.playerColliderAnchorToVirtualGridPos(playerCollider.X, playerCollider.Y, player) newVx, newVy := pR.playerColliderAnchorToVirtualGridPos(playerCollider.X-effPushbacks[joinIndex-1].X, playerCollider.Y-effPushbacks[joinIndex-1].Y, player)
nextRenderFramePlayers[playerId].VirtualGridX = newVx nextRenderFramePlayers[playerId].VirtualGridX = newVx
nextRenderFramePlayers[playerId].VirtualGridY = newVy nextRenderFramePlayers[playerId].VirtualGridY = newVy
} }

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
344.6705889248102, 377.5870760500153,
0, 0,
0, 0,
0, 0,

View File

@ -324,12 +324,10 @@ cc.Class({
self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others". self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others".
self.recentInputCache = new RingBuffer(1024); self.recentInputCache = new RingBuffer(1024);
self.latestCollisionSys = new collisions.Collisions(); self.collisionSys = new collisions.Collisions();
self.chaserCollisionSys = new collisions.Collisions();
self.collisionBarrierIndexPrefix = (1 << 16); // For tracking the movements of barriers, though not yet actually used self.collisionBarrierIndexPrefix = (1 << 16); // For tracking the movements of barriers, though not yet actually used
self.latestCollisionSysMap = new Map(); self.collisionSysMap = new Map();
self.chaserCollisionSysMap = new Map();
self.transitToState(ALL_MAP_STATES.VISUAL); self.transitToState(ALL_MAP_STATES.VISUAL);
@ -382,8 +380,7 @@ cc.Class({
window.clearBoundRoomIdInBothVolatileAndPersistentStorage(); window.clearBoundRoomIdInBothVolatileAndPersistentStorage();
window.initPersistentSessionClient(self.initAfterWSConnected, null /* Deliberately NOT passing in any `expectedRoomId`. -- YFLu */ ); window.initPersistentSessionClient(self.initAfterWSConnected, null /* Deliberately NOT passing in any `expectedRoomId`. -- YFLu */ );
}; };
resultPanelScriptIns.onCloseDelegate = () => { resultPanelScriptIns.onCloseDelegate = () => {};
};
self.gameRuleNode = cc.instantiate(self.gameRulePrefab); self.gameRuleNode = cc.instantiate(self.gameRulePrefab);
self.gameRuleNode.width = self.canvasNode.width; self.gameRuleNode.width = self.canvasNode.width;
@ -474,13 +471,11 @@ cc.Class({
for (let i = 0; i < boundaryObj.length; ++i) { for (let i = 0; i < boundaryObj.length; ++i) {
pts.push([boundaryObj[i].x - x0, boundaryObj[i].y - y0]); pts.push([boundaryObj[i].x - x0, boundaryObj[i].y - y0]);
} }
const newBarrierLatest = self.latestCollisionSys.createPolygon(x0, y0, pts); const newBarrier = self.collisionSys.createPolygon(x0, y0, pts);
// console.log("Created barrier: ", newBarrierLatest); // console.log("Created barrier: ", newBarrier);
const newBarrierChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts);
++barrierIdCounter; ++barrierIdCounter;
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter); const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
self.latestCollisionSysMap.set(collisionBarrierIndex, newBarrierLatest); self.collisionSysMap.set(collisionBarrierIndex, newBarrier);
self.chaserCollisionSysMap.set(collisionBarrierIndex, newBarrierChaser);
} }
self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer')); self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
@ -587,7 +582,7 @@ cc.Class({
/* /*
Don't change Don't change
- lastAllConfirmedRenderFrameId, it's updated only in "rollbackAndChase" (except for when RING_BUFF_NON_CONSECUTIVE_SET) - lastAllConfirmedRenderFrameId, it's updated only in "rollbackAndChase" (except for when RING_BUFF_NON_CONSECUTIVE_SET)
- chaserRenderFrameId, it's updated only in "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;
} }
@ -755,11 +750,9 @@ cc.Class({
y0 = cpos[1]; y0 = cpos[1];
let pts = [[0, 0], [d, 0], [d, d], [0, d]]; let pts = [[0, 0], [d, 0], [d, d], [0, d]];
const newPlayerColliderLatest = self.latestCollisionSys.createPolygon(x0, y0, pts); const newPlayerCollider = self.collisionSys.createPolygon(x0, y0, pts);
const newPlayerColliderChaser = self.chaserCollisionSys.createPolygon(x0, y0, pts);
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
self.latestCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderLatest); self.collisionSysMap.set(collisionPlayerIndex, newPlayerCollider);
self.chaserCollisionSysMap.set(collisionPlayerIndex, newPlayerColliderChaser);
safelyAddChild(self.node, newPlayerNode); safelyAddChild(self.node, newPlayerNode);
setLocalZOrder(newPlayerNode, 5); setLocalZOrder(newPlayerNode, 5);
@ -806,12 +799,11 @@ cc.Class({
if (nextChaserRenderFrameId > self.renderFrameId) { if (nextChaserRenderFrameId > self.renderFrameId) {
nextChaserRenderFrameId = self.renderFrameId; nextChaserRenderFrameId = self.renderFrameId;
} }
self.rollbackAndChase(prevChaserRenderFrameId, nextChaserRenderFrameId, self.chaserCollisionSys, self.chaserCollisionSysMap); self.rollbackAndChase(prevChaserRenderFrameId, nextChaserRenderFrameId, self.collisionSys, self.collisionSysMap, true);
self.chaserRenderFrameId = nextChaserRenderFrameId; // Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic!
let t2 = performance.now(); let t2 = performance.now();
// Inside the following "self.rollbackAndChase" (which actually ROLLS FORWARD), the "self.latestCollisionSys" is ALWAYS "ROLLED BACK" to "self.recentRenderCache.get(self.renderFrameId)" before being applied dynamics from corresponding delayedInputFrame, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now. // Inside the following "self.rollbackAndChase" actually ROLLS FORWARD w.r.t. the corresponding delayedInputFrame, REGARDLESS OF whether or not "self.chaserRenderFrameId == self.renderFrameId" now.
const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.latestCollisionSys, self.latestCollisionSysMap); const rdf = self.rollbackAndChase(self.renderFrameId, self.renderFrameId + 1, self.collisionSys, self.collisionSysMap, false);
/* /*
const nonTrivialChaseEnded = (prevChaserRenderFrameId < nextChaserRenderFrameId && nextChaserRenderFrameId == self.renderFrameId); const nonTrivialChaseEnded = (prevChaserRenderFrameId < nextChaserRenderFrameId && nextChaserRenderFrameId == self.renderFrameId);
if (nonTrivialChaseEnded) { if (nonTrivialChaseEnded) {
@ -981,6 +973,7 @@ cc.Class({
// TODO: Write unit-test for this function to compare with its backend counter part // TODO: Write unit-test for this function to compare with its backend counter part
applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap) { applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap) {
const self = this;
const nextRenderFramePlayers = {} const nextRenderFramePlayers = {}
for (let playerId in currRenderFrame.players) { for (let playerId in currRenderFrame.players) {
const currPlayerDownsync = currRenderFrame.players[playerId]; const currPlayerDownsync = currRenderFrame.players[playerId];
@ -1001,19 +994,19 @@ cc.Class({
} }
const toRet = { const toRet = {
id: currRenderFrame.id, id: currRenderFrame.id + 1,
players: nextRenderFramePlayers, players: nextRenderFramePlayers,
}; };
if (null != delayedInputFrame) { if (null != delayedInputFrame) {
const inputList = delayedInputFrame.inputList; const inputList = delayedInputFrame.inputList;
// [WARNING] Traverse in the order of joinIndices to guarantee determinism. const effPushbacks = new Array(inputList.length).fill([0.0, 0.0]); // Guaranteed determinism regardless of traversal order
for (let j in self.playerRichInfoArr) { for (let j in self.playerRichInfoArr) {
const joinIndex = parseInt(j) + 1; const joinIndex = parseInt(j) + 1;
const playerId = self.playerRichInfoArr[j].id; const playerId = self.playerRichInfoArr[j].id;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex; const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex); const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const player = renderFrame.players[playerId]; const player = currRenderFrame.players[playerId];
const encodedInput = inputList[joinIndex - 1]; const encodedInput = inputList[joinIndex - 1];
const decodedInput = self.ctrl.decodeDirection(encodedInput); const decodedInput = self.ctrl.decodeDirection(encodedInput);
@ -1021,6 +1014,7 @@ cc.Class({
continue; continue;
} }
// console.log(`Got non-zero inputs for playerId=${playerId}, decodedInput=${JSON.stringify(decodedInput)} @currRenderFrame.id=${currRenderFrame.id}, delayedInputFrame.id=${delayedInputFrame.id}`);
/* /*
Reset "position" of players in "collisionSys" according to "virtual grid position". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics. Reset "position" of players in "collisionSys" according to "virtual grid position". The easy part is that we don't have path-dependent-integrals to worry about like that of thermal dynamics.
*/ */
@ -1029,10 +1023,13 @@ cc.Class({
const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[j]); const newCpos = self.virtualGridToPlayerColliderPos(newVx, newVy, self.playerRichInfoArr[j]);
playerCollider.x = newCpos[0]; playerCollider.x = newCpos[0];
playerCollider.y = newCpos[1]; playerCollider.y = newCpos[1];
// Update directions and thus would eventually update moving animation accordingly
nextRenderFramePlayers[playerId].dir.dx = decodedInput.dx;
nextRenderFramePlayers[playerId].dir.dy = decodedInput.dy;
} }
collisionSys.update(); collisionSys.update();
const result = collisionSys.createResult(); // Can I reuse a "self.latestCollisionSysResult" object throughout the whole battle? const result = collisionSys.createResult(); // Can I reuse a "self.collisionSysResult" object throughout the whole battle?
for (let j in self.playerRichInfoArr) { for (let j in self.playerRichInfoArr) {
const joinIndex = parseInt(j) + 1; const joinIndex = parseInt(j) + 1;
@ -1044,11 +1041,19 @@ cc.Class({
// Test if the player collides with the wall // Test if the player collides with the wall
if (!playerCollider.collides(potential, result)) continue; if (!playerCollider.collides(potential, result)) continue;
// Push the player out of the wall // Push the player out of the wall
playerCollider.x -= result.overlap * result.overlap_x; effPushbacks[j][0] += result.overlap * result.overlap_x;
playerCollider.y -= result.overlap * result.overlap_y; effPushbacks[j][1] += result.overlap * result.overlap_y;
} }
const newVpos = self.playerColliderAnchorToVirtualGridPos(playerCollider.x, playerCollider.y, self.playerRichInfoArr[j]); const newVpos = self.playerColliderAnchorToVirtualGridPos(playerCollider.x, playerCollider.y, self.playerRichInfoArr[j]);
}
for (let j in self.playerRichInfoArr) {
const joinIndex = parseInt(j) + 1;
const playerId = self.playerRichInfoArr[j].id;
const collisionPlayerIndex = self.collisionPlayerIndexPrefix + joinIndex;
const playerCollider = collisionSysMap.get(collisionPlayerIndex);
const newVpos = self.playerColliderAnchorToVirtualGridPos(playerCollider.x - effPushbacks[j][0], playerCollider.y - effPushbacks[j][1], self.playerRichInfoArr[j]);
nextRenderFramePlayers[playerId].virtualGridX = newVpos[0]; nextRenderFramePlayers[playerId].virtualGridX = newVpos[0];
nextRenderFramePlayers[playerId].virtualGridY = newVpos[1]; nextRenderFramePlayers[playerId].virtualGridY = newVpos[1];
} }
@ -1057,14 +1062,15 @@ cc.Class({
return toRet; return toRet;
}, },
rollbackAndChase(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap) { rollbackAndChase(renderFrameIdSt, renderFrameIdEd, collisionSys, collisionSysMap, isChasing) {
/* /*
This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd". This function eventually calculates a "RoomDownsyncFrame" where "RoomDownsyncFrame.id == renderFrameIdEd" if not interruptted.
*/ */
const self = this; const self = this;
let latestRdf = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame" let latestRdf = self.recentRenderCache.getByFrameId(renderFrameIdSt); // typed "RoomDownsyncFrame"
if (null == latestRdf) { if (null == latestRdf) {
console.error("Couldn't find renderFrameId=", renderFrameIdSt, " to rollback, lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); console.error("Couldn't find renderFrameId=", renderFrameIdSt, " to rollback, lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false));
return latestRdf;
} }
if (renderFrameIdSt >= renderFrameIdEd) { if (renderFrameIdSt >= renderFrameIdEd) {
@ -1072,14 +1078,16 @@ cc.Class({
} }
for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) { for (let i = renderFrameIdSt; i < renderFrameIdEd; ++i) {
const currRenderFrame = self.recentRenderCache.getByFrameId(i); // typed "RoomDownsyncFrame"; FIXME: onRoomDownsyncFrame(rdf) might get called asynchronously and thus made this line return "null"! const currRenderFrame = 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"!
if (null == currRenderFrame) { if (null == currRenderFrame) {
console.error("Couldn't find renderFrameId=", i, " to rollback, lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); 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`");
return latestRdf;
} }
const j = self._convertToInputFrameId(i, self.inputDelayFrames); const j = self._convertToInputFrameId(i, self.inputDelayFrames);
const delayedInputFrame = self.getCachedInputFrameDownsyncWithPrediction(j); const delayedInputFrame = self.getCachedInputFrameDownsyncWithPrediction(j);
if (null == delayedInputFrame) { if (null == delayedInputFrame) {
console.error("Failed to get cached delayedInputFrame for renderFrameId=", i, ", inputFrameId=", j, "lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId, ", recentRenderCache=", self._stringifyRecentRenderCache(false), ", recentInputCache=", self._stringifyRecentInputCache(false)); console.warn("Failed to get cached delayedInputFrame for i=", i, ", j=", j, ", self.renderFrameId=", self.renderFrameId, ", lastAllConfirmedRenderFrameId=", self.lastAllConfirmedRenderFrameId, ", lastAllConfirmedInputFrameId=", self.lastAllConfirmedInputFrameId);
return latestRdf;
} }
latestRdf = self.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap); latestRdf = self.applyInputFrameDownsyncDynamicsOnSingleRenderFrame(delayedInputFrame, currRenderFrame, collisionSys, collisionSysMap);
@ -1091,10 +1099,15 @@ cc.Class({
// We got a more up-to-date "all-confirmed-render-frame". // We got a more up-to-date "all-confirmed-render-frame".
self.lastAllConfirmedRenderFrameId = latestRdf.id; self.lastAllConfirmedRenderFrameId = latestRdf.id;
if (latestRdf.id > self.chaserRenderFrameId) { if (latestRdf.id > self.chaserRenderFrameId) {
// it must be true that "chaserRenderFrameId >= lastAllConfirmedRenderFrameId" // it must be true that "chaserRenderFrameId >= lastAllConfirmedRenderFrameId", regardeless of the "isChasing" param
self.chaserRenderFrameId = latestRdf.id; self.chaserRenderFrameId = latestRdf.id;
} }
} }
if (true == isChasing) {
// Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic!
self.chaserRenderFrameId = latestRdf.id;
}
self.dumpToRenderCache(latestRdf); self.dumpToRenderCache(latestRdf);
} }