app.hypergames.hypersdk 1.10.0 → 1.11.0-beta.2
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 +34 -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 +82 -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 +365 -17
- package/Runtime/Internal/Managers/ReplayManager.cs +117 -0
- package/Runtime/Internal/Managers/ReplayManager.cs.meta +11 -0
- package/Runtime/Internal/Managers/TimeManager.cs +54 -12
- 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/ControlBar.cs +25 -2
- package/Runtime/Internal/UI/Components/PauseButton.cs +4 -1
- package/Runtime/Internal/UI/Components/ReplayController.cs +527 -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/Modals/LoadingFailedModal.cs +12 -0
- package/Runtime/Internal/UI/Modals/ReplayOverModal.cs +61 -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/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/Replays/Recording.png.meta +156 -0
- 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 +169 -2
- 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/SnackBar.prefab +2 -2
- 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/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 +8 -1
|
@@ -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()
|
|
@@ -838,19 +1068,33 @@ namespace Hyper.Internal.Managers
|
|
|
838
1068
|
{
|
|
839
1069
|
HideGameOverModal();
|
|
840
1070
|
HidePauseModals();
|
|
1071
|
+
if (_replayOverModal != null) _replayOverModal.SetActive(false);
|
|
841
1072
|
|
|
842
1073
|
// Reset game state flags
|
|
843
1074
|
_hasGameStarted = false;
|
|
1075
|
+
_gameStartRequested = false;
|
|
844
1076
|
_hasGameEnded = false;
|
|
1077
|
+
_replayEndHandled = false;
|
|
1078
|
+
_replayFocusSuspended = false;
|
|
845
1079
|
_isPaused = false;
|
|
846
1080
|
_isFrozenBeforePause = false;
|
|
847
1081
|
|
|
848
|
-
HyperRuntime.Timer.
|
|
1082
|
+
HyperRuntime.Timer.ResetForGameRestart();
|
|
849
1083
|
HyperRuntime.DataManager.UpdateScore(0);
|
|
850
1084
|
HyperRuntime.DataManager.RestoreAudioAfterGameSessionEnd();
|
|
851
1085
|
|
|
852
1086
|
ShowControlBarElements();
|
|
853
|
-
|
|
1087
|
+
_replayControllerComponent?.ResetForReplayRestart();
|
|
1088
|
+
|
|
1089
|
+
if (IsReplayMode && ReplaySessionState.IsSeeking)
|
|
1090
|
+
{
|
|
1091
|
+
HideStartingInstructionModal();
|
|
1092
|
+
StartCoroutine(StartSeekingReplayWhenReady());
|
|
1093
|
+
}
|
|
1094
|
+
else
|
|
1095
|
+
{
|
|
1096
|
+
ShowStartingInstructionModal();
|
|
1097
|
+
}
|
|
854
1098
|
}
|
|
855
1099
|
|
|
856
1100
|
#endregion
|
|
@@ -859,6 +1103,12 @@ namespace Hyper.Internal.Managers
|
|
|
859
1103
|
|
|
860
1104
|
private void EndGameInternal()
|
|
861
1105
|
{
|
|
1106
|
+
if (IsReplayMode)
|
|
1107
|
+
{
|
|
1108
|
+
EndReplayGame();
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
862
1112
|
SetGameEnded();
|
|
863
1113
|
|
|
864
1114
|
_eventManager.InvokeGameEndedEvent();
|
|
@@ -878,6 +1128,57 @@ namespace Hyper.Internal.Managers
|
|
|
878
1128
|
}
|
|
879
1129
|
}
|
|
880
1130
|
|
|
1131
|
+
private void EndReplayGame()
|
|
1132
|
+
{
|
|
1133
|
+
if (ReplaySessionState.IsSeekRestartPending) return;
|
|
1134
|
+
|
|
1135
|
+
// Time Up marks _hasGameEnded before its two-second presentation finishes.
|
|
1136
|
+
// Track replay finalization separately so that delayed end still runs once.
|
|
1137
|
+
if (_replayEndHandled) return;
|
|
1138
|
+
|
|
1139
|
+
_replayEndHandled = true;
|
|
1140
|
+
_hasGameEnded = true;
|
|
1141
|
+
_replayFocusSuspended = false;
|
|
1142
|
+
Time.timeScale = 1f;
|
|
1143
|
+
_eventManager.InvokeGameEndedEvent();
|
|
1144
|
+
HyperRuntime.Timer.StopAllTimers();
|
|
1145
|
+
_dataManager.StopCapturingScores();
|
|
1146
|
+
HyperRuntime.BackendManager.StopSendingScores();
|
|
1147
|
+
ReplaySessionState.MarkEnded();
|
|
1148
|
+
_replayControllerComponent?.SetReplayEnded();
|
|
1149
|
+
ShowReplayOverModal();
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
internal void ExitReplayEarly()
|
|
1153
|
+
{
|
|
1154
|
+
if (!IsReplayMode) return;
|
|
1155
|
+
|
|
1156
|
+
if (!_replayEndHandled)
|
|
1157
|
+
{
|
|
1158
|
+
_replayEndHandled = true;
|
|
1159
|
+
_hasGameEnded = true;
|
|
1160
|
+
_eventManager.InvokeGameEndedEvent();
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
Time.timeScale = 1f;
|
|
1164
|
+
HyperRuntime.Timer.StopAllTimers();
|
|
1165
|
+
_dataManager.StopCapturingScores();
|
|
1166
|
+
HyperRuntime.BackendManager.StopSendingScores();
|
|
1167
|
+
_replayControllerComponent?.SetReplayEnded();
|
|
1168
|
+
ReplaySessionState.Clear();
|
|
1169
|
+
ReplayPresentationCanvas.DestroyCurrent();
|
|
1170
|
+
HyperRuntime.BackendManager.LogEvent(ConsoleEventType.PracticeGameEnd);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
internal void ExitCompletedReplay()
|
|
1174
|
+
{
|
|
1175
|
+
if (!IsReplayMode) return;
|
|
1176
|
+
|
|
1177
|
+
ReplaySessionState.Clear();
|
|
1178
|
+
ReplayPresentationCanvas.DestroyCurrent();
|
|
1179
|
+
HyperRuntime.BackendManager.LogEvent(ConsoleEventType.PracticeGameEnd);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
881
1182
|
internal void EndGame(string key)
|
|
882
1183
|
{
|
|
883
1184
|
if (key == "Hyper_Time_Up")
|
|
@@ -969,6 +1270,12 @@ namespace Hyper.Internal.Managers
|
|
|
969
1270
|
private GameObject _blockerUI;
|
|
970
1271
|
private Image _blockerUIImage;
|
|
971
1272
|
private GameObject _safeArea;
|
|
1273
|
+
private GameObject _replayInputBlocker;
|
|
1274
|
+
private GameObject _replayControllerObject;
|
|
1275
|
+
private ReplayController _replayControllerComponent;
|
|
1276
|
+
private GameObject _replayOverModal;
|
|
1277
|
+
private Coroutine _replayConnectionCheckCoroutine;
|
|
1278
|
+
private Coroutine _replayFocusResumeCoroutine;
|
|
972
1279
|
|
|
973
1280
|
private GameObject _connectionStatusBar;
|
|
974
1281
|
private GameObject _startGameDeadlineStatusBar;
|
|
@@ -1085,7 +1392,7 @@ namespace Hyper.Internal.Managers
|
|
|
1085
1392
|
#endif
|
|
1086
1393
|
}
|
|
1087
1394
|
|
|
1088
|
-
private void SetBlockerActive(bool isActive)
|
|
1395
|
+
private void SetBlockerActive(bool isActive, float opacity = 0.55f)
|
|
1089
1396
|
{
|
|
1090
1397
|
if (_blockerUI == null) return;
|
|
1091
1398
|
|
|
@@ -1093,7 +1400,7 @@ namespace Hyper.Internal.Managers
|
|
|
1093
1400
|
|
|
1094
1401
|
if (isActive)
|
|
1095
1402
|
{
|
|
1096
|
-
_blockerUIImage.color = new Color(0f, 0f, 0f,
|
|
1403
|
+
_blockerUIImage.color = new Color(0f, 0f, 0f, Mathf.Clamp01(opacity));
|
|
1097
1404
|
int lastActiveIndex = GetLastActiveChildIndex();
|
|
1098
1405
|
_blockerUI.transform.SetSiblingIndex(lastActiveIndex - 1);
|
|
1099
1406
|
}
|
|
@@ -1121,6 +1428,11 @@ namespace Hyper.Internal.Managers
|
|
|
1121
1428
|
if (_controlBarElements == null) CreateControlBarElements();
|
|
1122
1429
|
|
|
1123
1430
|
_controlBarElements.SetActive(true);
|
|
1431
|
+
|
|
1432
|
+
if (IsReplayMode)
|
|
1433
|
+
{
|
|
1434
|
+
EnsureReplayPresentation();
|
|
1435
|
+
}
|
|
1124
1436
|
}
|
|
1125
1437
|
|
|
1126
1438
|
private void HideTopBarElements()
|
|
@@ -1157,6 +1469,42 @@ namespace Hyper.Internal.Managers
|
|
|
1157
1469
|
_controlBarModal = _controlBarElements.GetComponent<ControlBar>();
|
|
1158
1470
|
}
|
|
1159
1471
|
|
|
1472
|
+
private void EnsureReplayPresentation()
|
|
1473
|
+
{
|
|
1474
|
+
ReplayPresentationCanvas presentation = ReplayPresentationCanvas.GetOrCreate(_isPortrait);
|
|
1475
|
+
_replayInputBlocker = presentation.InputBlocker;
|
|
1476
|
+
_replayControllerObject = presentation.ControllerObject;
|
|
1477
|
+
_replayControllerComponent = presentation.Controller;
|
|
1478
|
+
EventSystem.current?.SetSelectedGameObject(null);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
private void ShowReplayOverModal()
|
|
1482
|
+
{
|
|
1483
|
+
HidePauseModals();
|
|
1484
|
+
|
|
1485
|
+
if (_replayOverModal == null)
|
|
1486
|
+
{
|
|
1487
|
+
if (_canvas == null) CreateCanvas();
|
|
1488
|
+
GameObject prefab = Resources.Load<GameObject>("UIPrefabs/ReplayOverModal");
|
|
1489
|
+
if (prefab == null)
|
|
1490
|
+
{
|
|
1491
|
+
HyperDebug.LogWarning("[GameManager] ReplayOverModal prefab not found.");
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
_replayOverModal = InstantiateModal(prefab);
|
|
1496
|
+
if (_replayOverModal.GetComponent<ReplayOverModal>() == null)
|
|
1497
|
+
{
|
|
1498
|
+
_replayOverModal.AddComponent<ReplayOverModal>();
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
_replayOverModal.SetActive(true);
|
|
1503
|
+
_replayOverModal.transform.SetAsLastSibling();
|
|
1504
|
+
SetBlockerActive(true);
|
|
1505
|
+
_replayOverModal.transform.SetAsLastSibling();
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1160
1508
|
private void EnsureConnectionStatusBar()
|
|
1161
1509
|
{
|
|
1162
1510
|
if (_connectionStatusBar != null) return;
|