Skip to content

Commit bdec7df

Browse files
authored
10 min
10 min
2 parents 1284dc5 + 4e566ab commit bdec7df

5 files changed

Lines changed: 75 additions & 20 deletions

File tree

interface/AvaloniaUI/ViewModels/PlaybackViewModel.cs

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,19 @@ public enum PlaybackState
1818
public partial class PlaybackViewModel : ViewModelBase
1919
{
2020
private const double FrameIntervalMs = 100;
21-
private const int MaximumReasonableLiveGameMilliseconds = 2 * 60 * 60 * 1000;
22-
private const int MaximumSingleLiveTimeJumpMilliseconds = 10 * 60 * 1000;
21+
private const int OfficialMatchDurationMilliseconds = 10 * 60 * 1000;
22+
private const int TimelineGameTimeGuardSlackMilliseconds = 5 * 60 * 1000;
23+
private const int MaximumReasonableTimelineGameMilliseconds =
24+
OfficialMatchDurationMilliseconds + TimelineGameTimeGuardSlackMilliseconds;
25+
private const int MaximumSingleTimelineTimeJumpMilliseconds =
26+
OfficialMatchDurationMilliseconds + TimelineGameTimeGuardSlackMilliseconds;
2327
private static readonly double[] PlaybackSpeeds = { 0.5, 1, 2, 4 };
2428
private readonly PlaybackReader _reader = new();
2529
private DispatcherTimer? _playbackTimer;
2630
private int _currentFrame;
2731
private int _totalFrames;
32+
private int _stablePlaybackGameTimeMs;
33+
private bool _hasStablePlaybackGameTime;
2834
private int _stableLiveGameTimeMs;
2935
private bool _hasStableLiveGameTime;
3036
private Action<MessageToClient>? _onMessageReceived;
@@ -71,6 +77,7 @@ public void LoadPlayback(string filePath)
7177
IsFileLoaded = true;
7278
_totalFrames = CountTotalFrames();
7379
_currentFrame = 0;
80+
ResetStablePlaybackGameTime();
7481
State = PlaybackState.Stopped;
7582
UpdatePlaybackProgress(null);
7683
}
@@ -80,6 +87,7 @@ public void LoadPlayback(string filePath)
8087
FilePath = string.Empty;
8188
_currentFrame = 0;
8289
_totalFrames = 0;
90+
ResetStablePlaybackGameTime();
8391
UpdatePlaybackProgress(null);
8492
System.Diagnostics.Debug.WriteLine($"加载回放失败:{ex.Message}");
8593
throw;
@@ -127,6 +135,7 @@ public void Stop()
127135
StopPlaybackTimer();
128136
_reader.Reset();
129137
_currentFrame = 0;
138+
ResetStablePlaybackGameTime();
130139
State = PlaybackState.Stopped;
131140
UpdatePlaybackProgress(null);
132141
}
@@ -173,11 +182,32 @@ private TimeSpan GetPlaybackInterval()
173182

174183
private void UpdatePlaybackProgress(MessageToClient? message)
175184
{
176-
int gameTimeMs = message?.AllMessage?.GameTime ?? (int)Math.Round(_currentFrame * FrameIntervalMs);
185+
int gameTimeMs = ResolveStablePlaybackGameTime(message, _currentFrame);
177186
PlaybackTimeText = FormatGameTime(gameTimeMs);
178187
FrameProgressText = IsFileLoaded ? $"帧 {_currentFrame}/{_totalFrames}" : "帧 0/0";
179188
}
180189

