app.hypergames.hypersdk 1.11.0-beta.16 → 1.11.0-beta.18

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.18](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.17...v1.11.0-beta.18) (2026-09-01)
2
+
3
+
4
+ ### Features
5
+
6
+ * **replay:** support custom replay restarts ([bf722ab](https://github.com/mvmhyper/hyper-sdk/commit/bf722abe48add18c356922c60231cb2017e28443))
7
+
8
+ # [1.11.0-beta.17](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.16...v1.11.0-beta.17) (2026-09-01)
9
+
10
+
11
+ ### Features
12
+
13
+ * **replay:** refine seek loading progress ([3d01f94](https://github.com/mvmhyper/hyper-sdk/commit/3d01f94d7e6ce360a61dd3d257149cf7f3237bcb))
14
+
1
15
  # [1.11.0-beta.16](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.15...v1.11.0-beta.16) (2026-08-31)
2
16
 
3
17
 
@@ -15,6 +15,8 @@ namespace Hyper.Internal
15
15
  /// </summary>
16
16
  internal static class HyperRuntime
17
17
  {
18
+ private const float ReplayCustomRestartTimeoutSeconds = 10f;
19
+
18
20
  private static HyperRuntimeHost _host;
19
21
  private static GameObject _singletonObject;
20
22
 
@@ -22,6 +24,7 @@ namespace Hyper.Internal
22
24
  private static bool _isSoftRestarting;
23
25
  private static bool _replayRandomResetPending;
24
26
  private static bool _replayCustomRestartPending;
27
+ private static int _replayCustomRestartVersion;
25
28
  private static bool _customLoadingComplete;
26
29
  private static bool _customLoadingActive;
27
30
  private static float _customLoadingProgress;
@@ -123,7 +126,8 @@ namespace Hyper.Internal
123
126
  }
124
127
 
125
128
  /// <summary>
126
- /// Restarts replay playback through HyperLoader while preserving replay session data.
129
+ /// Restarts replay playback while preserving replay session data. Uses the configured
130
+ /// game-owned custom restart flow when available and falls back to HyperLoader.
127
131
  /// </summary>
128
132
  internal static void RestartReplaySession()
129
133
  {
@@ -147,8 +151,64 @@ namespace Hyper.Internal
147
151
 
148
152
  _replayCustomRestartPending = false;
149
153
  _replayRandomResetPending = false;
154
+ int restartVersion = ++_replayCustomRestartVersion;
150
155
  _replayManager?.PrepareForRestart();
151
156
 
157
+ if (_settings != null && _settings.useCustomRestartFlow &&
158
+ _host != null && _gameManager != null && _eventManager != null)
159
+ {
160
+ _replayCustomRestartPending = true;
161
+ _replayRandomResetPending = true;
162
+ _gameManager.ResetUIStateForCustomRestart();
163
+
164
+ try
165
+ {
166
+ if (!_eventManager.InvokeCustomRestartEvent())
167
+ {
168
+ HyperDebug.LogWarning(
169
+ "[Replay] Custom restart has no handler; falling back to HyperLoader.");
170
+ BeginReplaySceneRestart(restartVersion);
171
+ return;
172
+ }
173
+
174
+ _host.StartCoroutine(WaitForReplayCustomRestart(restartVersion));
175
+ return;
176
+ }
177
+ catch (Exception exception)
178
+ {
179
+ HyperDebug.LogWarning(
180
+ $"[Replay] Custom restart failed; falling back to HyperLoader: {exception.Message}");
181
+ BeginReplaySceneRestart(restartVersion);
182
+ return;
183
+ }
184
+ }
185
+
186
+ BeginReplaySceneRestart(restartVersion);
187
+ }
188
+
189
+ private static IEnumerator WaitForReplayCustomRestart(int restartVersion)
190
+ {
191
+ float timeoutAt = Time.realtimeSinceStartup + ReplayCustomRestartTimeoutSeconds;
192
+ while (_replayCustomRestartPending && restartVersion == _replayCustomRestartVersion &&
193
+ Time.realtimeSinceStartup < timeoutAt)
194
+ {
195
+ yield return null;
196
+ }
197
+
198
+ if (!_replayCustomRestartPending || restartVersion != _replayCustomRestartVersion) yield break;
199
+
200
+ HyperDebug.LogWarning(
201
+ $"[Replay] Custom restart did not complete within {ReplayCustomRestartTimeoutSeconds:0} seconds; " +
202
+ "falling back to HyperLoader.");
203
+ BeginReplaySceneRestart(restartVersion);
204
+ }
205
+
206
+ private static void BeginReplaySceneRestart(int restartVersion)
207
+ {
208
+ if (restartVersion != _replayCustomRestartVersion) return;
209
+
210
+ _replayCustomRestartPending = false;
211
+ _replayRandomResetPending = false;
152
212
  if (_host != null)
153
213
  {
154
214
  _host.StartCoroutine(RestartSessionCoroutine());
@@ -443,6 +503,7 @@ namespace Hyper.Internal
443
503
  _customLoadingProgress = 0f;
444
504
  _replayRandomResetPending = false;
445
505
  _replayCustomRestartPending = false;
506
+ _replayCustomRestartVersion++;
446
507
  _fixedSimulationTimeSeconds = 0d;
447
508
 
448
509
  if (_singletonObject != null)
@@ -504,6 +565,7 @@ namespace Hyper.Internal
504
565
  _customLoadingProgress = 0f;
505
566
  _replayRandomResetPending = false;
506
567
  _replayCustomRestartPending = false;
568
+ _replayCustomRestartVersion++;
507
569
  _fixedSimulationTimeSeconds = 0d;
508
570
  }
509
571
  }
@@ -79,7 +79,13 @@ namespace Hyper.Internal.Managers
79
79
  internal void InvokeGameEndedEvent() => OnGameEnded?.Invoke();
80
80
  internal void InvokeLivesExhaustedEvent() => OnLivesExhausted?.Invoke();
81
81
  internal void InvokeHealthExhaustedEvent() => OnHealthExhausted?.Invoke();
82
- internal void InvokeCustomRestartEvent() => OnCustomRestart?.Invoke();
82
+ internal bool InvokeCustomRestartEvent()
83
+ {
84
+ Action handler = OnCustomRestart;
85
+ if (handler == null) return false;
86
+ handler.Invoke();
87
+ return true;
88
+ }
83
89
  internal void InvokeConnectionLostEvent() => OnInternetConnectionLost?.Invoke();
84
90
  internal void InvokeConnectionFoundEvent() => OnInternetConnectionFound?.Invoke();
85
91
  internal void InvokeBackendSocketManuallyClosedEvent() => OnBackendSocketManuallyClosed?.Invoke();
@@ -22,7 +22,8 @@ namespace Hyper.Internal
22
22
  [SerializeField, Min(1f)] private float seekPlaybackSpeed = 10f;
23
23
  [SerializeField, Range(0.25f, 1f)] private float seekSnapshotScale = 0.55f;
24
24
  [SerializeField, Range(0f, 0.5f)] private float seekPreparationProgress = 0.2f;
25
- [SerializeField, Min(0.1f)] private float seekPreparationDuration = 1f;
25
+ [SerializeField, Range(0f, 0.15f)] private float seekPreparationProgressVariation = 0.05f;
26
+ [SerializeField, Min(0.1f)] private float seekPreparationDuration = 3f;
26
27
  [SerializeField] private GameObject seekLoadingOverlay;
27
28
  [SerializeField] private RawImage seekSnapshotImage;
28
29
 
@@ -35,7 +36,11 @@ namespace Hyper.Internal
35
36
  private Image seekOverlayBackground;
36
37
  private Color seekOverlayBackgroundColor;
37
38
  private ReplaySeekOverlay seekOverlay;
39
+ private static readonly System.Random SeekProgressRandom = new System.Random();
38
40
  private static float displayedSeekProgress;
41
+ private static float firstSeekPreparationTarget;
42
+ private static float secondSeekPreparationTarget;
43
+ private static float seekPreparationCeiling;
39
44
 
40
45
  private void Awake()
41
46
  {
@@ -198,7 +203,7 @@ namespace Hyper.Internal
198
203
 
199
204
  float resumeSpeed = SanitizePlaybackSpeed(ReplaySessionState.UserPlaybackSpeed);
200
205
  float fastForwardSpeed = SanitizePlaybackSpeed(seekPlaybackSpeed);
201
- displayedSeekProgress = 0f;
206
+ InitializeSeekProgressPresentation();
202
207
  StartCoroutine(CaptureAndSeek(targetProgress, resumeSpeed, fastForwardSpeed));
203
208
  }
204
209
 
@@ -431,30 +436,98 @@ namespace Hyper.Internal
431
436
  }
432
437
  else if (isCapturingSeek || ReplaySessionState.IsSeekRestartPending)
433
438
  {
434
- float duration = Mathf.Max(0.1f, seekPreparationDuration);
435
- displayedSeekProgress = Mathf.MoveTowards(
436
- displayedSeekProgress,
437
- seekPreparationProgress,
438
- seekPreparationProgress * Time.unscaledDeltaTime / duration);
439
+ AdvanceSeekPreparationProgress();
439
440
  progress = displayedSeekProgress;
440
441
  }
441
442
  else
442
443
  {
444
+ EnsureSeekProgressPresentation();
443
445
  float target = ReplaySessionState.SeekTarget01;
444
446
  float reconstructionProgress = target <= Mathf.Epsilon
445
447
  ? 1f
446
448
  : ReplaySessionState.Progress01 / target;
447
449
  float realProgress = Mathf.Lerp(
448
- seekPreparationProgress,
450
+ seekPreparationCeiling,
449
451
  1f,
450
452
  Mathf.Clamp01(reconstructionProgress));
451
- displayedSeekProgress = Mathf.Max(displayedSeekProgress, realProgress);
453
+ AdvanceSeekReconstructionProgress(realProgress);
452
454
  progress = displayedSeekProgress;
453
455
  }
454
456
 
455
457
  seekOverlay.SetProgress(progress);
456
458
  }
457
459
 
460
+ private void AdvanceSeekPreparationProgress()
461
+ {
462
+ EnsureSeekProgressPresentation();
463
+ float duration = Mathf.Max(0.1f, seekPreparationDuration);
464
+ float deltaTime = Mathf.Min(Time.unscaledDeltaTime, 0.1f);
465
+
466
+ if (displayedSeekProgress < firstSeekPreparationTarget)
467
+ {
468
+ float speed = firstSeekPreparationTarget / (duration * 0.35f);
469
+ displayedSeekProgress = Mathf.MoveTowards(
470
+ displayedSeekProgress,
471
+ firstSeekPreparationTarget,
472
+ speed * deltaTime);
473
+ return;
474
+ }
475
+
476
+ if (displayedSeekProgress < secondSeekPreparationTarget)
477
+ {
478
+ float speed = (secondSeekPreparationTarget - firstSeekPreparationTarget) / (duration * 0.2f);
479
+ displayedSeekProgress = Mathf.MoveTowards(
480
+ displayedSeekProgress,
481
+ secondSeekPreparationTarget,
482
+ speed * deltaTime);
483
+ return;
484
+ }
485
+
486
+ float crawlResponse = 1f - Mathf.Exp(-deltaTime / (duration * 0.65f));
487
+ displayedSeekProgress = Mathf.Lerp(displayedSeekProgress, seekPreparationCeiling, crawlResponse);
488
+ }
489
+
490
+ private void InitializeSeekProgressPresentation()
491
+ {
492
+ float center = Mathf.Clamp01(seekPreparationProgress);
493
+ float variation = Mathf.Max(0f, seekPreparationProgressVariation);
494
+ float minimum = Mathf.Clamp01(center - variation);
495
+ float maximum = Mathf.Clamp01(center + variation);
496
+
497
+ displayedSeekProgress = 0f;
498
+ seekPreparationCeiling = RandomRange(minimum, maximum);
499
+ firstSeekPreparationTarget = seekPreparationCeiling * RandomRange(0.25f, 0.35f);
500
+ secondSeekPreparationTarget = seekPreparationCeiling * RandomRange(0.62f, 0.76f);
501
+ }
502
+
503
+ private void EnsureSeekProgressPresentation()
504
+ {
505
+ if (seekPreparationCeiling > 0f) return;
506
+ InitializeSeekProgressPresentation();
507
+ }
508
+
509
+ private static float RandomRange(float minimum, float maximum)
510
+ {
511
+ if (maximum <= minimum) return minimum;
512
+ return minimum + ((float)SeekProgressRandom.NextDouble() * (maximum - minimum));
513
+ }
514
+
515
+ private void AdvanceSeekReconstructionProgress(float realProgress)
516
+ {
517
+ float target = Mathf.Max(displayedSeekProgress, realProgress);
518
+ if (target >= 0.999f)
519
+ {
520
+ displayedSeekProgress = 1f;
521
+ return;
522
+ }
523
+
524
+ float deltaTime = Mathf.Min(Time.unscaledDeltaTime, 0.1f);
525
+ float gap = target - displayedSeekProgress;
526
+ float responseSpeed = Mathf.Lerp(5f, 14f, Mathf.Clamp01(gap * 4f));
527
+ float response = 1f - Mathf.Exp(-responseSpeed * deltaTime);
528
+ displayedSeekProgress = Mathf.Lerp(displayedSeekProgress, target, response);
529
+ }
530
+
458
531
  private void ConfigureSeekSnapshot()
459
532
  {
460
533
  if (seekSnapshotImage == null)
@@ -320,7 +320,8 @@ MonoBehaviour:
320
320
  seekPlaybackSpeed: 10
321
321
  seekSnapshotScale: 0.725
322
322
  seekPreparationProgress: 0.2
323
- seekPreparationDuration: 1
323
+ seekPreparationProgressVariation: 0.05
324
+ seekPreparationDuration: 3
324
325
  seekLoadingOverlay: {fileID: 0}
325
326
  seekSnapshotImage: {fileID: 0}
326
327
  --- !u!1 &1862654115017658012
@@ -288,7 +288,7 @@ MonoBehaviour:
288
288
  m_Name:
289
289
  m_EditorClassIdentifier:
290
290
  m_Material: {fileID: 0}
291
- m_Color: {r: 1, g: 1, b: 1, a: 0.10980392}
291
+ m_Color: {r: 1, g: 1, b: 1, a: 0.1764706}
292
292
  m_RaycastTarget: 1
293
293
  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
294
294
  m_Maskable: 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "app.hypergames.hypersdk",
3
- "version": "1.11.0-beta.16",
3
+ "version": "1.11.0-beta.18",
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",