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 +14 -0
- package/Runtime/Internal/Core/HyperRuntime.cs +63 -1
- package/Runtime/Internal/Managers/EventManager.cs +7 -1
- package/Runtime/Internal/UI/Components/ReplayController.cs +82 -9
- package/Runtime/Resources/UIPrefabs/ReplayController.prefab +2 -1
- package/Runtime/Resources/UIPrefabs/ReplaySeekOverlay.prefab +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
450
|
+
seekPreparationCeiling,
|
|
449
451
|
1f,
|
|
450
452
|
Mathf.Clamp01(reconstructionProgress));
|
|
451
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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",
|