190+
private int ResolveStablePlaybackGameTime(MessageToClient? message, int frameIndex)
191+
{
192+
int estimatedGameTimeMs = EstimateTimelineGameTime(frameIndex);
193+
194+
if (message?.AllMessage == null)
195+
{
196+
return _hasStablePlaybackGameTime ? _stablePlaybackGameTimeMs : estimatedGameTimeMs;
197+
}
198+
199+
int rawGameTimeMs = message.AllMessage.GameTime;
200+
if (AcceptTimelineGameTime(rawGameTimeMs, message.GameState, _hasStablePlaybackGameTime, _stablePlaybackGameTimeMs))
201+
{
202+
_stablePlaybackGameTimeMs = _hasStablePlaybackGameTime
203+
? Math.Max(_stablePlaybackGameTimeMs, rawGameTimeMs)
204+
: rawGameTimeMs;
205+
_hasStablePlaybackGameTime = true;
206+
}
207+
208+
return _hasStablePlaybackGameTime ? _stablePlaybackGameTimeMs : estimatedGameTimeMs;
209+
}
210+
181211
public void UpdateLiveProgress(MessageToClient? message, int liveFrameCount)
182212
{
183213
int safeFrameCount = Math.Max(liveFrameCount, 0);
@@ -188,7 +218,7 @@ public void UpdateLiveProgress(MessageToClient? message, int liveFrameCount)
188218

189219
private int ResolveStableLiveGameTime(MessageToClient? message, int liveFrameCount)
190220
{
191-
int estimatedGameTimeMs = EstimateLiveGameTime(liveFrameCount);
221+
int estimatedGameTimeMs = EstimateTimelineGameTime(liveFrameCount);
192222

193223
if (message?.AllMessage == null)
194224
{
@@ -201,7 +231,7 @@ private int ResolveStableLiveGameTime(MessageToClient? message, int liveFrameCou
201231
}
202232

203233
int rawGameTimeMs = message.AllMessage.GameTime;
204-
if (AcceptLiveGameTime(rawGameTimeMs, message.GameState))
234+
if (AcceptTimelineGameTime(rawGameTimeMs, message.GameState, _hasStableLiveGameTime, _stableLiveGameTimeMs))
205235
{
206236
_stableLiveGameTimeMs = _hasStableLiveGameTime
207237
? Math.Max(_stableLiveGameTimeMs, rawGameTimeMs)
@@ -212,29 +242,41 @@ private int ResolveStableLiveGameTime(MessageToClient? message, int liveFrameCou
212242
return _hasStableLiveGameTime ? _stableLiveGameTimeMs : estimatedGameTimeMs;
213243
}
214244

215-
private bool AcceptLiveGameTime(int rawGameTimeMs, GameState gameState)
245+
private static bool AcceptTimelineGameTime(
246+
int rawGameTimeMs,
247+
GameState gameState,
248+
bool hasStableGameTime,
249+
int stableGameTimeMs)
216250
{
217-
if (rawGameTimeMs < 0 || rawGameTimeMs > MaximumReasonableLiveGameMilliseconds)
251+
if (rawGameTimeMs < 0 || rawGameTimeMs > MaximumReasonableTimelineGameMilliseconds)
218252
{
219253
return false;
220254
}
221255

222-
if (!_hasStableLiveGameTime)
256+
if (!hasStableGameTime)
223257
{
224258
return true;
225259
}
226260

227-
if (gameState == GameState.GameEnd && rawGameTimeMs > _stableLiveGameTimeMs + MaximumSingleLiveTimeJumpMilliseconds)
261+
if (gameState == GameState.GameEnd &&
262+
rawGameTimeMs > stableGameTimeMs + MaximumSingleTimelineTimeJumpMilliseconds)
228263
{
229264
return false;
230265
}
231266

232-
return rawGameTimeMs <= _stableLiveGameTimeMs + MaximumSingleLiveTimeJumpMilliseconds;
267+
return rawGameTimeMs <= stableGameTimeMs + MaximumSingleTimelineTimeJumpMilliseconds;
268+
}
269+
270+
private static int EstimateTimelineGameTime(int frameCount)
271+
{
272+
int estimatedGameTimeMs = (int)Math.Round(Math.Max(frameCount, 0) * FrameIntervalMs);
273+
return Math.Min(estimatedGameTimeMs, MaximumReasonableTimelineGameMilliseconds);
233274
}
234275

235-
private static int EstimateLiveGameTime(int liveFrameCount)
276+
private void ResetStablePlaybackGameTime()
236277
{
237-
return (int)Math.Round(Math.Max(liveFrameCount, 0) * FrameIntervalMs);
278+
_stablePlaybackGameTimeMs = 0;
279+
_hasStablePlaybackGameTime = false;
238280
}
239281

240282
private void ResetStableLiveGameTime()
@@ -250,6 +292,7 @@ private static string FormatGameTime(int gameTimeMs)
250292
gameTimeMs = 0;
251293
}
252294

295+
gameTimeMs = Math.Min(gameTimeMs, MaximumReasonableTimelineGameMilliseconds);
253296
var time = TimeSpan.FromMilliseconds(gameTimeMs);
254297
return time.TotalHours >= 1
255298
? $"{(int)time.TotalHours:00}:{time.Minutes:00}:{time.Seconds:00}"

interface/Unity/Assets/Scripts/Core/CoreParam.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,11 @@ public static class CoreParam
6969

7070
// Server GameEnd frames can report an overflowed timer after the match timer stops.
7171
// Keep the UI clock on the last sane live value instead of rendering 500+ minutes.
72-
private const int MaximumReasonableLiveGameMilliseconds = 2 * 60 * 60 * 1000;
73-
private const int MaximumSingleLiveTimeJumpMilliseconds = 10 * 60 * 1000;
72+
public const int OfficialMatchDurationMilliseconds = 10 * 60 * 1000;
73+
public const int GameTimeGuardSlackMilliseconds = 5 * 60 * 1000;
74+
public const int MaximumDisplayGameMilliseconds =
75+
OfficialMatchDurationMilliseconds + GameTimeGuardSlackMilliseconds;
76+
private const int MaximumSingleLiveTimeJumpMilliseconds = MaximumDisplayGameMilliseconds;
7477
private static bool hasStableLiveGameTime;
7578

7679
public static Dictionary<long, MessageOfCharacter> characters = new Dictionary<long, MessageOfCharacter>();
@@ -147,7 +150,7 @@ private static void UpdateStableLiveGameTime(MessageOfAll message, GameState sta
147150
}
148151

149152
int rawTime = message.GameTime;
150-
bool rawLooksValid = rawTime >= 0 && rawTime <= MaximumReasonableLiveGameMilliseconds;
153+
bool rawLooksValid = rawTime >= 0 && rawTime <= MaximumDisplayGameMilliseconds;
151154
if (!rawLooksValid)
152155
{
153156
return;
@@ -173,6 +176,11 @@ private static void UpdateStableLiveGameTime(MessageOfAll message, GameState sta
173176
stableLiveGameMilliseconds = Math.Max(rawTime, stableLiveGameMilliseconds);
174177
}
175178

179+
public static int ClampDisplayGameMilliseconds(int totalMilliseconds)
180+
{
181+
return Math.Max(0, Math.Min(totalMilliseconds, MaximumDisplayGameMilliseconds));
182+
}
183+
176184
private static void DestroyAll<TKey>(Dictionary<TKey, GameObject> objects)
177185
{
178186
foreach (var kvp in objects)

interface/Unity/Assets/Scripts/Playback/PlaybackController.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -617,13 +617,17 @@ private int GetElapsedPlaybackMilliseconds(MessageToClient frame, int frameIndex
617617
if (firstFrameGameTimeMs >= 0 && gameTimeMs >= firstFrameGameTimeMs)
618618
{
619619
int elapsed = gameTimeMs - firstFrameGameTimeMs;
620-
int fallbackElapsed = Mathf.RoundToInt(Mathf.Max(frameIndex, 0) * PlayBackConstant.SERVER_FRAME_INTERVAL_MS);
620+
int fallbackElapsed = CoreParam.ClampDisplayGameMilliseconds(
621+
Mathf.RoundToInt(Mathf.Max(frameIndex, 0) * PlayBackConstant.SERVER_FRAME_INTERVAL_MS));
621622
int reasonableUpperBound = Mathf.RoundToInt((Mathf.Max(frameIndex, 0) + 1) * PlayBackConstant.MAX_REASONABLE_FRAME_DELTA_MS);
622623

623-
return elapsed <= reasonableUpperBound ? elapsed : fallbackElapsed;
624+
return elapsed <= reasonableUpperBound && elapsed <= CoreParam.MaximumDisplayGameMilliseconds
625+
? elapsed
626+
: fallbackElapsed;
624627
}
625628

626-
return Mathf.RoundToInt(Mathf.Max(frameIndex, 0) * PlayBackConstant.SERVER_FRAME_INTERVAL_MS);
629+
return CoreParam.ClampDisplayGameMilliseconds(
630+
Mathf.RoundToInt(Mathf.Max(frameIndex, 0) * PlayBackConstant.SERVER_FRAME_INTERVAL_MS));
627631
}
628632

629633
private static int GetFrameGameTimeMs(MessageToClient frame)

interface/Unity/Assets/Scripts/Render/RenderManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1449,7 +1449,7 @@ private static Vector3 GetCompactUnitScale(CharacterType type)
14491449

14501450
private static string FormatPlaybackTime(int totalMilliseconds)
14511451
{
1452-
totalMilliseconds = Mathf.Max(totalMilliseconds, 0);
1452+
totalMilliseconds = CoreParam.ClampDisplayGameMilliseconds(totalMilliseconds);
14531453
int minutes = totalMilliseconds / 60000;
14541454
int seconds = totalMilliseconds / 1000 % 60;
14551455
return $"{minutes:D2}:{seconds:D2}";

interface/Unity/Assets/Scripts/UI/UIController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ private static int GetCurrentLiveGameMilliseconds()
384384

385385
private static string FormatPlaybackTime(int totalMilliseconds)
386386
{
387-
totalMilliseconds = Mathf.Max(totalMilliseconds, 0);
387+
totalMilliseconds = CoreParam.ClampDisplayGameMilliseconds(totalMilliseconds);
388388
int minutes = totalMilliseconds / 60000;
389389
int seconds = totalMilliseconds / 1000 % 60;
390390
return $"{minutes:D2}:{seconds:D2}";

0 commit comments

Comments
 (0)