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)
rand.Seed(time.Now().Unix())
stageNameList := []string{"pacman" /*, "richsoil"*/}
stageNameList := []string{/*"pacman" ,*/ "richsoil"}
chosenStageIndex := rand.Int() % len(stageNameList) // Hardcoded temporarily. -- YFLu
pR.StageName = stageNameList[chosenStageIndex]

View File

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

View File

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

View File

@ -41,11 +41,6 @@ module.export = cc.Class({
self.computedNewDifferentPosLocalToParentWithinCurrentFrame = null;
self.actionMangerSingleton = new cc.ActionManager();
self.scheduledDirection = {
dx: 0,
dy: 0
};
self.activeDirection = {
dx: 0,
dy: 0
@ -65,6 +60,7 @@ module.export = cc.Class({
'2-1': 'BottomRight'
};
const canvasNode = self.mapNode.parent;
self.mapIns = self.mapNode.getComponent("Map");
self.contactedBarriers = [];
const joystickInputControllerScriptIns = canvasNode.getComponent("TouchEventsManager");
self.ctrl = joystickInputControllerScriptIns;
@ -77,8 +73,9 @@ module.export = cc.Class({
return;
}
if (forceAnimSwitch || null == this.scheduledDirection || (newScheduledDirection.dx != this.scheduledDirection.dx || newScheduledDirection.dy != this.scheduledDirection.dy)) {
this.scheduledDirection = newScheduledDirection;
if (forceAnimSwitch || null == this.activeDirection || (newScheduledDirection.dx != this.activeDirection.dx || newScheduledDirection.dy != this.activeDirection.dy)) {
this.activeDirection = newScheduledDirection;
this.activeDirection = newScheduledDirection;
const clipKey = newScheduledDirection.dx.toString() + newScheduledDirection.dy.toString();
const clips = (this.attacked ? this.attackedClips : this.clips);
let clip = clips[clipKey];
@ -90,7 +87,7 @@ module.export = cc.Class({
} else {
this.animComp.play(clip);
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) {
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);
if (self._canMoveBy(vecToMoveBy)) {
self.node.position = self.computedNewDifferentPosLocalToParentWithinCurrentFrame;
@ -366,8 +363,8 @@ module.export = cc.Class({
lateUpdate(dt) {
const self = this;
self.activeDirection.dx = self.scheduledDirection.dx;
self.activeDirection.dy = self.scheduledDirection.dy;
self.activeDirection.dx = self.activeDirection.dx;
self.activeDirection.dy = self.activeDirection.dy;
const now = new Date().getTime();
self.lastMovedAt = now;
},
@ -460,12 +457,10 @@ module.export = cc.Class({
startFrozenDisplay() {
const self = this;
self.attacked = true;
self.scheduleNewDirection(self.scheduledDirection, true);
},
stopFrozenDisplay() {
const self = this;
self.attacked = false;
self.scheduleNewDirection(self.scheduledDirection, true);
},
});

View File

@ -123,6 +123,10 @@ cc.Class({
type: cc.TiledMap,
default: null
},
rollbackEstimatedDt: {
type: cc.Float,
default: 1.0/60
},
},
_inputFrameIdDebuggable(inputFrameId) {
@ -338,13 +342,12 @@ cc.Class({
self.recentFrameCache = {};
self.recentFrameCacheCurrentSize = 0;
self.recentFrameCacheMaxCount = 2048;
self.recentFrameCacheMaxCount = 1024;
self.selfPlayerInfo = null; // This field is kept for distinguishing "self" and "others".
self.recentInputCache = {}; // TODO: Use a ringbuf instead
self.recentInputCacheCurrentSize = 0;
self.recentInputCacheMaxCount = 1024;
self.rollbackEstimatedDt = 1.0/60;
self.transitToState(ALL_MAP_STATES.VISUAL);
self.battleState = ALL_BATTLE_STATES.WAITING;
@ -588,8 +591,9 @@ cc.Class({
return;
}
let firstPredictedYetIncorrectInputFrameId = -1;
let firstPredictedYetIncorrectInputFrameJoinIndex = -1;
// console.log("Received inputFrameDownsyncBatch=", batch, ", now correspondingLastLocalInputFrame=", self.recentInputCache[batch[batch.length-1].inputFrameId]);
let firstPredictedYetIncorrectInputFrameId = null;
let firstPredictedYetIncorrectInputFrameJoinIndex = null;
for (let k in batch) {
const inputFrameDownsync = batch[k];
const inputFrameDownsyncId = inputFrameDownsync.inputFrameId;
@ -597,7 +601,7 @@ cc.Class({
if (null == localInputFrame) {
console.warn("handleInputFrameDownsyncBatch: recentInputCache is NOT having inputFrameDownsyncId=", inputFrameDownsyncId, "; now recentInputCache=", self._stringifyRecentInputCache(false));
} else {
if (-1 == firstPredictedYetIncorrectInputFrameId) {
if (null == firstPredictedYetIncorrectInputFrameId) {
for (let i in localInputFrame.inputList) {
if (localInputFrame.inputList[i] != inputFrameDownsync.inputList[i]) {
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
self.lastDownsyncInputFrameId = inputFrameDownsyncId;
self.lastAllConfirmedInputFrameId = inputFrameDownsyncId;
if (self._inputFrameIdDebuggable(inputFrameDownsyncId)) {
console.log("Received inputFrameDownsyncId=", inputFrameDownsyncId);
}
}
if (-1 != firstPredictedYetIncorrectInputFrameId) {
if (null != firstPredictedYetIncorrectInputFrameId) {
const renderFrameId2 = self.renderFrameId;
const inputFrameId2 = self._convertToInputFrameId(renderFrameId2, self.inputDelayFrames);
const inputFrameId1 = firstPredictedYetIncorrectInputFrameId;
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);
self._rollback(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2);
self._rollbackAndReplay(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2);
} 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);
}
@ -774,7 +776,6 @@ cc.Class({
// TODO: Is the following statement run asynchronously in an implicit manner? Should I explicitly run it asynchronously?
self._sendInputFrameUpsyncBatch(noDelayInputFrameId);
}
++self.renderFrameId;
const delayedInputFrameId = self._convertToInputFrameId(self.renderFrameId, self.inputDelayFrames); // The "inputFrameId" to use at current "renderFrameId"
const delayedInputFrameDownsync = self.recentInputCache[delayedInputFrameId];
if (null == delayedInputFrameDownsync) {
@ -782,6 +783,14 @@ cc.Class({
} else {
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 canvasNode = mapNode.parent;
@ -792,7 +801,7 @@ cc.Class({
// update countdown
if (null != self.countdownNanos) {
self.countdownNanos -= dt*1000000000;
self.countdownNanos -= self.rollbackEstimatedDt*1000000000;
if (self.countdownNanos <= 0) {
self.onBattleStopped(self.playerRichInfoDict);
return;
@ -809,7 +818,6 @@ cc.Class({
}
if (null != self.ctrl) {
self.ctrl.justifyMapNodePosAndScale(self.ctrl.linearSpeedBase, self.ctrl.zoomingSpeedBase);
self._createRoomDownsyncFrameLocally();
}
},
@ -928,7 +936,7 @@ cc.Class({
joinIndex: joinIndex
};
}
self._dumpToFullFrameCache(rdf);
return rdf;
},
_applyRoomDownsyncFrameDynamics(rdf) {
@ -960,19 +968,21 @@ cc.Class({
}
},
_rollback(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2) {
_rollbackAndReplay(inputFrameId1, renderFrameId1, inputFrameId2, renderFrameId2) {
const self = this;
const rdf1 = self.recentFrameCache[renderFrameId1];
if (null == rdf1) {
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;
}
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 delayedInputFrame = self.recentInputCache[delayedInputFrameId];
self._applyInputFrameDownsyncDynamics(delayedInputFrame, true);
const delayedInputFrameDownsync = self.recentInputCache[delayedInputFrameId];
self._applyInputFrameDownsyncDynamics(delayedInputFrameDownsync, true);
// console.log("_rollbackAndReplay, AFTER:", self._stringifyRollbackResult(renderFrameId, delayedInputFrameDownsync));
}
},
@ -1000,10 +1010,17 @@ cc.Class({
_stringifyRecentFrameCache(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);
return "[stIRenderFrameId=" + keys[0] + ", edRenderFrameId=" + keys[keys.length-1] + "]";
const keys = Object.keys(this.recentFrameCache);
return "[stRenderFrameId=" + keys[0] + ", edRenderFrameId=" + keys[keys.length-1] + "]";
},
_stringifyRecentInputCache(usefullOutput) {
@ -1013,4 +1030,37 @@ cc.Class({
const keys = Object.keys(this.recentInputCache);
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);
},
});