mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-26 11:48:56 +00:00
Fixes on resync.
This commit is contained in:
parent
c6473db561
commit
348c889e14
@ -169,6 +169,7 @@ type Room struct {
|
|||||||
PlayerDefaultSpeed int32
|
PlayerDefaultSpeed int32
|
||||||
|
|
||||||
BulletBattleLocalIdCounter int32
|
BulletBattleLocalIdCounter int32
|
||||||
|
dilutedRollbackEstimatedDtNanos int64
|
||||||
|
|
||||||
BattleColliderInfo // Compositing to send centralized magic numbers
|
BattleColliderInfo // Compositing to send centralized magic numbers
|
||||||
}
|
}
|
||||||
@ -465,7 +466,7 @@ func (pR *Room) StartBattle() {
|
|||||||
dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt
|
dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt
|
||||||
}
|
}
|
||||||
if 0 < unconfirmedMask {
|
if 0 < unconfirmedMask {
|
||||||
Logger.Warn(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, room.CurDynamicsRenderFrameId=%v, room.LastAllConfirmedInputFrameId=%v, unconfirmedMask=%v", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LastAllConfirmedInputFrameId, unconfirmedMask))
|
Logger.Debug(fmt.Sprintf("roomId=%v, room.RenderFrameId=%v, room.CurDynamicsRenderFrameId=%v, room.LastAllConfirmedInputFrameId=%v, unconfirmedMask=%v", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LastAllConfirmedInputFrameId, unconfirmedMask))
|
||||||
// Otherwise no need to downsync immediately
|
// Otherwise no need to downsync immediately
|
||||||
pR.downsyncToAllPlayers(pR.LastAllConfirmedInputFrameId, unconfirmedMask, false)
|
pR.downsyncToAllPlayers(pR.LastAllConfirmedInputFrameId, unconfirmedMask, false)
|
||||||
}
|
}
|
||||||
@ -477,7 +478,7 @@ func (pR *Room) StartBattle() {
|
|||||||
if elapsedInCalculation > pR.RollbackEstimatedDtNanos {
|
if elapsedInCalculation > pR.RollbackEstimatedDtNanos {
|
||||||
Logger.Warn(fmt.Sprintf("SLOW FRAME! Elapsed time statistics: roomId=%v, room.RenderFrameId=%v, elapsedInCalculation=%v ns, dynamicsDuration=%v ns, RollbackEstimatedDtNanos=%v", pR.Id, pR.RenderFrameId, elapsedInCalculation, dynamicsDuration, pR.RollbackEstimatedDtNanos))
|
Logger.Warn(fmt.Sprintf("SLOW FRAME! Elapsed time statistics: roomId=%v, room.RenderFrameId=%v, elapsedInCalculation=%v ns, dynamicsDuration=%v ns, RollbackEstimatedDtNanos=%v", pR.Id, pR.RenderFrameId, elapsedInCalculation, dynamicsDuration, pR.RollbackEstimatedDtNanos))
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(pR.RollbackEstimatedDtNanos - elapsedInCalculation))
|
time.Sleep(time.Duration(pR.dilutedRollbackEstimatedDtNanos - elapsedInCalculation))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,6 +715,8 @@ func (pR *Room) OnDismissed() {
|
|||||||
pR.ServerFps = 60
|
pR.ServerFps = 60
|
||||||
pR.RollbackEstimatedDtMillis = 16.667 // Use fixed-and-low-precision to mitigate the inconsistent floating-point-number issue between Golang and JavaScript
|
pR.RollbackEstimatedDtMillis = 16.667 // Use fixed-and-low-precision to mitigate the inconsistent floating-point-number issue between Golang and JavaScript
|
||||||
pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for logging FAST FRAME
|
pR.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for logging FAST FRAME
|
||||||
|
dilutedServerFps := int64(58)
|
||||||
|
pR.dilutedRollbackEstimatedDtNanos = pR.RollbackEstimatedDtNanos * (int64(pR.ServerFps) / dilutedServerFps)
|
||||||
pR.BattleDurationFrames = 30 * pR.ServerFps
|
pR.BattleDurationFrames = 30 * pR.ServerFps
|
||||||
pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1)
|
pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1)
|
||||||
pR.InputFrameUpsyncDelayTolerance = 2
|
pR.InputFrameUpsyncDelayTolerance = 2
|
||||||
@ -921,7 +924,7 @@ func (pR *Room) OnPlayerBattleColliderAcked(playerId int32) bool {
|
|||||||
This function is triggered by an upsync message via WebSocket, thus downsync sending is also available by now.
|
This function is triggered by an upsync message via WebSocket, thus downsync sending is also available by now.
|
||||||
*/
|
*/
|
||||||
thatPlayerBattleState := atomic.LoadInt32(&(thatPlayer.BattleState))
|
thatPlayerBattleState := atomic.LoadInt32(&(thatPlayer.BattleState))
|
||||||
Logger.Info(fmt.Sprintf("OnPlayerBattleColliderAcked-middle: roomId=%v, roomState=%v, targetPlayerId=%v, targetPlayerBattleState=%v, thatPlayerId=%v, thatPlayerBattleState=%v", pR.Id, pR.State, targetPlayer.Id, targetPlayer.BattleState, thatPlayer.Id, thatPlayerBattleState))
|
Logger.Debug(fmt.Sprintf("OnPlayerBattleColliderAcked-middle: roomId=%v, roomState=%v, targetPlayerId=%v, targetPlayerBattleState=%v, thatPlayerId=%v, thatPlayerBattleState=%v", pR.Id, pR.State, targetPlayer.Id, targetPlayer.BattleState, thatPlayer.Id, thatPlayerBattleState))
|
||||||
if thatPlayerId == targetPlayer.Id || (PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK == thatPlayerBattleState || PlayerBattleStateIns.ACTIVE == thatPlayerBattleState) {
|
if thatPlayerId == targetPlayer.Id || (PlayerBattleStateIns.ADDED_PENDING_BATTLE_COLLIDER_ACK == thatPlayerBattleState || PlayerBattleStateIns.ACTIVE == thatPlayerBattleState) {
|
||||||
Logger.Debug(fmt.Sprintf("OnPlayerBattleColliderAcked-sending DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED: roomId=%v, roomState=%v, targetPlayerId=%v, targetPlayerBattleState=%v, capacity=%v, EffectivePlayerCount=%v", pR.Id, pR.State, targetPlayer.Id, targetPlayer.BattleState, pR.Capacity, pR.EffectivePlayerCount))
|
Logger.Debug(fmt.Sprintf("OnPlayerBattleColliderAcked-sending DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED: roomId=%v, roomState=%v, targetPlayerId=%v, targetPlayerBattleState=%v, capacity=%v, EffectivePlayerCount=%v", pR.Id, pR.State, targetPlayer.Id, targetPlayer.BattleState, pR.Capacity, pR.EffectivePlayerCount))
|
||||||
pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED, thatPlayer.Id)
|
pR.sendSafely(playerAckedFrame, nil, DOWNSYNC_MSG_ACT_PLAYER_ADDED_AND_ACKED, thatPlayer.Id)
|
||||||
@ -1448,26 +1451,28 @@ func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, upperToSe
|
|||||||
lowerToSentInputFrameId := player.LastSentInputFrameId + 1
|
lowerToSentInputFrameId := player.LastSentInputFrameId + 1
|
||||||
/*
|
/*
|
||||||
[WARNING]
|
[WARNING]
|
||||||
Upon resynced on frontend, "refRenderFrameId" MUST BE CAPPED somehow by "upperToSendInputFrameId", if frontend resyncs itself to a more advanced value than given below, upon the next renderFrame tick on the frontend it might generate non-consecutive "nextInputFrameId > frontend.recentInputCache.edFrameId+1".
|
Upon resynced on frontend, "refRenderFrameId" is now set to as advanced as possible, and it's the frontend's responsibility now to pave way for the "gap inputFrames"
|
||||||
|
|
||||||
If "NstDelayFrames" becomes larger, "pR.RenderFrameId - refRenderFrameId" possibly becomes larger because the force confirmation is delayed more.
|
If "NstDelayFrames" becomes larger, "pR.RenderFrameId - refRenderFrameId" possibly becomes larger because the force confirmation is delayed more.
|
||||||
|
|
||||||
Upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId" -- and this is allowed.
|
Upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId" -- and this is allowed.
|
||||||
*/
|
*/
|
||||||
refRenderFrameId := pR.ConvertToGeneratingRenderFrameId(upperToSendInputFrameId + 1) // for the frontend to jump immediately into generating & upsyncing the next input frame, thus getting rid of "resync avalanche"
|
refRenderFrameId := pR.CurDynamicsRenderFrameId - 1
|
||||||
if refRenderFrameId > pR.RenderFrameId {
|
|
||||||
refRenderFrameId = pR.RenderFrameId
|
|
||||||
}
|
|
||||||
|
|
||||||
if MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId {
|
shouldResync1 := (MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId)
|
||||||
|
shouldResync2 := (0 < (unconfirmedMask & uint64(1<<uint32(player.JoinIndex-1)))) // This condition is critical, if we don't send resync upon this condition, the "reconnected or slowly-clocking player" might never get its input synced
|
||||||
|
// shouldResync2 := (0 < unconfirmedMask) // An easier version of the above, might keep sending "refRenderFrame"s to still connected players when any player is disconnected
|
||||||
|
shouldResyncOverall := (shouldResync1 || shouldResync2)
|
||||||
|
|
||||||
|
if shouldResyncOverall {
|
||||||
// A rejoined player, should guarantee that when it resyncs to "refRenderFrameId" a matching inputFrame to apply exists
|
// A rejoined player, should guarantee that when it resyncs to "refRenderFrameId" a matching inputFrame to apply exists
|
||||||
lowerToSentInputFrameId = pR.ConvertToInputFrameId(refRenderFrameId, pR.InputDelayFrames)
|
lowerToSentInputFrameId = pR.ConvertToInputFrameId(refRenderFrameId, pR.InputDelayFrames)
|
||||||
Logger.Debug(fmt.Sprintf("Resetting refRenderFrame for rejoined player: roomId=%v, renderFrameId=%v, playerId=%v, refRenderFrameId=%v, lowerToSentInputFrameId=%v, upperToSendInputFrameId=%v", pR.Id, pR.RenderFrameId, playerId, refRenderFrameId, lowerToSentInputFrameId, upperToSendInputFrameId))
|
Logger.Info(fmt.Sprintf("Resyncing player: roomId=%v, playerId=%v, playerJoinIndex=%v, renderFrameId=%v, curDynamicsRenderFrameId=%v, playerLastSentInputFrameId=%v, refRenderFrameId=%v, lowerToSentInputFrameId=%v, upperToSendInputFrameId=%v", pR.Id, playerId, player.JoinIndex, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, player.LastSentInputFrameId, refRenderFrameId, lowerToSentInputFrameId, upperToSendInputFrameId))
|
||||||
// TODO: What if "lowerToSentInputFrameId > pR.InputsBuffer.StFrameId" now?
|
// TODO: What if "lowerToSentInputFrameId > pR.InputsBuffer.StFrameId" now?
|
||||||
}
|
}
|
||||||
|
|
||||||
if lowerToSentInputFrameId > upperToSendInputFrameId {
|
if lowerToSentInputFrameId > upperToSendInputFrameId {
|
||||||
Logger.Warn(fmt.Sprintf("Not sending due to potentially empty toSendInputFrameDownsyncs: roomId=%v, playerId=%v, refRenderFrameId=%v, lowerToSentInputFrameId=%v, upperToSendInputFrameId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v", pR.Id, playerId, refRenderFrameId, lowerToSentInputFrameId, upperToSendInputFrameId, player.LastSentInputFrameId, player.AckingInputFrameId))
|
Logger.Debug(fmt.Sprintf("Not sending due to potentially empty toSendInputFrameDownsyncs: roomId=%v, playerId=%v, playerLastSentInputFrameId=%v, refRenderFrameId=%v, lowerToSentInputFrameId=%v, upperToSendInputFrameId=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v", pR.Id, playerId, player.LastSentInputFrameId, refRenderFrameId, lowerToSentInputFrameId, upperToSendInputFrameId, player.LastSentInputFrameId, player.AckingInputFrameId))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1481,7 +1486,7 @@ func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, upperToSe
|
|||||||
j, toSendInputFrameDownsyncs := pR.InputsBuffer.cloneInputFrameDownsyncsByFrameIdRange(lowerToSentInputFrameId, upperToSendInputFrameId+1, theInputsBufferLockToUse)
|
j, toSendInputFrameDownsyncs := pR.InputsBuffer.cloneInputFrameDownsyncsByFrameIdRange(lowerToSentInputFrameId, upperToSendInputFrameId+1, theInputsBufferLockToUse)
|
||||||
|
|
||||||
if 0 >= len(toSendInputFrameDownsyncs) {
|
if 0 >= len(toSendInputFrameDownsyncs) {
|
||||||
Logger.Debug(fmt.Sprintf("Not sending due to actually empty toSendInputFrameDownsyncs: roomId=%v, playerId=%v, refRenderFrameId=%v, lowerToSentInputFrameId=%v, upperToSendInputFrameId=%v, j=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v", pR.Id, playerId, refRenderFrameId, lowerToSentInputFrameId, upperToSendInputFrameId, j, player.LastSentInputFrameId, player.AckingInputFrameId))
|
Logger.Debug(fmt.Sprintf("Not sending due to actually empty toSendInputFrameDownsyncs: roomId=%v, playerId=%v, playerLastSentInputFrameId=%v, refRenderFrameId=%v, lowerToSentInputFrameId=%v, upperToSendInputFrameId=%v, j=%v, lastSentInputFrameId=%v, playerAckingInputFrameId=%v", pR.Id, playerId, player.LastSentInputFrameId, refRenderFrameId, lowerToSentInputFrameId, upperToSendInputFrameId, j, player.LastSentInputFrameId, player.AckingInputFrameId))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1490,17 +1495,17 @@ func (pR *Room) downsyncToSinglePlayer(playerId int32, player *Player, upperToSe
|
|||||||
1. when player with a slower frontend clock lags significantly behind and thus wouldn't get its inputUpsync recognized due to faster "forceConfirmation"
|
1. when player with a slower frontend clock lags significantly behind and thus wouldn't get its inputUpsync recognized due to faster "forceConfirmation"
|
||||||
2. reconnection
|
2. reconnection
|
||||||
*/
|
*/
|
||||||
shouldResync1 := (MAGIC_LAST_SENT_INPUT_FRAME_ID_READDED == player.LastSentInputFrameId)
|
if pR.BackendDynamicsEnabled && shouldResyncOverall {
|
||||||
shouldResync2 := (0 < (unconfirmedMask & uint64(1<<uint32(player.JoinIndex-1)))) // This condition is critical, if we don't send resync upon this condition, the "reconnected or slowly-clocking player" might never get its input synced
|
|
||||||
// shouldResync2 := (0 < unconfirmedMask) // An easier version of the above, might keep sending "refRenderFrame"s to still connected players when any player is disconnected
|
|
||||||
if pR.BackendDynamicsEnabled && (shouldResync1 || shouldResync2) {
|
|
||||||
tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId)
|
tmp := pR.RenderFrameBuffer.GetByFrameId(refRenderFrameId)
|
||||||
if nil == tmp {
|
if nil == tmp {
|
||||||
panic(fmt.Sprintf("Required refRenderFrameId=%v for roomId=%v, renderFrameId=%v, playerId=%v, j=%v doesn't exist! InputsBuffer=%v, RenderFrameBuffer=%v", refRenderFrameId, pR.Id, pR.RenderFrameId, playerId, j, pR.InputsBufferString(false), pR.RenderFrameBufferString()))
|
panic(fmt.Sprintf("Required refRenderFrameId=%v for roomId=%v, renderFrameId=%v, playerId=%v, playerLastSentInputFrameId=%v, j=%v doesn't exist! InputsBuffer=%v, RenderFrameBuffer=%v", refRenderFrameId, pR.Id, pR.RenderFrameId, playerId, player.LastSentInputFrameId, j, pR.InputsBufferString(false), pR.RenderFrameBufferString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Warn(fmt.Sprintf("Sending refRenderFrameId=%v for roomId=%v, renderFrameId=%v, curDynamicsRenderFrameId=%v, playerId=%v, j=%v: InputsBuffer=%v", refRenderFrameId, pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, playerId, j, pR.InputsBufferString(false)))
|
Logger.Warn(fmt.Sprintf("Sending refRenderFrameId=%v for roomId=%v, , playerId=%v, playerJoinIndex=%v, renderFrameId=%v, curDynamicsRenderFrameId=%v, playerLastSentInputFrameId=%v, lowerToSentInputFrameId=%v, upperToSendInputFrameId=%v, j=%v: InputsBuffer=%v", refRenderFrameId, pR.Id, playerId, player.JoinIndex, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, player.LastSentInputFrameId, lowerToSentInputFrameId, upperToSendInputFrameId, j, pR.InputsBufferString(false)))
|
||||||
refRenderFrame := tmp.(*RoomDownsyncFrame)
|
refRenderFrame := tmp.(*RoomDownsyncFrame)
|
||||||
|
for playerId, player := range pR.Players {
|
||||||
|
refRenderFrame.Players[playerId].ColliderRadius = player.ColliderRadius // hardcoded for now
|
||||||
|
}
|
||||||
refRenderFrame.BackendUnconfirmedMask = unconfirmedMask
|
refRenderFrame.BackendUnconfirmedMask = unconfirmedMask
|
||||||
pR.sendSafely(refRenderFrame, toSendInputFrameDownsyncs, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId)
|
pR.sendSafely(refRenderFrame, toSendInputFrameDownsyncs, DOWNSYNC_MSG_ACT_FORCED_RESYNC, playerId)
|
||||||
} else {
|
} else {
|
||||||
|
@ -440,7 +440,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
342.9460598986377,
|
216.50635094610968,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -454,7 +454,7 @@
|
|||||||
"array": [
|
"array": [
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
342.9460598986377,
|
216.50635094610968,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -104,32 +104,6 @@ cc.Class({
|
|||||||
return (0 == inputFrameId % 10);
|
return (0 == inputFrameId % 10);
|
||||||
},
|
},
|
||||||
|
|
||||||
dumpToRenderCache: function(rdf) {
|
|
||||||
const self = this;
|
|
||||||
const minToKeepRenderFrameId = self.lastAllConfirmedRenderFrameId - 1; // Keep at least 1 prev render frame for anim triggering
|
|
||||||
while (0 < self.recentRenderCache.cnt && self.recentRenderCache.stFrameId < minToKeepRenderFrameId) {
|
|
||||||
self.recentRenderCache.pop();
|
|
||||||
}
|
|
||||||
const [ret, oldStFrameId, oldEdFrameId] = self.recentRenderCache.setByFrameId(rdf, rdf.id);
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
|
|
||||||
dumpToInputCache: function(inputFrameDownsync) {
|
|
||||||
const self = this;
|
|
||||||
let minToKeepInputFrameId = self._convertToInputFrameId(self.lastAllConfirmedRenderFrameId, self.inputDelayFrames) - self.spAtkLookupFrames; // [WARNING] This could be different from "self.lastAllConfirmedInputFrameId". We'd like to keep the corresponding delayedInputFrame for "self.lastAllConfirmedRenderFrameId" such that a rollback could place "self.chaserRenderFrameId = self.lastAllConfirmedRenderFrameId" for the worst case incorrect prediction.
|
|
||||||
if (minToKeepInputFrameId > self.lastAllConfirmedInputFrameId) {
|
|
||||||
minToKeepInputFrameId = self.lastAllConfirmedInputFrameId;
|
|
||||||
}
|
|
||||||
while (0 < self.recentInputCache.cnt && self.recentInputCache.stFrameId < minToKeepInputFrameId) {
|
|
||||||
self.recentInputCache.pop();
|
|
||||||
}
|
|
||||||
const [ret, oldStFrameId, oldEdFrameId] = self.recentInputCache.setByFrameId(inputFrameDownsync, inputFrameDownsync.inputFrameId);
|
|
||||||
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)}`;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
|
|
||||||
_convertToInputFrameId(renderFrameId, inputDelayFrames) {
|
_convertToInputFrameId(renderFrameId, inputDelayFrames) {
|
||||||
if (renderFrameId < inputDelayFrames) return 0;
|
if (renderFrameId < inputDelayFrames) return 0;
|
||||||
return ((renderFrameId - inputDelayFrames) >> this.inputScaleFrames);
|
return ((renderFrameId - inputDelayFrames) >> this.inputScaleFrames);
|
||||||
@ -156,29 +130,31 @@ cc.Class({
|
|||||||
throw `noDelayInputFrameId=${inputFrameId} couldn't be generated: recentInputCache=${self._stringifyRecentInputCache(false)}`;
|
throw `noDelayInputFrameId=${inputFrameId} couldn't be generated: recentInputCache=${self._stringifyRecentInputCache(false)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let previousSelfInput = null,
|
||||||
|
currSelfInput = null;
|
||||||
const joinIndex = self.selfPlayerInfo.joinIndex;
|
const joinIndex = self.selfPlayerInfo.joinIndex;
|
||||||
const previousInputFrameDownsyncWithPrediction = self.getCachedInputFrameDownsyncWithPrediction(inputFrameId);
|
// [WARNING] The while-loop here handles a situation where the "resync rdf & accompaniedInputFrameDownsyncBatch" mismatched and we have to predict some "gap-inputFrames"!
|
||||||
const previousSelfInput = (null == previousInputFrameDownsyncWithPrediction ? null : previousInputFrameDownsyncWithPrediction.inputList[joinIndex - 1]);
|
while (self.recentInputCache.edFrameId <= inputFrameId) {
|
||||||
|
// TODO: find some kind of synchronization mechanism against "onInputFrameDownsyncBatch"!
|
||||||
|
const previousInputFrameDownsyncWithPrediction = self.getCachedInputFrameDownsyncWithPrediction(inputFrameId - 1);
|
||||||
|
previousSelfInput = (null == previousInputFrameDownsyncWithPrediction ? null : previousInputFrameDownsyncWithPrediction.inputList[joinIndex - 1]);
|
||||||
|
|
||||||
// If "forceConfirmation" is active on backend, we shouldn't override the already downsynced "inputFrameDownsync"s.
|
// If "forceConfirmation" is active on backend, there's a chance that the already downsynced "inputFrameDownsync"s are ahead of a locally generating inputFrameId, in this case we respect the downsynced one.
|
||||||
const existingInputFrame = self.recentInputCache.getByFrameId(inputFrameId);
|
const existingInputFrame = self.recentInputCache.getByFrameId(inputFrameId);
|
||||||
if (null != existingInputFrame && self._allConfirmed(existingInputFrame.confirmedList)) {
|
if (null != existingInputFrame && self._allConfirmed(existingInputFrame.confirmedList)) {
|
||||||
console.log(`noDelayInputFrameId=${inputFrameId} already exists in recentInputCache and is all-confirmed: recentInputCache=${self._stringifyRecentInputCache(false)}`);
|
console.log(`noDelayInputFrameId=${inputFrameId} already exists in recentInputCache and is all-confirmed: recentInputCache=${self._stringifyRecentInputCache(false)}`);
|
||||||
return [previousSelfInput, existingInputFrame.inputList[joinIndex - 1]];
|
return [previousSelfInput, existingInputFrame.inputList[joinIndex - 1]];
|
||||||
}
|
}
|
||||||
const prefabbedInputList = (null == previousInputFrameDownsyncWithPrediction ? new Array(self.playerRichInfoDict.size).fill(0) : previousInputFrameDownsyncWithPrediction.inputList.slice());
|
const prefabbedInputList = (null == previousInputFrameDownsyncWithPrediction ? new Array(self.playerRichInfoDict.size).fill(0) : previousInputFrameDownsyncWithPrediction.inputList.slice());
|
||||||
const currSelfInput = self.ctrl.getEncodedInput();
|
currSelfInput = self.ctrl.getEncodedInput();
|
||||||
prefabbedInputList[(joinIndex - 1)] = currSelfInput;
|
prefabbedInputList[(joinIndex - 1)] = currSelfInput;
|
||||||
const prefabbedInputFrameDownsync = window.pb.protos.InputFrameDownsync.create({
|
const prefabbedInputFrameDownsync = window.pb.protos.InputFrameDownsync.create({
|
||||||
inputFrameId: inputFrameId,
|
inputFrameId: self.recentInputCache.edFrameId,
|
||||||
inputList: prefabbedInputList,
|
inputList: prefabbedInputList,
|
||||||
confirmedList: (1 << (self.selfPlayerInfo.joinIndex - 1))
|
confirmedList: (1 << (self.selfPlayerInfo.joinIndex - 1))
|
||||||
});
|
});
|
||||||
|
|
||||||
self.dumpToInputCache(prefabbedInputFrameDownsync); // A prefabbed inputFrame, would certainly be adding a new inputFrame to the cache, because server only downsyncs "all-confirmed inputFrames"
|
self.recentInputCache.put(prefabbedInputFrameDownsync); // A prefabbed inputFrame, would certainly be adding a new inputFrame to the cache, because server only downsyncs "all-confirmed inputFrames"
|
||||||
|
|
||||||
if (inputFrameId >= self.recentInputCache.edFrameId) {
|
|
||||||
throw `noDelayInputFrameId=${inputFrameId} seems not properly dumped #1: recentInputCache=${self._stringifyRecentInputCache(false)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [previousSelfInput, currSelfInput];
|
return [previousSelfInput, currSelfInput];
|
||||||
@ -328,6 +304,8 @@ cc.Class({
|
|||||||
self.collisionBulletIndexPrefix = (1 << 15); // For tracking the movements of bullets
|
self.collisionBulletIndexPrefix = (1 << 15); // For tracking the movements of bullets
|
||||||
self.collisionSysMap = new Map();
|
self.collisionSysMap = new Map();
|
||||||
|
|
||||||
|
console.log(`collisionSys & collisionSysMap reset`);
|
||||||
|
|
||||||
self.transitToState(ALL_MAP_STATES.VISUAL);
|
self.transitToState(ALL_MAP_STATES.VISUAL);
|
||||||
|
|
||||||
self.battleState = ALL_BATTLE_STATES.WAITING;
|
self.battleState = ALL_BATTLE_STATES.WAITING;
|
||||||
@ -389,8 +367,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;
|
||||||
@ -422,6 +399,7 @@ cc.Class({
|
|||||||
/** Init required prefab ended. */
|
/** Init required prefab ended. */
|
||||||
|
|
||||||
window.handleBattleColliderInfo = function(parsedBattleColliderInfo) {
|
window.handleBattleColliderInfo = function(parsedBattleColliderInfo) {
|
||||||
|
console.log(`Received parsedBattleColliderInfo via ws`);
|
||||||
// TODO: Upon reconnection, the backend might have already been sending down data that'd trigger "onRoomDownsyncFrame & onInputFrameDownsyncBatch", but frontend could reject those data due to "battleState != PlayerBattleState.ACTIVE".
|
// TODO: Upon reconnection, the backend might have already been sending down data that'd trigger "onRoomDownsyncFrame & onInputFrameDownsyncBatch", but frontend could reject those data due to "battleState != PlayerBattleState.ACTIVE".
|
||||||
Object.assign(self, parsedBattleColliderInfo);
|
Object.assign(self, parsedBattleColliderInfo);
|
||||||
self.tooFastDtIntervalMillis = 0.5 * self.rollbackEstimatedDtMillis;
|
self.tooFastDtIntervalMillis = 0.5 * self.rollbackEstimatedDtMillis;
|
||||||
@ -469,7 +447,7 @@ cc.Class({
|
|||||||
const x0 = boundaryObj.anchor.x,
|
const x0 = boundaryObj.anchor.x,
|
||||||
y0 = boundaryObj.anchor.y;
|
y0 = boundaryObj.anchor.y;
|
||||||
|
|
||||||
const newBarrier = self.collisionSys.createPolygon(x0, y0, Array.from(boundaryObj, p => {
|
const newBarrierCollider = self.collisionSys.createPolygon(x0, y0, Array.from(boundaryObj, p => {
|
||||||
return [p.x, p.y];
|
return [p.x, p.y];
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -504,10 +482,10 @@ cc.Class({
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// console.log("Created barrier: ", newBarrier);
|
|
||||||
++barrierIdCounter;
|
++barrierIdCounter;
|
||||||
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
|
const collisionBarrierIndex = (self.collisionBarrierIndexPrefix + barrierIdCounter);
|
||||||
self.collisionSysMap.set(collisionBarrierIndex, newBarrier);
|
self.collisionSysMap.set(collisionBarrierIndex, newBarrierCollider);
|
||||||
|
console.log(`Created new barrier collider: ${newBarrierCollider}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
|
self.selfPlayerInfo = JSON.parse(cc.sys.localStorage.getItem('selfPlayer'));
|
||||||
@ -520,6 +498,7 @@ cc.Class({
|
|||||||
act: window.UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK,
|
act: window.UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK,
|
||||||
}).finish();
|
}).finish();
|
||||||
window.sendSafely(reqData);
|
window.sendSafely(reqData);
|
||||||
|
console.log(`Sent UPSYNC_MSG_ACT_PLAYER_COLLIDER_ACK via ws`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -594,7 +573,7 @@ cc.Class({
|
|||||||
const shouldForceDumping1 = (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START == rdf.id);
|
const shouldForceDumping1 = (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START == rdf.id);
|
||||||
const shouldForceDumping2 = (rdf.id > self.renderFrameId + self.renderFrameIdLagTolerance);
|
const shouldForceDumping2 = (rdf.id > self.renderFrameId + self.renderFrameIdLagTolerance);
|
||||||
|
|
||||||
const dumpRenderCacheRet = (shouldForceDumping1 || shouldForceDumping2) ? self.dumpToRenderCache(rdf) : window.RING_BUFF_CONSECUTIVE_SET;
|
const [dumpRenderCacheRet, oldStRenderFrameId, oldEdRenderFrameId] = (shouldForceDumping1 || shouldForceDumping2) ? 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}, lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, lastAllConfirmedInputFrameId=${self.lastAllConfirmedInputFrameId}; recentRenderCache=${self._stringifyRecentRenderCache(false)}, recentInputCache=${self._stringifyRecentInputCache(false)}`;
|
||||||
}
|
}
|
||||||
@ -625,6 +604,7 @@ cc.Class({
|
|||||||
if (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START == rdf.id) {
|
if (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START == rdf.id) {
|
||||||
console.log('On battle started! renderFrameId=', rdf.id);
|
console.log('On battle started! renderFrameId=', rdf.id);
|
||||||
} else {
|
} else {
|
||||||
|
self.hideFindingPlayersGUI(rdf);
|
||||||
self.onInputFrameDownsyncBatch(accompaniedInputFrameDownsyncBatch); // Important to do this step before setting IN_BATTLE
|
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}, @lastAllConfirmedRenderFrameId=${self.lastAllConfirmedRenderFrameId}, @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)}`);
|
||||||
}
|
}
|
||||||
@ -671,6 +651,7 @@ cc.Class({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onInputFrameDownsyncBatch(batch) {
|
onInputFrameDownsyncBatch(batch) {
|
||||||
|
// TODO: find some kind of synchronization mechanism against "_generateInputFrameUpsync"!
|
||||||
const self = this;
|
const self = this;
|
||||||
if (!self.recentInputCache) {
|
if (!self.recentInputCache) {
|
||||||
return;
|
return;
|
||||||
@ -698,7 +679,10 @@ cc.Class({
|
|||||||
}
|
}
|
||||||
// [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".
|
||||||
inputFrameDownsync.confirmedList = (1 << self.playerRichInfoDict.size) - 1;
|
inputFrameDownsync.confirmedList = (1 << self.playerRichInfoDict.size) - 1;
|
||||||
self.dumpToInputCache(inputFrameDownsync);
|
const [ret, oldStFrameId, oldEdFrameId] = self.recentInputCache.setByFrameId(inputFrameDownsync, inputFrameDownsync.inputFrameId);
|
||||||
|
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)}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null == firstPredictedYetIncorrectInputFrameId) return;
|
if (null == firstPredictedYetIncorrectInputFrameId) return;
|
||||||
@ -794,6 +778,8 @@ cc.Class({
|
|||||||
newPlayerCollider.data = playerDownsyncInfo;
|
newPlayerCollider.data = playerDownsyncInfo;
|
||||||
self.collisionSysMap.set(collisionPlayerIndex, newPlayerCollider);
|
self.collisionSysMap.set(collisionPlayerIndex, newPlayerCollider);
|
||||||
|
|
||||||
|
console.log(`Created new player collider: joinIndex=${joinIndex}, colliderRadius=${playerDownsyncInfo.colliderRadius}`);
|
||||||
|
|
||||||
safelyAddChild(self.node, newPlayerNode);
|
safelyAddChild(self.node, newPlayerNode);
|
||||||
setLocalZOrder(newPlayerNode, 5);
|
setLocalZOrder(newPlayerNode, 5);
|
||||||
|
|
||||||
@ -1266,7 +1252,7 @@ cc.Class({
|
|||||||
// Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic!
|
// Move the cursor "self.chaserRenderFrameId", keep in mind that "self.chaserRenderFrameId" is not monotonic!
|
||||||
self.chaserRenderFrameId = latestRdf.id;
|
self.chaserRenderFrameId = latestRdf.id;
|
||||||
}
|
}
|
||||||
self.dumpToRenderCache(latestRdf);
|
self.recentRenderCache.setByFrameId(latestRdf, latestRdf.id);
|
||||||
++i;
|
++i;
|
||||||
} while (i < renderFrameIdEd);
|
} while (i < renderFrameIdEd);
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ cc.Class({
|
|||||||
self.inputScaleFrames = 2;
|
self.inputScaleFrames = 2;
|
||||||
self.inputFrameUpsyncDelayTolerance = 2;
|
self.inputFrameUpsyncDelayTolerance = 2;
|
||||||
|
|
||||||
|
self.renderCacheSize = 1024;
|
||||||
self.rollbackEstimatedDt = 0.016667;
|
self.rollbackEstimatedDt = 0.016667;
|
||||||
self.rollbackEstimatedDtMillis = 16.667;
|
self.rollbackEstimatedDtMillis = 16.667;
|
||||||
self.rollbackEstimatedDtNanos = 16666666;
|
self.rollbackEstimatedDtNanos = 16666666;
|
||||||
|
@ -134,7 +134,7 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
clientSession.binaryType = 'arraybuffer'; // Make 'event.data' of 'onmessage' an "ArrayBuffer" instead of a "Blob"
|
clientSession.binaryType = 'arraybuffer'; // Make 'event.data' of 'onmessage' an "ArrayBuffer" instead of a "Blob"
|
||||||
|
|
||||||
clientSession.onopen = function(evt) {
|
clientSession.onopen = function(evt) {
|
||||||
console.log("The WS clientSession is opened. clientSession.id=", clientSession.id);
|
console.log("The WS clientSession is opened.");
|
||||||
window.clientSession = clientSession;
|
window.clientSession = clientSession;
|
||||||
if (null == onopenCb) return;
|
if (null == onopenCb) return;
|
||||||
onopenCb();
|
onopenCb();
|
||||||
@ -171,7 +171,6 @@ window.initPersistentSessionClient = function(onopenCb, expectedRoomId) {
|
|||||||
${JSON.stringify(resp, null, 2)}`);
|
${JSON.stringify(resp, null, 2)}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mapIns.hideFindingPlayersGUI(resp.rdf);
|
|
||||||
const inputFrameIdConsecutive = (resp.inputFrameDownsyncBatch[0].inputFrameId == mapIns.lastAllConfirmedInputFrameId + 1);
|
const inputFrameIdConsecutive = (resp.inputFrameDownsyncBatch[0].inputFrameId == mapIns.lastAllConfirmedInputFrameId + 1);
|
||||||
mapIns.onRoomDownsyncFrame(resp.rdf, resp.inputFrameDownsyncBatch);
|
mapIns.onRoomDownsyncFrame(resp.rdf, resp.inputFrameDownsyncBatch);
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user