app.hypergames.hypersdk 1.11.0-beta.6 → 1.11.0-beta.8

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.8](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.7...v1.11.0-beta.8) (2026-08-30)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **replay:** mute audio while seeking ([5ab8cd1](https://github.com/mvmhyper/hyper-sdk/commit/5ab8cd1411493cb5add07b51dc190c85251f99d2))
7
+
8
+ # [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)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **replay:** stabilize scene reload seeking ([dd9ac63](https://github.com/mvmhyper/hyper-sdk/commit/dd9ac632d08349d9578f64ae0b2dc2ab905a26af))
14
+
1
15
  # [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
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
  {
@@ -219,6 +219,7 @@ namespace Hyper.Internal.Managers
219
219
  internal void ConfigurePlayerSoundSetting()
220
220
  {
221
221
  SetSound(GetSound());
222
+ ReplaySessionState.EnsureAudioMutedDuringSeek();
222
223
  if (_gameManager == null) _gameManager = HyperRuntime.GameManager;
223
224
  }
224
225
 
@@ -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");
@@ -32,6 +33,8 @@ namespace Hyper.Internal
32
33
 
33
34
  private static bool _seekPlaybackReady;
34
35
  private static bool _watchAgainAutoStartPending;
36
+ private static bool _isSeekAudioMuted;
37
+ private static float _volumeBeforeSeek;
35
38
 
36
39
  public static void Configure(
37
40
  string playerName,
@@ -39,6 +42,7 @@ namespace Hyper.Internal
39
42
  long originalRandomSeed,
40
43
  string entryId = null)
41
44
  {
45
+ RestoreAudioAfterSeek();
42
46
  IsActive = true;
43
47
  PlayerName = playerName ?? string.Empty;
44
48
  AvatarUrl = avatarUrl ?? string.Empty;
@@ -54,6 +58,7 @@ namespace Hyper.Internal
54
58
  _seekPlaybackReady = false;
55
59
  _watchAgainAutoStartPending = false;
56
60
  HasEnded = false;
61
+ GameplayStartProgress01 = 0f;
57
62
  }
58
63
 
59
64
  public static void SetMetadata(string playerName, string avatarUrl, long originalRandomSeed)
@@ -82,13 +87,19 @@ namespace Hyper.Internal
82
87
  {
83
88
  if (!IsActive) return;
84
89
  if (IsSeeking && !_seekPlaybackReady) return;
85
- Progress01 = Math.Max(0f, Math.Min(1f, progress));
86
-
87
- if (IsSeeking && Progress01 >= SeekTarget01)
90
+ float rawProgress = Math.Max(0f, Math.Min(1f, progress));
91
+ Progress01 = GameplayStartProgress01 >= 1f
92
+ ? 0f
93
+ : Math.Max(0f, Math.Min(1f,
94
+ (rawProgress - GameplayStartProgress01) / (1f - GameplayStartProgress01)));
95
+
96
+ bool hasReachedGameplayStart = rawProgress + 0.000001f >= GameplayStartProgress01;
97
+ if (IsSeeking && hasReachedGameplayStart && Progress01 >= SeekTarget01)
88
98
  {
89
99
  IsSeeking = false;
90
100
  _seekPlaybackReady = false;
91
101
  PlaybackSpeed = UserPlaybackSpeed;
102
+ RestoreAudioAfterSeek();
92
103
  SeekCompleted?.Invoke(PlaybackSpeed);
93
104
  }
94
105
  }
@@ -107,6 +118,7 @@ namespace Hyper.Internal
107
118
  Progress01 = 1f;
108
119
  if (IsSeeking) PlaybackSpeed = UserPlaybackSpeed;
109
120
  IsSeeking = false;
121
+ RestoreAudioAfterSeek();
110
122
  HasEnded = true;
111
123
  }
112
124
 
@@ -119,6 +131,7 @@ namespace Hyper.Internal
119
131
  PlaybackSpeed = SanitizeSpeed(seekSpeed);
120
132
  IsSeeking = true;
121
133
  _seekPlaybackReady = false;
134
+ MuteAudioForSeek();
122
135
  }
123
136
 
124
137
  public static void ResetForSeekRestart()
@@ -136,9 +149,16 @@ namespace Hyper.Internal
136
149
  _seekPlaybackReady = true;
137
150
  }
138
151
 
152
+ internal static void EnsureAudioMutedDuringSeek()
153
+ {
154
+ if (_isSeekAudioMuted)
155
+ AudioListener.volume = 0f;
156
+ }
157
+
139
158
  public static void ResetForWatchAgain()
140
159
  {
141
160
  if (!IsActive) return;
161
+ RestoreAudioAfterSeek();
142
162
  Progress01 = 0f;
143
163
  PlaybackSpeed = 1f;
144
164
  UserPlaybackSpeed = 1f;
@@ -149,6 +169,12 @@ namespace Hyper.Internal
149
169
  HasEnded = false;
150
170
  }
151
171
 
172
+ public static void SetGameplayStartProgress(float progress)
173
+ {
174
+ if (!IsActive) return;
175
+ GameplayStartProgress01 = Math.Max(0f, Math.Min(1f, progress));
176
+ }
177
+
152
178
  public static bool ConsumeWatchAgainAutoStart()
153
179
  {
154
180
  bool pending = _watchAgainAutoStartPending;
@@ -158,6 +184,7 @@ namespace Hyper.Internal
158
184
 
159
185
  public static void Clear()
160
186
  {
187
+ RestoreAudioAfterSeek();
161
188
  IsActive = false;
162
189
  PlayerName = string.Empty;
163
190
  AvatarUrl = string.Empty;
@@ -173,6 +200,7 @@ namespace Hyper.Internal
173
200
  _seekPlaybackReady = false;
174
201
  _watchAgainAutoStartPending = false;
175
202
  HasEnded = false;
203
+ GameplayStartProgress01 = 0f;
176
204
  SeekCompleted = null;
177
205
  }
178
206
 
@@ -181,6 +209,23 @@ namespace Hyper.Internal
181
209
  return float.IsNaN(speed) || float.IsInfinity(speed) || speed <= 0f ? 1f : speed;
182
210
  }
183
211
 
212
+ private static void MuteAudioForSeek()
213
+ {
214
+ if (_isSeekAudioMuted) return;
215
+
216
+ _volumeBeforeSeek = AudioListener.volume;
217
+ _isSeekAudioMuted = true;
218
+ AudioListener.volume = 0f;
219
+ }
220
+
221
+ private static void RestoreAudioAfterSeek()
222
+ {
223
+ if (!_isSeekAudioMuted) return;
224
+
225
+ AudioListener.volume = _volumeBeforeSeek;
226
+ _isSeekAudioMuted = false;
227
+ }
228
+
184
229
  private static void ReleaseAvatarTexture()
185
230
  {
186
231
  if (AvatarTexture != null)
@@ -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.8",
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",