app.hypergames.hypersdk 1.9.1 → 1.11.0-beta.1
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 +37 -0
- package/Editor/Scripts/Setup/HyperSDKInstaller.cs +42 -0
- package/Editor/Scripts/Tools/HyperGameModeMenu.cs +27 -7
- package/Runtime/Internal/AssemblyInfo.cs +1 -0
- package/Runtime/Internal/Bootstrap/HyperLoader.cs +186 -8
- package/Runtime/Internal/Core/HyperRuntime.cs +58 -0
- package/Runtime/Internal/Core/HyperRuntimeHost.cs +6 -0
- package/Runtime/Internal/Data/URLData.cs +18 -1
- package/Runtime/Internal/Managers/BackendManager.cs +281 -24
- package/Runtime/Internal/Managers/DataManager.cs +207 -21
- package/Runtime/Internal/Managers/GameManager.cs +350 -15
- package/Runtime/Internal/Managers/ReplayManager.cs +111 -0
- package/Runtime/Internal/Managers/ReplayManager.cs.meta +11 -0
- package/Runtime/Internal/Managers/TimeManager.cs +56 -14
- package/Runtime/Internal/Replay/ReplayActivityRecorder.cs +182 -0
- package/Runtime/Internal/Replay/ReplayActivityRecorder.cs.meta +2 -0
- package/Runtime/Internal/Replay/ReplaySessionState.cs +181 -0
- package/Runtime/Internal/Replay/ReplaySessionState.cs.meta +2 -0
- package/Runtime/Internal/Replay.meta +8 -0
- package/Runtime/Internal/ScriptableObjects/HyperSDKSettings.cs +5 -0
- package/Runtime/Internal/UI/Animation/LoadingTextAnimation.cs +15 -1
- package/Runtime/Internal/UI/Components/ConcaveImage.cs +147 -0
- package/Runtime/Internal/UI/Components/ConcaveImage.cs.meta +11 -0
- package/Runtime/Internal/UI/Components/ControlBar.cs +86 -6
- package/Runtime/Internal/UI/Components/PauseButton.cs +4 -1
- package/Runtime/Internal/UI/Components/ReplayController.cs +501 -0
- package/Runtime/Internal/UI/Components/ReplayController.cs.meta +2 -0
- package/Runtime/Internal/UI/Components/ReplayMarkerPulse.cs +89 -0
- package/Runtime/Internal/UI/Components/ReplayMarkerPulse.cs.meta +2 -0
- package/Runtime/Internal/UI/Components/ReplayPresentationCanvas.cs +183 -0
- package/Runtime/Internal/UI/Components/ReplayPresentationCanvas.cs.meta +2 -0
- package/Runtime/Internal/UI/Components/SnackBar.cs +2 -9
- package/Runtime/Internal/UI/Modals/LoadingFailedModal.cs +12 -0
- package/Runtime/Internal/UI/Modals/ReplayOverModal.cs +54 -0
- package/Runtime/Internal/UI/Modals/ReplayOverModal.cs.meta +2 -0
- package/Runtime/Internal/UI/Modals/StartingInstructionModal.cs +38 -29
- package/Runtime/Public/AssemblyInfo.cs +3 -0
- package/Runtime/Public/AssemblyInfo.cs.meta +2 -0
- package/Runtime/Public/Core/HyperSDK.cs +305 -109
- package/Runtime/Public/Hyper.asmdef +2 -1
- package/Runtime/Public/Replay/ReplayPlaybackEngine.cs +555 -0
- package/Runtime/Public/Replay/ReplayPlaybackEngine.cs.meta +2 -0
- package/Runtime/Public/Replay.meta +8 -0
- package/Runtime/Resources/Fonts/Nunito/Nunito-Light SDF.asset +528 -521
- package/Runtime/Resources/Materials/HyperReplayScanlines.preset +340 -0
- package/Runtime/Resources/Materials/HyperReplayScanlines.preset.meta +8 -0
- package/Runtime/Resources/Materials/ReplayScanlineOverlay.mat +55 -0
- package/Runtime/Resources/Materials/ReplayScanlineOverlay.mat.meta +8 -0
- package/Runtime/Resources/Shaders/ConcaveImage.shader +155 -0
- package/Runtime/Resources/Shaders/ConcaveImage.shader.meta +10 -0
- package/Runtime/Resources/Shaders/ReplayScanlineOverlay.shader +150 -0
- package/Runtime/Resources/Shaders/ReplayScanlineOverlay.shader.meta +10 -0
- package/Runtime/Resources/Sprites/Replays/Fallback avatar.png +0 -0
- package/Runtime/Resources/Sprites/Replays/Fallback avatar.png.meta +156 -0
- package/Runtime/Resources/Sprites/Replays/Recording.png +0 -0
- package/Runtime/Resources/Sprites/{Control Bar/Top_Black_Rounded_Bar_Portrait_IV.png.meta → Replays/Recording.png.meta} +16 -29
- package/Runtime/Resources/Sprites/Replays/Replay frame.png +0 -0
- package/Runtime/Resources/Sprites/Replays/Replay frame.png.meta +156 -0
- package/Runtime/Resources/Sprites/Replays/Replay modal icon.png +0 -0
- package/Runtime/Resources/Sprites/Replays/Replay modal icon.png.meta +156 -0
- package/Runtime/Resources/Sprites/Replays.meta +8 -0
- package/Runtime/Resources/UIPrefabs/ControlBar.prefab +389 -42
- package/Runtime/Resources/UIPrefabs/GamePausedModal_Battle.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/ReplayController.prefab +1269 -0
- package/Runtime/Resources/UIPrefabs/ReplayController.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/ReplayInputBlocker.prefab +90 -0
- package/Runtime/Resources/UIPrefabs/ReplayInputBlocker.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/ReplayOverModal.prefab +958 -0
- package/Runtime/Resources/UIPrefabs/ReplayOverModal.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/ReplaySeekOverlay.prefab +189 -0
- package/Runtime/Resources/UIPrefabs/ReplaySeekOverlay.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/Rounded Cutout.prefab +96 -0
- package/Runtime/Resources/UIPrefabs/Rounded Cutout.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/SnackBar.prefab +23 -6
- package/Runtime/Shared/AssemblyInfo.cs +5 -0
- package/Runtime/Shared/AssemblyInfo.cs.meta +11 -0
- package/Runtime/Shared/DTOs/Enums.cs +8 -3
- package/Runtime/Shared/DTOs/Score/ScoreSnapshot.cs +13 -1
- package/Runtime/Shared/Replay/IReplayPlaybackEngine.cs +37 -0
- package/Runtime/Shared/Replay/IReplayPlaybackEngine.cs.meta +11 -0
- package/Runtime/Shared/Replay.meta +8 -0
- package/Runtime/Shared/Utils/HyperRandom.cs +25 -5
- package/Samples~/LaneRunner/LaneRunnerSample.unity +1603 -0
- package/Samples~/LaneRunner/LaneRunnerSample.unity.meta +7 -0
- package/Samples~/LaneRunner/Materials/DoubleScore.mat +86 -0
- package/Samples~/LaneRunner/Materials/DoubleScore.mat.meta +8 -0
- package/Samples~/LaneRunner/Materials/Ground.mat +84 -0
- package/Samples~/LaneRunner/Materials/Ground.mat.meta +8 -0
- package/Samples~/LaneRunner/Materials/Lane.mat +84 -0
- package/Samples~/LaneRunner/Materials/Lane.mat.meta +8 -0
- package/Samples~/LaneRunner/Materials/LaneMarker.mat +84 -0
- package/Samples~/LaneRunner/Materials/LaneMarker.mat.meta +8 -0
- package/Samples~/LaneRunner/Materials/Obstacle.mat +84 -0
- package/Samples~/LaneRunner/Materials/Obstacle.mat.meta +8 -0
- package/Samples~/LaneRunner/Materials/Player.mat +84 -0
- package/Samples~/LaneRunner/Materials/Player.mat.meta +8 -0
- package/Samples~/LaneRunner/Materials.meta +8 -0
- package/Samples~/LaneRunner/Prefabs/DoubleScorePickup.prefab +152 -0
- package/Samples~/LaneRunner/Prefabs/DoubleScorePickup.prefab.meta +7 -0
- package/Samples~/LaneRunner/Prefabs/Obstacle.prefab +152 -0
- package/Samples~/LaneRunner/Prefabs/Obstacle.prefab.meta +7 -0
- package/Samples~/LaneRunner/Prefabs.meta +8 -0
- package/Samples~/LaneRunner/README.md +35 -0
- package/Samples~/LaneRunner/README.md.meta +7 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerCommand.cs +9 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerCommand.cs.meta +2 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerGame.cs +93 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerGame.cs.meta +2 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerPlayer.cs +155 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerPlayer.cs.meta +2 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerSpawner.cs +110 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerSpawner.cs.meta +2 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerTrackItem.cs +41 -0
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerTrackItem.cs.meta +2 -0
- package/Samples~/LaneRunner/Scripts/Gameplay.meta +8 -0
- package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerReplayAdapter.cs +62 -0
- package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerReplayAdapter.cs.meta +2 -0
- package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerTournamentAdapter.cs +64 -0
- package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerTournamentAdapter.cs.meta +2 -0
- package/Samples~/LaneRunner/Scripts/Hyper.meta +8 -0
- package/Samples~/LaneRunner/Scripts.meta +8 -0
- package/Tests.meta +8 -0
- package/package.json +9 -3
- package/Runtime/Resources/Sprites/Control Bar/Top_Black_Rounded_Bar_Portrait_IV.png +0 -0
|
@@ -82,14 +82,20 @@ namespace Hyper.Internal.Managers
|
|
|
82
82
|
{
|
|
83
83
|
Destroy(_hyperCanvasObject);
|
|
84
84
|
}
|
|
85
|
+
|
|
86
|
+
if (!ReplaySessionState.IsSeeking)
|
|
87
|
+
{
|
|
88
|
+
ReplayPresentationCanvas.DestroyCurrent();
|
|
89
|
+
}
|
|
85
90
|
}
|
|
86
91
|
|
|
87
92
|
#endregion
|
|
88
93
|
|
|
89
94
|
#region Public API Implementation
|
|
90
95
|
|
|
91
|
-
public bool IsBattleMode => Config.
|
|
92
|
-
public bool IsPracticeMode =>
|
|
96
|
+
public bool IsBattleMode => Config.IsBattleGameplay;
|
|
97
|
+
public bool IsPracticeMode => Config.GameMode == GameMode.Practice;
|
|
98
|
+
public bool IsReplayMode => Config.GameMode == GameMode.Replay;
|
|
93
99
|
public bool IsPaused => _isPaused;
|
|
94
100
|
public bool HasGameStarted => _hasGameStarted;
|
|
95
101
|
public bool HasGameEnded => _hasGameEnded;
|
|
@@ -114,6 +120,12 @@ namespace Hyper.Internal.Managers
|
|
|
114
120
|
{
|
|
115
121
|
if (!IsBattleMode) return;
|
|
116
122
|
|
|
123
|
+
if (IsReplayMode)
|
|
124
|
+
{
|
|
125
|
+
EndReplayGame();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
117
129
|
var timeLeft = HyperRuntime.Timer.CurrentTime;
|
|
118
130
|
if (timeLeft <= 0f) return;
|
|
119
131
|
|
|
@@ -124,6 +136,12 @@ namespace Hyper.Internal.Managers
|
|
|
124
136
|
{
|
|
125
137
|
if (IsBattleMode) return;
|
|
126
138
|
|
|
139
|
+
if (ReplaySessionState.IsActive)
|
|
140
|
+
{
|
|
141
|
+
EndReplayGame();
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
127
145
|
SetGameEnded();
|
|
128
146
|
HyperRuntime.Timer.StopAllTimers();
|
|
129
147
|
|
|
@@ -158,6 +176,7 @@ namespace Hyper.Internal.Managers
|
|
|
158
176
|
|
|
159
177
|
internal void PauseGame()
|
|
160
178
|
{
|
|
179
|
+
if (IsReplayMode) return;
|
|
161
180
|
if (!_hasGameStarted || _isPaused || _hasGameEnded) return;
|
|
162
181
|
|
|
163
182
|
_isPaused = true;
|
|
@@ -190,11 +209,66 @@ namespace Hyper.Internal.Managers
|
|
|
190
209
|
|
|
191
210
|
void OnApplicationFocus(bool hasFocus)
|
|
192
211
|
{
|
|
212
|
+
if (IsReplayMode)
|
|
213
|
+
{
|
|
214
|
+
HandleReplayFocus(hasFocus);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
193
217
|
#if !UNITY_EDITOR
|
|
194
218
|
HandlePauseState(!hasFocus);
|
|
195
219
|
#endif
|
|
196
220
|
}
|
|
197
221
|
|
|
222
|
+
private void HandleReplayFocus(bool hasFocus)
|
|
223
|
+
{
|
|
224
|
+
if (!_hasGameStarted || _hasGameEnded) return;
|
|
225
|
+
|
|
226
|
+
if (!hasFocus && _replayFocusResumeCoroutine != null)
|
|
227
|
+
{
|
|
228
|
+
StopCoroutine(_replayFocusResumeCoroutine);
|
|
229
|
+
_replayFocusResumeCoroutine = null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!hasFocus && !_replayFocusSuspended)
|
|
233
|
+
{
|
|
234
|
+
_replayFocusSuspended = true;
|
|
235
|
+
_savedReplayTimeScale = Time.timeScale;
|
|
236
|
+
Time.timeScale = 0f;
|
|
237
|
+
HyperRuntime.Timer.PauseGameplayTimers();
|
|
238
|
+
|
|
239
|
+
// WebGL may stop producing frames immediately after focus loss,
|
|
240
|
+
// so suspend replay timing here instead of waiting for Update.
|
|
241
|
+
_previousTimeScale = 0f;
|
|
242
|
+
_eventManager.InvokeTimeScaleFrozenEvent();
|
|
243
|
+
HyperDebug.Log("Time scale frozen event invoked");
|
|
244
|
+
}
|
|
245
|
+
else if (hasFocus && _replayFocusSuspended)
|
|
246
|
+
{
|
|
247
|
+
if (_replayFocusResumeCoroutine == null)
|
|
248
|
+
{
|
|
249
|
+
_replayFocusResumeCoroutine = StartCoroutine(ResumeReplayAfterFocus());
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private IEnumerator ResumeReplayAfterFocus()
|
|
255
|
+
{
|
|
256
|
+
// WebGL reports focus before Unity refreshes its unscaled clock. Keep
|
|
257
|
+
// gameplay and replay frozen briefly while session time continues.
|
|
258
|
+
yield return new WaitForSecondsRealtime(1f);
|
|
259
|
+
|
|
260
|
+
_replayFocusResumeCoroutine = null;
|
|
261
|
+
if (!_replayFocusSuspended || _hasGameEnded) yield break;
|
|
262
|
+
|
|
263
|
+
_replayFocusSuspended = false;
|
|
264
|
+
Time.timeScale = _savedReplayTimeScale > 0f ? _savedReplayTimeScale : 1f;
|
|
265
|
+
HyperRuntime.Timer.ResumeGameTimersAfterPause();
|
|
266
|
+
|
|
267
|
+
_previousTimeScale = Time.timeScale;
|
|
268
|
+
_eventManager.InvokeTimeScaleResumedEvent();
|
|
269
|
+
HyperDebug.Log("Time scale resumed event invoked");
|
|
270
|
+
}
|
|
271
|
+
|
|
198
272
|
void HandlePauseState(bool shouldPause)
|
|
199
273
|
{
|
|
200
274
|
if (shouldPause && !_isPaused)
|
|
@@ -214,7 +288,7 @@ namespace Hyper.Internal.Managers
|
|
|
214
288
|
_eventManager.InvokeTimeScaleFrozenEvent();
|
|
215
289
|
HyperDebug.Log("Time scale frozen event invoked");
|
|
216
290
|
|
|
217
|
-
if (_backendManager.isSocketConnected)
|
|
291
|
+
if (!IsReplayMode && _backendManager.isSocketConnected)
|
|
218
292
|
{
|
|
219
293
|
_dataManager.StopCapturingScores();
|
|
220
294
|
_backendManager.StopSendingScores();
|
|
@@ -227,13 +301,13 @@ namespace Hyper.Internal.Managers
|
|
|
227
301
|
|
|
228
302
|
// Always restart capturing when game resumes, even if offline,
|
|
229
303
|
// so scores accumulate for the offline batch and are flushed on reconnect.
|
|
230
|
-
if (!_hasGameEnded && _hasGameStarted)
|
|
304
|
+
if (!IsReplayMode && !_hasGameEnded && _hasGameStarted)
|
|
231
305
|
{
|
|
232
306
|
_dataManager.StartCapturingScores();
|
|
233
307
|
}
|
|
234
308
|
|
|
235
309
|
// Only resume server session if connected
|
|
236
|
-
if (_backendManager.isSocketConnected && !_hasGameEnded && _hasGameStarted)
|
|
310
|
+
if (!IsReplayMode && _backendManager.isSocketConnected && !_hasGameEnded && _hasGameStarted)
|
|
237
311
|
{
|
|
238
312
|
_backendManager.SendGameResumed();
|
|
239
313
|
}
|
|
@@ -336,6 +410,7 @@ namespace Hyper.Internal.Managers
|
|
|
336
410
|
|
|
337
411
|
internal void ShowConnectionLost()
|
|
338
412
|
{
|
|
413
|
+
if (IsReplayMode) return;
|
|
339
414
|
if (!IsInGameScene) return;
|
|
340
415
|
|
|
341
416
|
EnsureConnectionStatusBar();
|
|
@@ -352,6 +427,7 @@ namespace Hyper.Internal.Managers
|
|
|
352
427
|
|
|
353
428
|
internal void ShowConnectionRestored()
|
|
354
429
|
{
|
|
430
|
+
if (IsReplayMode) return;
|
|
355
431
|
if (!IsInGameScene) return;
|
|
356
432
|
|
|
357
433
|
EnsureConnectionStatusBar();
|
|
@@ -374,7 +450,7 @@ namespace Hyper.Internal.Managers
|
|
|
374
450
|
if (!_loadingFailedModal.activeInHierarchy)
|
|
375
451
|
{
|
|
376
452
|
_loadingFailedModal.SetActive(true);
|
|
377
|
-
SetBlockerActive(true);
|
|
453
|
+
SetBlockerActive(true, 0.05f);
|
|
378
454
|
}
|
|
379
455
|
_eventManager.InvokeConnectionLostEvent();
|
|
380
456
|
}
|
|
@@ -389,6 +465,59 @@ namespace Hyper.Internal.Managers
|
|
|
389
465
|
}
|
|
390
466
|
}
|
|
391
467
|
|
|
468
|
+
internal void RetryReplayLoading()
|
|
469
|
+
{
|
|
470
|
+
if (!IsReplayMode) return;
|
|
471
|
+
|
|
472
|
+
if (_replayConnectionCheckCoroutine != null)
|
|
473
|
+
{
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
_replayConnectionCheckCoroutine = StartCoroutine(CheckReplayConnection());
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private IEnumerator CheckReplayConnection()
|
|
481
|
+
{
|
|
482
|
+
const float timeoutSeconds = 5f;
|
|
483
|
+
float startedAt = Time.realtimeSinceStartup;
|
|
484
|
+
bool checkInProgress = false;
|
|
485
|
+
bool isConnected = false;
|
|
486
|
+
|
|
487
|
+
_loadingFailedModalComponent?.ShowRetryCountdown(Mathf.CeilToInt(timeoutSeconds));
|
|
488
|
+
|
|
489
|
+
while (Time.realtimeSinceStartup - startedAt < timeoutSeconds)
|
|
490
|
+
{
|
|
491
|
+
if (!isConnected && !checkInProgress)
|
|
492
|
+
{
|
|
493
|
+
checkInProgress = true;
|
|
494
|
+
StartCoroutine(HyperRuntime.BackendManager.CheckInternetConnection(connected =>
|
|
495
|
+
{
|
|
496
|
+
isConnected = connected;
|
|
497
|
+
checkInProgress = false;
|
|
498
|
+
}));
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
float remaining = timeoutSeconds - (Time.realtimeSinceStartup - startedAt);
|
|
502
|
+
_loadingFailedModalComponent?.ShowRetryCountdown(Mathf.CeilToInt(remaining));
|
|
503
|
+
yield return null;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
_replayConnectionCheckCoroutine = null;
|
|
507
|
+
|
|
508
|
+
if (isConnected)
|
|
509
|
+
{
|
|
510
|
+
// This emits connection-found, allowing the existing loader to restart
|
|
511
|
+
// bootstrap and fetch/validate all replay data through its normal path.
|
|
512
|
+
ShowLoadingFailedModal(false);
|
|
513
|
+
yield break;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// The probe may finish early or reach the five-second deadline. In either
|
|
517
|
+
// case restore the modal CTA state instead of waiting at 00:00.
|
|
518
|
+
_loadingFailedModalComponent?.AllAttemptsHaveFailed();
|
|
519
|
+
}
|
|
520
|
+
|
|
392
521
|
internal void ShowScoreSubmissionFailedModal()
|
|
393
522
|
{
|
|
394
523
|
if (IsPortrait())
|
|
@@ -484,6 +613,12 @@ namespace Hyper.Internal.Managers
|
|
|
484
613
|
|
|
485
614
|
internal void ShowGameOverModal()
|
|
486
615
|
{
|
|
616
|
+
if (IsReplayMode)
|
|
617
|
+
{
|
|
618
|
+
ShowReplayOverModal();
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
487
622
|
HidePauseModals();
|
|
488
623
|
|
|
489
624
|
if (IsBattleMode)
|
|
@@ -585,12 +720,16 @@ namespace Hyper.Internal.Managers
|
|
|
585
720
|
#region Private State
|
|
586
721
|
|
|
587
722
|
private bool _hasGameStarted;
|
|
723
|
+
private bool _gameStartRequested;
|
|
588
724
|
private bool _isPaused;
|
|
589
725
|
private bool _hasGameEnded;
|
|
726
|
+
private bool _replayEndHandled;
|
|
590
727
|
private bool _isFrozenBeforePause;
|
|
591
728
|
private bool _isPortrait;
|
|
592
729
|
private float _previousTimeScale = 1f;
|
|
593
730
|
private float _savedTimeScaleBeforePause = 1f;
|
|
731
|
+
private bool _replayFocusSuspended;
|
|
732
|
+
private float _savedReplayTimeScale = 1f;
|
|
594
733
|
|
|
595
734
|
#endregion
|
|
596
735
|
|
|
@@ -609,6 +748,9 @@ namespace Hyper.Internal.Managers
|
|
|
609
748
|
|
|
610
749
|
[DllImport("__Internal")]
|
|
611
750
|
private static extern void MarkGameLoadedOnHTML();
|
|
751
|
+
|
|
752
|
+
[DllImport("__Internal")]
|
|
753
|
+
private static extern void MarkGameStartedOnHTML();
|
|
612
754
|
#endif
|
|
613
755
|
|
|
614
756
|
private void HandleQueryParameters()
|
|
@@ -618,6 +760,8 @@ namespace Hyper.Internal.Managers
|
|
|
618
760
|
if (queryParamsPtr == IntPtr.Zero)
|
|
619
761
|
{
|
|
620
762
|
HyperDebug.LogError("Failed to get query parameters");
|
|
763
|
+
Config = new URLData { GameMode = GameMode.Practice };
|
|
764
|
+
SyncReplayContext();
|
|
621
765
|
return;
|
|
622
766
|
}
|
|
623
767
|
|
|
@@ -637,23 +781,32 @@ namespace Hyper.Internal.Managers
|
|
|
637
781
|
}
|
|
638
782
|
#else
|
|
639
783
|
Config = HyperRuntime.Settings._settings;
|
|
784
|
+
#if !UNITY_EDITOR
|
|
785
|
+
if (Config.GameMode == GameMode.Replay)
|
|
786
|
+
{
|
|
787
|
+
Config.GameMode = GameMode.Practice;
|
|
788
|
+
}
|
|
789
|
+
#endif
|
|
790
|
+
SyncReplayContext();
|
|
640
791
|
#endif
|
|
641
792
|
}
|
|
642
793
|
|
|
643
794
|
private void ParseQueryParameters()
|
|
644
795
|
{
|
|
645
|
-
|
|
796
|
+
SetGameMode();
|
|
646
797
|
SetBackendGameStatus();
|
|
647
798
|
SetOptionalParameters();
|
|
648
799
|
SetSafeAreaBounds();
|
|
800
|
+
SyncReplayContext();
|
|
649
801
|
}
|
|
650
802
|
|
|
651
|
-
private void
|
|
803
|
+
private void SetGameMode()
|
|
652
804
|
{
|
|
653
|
-
bool
|
|
654
|
-
|
|
805
|
+
bool isReplay = IsTrueQueryFlag("replay");
|
|
806
|
+
bool isPVP = IsTrueQueryFlag("pvp");
|
|
807
|
+
Config.SetModeFromLaunchFlags(isPVP, isReplay);
|
|
655
808
|
|
|
656
|
-
if (
|
|
809
|
+
if (Config.IsBattleGameplay && QueryParamsData.TryGetValue("time", out string timeValue))
|
|
657
810
|
{
|
|
658
811
|
if (float.TryParse(timeValue, out float timeLimit))
|
|
659
812
|
{
|
|
@@ -662,6 +815,35 @@ namespace Hyper.Internal.Managers
|
|
|
662
815
|
}
|
|
663
816
|
}
|
|
664
817
|
|
|
818
|
+
private bool IsTrueQueryFlag(string key)
|
|
819
|
+
{
|
|
820
|
+
return QueryParamsData.TryGetValue(key, out string value) &&
|
|
821
|
+
string.Equals(value, "true", StringComparison.OrdinalIgnoreCase);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
private void SyncReplayContext()
|
|
825
|
+
{
|
|
826
|
+
if (IsReplayMode)
|
|
827
|
+
{
|
|
828
|
+
string entryId = Config.EntryId ?? string.Empty;
|
|
829
|
+
bool isNewReplay = !ReplaySessionState.IsActive ||
|
|
830
|
+
!string.Equals(ReplaySessionState.EntryId, entryId, StringComparison.Ordinal);
|
|
831
|
+
|
|
832
|
+
if (isNewReplay)
|
|
833
|
+
{
|
|
834
|
+
ReplaySessionState.Configure(
|
|
835
|
+
string.Empty,
|
|
836
|
+
string.Empty,
|
|
837
|
+
(int)Config.RandomSeed,
|
|
838
|
+
entryId);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
else
|
|
842
|
+
{
|
|
843
|
+
ReplaySessionState.Clear();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
665
847
|
private void SetBackendGameStatus()
|
|
666
848
|
{
|
|
667
849
|
string[] requiredFields = { "tournamentId", "gameId", "entryId", "host", "embedUrl" };
|
|
@@ -680,6 +862,14 @@ namespace Hyper.Internal.Managers
|
|
|
680
862
|
|
|
681
863
|
private void SetOptionalParameters()
|
|
682
864
|
{
|
|
865
|
+
// Replay launches only require replay=true and entryId, so capture it
|
|
866
|
+
// independently of the full backend PvP launch contract.
|
|
867
|
+
if (QueryParamsData.TryGetValue("entryId", out string entryId) &&
|
|
868
|
+
!string.IsNullOrWhiteSpace(entryId))
|
|
869
|
+
{
|
|
870
|
+
Config.EntryId = entryId;
|
|
871
|
+
}
|
|
872
|
+
|
|
683
873
|
if (QueryParamsData.TryGetValue("gameSlug", out string gameSlug))
|
|
684
874
|
Config.GameSlug = gameSlug;
|
|
685
875
|
|
|
@@ -766,6 +956,7 @@ namespace Hyper.Internal.Managers
|
|
|
766
956
|
{
|
|
767
957
|
IsInGameScene = false;
|
|
768
958
|
_hasGameStarted = false;
|
|
959
|
+
_gameStartRequested = false;
|
|
769
960
|
_hasGameEnded = false;
|
|
770
961
|
_isPaused = false;
|
|
771
962
|
_isFrozenBeforePause = false;
|
|
@@ -790,7 +981,9 @@ namespace Hyper.Internal.Managers
|
|
|
790
981
|
HideGameOverModal();
|
|
791
982
|
IsInGameScene = true;
|
|
792
983
|
_hasGameStarted = false;
|
|
984
|
+
_gameStartRequested = false;
|
|
793
985
|
_hasGameEnded = false;
|
|
986
|
+
_replayEndHandled = false;
|
|
794
987
|
_isPaused = false;
|
|
795
988
|
_isFrozenBeforePause = false;
|
|
796
989
|
_previousTimeScale = Time.timeScale; // Sync with actual time scale on scene load
|
|
@@ -798,7 +991,7 @@ namespace Hyper.Internal.Managers
|
|
|
798
991
|
|
|
799
992
|
private void InitializeSceneUI()
|
|
800
993
|
{
|
|
801
|
-
if (
|
|
994
|
+
if (IsBattleMode)
|
|
802
995
|
{
|
|
803
996
|
HyperRuntime.Timer.StartPreSessionTimer();
|
|
804
997
|
HyperRuntime.Timer.StartFiveSecondCountdown();
|
|
@@ -810,7 +1003,44 @@ namespace Hyper.Internal.Managers
|
|
|
810
1003
|
|
|
811
1004
|
ShowControlBarElements();
|
|
812
1005
|
AddTutorialModal();
|
|
813
|
-
|
|
1006
|
+
if (IsReplayMode && ReplaySessionState.IsSeeking)
|
|
1007
|
+
{
|
|
1008
|
+
StartCoroutine(StartSeekingReplayWhenReady());
|
|
1009
|
+
}
|
|
1010
|
+
else
|
|
1011
|
+
{
|
|
1012
|
+
ShowTutorialAnnouncementOrStartingInstruction();
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
private IEnumerator StartSeekingReplayWhenReady()
|
|
1017
|
+
{
|
|
1018
|
+
// Allow scene scripts to subscribe to OnGameStart without presenting Tap to Start.
|
|
1019
|
+
yield return null;
|
|
1020
|
+
StartGameplay();
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
internal void StartGameplay()
|
|
1024
|
+
{
|
|
1025
|
+
if (_gameStartRequested || _hasGameStarted || _hasGameEnded || !IsInGameScene) return;
|
|
1026
|
+
_gameStartRequested = true;
|
|
1027
|
+
|
|
1028
|
+
HideStartGameDeadlineSnackBar();
|
|
1029
|
+
_dataManager.UpdateScore(0);
|
|
1030
|
+
HyperRuntime.Timer.StartActiveGameTimer();
|
|
1031
|
+
_dataManager.StartCapturingScores();
|
|
1032
|
+
_backendManager.StartSendingScores();
|
|
1033
|
+
|
|
1034
|
+
if (_backendManager.isSocketConnected)
|
|
1035
|
+
{
|
|
1036
|
+
_backendManager.SendGameStarted();
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
1040
|
+
MarkGameStartedOnHTML();
|
|
1041
|
+
#endif
|
|
1042
|
+
_eventManager.InvokeGameStartEvent();
|
|
1043
|
+
HideStartingInstructionModal();
|
|
814
1044
|
}
|
|
815
1045
|
|
|
816
1046
|
private IEnumerator BootstrapAndStart()
|
|
@@ -841,6 +1071,7 @@ namespace Hyper.Internal.Managers
|
|
|
841
1071
|
|
|
842
1072
|
// Reset game state flags
|
|
843
1073
|
_hasGameStarted = false;
|
|
1074
|
+
_gameStartRequested = false;
|
|
844
1075
|
_hasGameEnded = false;
|
|
845
1076
|
_isPaused = false;
|
|
846
1077
|
_isFrozenBeforePause = false;
|
|
@@ -859,6 +1090,12 @@ namespace Hyper.Internal.Managers
|
|
|
859
1090
|
|
|
860
1091
|
private void EndGameInternal()
|
|
861
1092
|
{
|
|
1093
|
+
if (IsReplayMode)
|
|
1094
|
+
{
|
|
1095
|
+
EndReplayGame();
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
862
1099
|
SetGameEnded();
|
|
863
1100
|
|
|
864
1101
|
_eventManager.InvokeGameEndedEvent();
|
|
@@ -878,6 +1115,57 @@ namespace Hyper.Internal.Managers
|
|
|
878
1115
|
}
|
|
879
1116
|
}
|
|
880
1117
|
|
|
1118
|
+
private void EndReplayGame()
|
|
1119
|
+
{
|
|
1120
|
+
if (ReplaySessionState.IsSeekRestartPending) return;
|
|
1121
|
+
|
|
1122
|
+
// Time Up marks _hasGameEnded before its two-second presentation finishes.
|
|
1123
|
+
// Track replay finalization separately so that delayed end still runs once.
|
|
1124
|
+
if (_replayEndHandled) return;
|
|
1125
|
+
|
|
1126
|
+
_replayEndHandled = true;
|
|
1127
|
+
_hasGameEnded = true;
|
|
1128
|
+
_replayFocusSuspended = false;
|
|
1129
|
+
Time.timeScale = 1f;
|
|
1130
|
+
_eventManager.InvokeGameEndedEvent();
|
|
1131
|
+
HyperRuntime.Timer.StopAllTimers();
|
|
1132
|
+
_dataManager.StopCapturingScores();
|
|
1133
|
+
HyperRuntime.BackendManager.StopSendingScores();
|
|
1134
|
+
ReplaySessionState.MarkEnded();
|
|
1135
|
+
_replayControllerComponent?.SetReplayEnded();
|
|
1136
|
+
ShowReplayOverModal();
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
internal void ExitReplayEarly()
|
|
1140
|
+
{
|
|
1141
|
+
if (!IsReplayMode) return;
|
|
1142
|
+
|
|
1143
|
+
if (!_replayEndHandled)
|
|
1144
|
+
{
|
|
1145
|
+
_replayEndHandled = true;
|
|
1146
|
+
_hasGameEnded = true;
|
|
1147
|
+
_eventManager.InvokeGameEndedEvent();
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
Time.timeScale = 1f;
|
|
1151
|
+
HyperRuntime.Timer.StopAllTimers();
|
|
1152
|
+
_dataManager.StopCapturingScores();
|
|
1153
|
+
HyperRuntime.BackendManager.StopSendingScores();
|
|
1154
|
+
_replayControllerComponent?.SetReplayEnded();
|
|
1155
|
+
ReplaySessionState.Clear();
|
|
1156
|
+
ReplayPresentationCanvas.DestroyCurrent();
|
|
1157
|
+
HyperRuntime.BackendManager.LogEvent(ConsoleEventType.PracticeGameEnd);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
internal void ExitCompletedReplay()
|
|
1161
|
+
{
|
|
1162
|
+
if (!IsReplayMode) return;
|
|
1163
|
+
|
|
1164
|
+
ReplaySessionState.Clear();
|
|
1165
|
+
ReplayPresentationCanvas.DestroyCurrent();
|
|
1166
|
+
HyperRuntime.BackendManager.LogEvent(ConsoleEventType.PracticeGameEnd);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
881
1169
|
internal void EndGame(string key)
|
|
882
1170
|
{
|
|
883
1171
|
if (key == "Hyper_Time_Up")
|
|
@@ -969,6 +1257,12 @@ namespace Hyper.Internal.Managers
|
|
|
969
1257
|
private GameObject _blockerUI;
|
|
970
1258
|
private Image _blockerUIImage;
|
|
971
1259
|
private GameObject _safeArea;
|
|
1260
|
+
private GameObject _replayInputBlocker;
|
|
1261
|
+
private GameObject _replayControllerObject;
|
|
1262
|
+
private ReplayController _replayControllerComponent;
|
|
1263
|
+
private GameObject _replayOverModal;
|
|
1264
|
+
private Coroutine _replayConnectionCheckCoroutine;
|
|
1265
|
+
private Coroutine _replayFocusResumeCoroutine;
|
|
972
1266
|
|
|
973
1267
|
private GameObject _connectionStatusBar;
|
|
974
1268
|
private GameObject _startGameDeadlineStatusBar;
|
|
@@ -1085,7 +1379,7 @@ namespace Hyper.Internal.Managers
|
|
|
1085
1379
|
#endif
|
|
1086
1380
|
}
|
|
1087
1381
|
|
|
1088
|
-
private void SetBlockerActive(bool isActive)
|
|
1382
|
+
private void SetBlockerActive(bool isActive, float opacity = 0.55f)
|
|
1089
1383
|
{
|
|
1090
1384
|
if (_blockerUI == null) return;
|
|
1091
1385
|
|
|
@@ -1093,7 +1387,7 @@ namespace Hyper.Internal.Managers
|
|
|
1093
1387
|
|
|
1094
1388
|
if (isActive)
|
|
1095
1389
|
{
|
|
1096
|
-
_blockerUIImage.color = new Color(0f, 0f, 0f,
|
|
1390
|
+
_blockerUIImage.color = new Color(0f, 0f, 0f, Mathf.Clamp01(opacity));
|
|
1097
1391
|
int lastActiveIndex = GetLastActiveChildIndex();
|
|
1098
1392
|
_blockerUI.transform.SetSiblingIndex(lastActiveIndex - 1);
|
|
1099
1393
|
}
|
|
@@ -1121,6 +1415,11 @@ namespace Hyper.Internal.Managers
|
|
|
1121
1415
|
if (_controlBarElements == null) CreateControlBarElements();
|
|
1122
1416
|
|
|
1123
1417
|
_controlBarElements.SetActive(true);
|
|
1418
|
+
|
|
1419
|
+
if (IsReplayMode)
|
|
1420
|
+
{
|
|
1421
|
+
EnsureReplayPresentation();
|
|
1422
|
+
}
|
|
1124
1423
|
}
|
|
1125
1424
|
|
|
1126
1425
|
private void HideTopBarElements()
|
|
@@ -1157,6 +1456,42 @@ namespace Hyper.Internal.Managers
|
|
|
1157
1456
|
_controlBarModal = _controlBarElements.GetComponent<ControlBar>();
|
|
1158
1457
|
}
|
|
1159
1458
|
|
|
1459
|
+
private void EnsureReplayPresentation()
|
|
1460
|
+
{
|
|
1461
|
+
ReplayPresentationCanvas presentation = ReplayPresentationCanvas.GetOrCreate(_isPortrait);
|
|
1462
|
+
_replayInputBlocker = presentation.InputBlocker;
|
|
1463
|
+
_replayControllerObject = presentation.ControllerObject;
|
|
1464
|
+
_replayControllerComponent = presentation.Controller;
|
|
1465
|
+
EventSystem.current?.SetSelectedGameObject(null);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
private void ShowReplayOverModal()
|
|
1469
|
+
{
|
|
1470
|
+
HidePauseModals();
|
|
1471
|
+
|
|
1472
|
+
if (_replayOverModal == null)
|
|
1473
|
+
{
|
|
1474
|
+
if (_canvas == null) CreateCanvas();
|
|
1475
|
+
GameObject prefab = Resources.Load<GameObject>("UIPrefabs/ReplayOverModal");
|
|
1476
|
+
if (prefab == null)
|
|
1477
|
+
{
|
|
1478
|
+
HyperDebug.LogWarning("[GameManager] ReplayOverModal prefab not found.");
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
_replayOverModal = InstantiateModal(prefab);
|
|
1483
|
+
if (_replayOverModal.GetComponent<ReplayOverModal>() == null)
|
|
1484
|
+
{
|
|
1485
|
+
_replayOverModal.AddComponent<ReplayOverModal>();
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
_replayOverModal.SetActive(true);
|
|
1490
|
+
_replayOverModal.transform.SetAsLastSibling();
|
|
1491
|
+
SetBlockerActive(true);
|
|
1492
|
+
_replayOverModal.transform.SetAsLastSibling();
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1160
1495
|
private void EnsureConnectionStatusBar()
|
|
1161
1496
|
{
|
|
1162
1497
|
if (_connectionStatusBar != null) return;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
using Hyper.Shared;
|
|
2
|
+
|
|
3
|
+
namespace Hyper.Internal.Managers
|
|
4
|
+
{
|
|
5
|
+
/// <summary>
|
|
6
|
+
/// Owns replay playback for the active SDK session. Games only subscribe to
|
|
7
|
+
/// typed activity callbacks; loading, timing, progress, suspension and ending
|
|
8
|
+
/// are handled here.
|
|
9
|
+
/// </summary>
|
|
10
|
+
internal sealed class ReplayManager
|
|
11
|
+
{
|
|
12
|
+
private IReplayPlaybackEngine _playback;
|
|
13
|
+
private EventManager _eventManager;
|
|
14
|
+
private bool _endingGame;
|
|
15
|
+
|
|
16
|
+
internal void Initialize(EventManager eventManager)
|
|
17
|
+
{
|
|
18
|
+
_eventManager = eventManager;
|
|
19
|
+
_eventManager.OnGameStart += HandleGameStart;
|
|
20
|
+
_eventManager.OnGameEnded += HandleGameEnded;
|
|
21
|
+
_eventManager.OnTimeScaleFrozen += HandleTimeScaleFrozen;
|
|
22
|
+
_eventManager.OnTimeScaleResumed += HandleTimeScaleResumed;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
internal void AttachPlayback(IReplayPlaybackEngine playback)
|
|
26
|
+
{
|
|
27
|
+
if (ReferenceEquals(_playback, playback)) return;
|
|
28
|
+
DetachPlayback();
|
|
29
|
+
_playback = playback;
|
|
30
|
+
if (_playback == null) return;
|
|
31
|
+
|
|
32
|
+
_playback.OnGameEnded += HandlePlaybackEnded;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
internal void FixedTick()
|
|
36
|
+
{
|
|
37
|
+
if (_playback == null || !_playback.IsPlaying) return;
|
|
38
|
+
_playback.Tick();
|
|
39
|
+
ReplaySessionState.SetProgress(_playback.Progress01);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
internal void Cleanup()
|
|
43
|
+
{
|
|
44
|
+
if (_eventManager != null)
|
|
45
|
+
{
|
|
46
|
+
_eventManager.OnGameStart -= HandleGameStart;
|
|
47
|
+
_eventManager.OnGameEnded -= HandleGameEnded;
|
|
48
|
+
_eventManager.OnTimeScaleFrozen -= HandleTimeScaleFrozen;
|
|
49
|
+
_eventManager.OnTimeScaleResumed -= HandleTimeScaleResumed;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
DetachPlayback();
|
|
53
|
+
_eventManager = null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private void HandleGameStart()
|
|
57
|
+
{
|
|
58
|
+
if (HyperRuntime.GameManager == null || !HyperRuntime.GameManager.IsReplayMode ||
|
|
59
|
+
!ReplaySessionState.HasReplayData || _playback == null)
|
|
60
|
+
{
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (ReplaySessionState.IsSeeking)
|
|
65
|
+
{
|
|
66
|
+
ReplaySessionState.NotifySeekPlaybackStarted();
|
|
67
|
+
UnityEngine.Time.timeScale = ReplaySessionState.PlaybackSpeed;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
_playback.Begin(ReplaySessionState.ReplayNdjson);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private void HandleGameEnded()
|
|
74
|
+
{
|
|
75
|
+
if (_endingGame) return;
|
|
76
|
+
_playback?.Stop();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private void HandleTimeScaleFrozen()
|
|
80
|
+
{
|
|
81
|
+
if (HyperRuntime.GameManager != null && HyperRuntime.GameManager.IsReplayMode)
|
|
82
|
+
_playback?.Suspend();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private void HandleTimeScaleResumed()
|
|
86
|
+
{
|
|
87
|
+
if (HyperRuntime.GameManager != null && HyperRuntime.GameManager.IsReplayMode)
|
|
88
|
+
_playback?.Resume();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private void HandlePlaybackEnded()
|
|
92
|
+
{
|
|
93
|
+
ReplaySessionState.MarkEnded();
|
|
94
|
+
|
|
95
|
+
GameManager gameManager = HyperRuntime.GameManager;
|
|
96
|
+
if (gameManager == null || gameManager.HasGameEnded) return;
|
|
97
|
+
|
|
98
|
+
_endingGame = true;
|
|
99
|
+
gameManager.EndGame();
|
|
100
|
+
_endingGame = false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private void DetachPlayback()
|
|
104
|
+
{
|
|
105
|
+
if (_playback == null) return;
|
|
106
|
+
_playback.Stop();
|
|
107
|
+
_playback.OnGameEnded -= HandlePlaybackEnded;
|
|
108
|
+
_playback = null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|