Minor fix on rollback frame id counting.

This commit is contained in:
genxium 2022-09-21 17:22:34 +08:00
parent 372bfc2b48
commit f3428c88df
5 changed files with 86 additions and 41 deletions

View File

@ -535,7 +535,7 @@ func (pR *Room) ChooseStage() error {
ErrFatal(err) ErrFatal(err)
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
stageNameList := []string{"pacman" /*, "richsoil"*/} stageNameList := []string{/*"pacman" ,*/ "richsoil"}
chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu
pR.StageName = stageNameList[chosenStageIndex] pR.StageName = stageNameList[chosenStageIndex]

View File

@ -7,11 +7,11 @@
eJztz0ENACAMALGR4F8zNspyjwronZm7xPlcD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPy7bHBg/ldQwR eJztz0ENACAMALGR4F8zNspyjwronZm7xPlcD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPSw9LD0sPy7bHBg/ldQwR
</data> </data>
</layer> </layer>
<objectgroup id="2" name="PlayerStartingPos" locked="1"> <objectgroup id="2" name="PlayerStartingPos">
<object id="135" x="320" y="2884"> <object id="135" x="1689.33" y="1872">
<point/> <point/>
</object> </object>
<object id="137" x="2876" y="320"> <object id="137" x="1926.67" y="1626.67">
<point/> <point/>
</object> </object>
</objectgroup> </objectgroup>

View File

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

View File

@ -41,11 +41,6 @@ module.export = cc.Class({
self.computedNewDifferentPosLocalToParentWithinCurrentFrame = null; self.computedNewDifferentPosLocalToParentWithinCurrentFrame = null;
self.actionMangerSingleton = new cc.ActionManager(); self.actionMangerSingleton = new cc.ActionManager();
self.scheduledDirection = {
dx: 0,
dy: 0
};
self.activeDirection = { self.activeDirection = {
dx: 0, dx: 0,
dy: 0 dy: 0
@ -65,6 +60,7 @@ module.export = cc.Class({
'2-1': 'BottomRight' '2-1': 'BottomRight'
}; };
const canvasNode = self.mapNode.parent; const canvasNode = self.mapNode.parent;
self.mapIns = self.mapNode.getComponent("Map");
self.contactedBarriers = []; self.contactedBarriers = [];
const joystickInputControllerScriptIns = canvasNode.getComponent("TouchEventsManager"); const joystickInputControllerScriptIns = canvasNode.getComponent("TouchEventsManager");
self.ctrl = joystickInputControllerScriptIns; self.ctrl = joystickInputControllerScriptIns;
@ -77,8 +73,9 @@ module.export = cc.Class({
return; return;
} }
if (forceAnimSwitch || null == this.scheduledDirection || (newScheduledDirection.dx != this.scheduledDirection.dx || newScheduledDirection.dy != this.scheduledDirection.dy)) { if (forceAnimSwitch || null == this.activeDirection || (newScheduledDirection.dx != this.activeDirection.dx || newScheduledDirection.dy != this.activeDirection.dy)) {
this.scheduledDirection = newScheduledDirection; this.activeDirection = newScheduledDirection;
this.activeDirection = newScheduledDirection;
const clipKey = newScheduledDirection.dx.toString() + newScheduledDirection.dy.toString(); const clipKey = newScheduledDirection.dx.toString() + newScheduledDirection.dy.toString();
const clips = (this.attacked ? this.attackedClips : this.clips); const clips = (this.attacked ? this.attackedClips : this.clips);
let clip = clips[clipKey]; let clip = clips[clipKey];
@ -90,7 +87,7 @@ module.export = cc.Class({
} else { } else {
this.animComp.play(clip); this.animComp.play(clip);
if (this.attacked) { if (this.attacked) {
cc.log(`Attacked, switching to play clipKey = ${clipKey}, clip == ${clip}, this.activeDirection == ${JSON.stringify(this.activeDirection)}, this.scheduledDirection == ${JSON.stringify(this.scheduledDirection)}.`); cc.log(`Attacked, switching to play clipKey = ${clipKey}, clip == ${clip}, this.activeDirection == ${JSON.stringify(this.activeDirection)}, this.activeDirection == ${JSON.stringify(this.activeDirection)}.`);
} }
} }
} }
@ -357,7 +354,7 @@ module.export = cc.Class({
update(dt) { update(dt) {
const self = this; const self = this;
const vecToMoveBy = self._calculateVecToMoveBy(dt); const vecToMoveBy = self._calculateVecToMoveBy(self.mapIns.rollbackEstimatedDt); // To be consistent w.r.t. rollback dynamics
// console.log("activeDirection=", self.activeDirection, "vecToMoveBy=", vecToMoveBy, ", computedNewDifferentPosLocalToParentWithinCurrentFrame=", self.computedNewDifferentPosLocalToParentWithinCurrentFrame); // console.log("activeDirection=", self.activeDirection, "vecToMoveBy=", vecToMoveBy, ", computedNewDifferentPosLocalToParentWithinCurrentFrame=", self.computedNewDifferentPosLocalToParentWithinCurrentFrame);
if (self._canMoveBy(vecToMoveBy)) { if (self._canMoveBy(vecToMoveBy)) {
self.node.position = self.computedNewDifferentPosLocalToParentWithinCurrentFrame; self.node.position = self.computedNewDifferentPosLocalToParentWithinCurrentFrame;
@ -366,8 +363,8 @@ module.export = cc.Class({
lateUpdate(dt) { lateUpdate(dt) {
const self = this; const self = this;
self.activeDirection.dx = self.scheduledDirection.dx; self.activeDirection.dx = self.activeDirection.dx;
self.activeDirection.dy = self.scheduledDirection.dy; self.activeDirection.dy = self.activeDirection.dy;
const now = new Date().getTime(); const now = new Date().getTime();
self.lastMovedAt = now; self.lastMovedAt = now;
}, },
@ -460,12 +457,10 @@ module.export = cc.Class({
startFrozenDisplay() { startFrozenDisplay() {
const self = this; const self = this;
self.attacked = true; self.attacked = true;
self.scheduleNewDirection(self.scheduledDirection, true);
}, },
stopFrozenDisplay() { stopFrozenDisplay() {
const self = this; const self = this;
self.attacked = false; self.attacked = false;
self.scheduleNewDirection(self.scheduledDirection, true);
}, },
}); });

View File

@ -123,6 +123,10 @@ cc.Class({
type: cc.TiledMap, type: cc.TiledMap,
default: null default: null
}, },
rollbackEstimatedDt: {
type: cc.Float,
default: 1.0/60
},
}, },
_inputFrameIdDebuggable(inputFrameId) { _inputFrameIdDebuggable(inputFrameId) {
@ -338,13 +342,12 @@ cc.Class({
self.recentFrameCache = {}; self.recentFrameCache = {};
self.recentFrameCacheCurrentSize = 0; self.recentFrameCacheCurrentSize = 0;
self.recentFrameCacheMaxCount = 2048; self.recentFrameCacheMaxCount = 1024;
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 = {}; // TODO: Use a ringbuf instead self.recentInputCache = {}; // TODO: Use a ringbuf instead
self.recentInputCacheCurrentSize = 0; self.recentInputCacheCurrentSize = 0;
self.recentInputCacheMaxCount = 1024; self.recentInputCacheMaxCount = 1024;
self.rollbackEstimatedDt = 1.0/60;
self.transitToState(ALL_MAP_STATES.VISUAL); self.transitToState(ALL_MAP_STATES.VISUAL);
self.battleState = ALL_BATTLE_STATES.WAITING; self.battleState = ALL_BATTLE_STATES.WAITING;
@ -588,8 +591,9 @@ cc.Class({
return; return;
} }
let firstPredictedYetIncorrectInputFrameId = -1; // console.log("Received inputFrameDownsyncBatch=", batch, ", now correspondingLastLocalInputFrame=", self.recentInputCache[batch[batch.length-1].inputFrameId]);
let firstPredictedYetIncorrectInputFrameJoinIndex = -1; let firstPredictedYetIncorrectInputFrameId = null;
let firstPredictedYetIncorrectInputFrameJoinIndex = null;
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;
@ -597,7 +601,7 @@ cc.Class({
if (null == localInputFrame) { if (null == localInputFrame) {
console.warn("handleInputFrameDownsyncBatch: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false)); console.warn("handleInputFrameDownsyncBatch: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false));
} else { } else {
if (-1 == firstPredictedYetIncorrectInputFrameId) { if (null == firstPredictedYetIncorrectInputFrameId) {
for (let i in localInputFrame.inputList) { for (let i in localInputFrame.inputList) {
if (localInputFrame.inputList[i] != inputFrameDownsync.inputList[i]) { if (localInputFrame.inputList[i] != inputFrameDownsync.inputList[i]) {
firstPredictedYetIncorrectInputFrameId = inputFrameDownsyncId; firstPredictedYetIncorrectInputFrameId = inputFrameDownsyncId;
@ -611,19 +615,17 @@ cc.Class({
// [WARNING] Currently "lastDownsyncInputFrameId" and "lastAllConfirmedInputFrameId" are identical, but they (their definitions) are prone to changes in the future // [WARNING] Currently "lastDownsyncInputFrameId" and "lastAllConfirmedInputFrameId" are identical, but they (their definitions) are prone to changes in the future
self.lastDownsyncInputFrameId = inputFrameDownsyncId; self.lastDownsyncInputFrameId = inputFrameDownsyncId;
self.lastAllConfirmedInputFrameId = inputFrameDownsyncId; self.lastAllConfirmedInputFrameId = inputFrameDownsyncId;
if (self._inputFrameIdDebuggable(inputFrameDownsyncId)) {
console.log("Received inputFrameDownsyncId=", inputFrameDownsyncId);
}
} }
if (-1 != firstPredictedYetIncorrectInputFrameId) { if (null != firstPredictedYetIncorrectInputFrameId) {
const renderFrameId2 = self.renderFrameId; const renderFrameId2 = self.renderFrameId;
const inputFrameId2 = self._convertToInputFrameId(renderFrameId2, self.inputDelayFrames); const inputFrameId2 = self._convertToInputFrameId(renderFrameId2, self.inputDelayFrames);
const inputFrameId1 = firstPredictedYetIncorrectInputFrameId; const inputFrameId1 = firstPredictedYetIncorrectInputFrameId;
const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId" const renderFrameId1 = self._convertToRenderFrameId(inputFrameId1, self.inputDelayFrames); // a.k.a. "firstRenderFrameIdUsingIncorrectInputFrameId"
if (renderFrameId1 <= renderFrameId2) { if (renderFrameId1 < renderFrameId2) {
// No need to rollback when "renderFrameId1 == renderFrameId2", because the "delayedInputFrame for renderFrameId2" is not yet executed by now, it just went through "++self.renderFrameId" in "update(dt)" and js-runtime is mostly single-threaded in our programmable range.
console.warn("Mismatched input! inputFrameId1=", inputFrameId1, ", renderFrameId1=", renderFrameId1, ": at least 1 incorrect input predicted for joinIndex=", firstPredictedYetIncorrectInputFrameJoinIndex, ", inputFrameId2 = ", inputFrameId2, ", renderFrameId2=", renderFrameId2); console.warn("Mismatched input! inputFrameId1=", inputFrameId1, ", renderFrameId1=", renderFrameId1, ": at least 1 incorrect input predicted for joinIndex=", firstPredictedYetIncorrectInputFrameJoinIndex, ", inputFrameId2 = ", inputFrameId2, ", renderFrameId2=", renderFrameId2);
self._rollback(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2); self._rollbackAndReplay(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2);
} else { } else {
console.log("Mismatched input yet no rollback needed. inputFrameId1=", inputFrameId1, ", renderFrameId1=", renderFrameId1, ": at least 1 incorrect input predicted for joinIndex=", firstPredictedYetIncorrectInputFrameJoinIndex, ", inputFrameId2 = ", inputFrameId2, ", renderFrameId2=", renderFrameId2); console.log("Mismatched input yet no rollback needed. inputFrameId1=", inputFrameId1, ", renderFrameId1=", renderFrameId1, ": at least 1 incorrect input predicted for joinIndex=", firstPredictedYetIncorrectInputFrameJoinIndex, ", inputFrameId2 = ", inputFrameId2, ", renderFrameId2=", renderFrameId2);
} }
@ -774,7 +776,6 @@ cc.Class({
// TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously? // TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously?
self._sendInputFrameUpsyncBatch(noDelayInputFrameId); self._sendInputFrameUpsyncBatch(noDelayInputFrameId);
} }
++self.renderFrameId;
const delayedInputFrameId = self._convertToInputFrameId(self.renderFrameId, self.inputDelayFrames); // The "inputFrameId" to use at current "renderFrameId" const delayedInputFrameId = self._convertToInputFrameId(self.renderFrameId, self.inputDelayFrames); // The "inputFrameId" to use at current "renderFrameId"
const delayedInputFrameDownsync = self.recentInputCache[delayedInputFrameId]; const delayedInputFrameDownsync = self.recentInputCache[delayedInputFrameId];
if (null == delayedInputFrameDownsync) { if (null == delayedInputFrameDownsync) {
@ -782,6 +783,14 @@ cc.Class({
} else { } else {
self._applyInputFrameDownsyncDynamics(delayedInputFrameDownsync, false); self._applyInputFrameDownsyncDynamics(delayedInputFrameDownsync, false);
} }
const rdf = self._createRoomDownsyncFrameLocally();
self._dumpToFullFrameCache(rdf);
/*
if (null != delayedInputFrameDownsync && null != delayedInputFrameDownsync.inputList && 0 < delayedInputFrameDownsync.inputList[self.selfPlayerInfo.joinIndex-1]) {
console.log("My critical status: renderFrame=", JSON.stringify(rdf), ", delayedInputFrameDownsync=", JSON.stringify(delayedInputFrameDownsync));
}
*/
++self.renderFrameId; // [WARNING] It's important to increment the renderFrameId AFTER all the operations above!!!
} }
const mapNode = self.node; const mapNode = self.node;
const canvasNode = mapNode.parent; const canvasNode = mapNode.parent;
@ -792,7 +801,7 @@ cc.Class({
// update countdown // update countdown
if (null != self.countdownNanos) { if (null != self.countdownNanos) {
self.countdownNanos -= dt*1000000000; self.countdownNanos -= self.rollbackEstimatedDt*1000000000;
if (self.countdownNanos <= 0) { if (self.countdownNanos <= 0) {
self.onBattleStopped(self.playerRichInfoDict); self.onBattleStopped(self.playerRichInfoDict);
return; return;
@ -809,7 +818,6 @@ cc.Class({
} }
if (null != self.ctrl) { if (null != self.ctrl) {
self.ctrl.justifyMapNodePosAndScale(self.ctrl.linearSpeedBase, self.ctrl.zoomingSpeedBase); self.ctrl.justifyMapNodePosAndScale(self.ctrl.linearSpeedBase, self.ctrl.zoomingSpeedBase);
self._createRoomDownsyncFrameLocally();
} }
}, },
@ -928,7 +936,7 @@ cc.Class({
joinIndex: joinIndex joinIndex: joinIndex
}; };
} }
self._dumpToFullFrameCache(rdf); return rdf;
}, },
_applyRoomDownsyncFrameDynamics(rdf) { _applyRoomDownsyncFrameDynamics(rdf) {
@ -960,19 +968,21 @@ cc.Class({
} }
}, },
_rollback(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2) { _rollbackAndReplay(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2) {
const self = this; const self = this;
const rdf1 = self.recentFrameCache[renderFrameId1]; const rdf1 = self.recentFrameCache[renderFrameId1];
if (null == rdf1) { if (null == rdf1) {
const recentFrameCacheKeys = Object.keys(self.recentFrameCache); const recentFrameCacheKeys = Object.keys(self.recentFrameCache);
console.warn("renderFrameId1=", renderFrameId1, "doesn't exist in recentFrameCache ", self._stringifyRecentFrameCache(false), ": COULDN'T ROLLBACK!"); console.error("renderFrameId1=", renderFrameId1, "doesn't exist in recentFrameCache ", self._stringifyRecentFrameCache(false), ": COULDN'T ROLLBACK!");
return; return;
} }
self._applyRoomDownsyncFrameDynamics(rdf1); self._applyRoomDownsyncFrameDynamics(rdf1);
for (let renderFrameId = renderFrameId1; renderFrameId <= renderFrameId2; ++renderFrameId) { // DON'T apply inputFrameDownsync dynamics for exactly "renderFrameId2", see the comment around the invocation of "_rollbackAndReplay".
for (let renderFrameId = renderFrameId1; renderFrameId < renderFrameId2; ++renderFrameId) {
const delayedInputFrameId = self._convertToInputFrameId(renderFrameId, self.inputDelayFrames); const delayedInputFrameId = self._convertToInputFrameId(renderFrameId, self.inputDelayFrames);
const delayedInputFrame = self.recentInputCache[delayedInputFrameId]; const delayedInputFrameDownsync = self.recentInputCache[delayedInputFrameId];
self._applyInputFrameDownsyncDynamics(delayedInputFrame, true); self._applyInputFrameDownsyncDynamics(delayedInputFrameDownsync, true);
// console.log("_rollbackAndReplay, AFTER:", self._stringifyRollbackResult(renderFrameId, delayedInputFrameDownsync));
} }
}, },
@ -1000,10 +1010,17 @@ cc.Class({
_stringifyRecentFrameCache(usefullOutput) { _stringifyRecentFrameCache(usefullOutput) {
if (true == usefullOutput) { if (true == usefullOutput) {
return JSON.stringify(this.recentFrameCache);
let s = [];
for (let renderFrameId in self.recentFrameCache) {
const roomDownsyncFrame = self.recentFrameCache[renderFrameId];
s.push(JSON.stringify(roomDownsyncFrame));
}
return s.join('\n');
} }
const keys = Object.keys(this.recentInputCache); const keys = Object.keys(this.recentFrameCache);
return "[stIRenderFrameId=" + keys[0] + ", edRenderFrameId=" + keys[keys.length-1] + "]"; return "[stRenderFrameId=" + keys[0] + ", edRenderFrameId=" + keys[keys.length-1] + "]";
}, },
_stringifyRecentInputCache(usefullOutput) { _stringifyRecentInputCache(usefullOutput) {
@ -1013,4 +1030,37 @@ cc.Class({
const keys = Object.keys(this.recentInputCache); const keys = Object.keys(this.recentInputCache);
return "[stInputFrameId=" + keys[0] + ", edInputFrameId=" + keys[keys.length-1] + "]"; return "[stInputFrameId=" + keys[0] + ", edInputFrameId=" + keys[keys.length-1] + "]";
}, },
_stringifyRollbackResult(renderFrameId, delayedInputFrameDownsync) {
// Slightly different from "_createRoomDownsyncFrameLocally"
const self = this;
const s = (
null == delayedInputFrameDownsync
?
{
renderFrameId: renderFrameId,
players: {}
}
:
{
renderFrameId: renderFrameId,
players: {},
delayedInputFrameDownsync: delayedInputFrameDownsync,
}
);
let players = {};
for (let playerId in self.playerRichInfoDict) {
const playerRichInfo = self.playerRichInfoDict[playerId];
const joinIndex = playerRichInfo.joinIndex;
const playerNode = playerRichInfo.node;
const playerScriptIns = playerRichInfo.scriptIns;
s.players[playerRichInfo.id] = {
id: playerRichInfo.id,
x: playerNode.position.x,
y: playerNode.position.y,
};
}
return JSON.stringify(s);
},
}); });