app.hypergames.hypersdk 1.11.0-beta.6 → 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,10 @@
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
+
1
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)
2
9
 
3
10
 
@@ -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()
@@ -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.6",
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",