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 +7 -0
- package/Runtime/Internal/Core/HyperRuntime.cs +6 -13
- package/Runtime/Internal/Managers/ReplayManager.cs +1 -0
- package/Runtime/Internal/Replay/ReplaySessionState.cs +17 -3
- package/Runtime/Public/Core/HyperSDK.cs +8 -0
- package/Runtime/Public/Replay/ReplayPlaybackEngine.cs +56 -1
- package/Runtime/Shared/Replay/IReplayPlaybackEngine.cs +3 -0
- package/package.json +1 -1
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
|
|
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
|
-
|
|
150
|
-
|
|
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
|
{
|
|
@@ -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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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.
|
|
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",
|