Working on reduction of resync received in frontend.

This commit is contained in:
yflu 2022-11-29 12:49:49 +08:00
parent 9469b27348
commit 080a384ade
4 changed files with 33 additions and 26 deletions

View File

@ -154,7 +154,7 @@ type Room struct {
State int32 State int32
Index int Index int
RenderFrameId int32 RenderFrameId int32
CurDynamicsRenderFrameId int32 // [WARNING] The dynamics of backend is ALWAYS MOVING FORWARD BY ALL-CONFIRMED INPUTFRAMES (either by upsync or forced), i.e. no rollback CurDynamicsRenderFrameId int32 // [WARNING] The dynamics of backend is ALWAYS MOVING FORWARD BY ALL-CONFIRMED INPUTFRAMES (either by upsync or forced), i.e. no rollback; Moreover when "true == BackendDynamicsEnabled" we always have "Room.CurDynamicsRenderFrameId >= Room.RenderFrameId" because each "all-confirmed inputFrame" is applied on "all applicable renderFrames" in one-go hence often sees a future "renderFrame" earlier
EffectivePlayerCount int32 EffectivePlayerCount int32
DismissalWaitGroup sync.WaitGroup DismissalWaitGroup sync.WaitGroup
Barriers map[int32]*Barrier Barriers map[int32]*Barrier
@ -166,9 +166,10 @@ type Room struct {
LastAllConfirmedInputList []uint64 LastAllConfirmedInputList []uint64
JoinIndexBooleanArr []bool JoinIndexBooleanArr []bool
BackendDynamicsEnabled bool BackendDynamicsEnabled bool
LastRenderFrameIdTriggeredAt int64 BackendDynamicsForceConfirmationEnabled bool
PlayerDefaultSpeed int32 LastRenderFrameIdTriggeredAt int64
PlayerDefaultSpeed int32
BulletBattleLocalIdCounter int32 BulletBattleLocalIdCounter int32
dilutedRollbackEstimatedDtNanos int64 dilutedRollbackEstimatedDtNanos int64
@ -334,6 +335,10 @@ func (pR *Room) ConvertToGeneratingRenderFrameId(inputFrameId int32) int32 {
return (inputFrameId << pR.InputScaleFrames) return (inputFrameId << pR.InputScaleFrames)
} }
func (pR *Room) ConvertToJustBeforeNextGeneratingRenderFrameId(inputFrameId int32) int32 {
return (inputFrameId << pR.InputScaleFrames) + (1 << pR.InputScaleFrames) - 1
}
func (pR *Room) ConvertToFirstUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 { func (pR *Room) ConvertToFirstUsedRenderFrameId(inputFrameId int32, inputDelayFrames int32) int32 {
return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames) return ((inputFrameId << pR.InputScaleFrames) + inputDelayFrames)
} }
@ -408,6 +413,7 @@ func (pR *Room) StartBattle() {
pR.onBattleStoppedForSettlement() pR.onBattleStoppedForSettlement()
}() }()
battleStartedAtNanos := utils.UnixtimeNano()
pR.LastRenderFrameIdTriggeredAt = utils.UnixtimeNano() pR.LastRenderFrameIdTriggeredAt = utils.UnixtimeNano()
Logger.Info("The `battleMainLoop` is started for:", zap.Any("roomId", pR.Id)) Logger.Info("The `battleMainLoop` is started for:", zap.Any("roomId", pR.Id))
@ -416,7 +422,10 @@ func (pR *Room) StartBattle() {
elapsedNanosSinceLastFrameIdTriggered := stCalculation - pR.LastRenderFrameIdTriggeredAt elapsedNanosSinceLastFrameIdTriggered := stCalculation - pR.LastRenderFrameIdTriggeredAt
if elapsedNanosSinceLastFrameIdTriggered < pR.dilutedRollbackEstimatedDtNanos { if elapsedNanosSinceLastFrameIdTriggered < pR.dilutedRollbackEstimatedDtNanos {
Logger.Debug(fmt.Sprintf("Avoiding too fast frame@roomId=%v, renderFrameId=%v: elapsedNanosSinceLastFrameIdTriggered=%v", pR.Id, pR.RenderFrameId, elapsedNanosSinceLastFrameIdTriggered)) totalElapsedNanos := (stCalculation - battleStartedAtNanos)
serverFpsByFar := float64(pR.RenderFrameId) * float64(1000000000) / float64(totalElapsedNanos)
Logger.Info(fmt.Sprintf("Avoiding too fast frame@roomId=%v, renderFrameId=%v, totalElapsedNanos=%v, serverFpsByFar=%v: elapsedNanosSinceLastFrameIdTriggered=%v", pR.Id, pR.RenderFrameId, totalElapsedNanos, serverFpsByFar, elapsedNanosSinceLastFrameIdTriggered))
time.Sleep(time.Duration(pR.dilutedRollbackEstimatedDtNanos - elapsedNanosSinceLastFrameIdTriggered))
continue continue
} }
@ -438,7 +447,7 @@ func (pR *Room) StartBattle() {
pR.markConfirmationIfApplicable() pR.markConfirmationIfApplicable()
unconfirmedMask := uint64(0) unconfirmedMask := uint64(0)
if pR.BackendDynamicsEnabled { if pR.BackendDynamicsForceConfirmationEnabled {
// Force setting all-confirmed of buffered inputFrames periodically // Force setting all-confirmed of buffered inputFrames periodically
unconfirmedMask = pR.forceConfirmationIfApplicable() unconfirmedMask = pR.forceConfirmationIfApplicable()
} }
@ -452,10 +461,7 @@ func (pR *Room) StartBattle() {
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 << pR.InputScaleFrames) - 1 refRenderFrameId := pR.ConvertToJustBeforeNextGeneratingRenderFrameId(upperToSendInputFrameId)
if refRenderFrameId > pR.RenderFrameId {
refRenderFrameId = pR.RenderFrameId
}
dynamicsDuration := int64(0) dynamicsDuration := int64(0)
if pR.BackendDynamicsEnabled { if pR.BackendDynamicsEnabled {
@ -467,11 +473,10 @@ func (pR *Room) StartBattle() {
pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY) pR.applyInputFrameDownsyncDynamics(pR.CurDynamicsRenderFrameId, nextDynamicsRenderFrameId, pR.collisionSpaceOffsetX, pR.collisionSpaceOffsetY)
dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt dynamicsDuration = utils.UnixtimeNano() - dynamicsStartedAt
} }
}
// [WARNING] The following inequality are seldom true, but just to avoid that in good network condition the frontend resyncs itself to a "too advanced frontend.renderFrameId", and then starts upsyncing "too advanced inputFrameId". if refRenderFrameId > pR.RenderFrameId {
if refRenderFrameId > pR.CurDynamicsRenderFrameId { refRenderFrameId = pR.RenderFrameId
refRenderFrameId = pR.CurDynamicsRenderFrameId
}
} }
for playerId, player := range pR.Players { for playerId, player := range pR.Players {
@ -576,7 +581,8 @@ func (pR *Room) StartBattle() {
} }
pR.RenderFrameId++ pR.RenderFrameId++
elapsedInCalculation := (utils.UnixtimeNano() - stCalculation) pR.LastRenderFrameIdTriggeredAt = utils.UnixtimeNano()
elapsedInCalculation := (pR.LastRenderFrameIdTriggeredAt - stCalculation)
if elapsedInCalculation > pR.dilutedRollbackEstimatedDtNanos { if elapsedInCalculation > pR.dilutedRollbackEstimatedDtNanos {
Logger.Warn(fmt.Sprintf("SLOW FRAME! Elapsed time statistics: roomId=%v, room.RenderFrameId=%v, elapsedInCalculation=%v ns, dynamicsDuration=%v ns, dilutedRollbackEstimatedDtNanos=%v", pR.Id, pR.RenderFrameId, elapsedInCalculation, dynamicsDuration, pR.dilutedRollbackEstimatedDtNanos)) Logger.Warn(fmt.Sprintf("SLOW FRAME! Elapsed time statistics: roomId=%v, room.RenderFrameId=%v, elapsedInCalculation=%v ns, dynamicsDuration=%v ns, dilutedRollbackEstimatedDtNanos=%v", pR.Id, pR.RenderFrameId, elapsedInCalculation, dynamicsDuration, pR.dilutedRollbackEstimatedDtNanos))
} }
@ -792,7 +798,7 @@ func (pR *Room) OnDismissed() {
pR.RenderFrameId = 0 pR.RenderFrameId = 0
pR.CurDynamicsRenderFrameId = 0 pR.CurDynamicsRenderFrameId = 0
pR.InputDelayFrames = 8 pR.InputDelayFrames = 8
pR.NstDelayFrames = 4 pR.NstDelayFrames = pR.InputDelayFrames
pR.InputScaleFrames = uint32(2) pR.InputScaleFrames = uint32(2)
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
@ -805,6 +811,7 @@ func (pR *Room) OnDismissed() {
pR.MaxChasingRenderFramesPerUpdate = 5 pR.MaxChasingRenderFramesPerUpdate = 5
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.BackendDynamicsForceConfirmationEnabled = (pR.BackendDynamicsEnabled && true)
punchSkillId := int32(1) punchSkillId := int32(1)
pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0) pR.MeleeSkillConfig = make(map[int32]*MeleeBullet, 0)
pR.MeleeSkillConfig[punchSkillId] = &MeleeBullet{ pR.MeleeSkillConfig[punchSkillId] = &MeleeBullet{

View File

@ -440,7 +440,7 @@
"array": [ "array": [
0, 0,
0, 0,
210.5241291124452, 342.9460598986377,
0, 0,
0, 0,
0, 0,

View File

@ -454,7 +454,7 @@
"array": [ "array": [
0, 0,
0, 0,
210.5241291124452, 342.9460598986377,
0, 0,
0, 0,
0, 0,

View File

@ -82,21 +82,21 @@ cc.Class({
// It turns out that "prevRdfPlayer.characterState" is not useful in this function :) // It turns out that "prevRdfPlayer.characterState" is not useful in this function :)
if (newAnimName == playingAnimName && window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(newCharacterState)) { if (newAnimName == playingAnimName && window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(newCharacterState)) {
// No need to interrupt // No need to interrupt
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, not interrupting ${newAnimName} while the playing anim is also ${playingAnimName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, , to: ${JSON.stringify(rdfPlayer)}`); // console.warn(`JoinIndex=${rdfPlayer.joinIndex}, not interrupting ${newAnimName} while the playing anim is also ${playingAnimName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, to: ${JSON.stringify(rdfPlayer)}`);
return; return;
} }
if (this.animComp instanceof dragonBones.ArmatureDisplay) { if (this.animComp instanceof dragonBones.ArmatureDisplay) {
this._interruptPlayingAnimAndPlayNewAnimDragonBones(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl); this._interruptPlayingAnimAndPlayNewAnimDragonBones(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl, playingAnimName);
} else { } else {
this._interruptPlayingAnimAndPlayNewAnimFrameAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName); this._interruptPlayingAnimAndPlayNewAnimFrameAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, playingAnimName);
} }
}, },
_interruptPlayingAnimAndPlayNewAnimDragonBones(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl) { _interruptPlayingAnimAndPlayNewAnimDragonBones(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, underlyingAnimationCtrl, playingAnimName) {
if (ATK_CHARACTER_STATE.Idle1[0] == newCharacterState || ATK_CHARACTER_STATE.Walking[0] == newCharacterState) { if (window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(newCharacterState)) {
// No "framesToRecover" // No "framesToRecover"
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, playing new ${newAnimName} from the beginning: while the playing anim is ${playAnimation}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, , to: ${JSON.stringify(rdfPlayer)}`); console.warn(`#DragonBones JoinIndex=${rdfPlayer.joinIndex}, playing new ${newAnimName} from the beginning: while the playing anim is ${playingAnimName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, to: ${JSON.stringify(rdfPlayer)}`);
underlyingAnimationCtrl.gotoAndPlayByFrame(newAnimName, 0, -1); underlyingAnimationCtrl.gotoAndPlayByFrame(newAnimName, 0, -1);
} else { } else {
const animationData = underlyingAnimationCtrl._animations[newAnimName]; const animationData = underlyingAnimationCtrl._animations[newAnimName];
@ -109,10 +109,10 @@ cc.Class({
} }
}, },
_interruptPlayingAnimAndPlayNewAnimFrameAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName) { _interruptPlayingAnimAndPlayNewAnimFrameAnim(rdfPlayer, prevRdfPlayer, newCharacterState, newAnimName, playingAnimName) {
if (window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(newCharacterState)) { if (window.ATK_CHARACTER_STATE_INTERRUPT_WAIVE_SET.has(newCharacterState)) {
// No "framesToRecover" // No "framesToRecover"
// console.warn(`JoinIndex=${rdfPlayer.joinIndex}, playing new ${newAnimName} from the beginning: while the playing anim is ${playAnimation}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, , to: ${JSON.stringify(rdfPlayer)}`); console.warn(`#FrameAnim JoinIndex=${rdfPlayer.joinIndex}, playing new ${newAnimName} from the beginning: while the playing anim is ${playingAnimName}, player rdf changed from: ${null == prevRdfPlayer ? null : JSON.stringify(prevRdfPlayer)}, to: ${JSON.stringify(rdfPlayer)}`);
this.animComp.play(newAnimName, 0); this.animComp.play(newAnimName, 0);
return; return;
} }