mirror of
https://github.com/genxium/DelayNoMore
synced 2024-12-25 11:18:55 +00:00
Further enhanced backend dynamics force confirmation trigger.
This commit is contained in:
parent
4e7c3060fe
commit
34c4a24b64
@ -742,12 +742,12 @@ func (pR *Room) OnDismissed() {
|
||||
pR.RenderFrameId = 0
|
||||
pR.CurDynamicsRenderFrameId = 0
|
||||
pR.InputDelayFrames = 8
|
||||
pR.NstDelayFrames = 8
|
||||
pR.NstDelayFrames = 12
|
||||
pR.InputScaleFrames = uint32(2)
|
||||
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.RollbackEstimatedDtNanos = 16666666 // A little smaller than the actual per frame time, just for logging FAST FRAME
|
||||
dilutedServerFps := float64(58.0) // Don't set this value too small, otherwise we might miss force confirmation needs for slow tickers!
|
||||
dilutedServerFps := float64(57.0) // Don't set this value too small, otherwise we might miss force confirmation needs for slow tickers!
|
||||
pR.dilutedRollbackEstimatedDtNanos = int64(float64(pR.RollbackEstimatedDtNanos) * float64(pR.ServerFps) / dilutedServerFps)
|
||||
pR.BattleDurationFrames = 90 * pR.ServerFps
|
||||
pR.BattleDurationNanos = int64(pR.BattleDurationFrames) * (pR.RollbackEstimatedDtNanos + 1)
|
||||
@ -1087,7 +1087,7 @@ func (pR *Room) markConfirmationIfApplicable(inputFrameUpsyncBatch []*InputFrame
|
||||
continue
|
||||
}
|
||||
if clientInputFrameId < pR.LastAllConfirmedInputFrameId {
|
||||
Logger.Info(fmt.Sprintf("Omitting obsolete inputFrameUpsync#2: roomId=%v, playerId=%v, clientInputFrameId=%v, InputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.InputsBufferString(false)))
|
||||
Logger.Debug(fmt.Sprintf("Omitting obsolete inputFrameUpsync#2: roomId=%v, playerId=%v, clientInputFrameId=%v, InputsBuffer=%v", pR.Id, playerId, clientInputFrameId, pR.InputsBufferString(false)))
|
||||
continue
|
||||
}
|
||||
if clientInputFrameId > pR.InputsBuffer.EdFrameId {
|
||||
@ -1171,78 +1171,61 @@ func (pR *Room) markConfirmationIfApplicable(inputFrameUpsyncBatch []*InputFrame
|
||||
|
||||
func (pR *Room) forceConfirmationIfApplicable(prevRenderFrameId int32) *InputsBufferSnapshot {
|
||||
// [WARNING] This function MUST BE called while "pR.InputsBufferLock" is locked!
|
||||
// Force confirmation of non-all-confirmed inputFrame EXACTLY ONE AT A TIME, returns the non-confirmed mask of players, e.g. in a 4-player-battle returning 1001 means that players with JoinIndex=1 and JoinIndex=4 are non-confirmed for inputFrameId2
|
||||
|
||||
inputFrameId2 := int32(-1)
|
||||
totPlayerCnt := uint32(pR.Capacity)
|
||||
allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
|
||||
var inputsBufferSnapshot *InputsBufferSnapshot = nil
|
||||
if pR.LatestPlayerUpsyncedInputFrameId > (pR.LastAllConfirmedInputFrameId + (pR.NstDelayFrames >> pR.InputScaleFrames)) {
|
||||
// Type#1 check whether there's a significantly slow ticker among players
|
||||
Logger.Warn(fmt.Sprintf("[type#1 forceConfirmation]For roomId=%d@renderFrameId=%d, curDynamicsRenderFrameId=%d, LatestPlayerUpsyncedInputFrameId:%d, LastAllConfirmedInputFrameId:%d, (pR.NstDelayFrames >> pR.InputScaleFrames):%d; there's a slow ticker suspect, forcing all-confirmation", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LatestPlayerUpsyncedInputFrameId, pR.LastAllConfirmedInputFrameId, (pR.NstDelayFrames >> pR.InputScaleFrames)))
|
||||
inputFrameId2 = pR.LastAllConfirmedInputFrameId + 1
|
||||
} else {
|
||||
// Type#2 check whether there's a significantly slow ticker w.r.t. BackendDynamics, this applies when all players are disconnected temporarily
|
||||
renderFrameId1 := (pR.RenderFrameId - pR.NstDelayFrames) // the "renderFrameId" which should've been rendered on frontend
|
||||
if 0 > renderFrameId1 {
|
||||
// Battle is still in an early stage, no action needed even if there were slow tickers
|
||||
for j := pR.LastAllConfirmedInputFrameId + 1; j <= pR.LatestPlayerUpsyncedInputFrameId; j++ {
|
||||
tmp := pR.InputsBuffer.GetByFrameId(j)
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("inputFrameId=%v doesn't exist for roomId=%v! InputsBuffer=%v", j, pR.Id, pR.InputsBufferString(false)))
|
||||
}
|
||||
inputFrameDownsync := tmp.(*InputFrameDownsync)
|
||||
pR.onInputFrameDownsyncAllConfirmed(inputFrameDownsync, -1)
|
||||
}
|
||||
refRenderFrameIdIfNeeded := pR.CurDynamicsRenderFrameId - 1
|
||||
if 0 > refRenderFrameIdIfNeeded {
|
||||
return nil
|
||||
}
|
||||
if ok, renderFrameId2 := pR.shouldPrefabInputFrameDownsync(prevRenderFrameId-pR.NstDelayFrames, renderFrameId1); ok {
|
||||
/*
|
||||
The backend "shouldPrefabInputFrameDownsync" shares the same rule as frontend "shouldGenerateInputFrameUpsync".
|
||||
Logger.Warn(fmt.Sprintf("[type#1 forceConfirmation] For roomId=%d@renderFrameId=%d, curDynamicsRenderFrameId=%d, LatestPlayerUpsyncedInputFrameId:%d, LastAllConfirmedInputFrameId:%d, (pR.NstDelayFrames >> pR.InputScaleFrames):%d; there's a slow ticker suspect, forcing all-confirmation", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LatestPlayerUpsyncedInputFrameId, pR.LastAllConfirmedInputFrameId, (pR.NstDelayFrames >> pR.InputScaleFrames)))
|
||||
snapshotStFrameId := pR.ConvertToInputFrameId(refRenderFrameIdIfNeeded, pR.InputDelayFrames)
|
||||
// Duplicate downsynced inputFrameIds will be filtered out by frontend.
|
||||
toSendInputFrameDownsyncs := pR.cloneInputsBuffer(snapshotStFrameId, pR.LatestPlayerUpsyncedInputFrameId+1)
|
||||
|
||||
It's also important that "forceConfirmationIfApplicable" is NOT EXECUTED for every renderFrame, such that when a player is forced to resync, it has some time, i.e. (1 << InputScaleFrames) renderFrames, to upsync again.
|
||||
*/
|
||||
inputFrameId2 = pR.ConvertToInputFrameId(renderFrameId2, 0) // The inputFrame to force confirmation (if necessary)
|
||||
if inputFrameId2 > pR.LastAllConfirmedInputFrameId {
|
||||
Logger.Debug(fmt.Sprintf("[type#2 forceConfirmation]For roomId=%d@renderFrameId=%d, curDynamicsRenderFrameId=%d, renderFrameId1:%d, renderFrameId2:%d, NstDelayFrames:%d, inputFrameId2:%d, LastAllConfirmedInputFrameId:%d; there's a slow ticker suspect, forcing all-confirmation", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, renderFrameId1, renderFrameId2, pR.NstDelayFrames, inputFrameId2, pR.LastAllConfirmedInputFrameId))
|
||||
inputsBufferSnapshot = &InputsBufferSnapshot{
|
||||
RefRenderFrameId: refRenderFrameIdIfNeeded,
|
||||
UnconfirmedMask: allConfirmedMask, // Will force resync all players in the same battle.
|
||||
ToSendInputFrameDownsyncs: toSendInputFrameDownsyncs,
|
||||
}
|
||||
} else {
|
||||
// Type#2 helps resolve the edge case when all players are disconnected temporarily
|
||||
shouldForceResync := false
|
||||
for _, player := range pR.PlayersArr {
|
||||
playerBattleState := atomic.LoadInt32(&(player.BattleState))
|
||||
if PlayerBattleStateIns.READDED_BATTLE_COLLIDER_ACKED == playerBattleState {
|
||||
shouldForceResync = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if shouldForceResync {
|
||||
refRenderFrameIdIfNeeded := pR.CurDynamicsRenderFrameId - 1
|
||||
if 0 > refRenderFrameIdIfNeeded {
|
||||
return nil
|
||||
}
|
||||
Logger.Warn(fmt.Sprintf("[type#2 forceConfirmation] For roomId=%d@renderFrameId=%d, curDynamicsRenderFrameId=%d, LatestPlayerUpsyncedInputFrameId:%d, LastAllConfirmedInputFrameId:%d; there's at least one reconnected player, forcing all-confirmation", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LatestPlayerUpsyncedInputFrameId, pR.LastAllConfirmedInputFrameId))
|
||||
snapshotStFrameId := pR.ConvertToInputFrameId(refRenderFrameIdIfNeeded, pR.InputDelayFrames)
|
||||
// Duplicate downsynced inputFrameIds will be filtered out by frontend.
|
||||
toSendInputFrameDownsyncs := pR.cloneInputsBuffer(snapshotStFrameId, pR.LatestPlayerUpsyncedInputFrameId+1)
|
||||
inputsBufferSnapshot = &InputsBufferSnapshot{
|
||||
RefRenderFrameId: refRenderFrameIdIfNeeded,
|
||||
UnconfirmedMask: allConfirmedMask, // Will force resync all players in the same battle.
|
||||
ToSendInputFrameDownsyncs: toSendInputFrameDownsyncs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pR.LastAllConfirmedInputFrameId >= inputFrameId2 {
|
||||
// No need to force confirmation for either type
|
||||
Logger.Debug(fmt.Sprintf("inputFrameId2=%v is already all-confirmed for roomId=%v, no need to force confirmation", inputFrameId2, pR.Id))
|
||||
return nil
|
||||
}
|
||||
|
||||
tmp := pR.InputsBuffer.GetByFrameId(inputFrameId2)
|
||||
if nil == tmp {
|
||||
panic(fmt.Sprintf("For roomId=%d, inputFrameId2=%v doesn't exist, this is abnormal because the server should prefab inputFrameDownsync in a most advanced pace, check the prefab logic! InputsBuffer=%v", pR.Id, inputFrameId2, pR.InputsBufferString(false)))
|
||||
}
|
||||
|
||||
totPlayerCnt := uint32(pR.Capacity)
|
||||
allConfirmedMask := uint64((1 << totPlayerCnt) - 1)
|
||||
|
||||
// Force confirmation of "inputFrame2"
|
||||
inputFrame2 := tmp.(*InputFrameDownsync)
|
||||
oldConfirmedList := inputFrame2.ConfirmedList
|
||||
unconfirmedMask := (oldConfirmedList ^ allConfirmedMask)
|
||||
inputFrame2.ConfirmedList = allConfirmedMask
|
||||
pR.onInputFrameDownsyncAllConfirmed(inputFrame2, -1)
|
||||
|
||||
if 0 < unconfirmedMask {
|
||||
// This condition should be rarely met!
|
||||
/*
|
||||
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.
|
||||
|
||||
Upon resync, it's still possible that "refRenderFrameId < frontend.chaserRenderFrameId" -- and this is allowed.
|
||||
*/
|
||||
refRenderFrameIdIfNeeded := pR.ConvertToLastUsedRenderFrameId(pR.LastAllConfirmedInputFrameId, pR.InputDelayFrames)
|
||||
if 0 > refRenderFrameIdIfNeeded {
|
||||
// Without a "refRenderFrame", there's no point to force confirmation, i.e. nothing to downsync to the "ACTIVE but slowly ticking frontend(s)"
|
||||
return nil
|
||||
}
|
||||
snapshotStFrameId := pR.ConvertToInputFrameId(refRenderFrameIdIfNeeded, pR.InputDelayFrames)
|
||||
toSendInputFrameDownsyncs := pR.cloneInputsBuffer(snapshotStFrameId, pR.LastAllConfirmedInputFrameId+1)
|
||||
return &InputsBufferSnapshot{
|
||||
RefRenderFrameId: refRenderFrameIdIfNeeded,
|
||||
UnconfirmedMask: unconfirmedMask,
|
||||
ToSendInputFrameDownsyncs: toSendInputFrameDownsyncs,
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
return inputsBufferSnapshot
|
||||
}
|
||||
|
||||
func (pR *Room) applyInputFrameDownsyncDynamics(fromRenderFrameId int32, toRenderFrameId int32, spaceOffsetX, spaceOffsetY float64) {
|
||||
@ -1652,7 +1635,7 @@ func (pR *Room) refreshColliders(spaceW, spaceH int32) {
|
||||
|
||||
topPadding, bottomPadding, leftPadding, rightPadding := pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap, pR.SnapIntoPlatformOverlap
|
||||
|
||||
minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 2) // the approx minimum distance a player can move per frame in world coordinate
|
||||
minStep := (int(float64(pR.PlayerDefaultSpeed)*pR.VirtualGridToWorldRatio) << 3) // the approx minimum distance a player can move per frame in world coordinate
|
||||
pR.Space = resolv.NewSpace(int(spaceW), int(spaceH), minStep, minStep) // allocate a new collision space everytime after a battle is settled
|
||||
for _, player := range pR.Players {
|
||||
wx, wy := VirtualGridToWorldPos(player.VirtualGridX, player.VirtualGridY, pR.VirtualGridToWorldRatio)
|
||||
@ -1709,7 +1692,7 @@ func (pR *Room) doBattleMainLoopPerTickBackendDynamicsWithProperLocking(prevRend
|
||||
}
|
||||
|
||||
if nil != inputsBufferSnapshot {
|
||||
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, inputsBufferSnapshot.UnconfirmedMask))
|
||||
Logger.Debug(fmt.Sprintf("[forceConfirmation] roomId=%v, room.RenderFrameId=%v, room.CurDynamicsRenderFrameId=%v, room.LastAllConfirmedInputFrameId=%v, unconfirmedMask=%v", pR.Id, pR.RenderFrameId, pR.CurDynamicsRenderFrameId, pR.LastAllConfirmedInputFrameId, inputsBufferSnapshot.UnconfirmedMask))
|
||||
pR.downsyncToAllPlayers(inputsBufferSnapshot)
|
||||
}
|
||||
}
|
||||
|
@ -440,7 +440,7 @@
|
||||
"array": [
|
||||
0,
|
||||
0,
|
||||
215.64032554232523,
|
||||
209.73151519075364,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
@ -614,7 +614,7 @@ cc.Class({
|
||||
}
|
||||
}
|
||||
|
||||
if (null == self.renderFrameId || self.renderFrameId <= rdf.id) {
|
||||
if (null == self.renderFrameId || self.renderFrameId <= rdf.id || shouldForceResync) {
|
||||
// In fact, not having "window.RING_BUFF_CONSECUTIVE_SET == dumpRenderCacheRet" should already imply that "self.renderFrameId <= rdf.id", but here we double check and log the anomaly
|
||||
|
||||
if (window.MAGIC_ROOM_DOWNSYNC_FRAME_ID.BATTLE_START == rdf.id) {
|
||||
|
Loading…
Reference in New Issue
Block a user