app.hypergames.hypersdk 1.11.0-beta.5 → 1.11.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.11.0-beta.7](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.6...v1.11.0-beta.7) (2026-08-30)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **replay:** stabilize scene reload seeking ([dd9ac63](https://github.com/mvmhyper/hyper-sdk/commit/dd9ac632d08349d9578f64ae0b2dc2ab905a26af))
7
+
8
+ # [1.11.0-beta.6](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.5...v1.11.0-beta.6) (2026-08-29)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **timer:** restore gameplay timers after pause ([ca82d57](https://github.com/mvmhyper/hyper-sdk/commit/ca82d57df87618807cbae611c0a9d6acc14f8470))
14
+
1
15
  # [1.11.0-beta.5](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.4...v1.11.0-beta.5) (2026-08-28)
2
16
 
3
17
 
@@ -123,8 +123,7 @@ namespace Hyper.Internal
123
123
  }
124
124
 
125
125
  /// <summary>
126
- /// Restarts replay playback while preserving its downloaded data and original seed.
127
- /// Uses the configured custom restart flow when available, otherwise reloads HyperLoader.
126
+ /// Restarts replay playback through HyperLoader while preserving replay session data.
128
127
  /// </summary>
129
128
  internal static void RestartReplaySession()
130
129
  {
@@ -146,17 +145,9 @@ namespace Hyper.Internal
146
145
  _isSoftRestarting = true;
147
146
  Time.timeScale = 1f;
148
147
 
149
- if (_settings != null && _settings.useCustomRestartFlow &&
150
- _gameManager != null && _eventManager != null)
151
- {
152
- _replayCustomRestartPending = true;
153
- _replayManager?.PrepareForRestart();
154
- // Custom cleanup may use random streams. Rewind after cleanup, immediately before OnGameStart.
155
- DeferReplayRandomReset();
156
- _gameManager.ResetUIStateForCustomRestart();
157
- _eventManager.InvokeCustomRestartEvent();
158
- return;
159
- }
148
+ _replayCustomRestartPending = false;
149
+ _replayRandomResetPending = false;
150
+ _replayManager?.PrepareForRestart();
160
151
 
161
152
  if (_host != null)
162
153
  {
@@ -218,6 +209,7 @@ namespace Hyper.Internal
218
209
  {
219
210
  yield return UnloadAllScenesCoroutine();
220
211
 
212
+ Clear();
221
213
  ClearAllDontDestroyOnLoadObjects();
222
214
  SceneManager.LoadScene("HyperLoader");
223
215
  }
@@ -451,6 +443,7 @@ namespace Hyper.Internal
451
443
  _customLoadingProgress = 0f;
452
444
  _replayRandomResetPending = false;
453
445
  _replayCustomRestartPending = false;
446
+ _fixedSimulationTimeSeconds = 0d;
454
447
 
455
448
  if (_singletonObject != null)
456
449
  {
@@ -74,6 +74,7 @@ namespace Hyper.Internal.Managers
74
74
  }
75
75
 
76
76
  _playback.Begin(ReplaySessionState.ReplayNdjson);
77
+ ReplaySessionState.SetGameplayStartProgress(_playback.GameplayStartProgress01);
77
78
  }
78
79
 
79
80
  private void HandleGameEnded()
@@ -32,6 +32,9 @@ namespace Hyper.Internal.Managers
32
32
  private float _battleTimer;
33
33
  private bool _isPracticeRunning;
34
34
  private bool _isBattleRunning;
35
+ private bool _resumePracticeAfterPause;
36
+ private bool _resumeBattleAfterPause;
37
+ private bool _hasPausedGameplayTimerState;
35
38
 
36
39
  private float _fiveSecondTimer = 5f;
37
40
  private bool _isFiveSecondTimerRunning;
@@ -111,6 +114,7 @@ namespace Hyper.Internal.Managers
111
114
  public void Reset()
112
115
  {
113
116
  _practiceTimer = 0;
117
+ ClearPausedGameplayTimerState();
114
118
  _lastUnscaledTime = Time.unscaledTime;
115
119
  }
116
120
 
@@ -120,6 +124,7 @@ namespace Hyper.Internal.Managers
120
124
  _battleTimer = _gameManager != null ? _gameManager.Config.TimeLimit : 0f;
121
125
  _isPracticeRunning = false;
122
126
  _isBattleRunning = false;
127
+ ClearPausedGameplayTimerState();
123
128
  _timeUpTriggered = false;
124
129
  _timeUpPending = false;
125
130
  _hasGameEnded = false;
@@ -129,18 +134,17 @@ namespace Hyper.Internal.Managers
129
134
 
130
135
  public void ResumeGameTimersAfterPause()
131
136
  {
132
- if (_gameManager.IsBattleMode && !_gameManager.IsReplayMode)
133
- {
134
- StartBattle();
135
- }
136
- else
137
- {
138
- StartPractice();
139
- }
137
+ if (!_hasPausedGameplayTimerState) return;
138
+
139
+ _isPracticeRunning = _resumePracticeAfterPause;
140
+ _isBattleRunning = _resumeBattleAfterPause;
141
+ ClearPausedGameplayTimerState();
140
142
  }
141
143
 
142
144
  public void StartActiveGameTimer()
143
145
  {
146
+ ClearPausedGameplayTimerState();
147
+
144
148
  if (_gameManager.IsBattleMode)
145
149
  {
146
150
  StartSessionTimer();
@@ -158,6 +162,7 @@ namespace Hyper.Internal.Managers
158
162
  {
159
163
  _hasGameEnded = true;
160
164
  _timeUpPending = false;
165
+ ClearPausedGameplayTimerState();
161
166
  StopBattle();
162
167
  StopPractice();
163
168
  }
@@ -168,6 +173,11 @@ namespace Hyper.Internal.Managers
168
173
  /// </summary>
169
174
  public void PauseGameplayTimers()
170
175
  {
176
+ if (_hasPausedGameplayTimerState) return;
177
+
178
+ _resumePracticeAfterPause = _isPracticeRunning;
179
+ _resumeBattleAfterPause = _isBattleRunning;
180
+ _hasPausedGameplayTimerState = true;
171
181
  StopBattle();
172
182
  StopPractice();
173
183
  }
@@ -208,6 +218,13 @@ namespace Hyper.Internal.Managers
208
218
  internal void StartBattle() => _isBattleRunning = true;
209
219
  internal void StopBattle() => _isBattleRunning = false;
210
220
 
221
+ private void ClearPausedGameplayTimerState()
222
+ {
223
+ _resumePracticeAfterPause = false;
224
+ _resumeBattleAfterPause = false;
225
+ _hasPausedGameplayTimerState = false;
226
+ }
227
+
211
228
  internal bool TrySetSessionDurationFromBackendRemainingTime(float remainingTimeSeconds)
212
229
  {
213
230
  if (_hasBackendSessionDuration)
@@ -24,6 +24,7 @@ namespace Hyper.Internal
24
24
  public static bool IsSeekRestartPending => IsSeeking && !_seekPlaybackReady;
25
25
  public static float SeekTarget01 { get; private set; }
26
26
  public static bool HasEnded { get; private set; }
27
+ public static float GameplayStartProgress01 { get; private set; }
27
28
 
28
29
  internal static string EditorReplayFilePath =>
29
30
  Path.Combine(Application.persistentDataPath, "hyper-replay.ndjson");
@@ -54,6 +55,7 @@ namespace Hyper.Internal
54
55
  _seekPlaybackReady = false;
55
56
  _watchAgainAutoStartPending = false;
56
57
  HasEnded = false;
58
+ GameplayStartProgress01 = 0f;
57
59
  }
58
60
 
59
61
  public static void SetMetadata(string playerName, string avatarUrl, long originalRandomSeed)
@@ -82,9 +84,14 @@ namespace Hyper.Internal
82
84
  {
83
85
  if (!IsActive) return;
84
86
  if (IsSeeking && !_seekPlaybackReady) return;
85
- Progress01 = Math.Max(0f, Math.Min(1f, progress));
86
-
87
- if (IsSeeking && Progress01 >= SeekTarget01)
87
+ float rawProgress = Math.Max(0f, Math.Min(1f, progress));
88
+ Progress01 = GameplayStartProgress01 >= 1f
89
+ ? 0f
90
+ : Math.Max(0f, Math.Min(1f,
91
+ (rawProgress - GameplayStartProgress01) / (1f - GameplayStartProgress01)));
92
+
93
+ bool hasReachedGameplayStart = rawProgress + 0.000001f >= GameplayStartProgress01;
94
+ if (IsSeeking && hasReachedGameplayStart && Progress01 >= SeekTarget01)
88
95
  {
89
96
  IsSeeking = false;
90
97
  _seekPlaybackReady = false;
@@ -149,6 +156,12 @@ namespace Hyper.Internal
149
156
  HasEnded = false;
150
157
  }
151
158
 
159
+ public static void SetGameplayStartProgress(float progress)
160
+ {
161
+ if (!IsActive) return;
162
+ GameplayStartProgress01 = Math.Max(0f, Math.Min(1f, progress));
163
+ }
164
+
152
165
  public static bool ConsumeWatchAgainAutoStart()
153
166
  {
154
167
  bool pending = _watchAgainAutoStartPending;
@@ -173,6 +186,7 @@ namespace Hyper.Internal
173
186
  _seekPlaybackReady = false;
174
187
  _watchAgainAutoStartPending = false;
175
188
  HasEnded = false;
189
+ GameplayStartProgress01 = 0f;
176
190
  SeekCompleted = null;
177
191
  }
178
192
 
@@ -486,10 +486,15 @@ namespace Hyper
486
486
  /// <summary>Fired when all replay activities have been dispatched.</summary>
487
487
  event Action OnEnded;
488
488
 
489
+ /// <summary>Fired at the recorded boundary where game-specific playable gameplay began.</summary>
490
+ event Action OnGameplayReady;
491
+
489
492
  bool IsPlaying { get; }
490
493
  double DurationSeconds { get; }
491
494
  double ElapsedSeconds { get; }
492
495
  float Progress01 { get; }
496
+ bool IsSeeking { get; }
497
+ bool HasGameplayReadyMarker { get; }
493
498
 
494
499
  /// <summary>
495
500
  /// Optional visual lead time. Gameplay actions still fire at their recorded time.
@@ -685,11 +690,14 @@ namespace Hyper
685
690
  public event Action<float, string> OnValueAction { add => _playback.OnValueAction += value; remove => _playback.OnValueAction -= value; }
686
691
  public event Action<string> OnActionPreparing { add => _playback.OnActionPreparing += value; remove => _playback.OnActionPreparing -= value; }
687
692
  public event Action OnEnded { add => _playback.OnGameEnded += value; remove => _playback.OnGameEnded -= value; }
693
+ public event Action OnGameplayReady { add => _playback.OnGameplayReady += value; remove => _playback.OnGameplayReady -= value; }
688
694
 
689
695
  public bool IsPlaying => _playback.IsPlaying;
690
696
  public double DurationSeconds => _playback.DurationSeconds;
691
697
  public double ElapsedSeconds => _playback.ElapsedSeconds;
692
698
  public float Progress01 => _playback.Progress01;
699
+ public bool IsSeeking => ReplaySessionState.IsSeeking;
700
+ public bool HasGameplayReadyMarker => _playback.HasGameplayReadyMarker;
693
701
  public double ActionPreparationLeadSeconds
694
702
  {
695
703
  get => _playback.ActionPreparationLeadSeconds;
@@ -11,6 +11,7 @@ namespace Hyper
11
11
  internal sealed class ReplayPlaybackEngine : IReplayPlaybackEngine
12
12
  {
13
13
  private const string GameStarted = "GS";
14
+ private const string GameplayReady = "GPR";
14
15
  private const string GamePaused = "GP";
15
16
  private const string GameResumed = "GR";
16
17
  private const string GameEnded = "GE";
@@ -45,6 +46,7 @@ namespace Hyper
45
46
  /// </summary>
46
47
  public event Action<string> OnActionPreparing;
47
48
  public event Action OnGameStarted;
49
+ public event Action OnGameplayReady;
48
50
  public event Action OnGameEnded;
49
51
 
50
52
  public bool IsPlaying { get; private set; }
@@ -55,6 +57,8 @@ namespace Hyper
55
57
  /// The action itself is still dispatched at its recorded timestamp.
56
58
  /// </summary>
57
59
  public double ActionPreparationLeadSeconds { get; set; }
60
+ public bool HasGameplayReadyMarker { get; private set; }
61
+ public float GameplayStartProgress01 { get; private set; }
58
62
  public float Progress01 => DurationSeconds <= 0d
59
63
  ? (IsPlaying ? 0f : 1f)
60
64
  : Mathf.Clamp01((float)(ElapsedSeconds / DurationSeconds));
@@ -73,6 +77,11 @@ namespace Hyper
73
77
  _completionPending = false;
74
78
  _nextActionPrepared = false;
75
79
  DurationSeconds = CalculateDuration(_ndjson);
80
+ double gameplayStartSeconds = CalculateGameplayStart(_ndjson, out bool hasGameplayReadyMarker);
81
+ HasGameplayReadyMarker = hasGameplayReadyMarker;
82
+ GameplayStartProgress01 = hasGameplayReadyMarker && DurationSeconds > 0d
83
+ ? Mathf.Clamp01((float)(gameplayStartSeconds / DurationSeconds))
84
+ : 0f;
76
85
  _hasNext = TryReadNext(out _next);
77
86
 
78
87
  if (!_hasNext)
@@ -214,6 +223,9 @@ namespace Hyper
214
223
  case GameStarted:
215
224
  OnGameStarted?.Invoke();
216
225
  break;
226
+ case GameplayReady:
227
+ OnGameplayReady?.Invoke();
228
+ break;
217
229
  case GamePaused:
218
230
  SkipRecordedPause(activity.Time);
219
231
  break;
@@ -246,7 +258,50 @@ namespace Hyper
246
258
  private static bool IsLifecycleAction(string action)
247
259
  {
248
260
  return action == GameStarted || action == GamePaused ||
249
- action == GameResumed || action == GameEnded;
261
+ action == GameResumed || action == GameEnded || action == GameplayReady;
262
+ }
263
+
264
+ private static double CalculateGameplayStart(string ndjson, out bool found)
265
+ {
266
+ found = false;
267
+ if (string.IsNullOrWhiteSpace(ndjson)) return 0d;
268
+
269
+ int cursor = 0;
270
+ bool hasFirst = false;
271
+ double firstTime = 0d;
272
+ double removedPauses = 0d;
273
+ double? pauseStartedAt = null;
274
+
275
+ while (TryReadLine(ndjson, ref cursor, out string line))
276
+ {
277
+ if (!TryParse(line, out ReplayActivity activity)) continue;
278
+ if (!hasFirst)
279
+ {
280
+ hasFirst = true;
281
+ firstTime = activity.Time;
282
+ }
283
+
284
+ if (activity.Kind != ReplayActivityKind.Action) continue;
285
+ if (activity.Action == GamePaused)
286
+ {
287
+ pauseStartedAt ??= activity.Time;
288
+ continue;
289
+ }
290
+
291
+ if (activity.Action == GameResumed && pauseStartedAt.HasValue)
292
+ {
293
+ removedPauses += Math.Max(0d, activity.Time - pauseStartedAt.Value);
294
+ pauseStartedAt = null;
295
+ continue;
296
+ }
297
+
298
+ if (activity.Action != GameplayReady) continue;
299
+
300
+ found = true;
301
+ return Math.Max(0d, activity.Time - firstTime - removedPauses);
302
+ }
303
+
304
+ return 0d;
250
305
  }
251
306
 
252
307
  private void SkipRecordedPause(double pauseStartedAt)
@@ -19,12 +19,15 @@ namespace Hyper.Shared
19
19
  event Action<float, string> OnValueAction;
20
20
  event Action<string> OnActionPreparing;
21
21
  event Action OnGameStarted;
22
+ event Action OnGameplayReady;
22
23
  event Action OnGameEnded;
23
24
 
24
25
  bool IsPlaying { get; }
25
26
  double DurationSeconds { get; }
26
27
  double ElapsedSeconds { get; }
27
28
  float Progress01 { get; }
29
+ bool HasGameplayReadyMarker { get; }
30
+ float GameplayStartProgress01 { get; }
28
31
  double ActionPreparationLeadSeconds { get; set; }
29
32
 
30
33
  void Begin(string ndjson);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "app.hypergames.hypersdk",
3
- "version": "1.11.0-beta.5",
3
+ "version": "1.11.0-beta.7",
4
4
  "displayName": "HyperSDK",
5
5
  "description": "Official Unity SDK for Hyper: multiplayer battles, leaderboards, deterministic RNG, session management, tutorials, and WebGL optimizations for production-ready games.",
6
6
  "unity": "6000.0",