app.hypergames.hypersdk 1.5.10 → 1.7.0
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 +21 -0
- package/Editor/Scripts/Tools/HyperBuildAssistant.cs +4 -3
- package/Editor/Scripts/Windows/HyperGameContentWindow.cs +10 -0
- package/Editor/Scripts/Windows/HyperSettingsWindow.cs +10 -0
- package/HyperLoader.unity +3 -3
- package/Runtime/Internal/Bootstrap/HyperLoader.cs +57 -10
- package/Runtime/Internal/Bootstrap/HyperProductionLoggingBootstrap.cs +19 -0
- package/Runtime/Internal/Bootstrap/HyperProductionLoggingBootstrap.cs.meta +11 -0
- package/Runtime/Internal/Core/HyperRuntime.cs +16 -0
- package/Runtime/Internal/Managers/BackendManager.cs +110 -82
- package/Runtime/Internal/Managers/DataManager.cs +0 -11
- package/Runtime/Internal/Managers/EventManager.cs +1 -1
- package/Runtime/Internal/Managers/GameManager.cs +29 -23
- package/Runtime/Internal/Managers/TimeManager.cs +5 -0
- package/Runtime/Internal/ScriptableObjects/HyperGameContentConfig.cs +8 -0
- package/Runtime/Internal/UI/Modals/GamePausedModal.cs +1 -8
- package/Runtime/Plugins/WebGL/HyperCorePlugin.jslib +26 -0
- package/Runtime/Public/Core/HyperSDK.cs +191 -0
- package/Runtime/Public/PlayerDeviceClassification.cs +41 -0
- package/Runtime/Public/PlayerDeviceClassification.cs.meta +2 -0
- package/Runtime/Resources/HyperGameContentConfig.asset +1 -0
- package/Runtime/Resources/UIPrefabs/GamePausedModal_Battle.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/ScoreSubmissionFailedModal_Landscape.prefab +16 -16
- package/Runtime/Resources/UIPrefabs/TimeUpModal.prefab +59 -14
- package/Runtime/Shared/DTOs/Enums.cs +1 -1
- package/Runtime/Shared/Utils/HyperDebug.cs +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# [1.7.0](https://github.com/mvmhyper/hyper-sdk/compare/v1.6.0...v1.7.0) (2026-04-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add loader API, device classification, and landscape canvas scaling fixes ([780ac39](https://github.com/mvmhyper/hyper-sdk/commit/780ac39b2d4f4c73286614a8e6b960fa947276f2))
|
|
7
|
+
|
|
8
|
+
# [1.6.0](https://github.com/mvmhyper/hyper-sdk/compare/v1.5.10...v1.6.0) (2026-03-28)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* avoid auto game resume on reconnect while paused ([ab52983](https://github.com/mvmhyper/hyper-sdk/commit/ab52983215218c658a17a022adda5efe1185bfa1))
|
|
14
|
+
* forward SDK events to FE, fix overlay coverage, disable release logs, and improve editor focus behavior ([1c424ad](https://github.com/mvmhyper/hyper-sdk/commit/1c424adb8dd74ee5f1bfe20896a21547fa3a0b11))
|
|
15
|
+
* send final score as score array payload ([7c06086](https://github.com/mvmhyper/hyper-sdk/commit/7c060869f971aa0a39f07ebc790d7da56462d1d9))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* batch score submission with offline backlog support ([b372643](https://github.com/mvmhyper/hyper-sdk/commit/b3726438239523119d7c13ba3b5f738dcd3b1381))
|
|
21
|
+
|
|
1
22
|
## [1.5.10](https://github.com/mvmhyper/hyper-sdk/compare/v1.5.9...v1.5.10) (2026-03-25)
|
|
2
23
|
|
|
3
24
|
|
|
@@ -1166,7 +1166,7 @@ namespace Hyper.Editor
|
|
|
1166
1166
|
|
|
1167
1167
|
EditorGUILayout.BeginVertical(infoBoxStyle);
|
|
1168
1168
|
{
|
|
1169
|
-
EditorGUILayout.LabelField("Fast builds with Brotli compression for testing. Uses shorter build time, debug symbols, and
|
|
1169
|
+
EditorGUILayout.LabelField("Fast builds with Brotli compression for testing. Uses shorter build time, debug symbols, full exceptions, and HYPER_DEVELOPMENT_BUILD so HyperSDK and Unity Debug.Log output stay available.", EditorStyles.wordWrappedMiniLabel);
|
|
1170
1170
|
EditorGUILayout.Space(8);
|
|
1171
1171
|
|
|
1172
1172
|
if (GUILayout.Button("Build Development", buttonStyle, GUILayout.Height(40)))
|
|
@@ -1198,7 +1198,7 @@ namespace Hyper.Editor
|
|
|
1198
1198
|
|
|
1199
1199
|
EditorGUILayout.BeginVertical(infoBoxStyle);
|
|
1200
1200
|
{
|
|
1201
|
-
EditorGUILayout.LabelField("Optimized production build that respects your Code Optimization setting and enforces LTO. Build time may exceed 20 minutes.", EditorStyles.wordWrappedMiniLabel);
|
|
1201
|
+
EditorGUILayout.LabelField("Optimized production build that respects your Code Optimization setting and enforces LTO. Sets HYPER_RELEASE_BUILD so Unity Debug.Log / LogWarning / LogError are disabled in the player. Build time may exceed 20 minutes.", EditorStyles.wordWrappedMiniLabel);
|
|
1202
1202
|
EditorGUILayout.Space(8);
|
|
1203
1203
|
|
|
1204
1204
|
if (GUILayout.Button("Build Release", buttonStyle, GUILayout.Height(40)))
|
|
@@ -1944,6 +1944,7 @@ namespace Hyper.Editor
|
|
|
1944
1944
|
ForceAssetRepack();
|
|
1945
1945
|
|
|
1946
1946
|
SetReleaseBuildDefineSymbol(true);
|
|
1947
|
+
AddOptimizationSummary("Runtime: Unity debug logger disabled in player (HYPER_RELEASE_BUILD)");
|
|
1947
1948
|
ApplyReleaseBuildSettings();
|
|
1948
1949
|
|
|
1949
1950
|
BuildPlayerOptions buildOptions = new BuildPlayerOptions
|
|
@@ -2216,7 +2217,7 @@ namespace Hyper.Editor
|
|
|
2216
2217
|
if (!hasReleaseDefine)
|
|
2217
2218
|
{
|
|
2218
2219
|
defines = string.IsNullOrEmpty(defines) ? RELEASE_DEFINE : $"{defines};{RELEASE_DEFINE}";
|
|
2219
|
-
if (IsSDKDeveloper()) HyperDebug.LogSuccess("Set HYPER_RELEASE_BUILD
|
|
2220
|
+
if (IsSDKDeveloper()) HyperDebug.LogSuccess("Set HYPER_RELEASE_BUILD (production API URL; Unity debug logger off in player)");
|
|
2220
2221
|
}
|
|
2221
2222
|
|
|
2222
2223
|
// Remove HYPER_DEVELOPMENT_BUILD if present
|
|
@@ -126,6 +126,16 @@ namespace Hyper.Editor
|
|
|
126
126
|
"• TRANSITION → optional loading/transition scene.",
|
|
127
127
|
MessageType.Info);
|
|
128
128
|
|
|
129
|
+
if (_contentConfig.EnableCustomLoading)
|
|
130
|
+
{
|
|
131
|
+
EditorGUILayout.Space(4);
|
|
132
|
+
EditorGUILayout.HelpBox(
|
|
133
|
+
"Custom Pre-Loading is enabled. The HyperLoader bootstrap will pause after SDK " +
|
|
134
|
+
"initialization and wait for HyperSDK.Loader.CompleteCustomLoading() to be called " +
|
|
135
|
+
"before loading the game scene.",
|
|
136
|
+
MessageType.Info);
|
|
137
|
+
}
|
|
138
|
+
|
|
129
139
|
EditorGUILayout.Space(10);
|
|
130
140
|
|
|
131
141
|
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
|
@@ -253,6 +253,16 @@ namespace Hyper.Editor
|
|
|
253
253
|
"• TRANSITION → optional loading/transition scene.",
|
|
254
254
|
MessageType.Info);
|
|
255
255
|
|
|
256
|
+
if (_contentConfig.EnableCustomLoading)
|
|
257
|
+
{
|
|
258
|
+
EditorGUILayout.Space(4);
|
|
259
|
+
EditorGUILayout.HelpBox(
|
|
260
|
+
"Custom Pre-Loading is enabled. The HyperLoader bootstrap will pause after SDK " +
|
|
261
|
+
"initialization and wait for HyperSDK.Loader.CompleteCustomLoading() to be called " +
|
|
262
|
+
"before loading the game scene.",
|
|
263
|
+
MessageType.Info);
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
EditorGUILayout.Space(8);
|
|
257
267
|
|
|
258
268
|
_scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
|
package/HyperLoader.unity
CHANGED
|
@@ -304,7 +304,7 @@ RectTransform:
|
|
|
304
304
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
305
305
|
m_AnchorMin: {x: 0, y: 1}
|
|
306
306
|
m_AnchorMax: {x: 0, y: 1}
|
|
307
|
-
m_AnchoredPosition: {x: 489.
|
|
307
|
+
m_AnchoredPosition: {x: 489.45853, y: -280.1698}
|
|
308
308
|
m_SizeDelta: {x: 1044.9, y: 239.347}
|
|
309
309
|
m_Pivot: {x: 0.5, y: 0.5}
|
|
310
310
|
--- !u!114 &751153944
|
|
@@ -840,7 +840,7 @@ GameObject:
|
|
|
840
840
|
m_Component:
|
|
841
841
|
- component: {fileID: 1064166296}
|
|
842
842
|
m_Layer: 0
|
|
843
|
-
m_Name:
|
|
843
|
+
m_Name: __HyperSDK_BuildMarker_639104165398939431
|
|
844
844
|
m_TagString: Untagged
|
|
845
845
|
m_Icon: {fileID: 0}
|
|
846
846
|
m_NavMeshLayer: 0
|
|
@@ -1092,7 +1092,7 @@ RectTransform:
|
|
|
1092
1092
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
1093
1093
|
m_AnchorMin: {x: 0, y: 1}
|
|
1094
1094
|
m_AnchorMax: {x: 0, y: 1}
|
|
1095
|
-
m_AnchoredPosition: {x: 489.
|
|
1095
|
+
m_AnchoredPosition: {x: 489.45853, y: -133.2274}
|
|
1096
1096
|
m_SizeDelta: {x: 875, y: 54.5378}
|
|
1097
1097
|
m_Pivot: {x: 0.5, y: 0.5}
|
|
1098
1098
|
--- !u!1 &2874543288936963705
|
|
@@ -42,6 +42,10 @@ namespace Hyper.Internal
|
|
|
42
42
|
private GameManager _gameManager;
|
|
43
43
|
private BackendManager _backendManager;
|
|
44
44
|
|
|
45
|
+
// Progress budget milestones (adjusted based on custom pre-loading)
|
|
46
|
+
private float _progressAfterInit = 0.20f;
|
|
47
|
+
private float _progressAfterBackend = 0.50f;
|
|
48
|
+
private float _progressAfterCustomLoad = 0.50f;
|
|
45
49
|
|
|
46
50
|
#endregion
|
|
47
51
|
|
|
@@ -231,6 +235,12 @@ namespace Hyper.Internal
|
|
|
231
235
|
_bootstrapProgress = 0f;
|
|
232
236
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
233
237
|
|
|
238
|
+
// Configure progress budget based on whether custom pre-loading is enabled
|
|
239
|
+
bool hasCustomPreLoading = _gameContentConfig != null && _gameContentConfig.EnableCustomLoading;
|
|
240
|
+
_progressAfterInit = 0.20f;
|
|
241
|
+
_progressAfterBackend = hasCustomPreLoading ? 0.45f : 0.50f;
|
|
242
|
+
_progressAfterCustomLoad = hasCustomPreLoading ? 0.75f : _progressAfterBackend;
|
|
243
|
+
|
|
234
244
|
// Step 1: Initialize HyperSDK
|
|
235
245
|
bool sdkInitialized = false;
|
|
236
246
|
HyperRuntime.Initialize(() =>
|
|
@@ -245,12 +255,12 @@ namespace Hyper.Internal
|
|
|
245
255
|
|
|
246
256
|
while (!sdkInitialized)
|
|
247
257
|
{
|
|
248
|
-
_bootstrapProgress = Mathf.MoveTowards(_bootstrapProgress,
|
|
258
|
+
_bootstrapProgress = Mathf.MoveTowards(_bootstrapProgress, _progressAfterInit, Time.deltaTime * 2f);
|
|
249
259
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
250
260
|
yield return null;
|
|
251
261
|
}
|
|
252
262
|
|
|
253
|
-
_bootstrapProgress =
|
|
263
|
+
_bootstrapProgress = _progressAfterInit;
|
|
254
264
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
255
265
|
|
|
256
266
|
// Step 2: Handle Backend/PVP or Practice mode initialization
|
|
@@ -267,6 +277,12 @@ namespace Hyper.Internal
|
|
|
267
277
|
InitializePracticeMode();
|
|
268
278
|
}
|
|
269
279
|
|
|
280
|
+
// Step 2.5: Custom Pre-Loading (if enabled)
|
|
281
|
+
if (hasCustomPreLoading)
|
|
282
|
+
{
|
|
283
|
+
yield return WaitForCustomPreLoading();
|
|
284
|
+
}
|
|
285
|
+
|
|
270
286
|
// Step 3: Load target scene asynchronously
|
|
271
287
|
yield return LoadTargetScene();
|
|
272
288
|
}
|
|
@@ -275,17 +291,20 @@ namespace Hyper.Internal
|
|
|
275
291
|
{
|
|
276
292
|
_targetSceneName = _gameContentConfig.GameScene?.SceneName;
|
|
277
293
|
|
|
294
|
+
// WebSocket milestone is midpoint between init and backend completion
|
|
295
|
+
float webSocketMilestone = Mathf.Lerp(_progressAfterInit, _progressAfterBackend, 0.5f);
|
|
296
|
+
|
|
278
297
|
// Initialize WebSocket
|
|
279
298
|
HyperRuntime.BackendManager.InitializeWebSocket();
|
|
280
299
|
|
|
281
300
|
while (!HyperRuntime.BackendManager.isSocketConnected)
|
|
282
301
|
{
|
|
283
|
-
_bootstrapProgress = Mathf.MoveTowards(_bootstrapProgress,
|
|
302
|
+
_bootstrapProgress = Mathf.MoveTowards(_bootstrapProgress, webSocketMilestone, Time.deltaTime * 0.5f);
|
|
284
303
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
285
304
|
yield return null;
|
|
286
305
|
}
|
|
287
306
|
|
|
288
|
-
_bootstrapProgress =
|
|
307
|
+
_bootstrapProgress = webSocketMilestone;
|
|
289
308
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
290
309
|
|
|
291
310
|
// Create session
|
|
@@ -297,12 +316,12 @@ namespace Hyper.Internal
|
|
|
297
316
|
|
|
298
317
|
while (!sessionCreated)
|
|
299
318
|
{
|
|
300
|
-
_bootstrapProgress = Mathf.MoveTowards(_bootstrapProgress,
|
|
319
|
+
_bootstrapProgress = Mathf.MoveTowards(_bootstrapProgress, _progressAfterBackend, Time.deltaTime * 0.5f);
|
|
301
320
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
302
321
|
yield return null;
|
|
303
322
|
}
|
|
304
323
|
|
|
305
|
-
_bootstrapProgress =
|
|
324
|
+
_bootstrapProgress = _progressAfterBackend;
|
|
306
325
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
307
326
|
}
|
|
308
327
|
|
|
@@ -322,6 +341,38 @@ namespace Hyper.Internal
|
|
|
322
341
|
: _gameContentConfig.GameScene?.SceneName;
|
|
323
342
|
}
|
|
324
343
|
|
|
344
|
+
private IEnumerator WaitForCustomPreLoading()
|
|
345
|
+
{
|
|
346
|
+
HyperRuntime.CustomLoadingComplete = false;
|
|
347
|
+
HyperRuntime.CustomLoadingProgress = 0f;
|
|
348
|
+
|
|
349
|
+
float simulatedProgress = 0f;
|
|
350
|
+
const float simulatedSpeed = 0.15f;
|
|
351
|
+
const float simulatedCap = 0.70f;
|
|
352
|
+
|
|
353
|
+
HyperDebug.Log("Custom pre-loading enabled. Waiting for HyperSDK.Loader.CompleteCustomLoading()");
|
|
354
|
+
|
|
355
|
+
while (!HyperRuntime.CustomLoadingComplete)
|
|
356
|
+
{
|
|
357
|
+
float devProgress = HyperRuntime.CustomLoadingProgress;
|
|
358
|
+
|
|
359
|
+
// Simulate progress if dev isn't reporting (auto-animate so bar doesn't look stuck)
|
|
360
|
+
simulatedProgress = Mathf.MoveTowards(simulatedProgress, simulatedCap, Time.unscaledDeltaTime * simulatedSpeed);
|
|
361
|
+
|
|
362
|
+
// Use whichever is higher: dev-reported or simulated
|
|
363
|
+
float effectiveProgress = Mathf.Max(devProgress, simulatedProgress);
|
|
364
|
+
|
|
365
|
+
_bootstrapProgress = Mathf.Lerp(_progressAfterBackend, _progressAfterCustomLoad, effectiveProgress);
|
|
366
|
+
_loadingBarSlider.value = _bootstrapProgress;
|
|
367
|
+
|
|
368
|
+
yield return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Snap to end of custom loading range
|
|
372
|
+
_bootstrapProgress = _progressAfterCustomLoad;
|
|
373
|
+
_loadingBarSlider.value = _bootstrapProgress;
|
|
374
|
+
}
|
|
375
|
+
|
|
325
376
|
private long GetRandomSeed()
|
|
326
377
|
{
|
|
327
378
|
#if UNITY_EDITOR
|
|
@@ -424,9 +475,5 @@ namespace Hyper.Internal
|
|
|
424
475
|
}
|
|
425
476
|
|
|
426
477
|
#endregion
|
|
427
|
-
|
|
428
|
-
#region Utility Methods
|
|
429
|
-
|
|
430
|
-
#endregion
|
|
431
478
|
}
|
|
432
479
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
using UnityEngine;
|
|
2
|
+
|
|
3
|
+
namespace Hyper.Internal
|
|
4
|
+
{
|
|
5
|
+
/// <summary>
|
|
6
|
+
/// Disables Unity's debug logger in production WebGL players built with HYPER_RELEASE_BUILD
|
|
7
|
+
/// (set by Build Assistant release builds). Development builds keep the default logger enabled.
|
|
8
|
+
/// </summary>
|
|
9
|
+
internal static class HyperProductionLoggingBootstrap
|
|
10
|
+
{
|
|
11
|
+
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
|
12
|
+
private static void Apply()
|
|
13
|
+
{
|
|
14
|
+
#if !UNITY_EDITOR && HYPER_RELEASE_BUILD
|
|
15
|
+
Debug.unityLogger.logEnabled = false;
|
|
16
|
+
#endif
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -19,6 +19,8 @@ namespace Hyper.Internal
|
|
|
19
19
|
|
|
20
20
|
private static bool _isInitialized;
|
|
21
21
|
private static bool _isSoftRestarting;
|
|
22
|
+
private static bool _customLoadingComplete;
|
|
23
|
+
private static float _customLoadingProgress;
|
|
22
24
|
private static HyperSDKSettings _settings;
|
|
23
25
|
private static HyperGameContentConfig _gameContent;
|
|
24
26
|
private static GameManager _gameManager;
|
|
@@ -33,6 +35,16 @@ namespace Hyper.Internal
|
|
|
33
35
|
get => _isSoftRestarting;
|
|
34
36
|
set => _isSoftRestarting = value;
|
|
35
37
|
}
|
|
38
|
+
internal static bool CustomLoadingComplete
|
|
39
|
+
{
|
|
40
|
+
get => _customLoadingComplete;
|
|
41
|
+
set => _customLoadingComplete = value;
|
|
42
|
+
}
|
|
43
|
+
internal static float CustomLoadingProgress
|
|
44
|
+
{
|
|
45
|
+
get => _customLoadingProgress;
|
|
46
|
+
set => _customLoadingProgress = value;
|
|
47
|
+
}
|
|
36
48
|
internal static HyperSDKSettings Settings => _settings;
|
|
37
49
|
internal static HyperGameContentConfig GameContent => _gameContent;
|
|
38
50
|
internal static GameManager GameManager => _gameManager;
|
|
@@ -287,6 +299,8 @@ namespace Hyper.Internal
|
|
|
287
299
|
|
|
288
300
|
HyperRandom.Clear();
|
|
289
301
|
_gameContent = null;
|
|
302
|
+
_customLoadingComplete = false;
|
|
303
|
+
_customLoadingProgress = 0f;
|
|
290
304
|
|
|
291
305
|
if (_singletonObject != null)
|
|
292
306
|
{
|
|
@@ -335,6 +349,8 @@ namespace Hyper.Internal
|
|
|
335
349
|
_dataManager = null;
|
|
336
350
|
_gameContent = null;
|
|
337
351
|
_isInitialized = false;
|
|
352
|
+
_customLoadingComplete = false;
|
|
353
|
+
_customLoadingProgress = 0f;
|
|
338
354
|
}
|
|
339
355
|
}
|
|
340
356
|
}
|
|
@@ -498,7 +498,11 @@ namespace Hyper.Internal.Managers
|
|
|
498
498
|
if (_gameManager.HasGameStarted)
|
|
499
499
|
{
|
|
500
500
|
_gameManager?.ShowConnectionRestored();
|
|
501
|
-
|
|
501
|
+
|
|
502
|
+
if (!_gameManager.IsPaused)
|
|
503
|
+
{
|
|
504
|
+
SendGameResumed();
|
|
505
|
+
}
|
|
502
506
|
}
|
|
503
507
|
else
|
|
504
508
|
{
|
|
@@ -723,10 +727,6 @@ namespace Hyper.Internal.Managers
|
|
|
723
727
|
LastSubmissionError = SubmissionErrorType.None;
|
|
724
728
|
|
|
725
729
|
// Keep connection alive during game end sequence
|
|
726
|
-
// WebSocket connections can be closed by proxies/firewalls if idle for too long (typically 30-60 seconds)
|
|
727
|
-
// During gameplay, scores are sent every second, keeping the connection active
|
|
728
|
-
// But at game end, we stop sending scores, so the connection might go idle
|
|
729
|
-
// We ensure the connection is active right before submission by checking and reconnecting if needed
|
|
730
730
|
if (!isSocketConnected)
|
|
731
731
|
{
|
|
732
732
|
HyperDebug.LogWarning("WebSocket disconnected at game end - attempting to reconnect before submission");
|
|
@@ -748,8 +748,7 @@ namespace Hyper.Internal.Managers
|
|
|
748
748
|
}
|
|
749
749
|
else
|
|
750
750
|
{
|
|
751
|
-
|
|
752
|
-
// The act of checking and the upcoming gameResumed call will keep it active
|
|
751
|
+
|
|
753
752
|
HyperDebug.Log("Connection active - maintaining during game end sequence");
|
|
754
753
|
}
|
|
755
754
|
|
|
@@ -777,7 +776,10 @@ namespace Hyper.Internal.Managers
|
|
|
777
776
|
var finalScoreData = _dataManager?.CurrentScoreData;
|
|
778
777
|
finalScoreData.isFinal = true;
|
|
779
778
|
|
|
780
|
-
|
|
779
|
+
// For consistency with per-second and backlog submissions, always send
|
|
780
|
+
// the final score as an array of ScoreData.
|
|
781
|
+
var finalScoreList = new List<ScoreData>(1) { finalScoreData };
|
|
782
|
+
string rawScoreJson = JsonConvert.SerializeObject(finalScoreList);
|
|
781
783
|
var compressedScoreData = JsonCompressor.CompressJson(rawScoreJson);
|
|
782
784
|
string scoreJson = compressedScoreData.MinifiedJson;
|
|
783
785
|
string encryptedScoreData = EncryptScore(scoreJson);
|
|
@@ -835,6 +837,18 @@ namespace Hyper.Internal.Managers
|
|
|
835
837
|
continue;
|
|
836
838
|
}
|
|
837
839
|
|
|
840
|
+
// Flush any offline backlog that accumulated while disconnected.
|
|
841
|
+
if (_dataManager?.PendingScores != null && _dataManager.PendingScores.Count > 0)
|
|
842
|
+
{
|
|
843
|
+
var backlog = new List<ScoreData>(_dataManager.PendingScores);
|
|
844
|
+
_dataManager.PendingScores.Clear();
|
|
845
|
+
int backlogCount = backlog.Count;
|
|
846
|
+
HyperDebug.Log($"[Score] Flushing {backlogCount} offline backlog score(s) before final submission.");
|
|
847
|
+
SendScoresOverWebSocket(backlog);
|
|
848
|
+
|
|
849
|
+
yield return new WaitForSecondsRealtime(1f);
|
|
850
|
+
}
|
|
851
|
+
|
|
838
852
|
// Sending final score via WebSocket
|
|
839
853
|
_finalScoreSubmitted = false;
|
|
840
854
|
_isWaitingForFinalScoreResponse = true;
|
|
@@ -986,15 +1000,30 @@ namespace Hyper.Internal.Managers
|
|
|
986
1000
|
{
|
|
987
1001
|
while (true)
|
|
988
1002
|
{
|
|
1003
|
+
// Don't drain PendingScores while offline - let them accumulate as an offline batch.
|
|
1004
|
+
// Scores will be flushed when the connection is restored.
|
|
1005
|
+
if (!isSocketConnected)
|
|
1006
|
+
{
|
|
1007
|
+
int pendingCount = _dataManager?.PendingScores?.Count ?? 0;
|
|
1008
|
+
HyperDebug.Log($"[Score] Socket disconnected - accumulating scores for offline batch. PendingCount={pendingCount}");
|
|
1009
|
+
yield return _scoreSendInterval;
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
989
1013
|
var scoresToSend = _scoreSendBuffer;
|
|
990
1014
|
scoresToSend.Clear();
|
|
991
1015
|
|
|
992
1016
|
if (_dataManager?.PendingScores.Count > 0)
|
|
993
1017
|
{
|
|
994
|
-
// Copy pending scores into our reusable buffer, then clear the source list
|
|
1018
|
+
// Copy pending scores into our reusable buffer, then clear the source list.
|
|
1019
|
+
// This may be 1 score (normal) or many (offline backlog after reconnection).
|
|
995
1020
|
scoresToSend.AddRange(_dataManager?.PendingScores);
|
|
996
1021
|
_dataManager?.PendingScores.Clear();
|
|
997
|
-
|
|
1022
|
+
int count = scoresToSend.Count;
|
|
1023
|
+
HyperDebug.Log($"[Score] Preparing to send {count} score sample(s) over WebSocket. LastPendingCumulativeScore={scoresToSend[count - 1].cumulativeScore}");
|
|
1024
|
+
|
|
1025
|
+
// Send the entire batch as an array payload.
|
|
1026
|
+
// Normal ticks send [1 score], reconnection flushes send [N scores].
|
|
998
1027
|
SendScoresOverWebSocket(scoresToSend);
|
|
999
1028
|
}
|
|
1000
1029
|
else
|
|
@@ -1021,77 +1050,36 @@ namespace Hyper.Internal.Managers
|
|
|
1021
1050
|
|
|
1022
1051
|
try
|
|
1023
1052
|
{
|
|
1024
|
-
// Clone the
|
|
1053
|
+
// Clone the batch into standalone ScoreData instances
|
|
1025
1054
|
// so that pooling/reset in DataManager does not zero out the values we log/use here.
|
|
1026
1055
|
_lastSentBatch.Clear();
|
|
1027
1056
|
|
|
1028
|
-
|
|
1029
|
-
if (scores.Count > 0)
|
|
1057
|
+
for (int i = 0; i < scores.Count; i++)
|
|
1030
1058
|
{
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
{
|
|
1035
|
-
var source = scores[i];
|
|
1036
|
-
if (source == null) continue;
|
|
1037
|
-
|
|
1038
|
-
var cloned = new ScoreData
|
|
1039
|
-
{
|
|
1040
|
-
cumulativeScore = source.cumulativeScore,
|
|
1041
|
-
isFinal = source.isFinal,
|
|
1042
|
-
timestampUtc = source.timestampUtc
|
|
1043
|
-
};
|
|
1044
|
-
|
|
1045
|
-
if (source.scoreSnapshot != null)
|
|
1046
|
-
{
|
|
1047
|
-
var snapshot = source.scoreSnapshot;
|
|
1048
|
-
var clonedSnapshot = new ScoreSnapshot
|
|
1049
|
-
{
|
|
1050
|
-
scoreTime = snapshot.scoreTime,
|
|
1051
|
-
scoreDelta = snapshot.scoreDelta
|
|
1052
|
-
};
|
|
1053
|
-
|
|
1054
|
-
if (snapshot.gameplayModifiers != null)
|
|
1055
|
-
{
|
|
1056
|
-
var mods = snapshot.gameplayModifiers;
|
|
1057
|
-
var clonedMods = new GameplayModifiers
|
|
1058
|
-
{
|
|
1059
|
-
comboModifiers = mods.comboModifiers,
|
|
1060
|
-
globalModifiers = mods.globalModifiers,
|
|
1061
|
-
activePowerUps = mods.activePowerUps != null
|
|
1062
|
-
? new List<string>(mods.activePowerUps)
|
|
1063
|
-
: null
|
|
1064
|
-
};
|
|
1065
|
-
|
|
1066
|
-
clonedSnapshot.gameplayModifiers = clonedMods;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
cloned.scoreSnapshot = clonedSnapshot;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
_lastSentBatch.Add(cloned);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
scoreData = scores[scores.Count - 1];
|
|
1059
|
+
var source = scores[i];
|
|
1060
|
+
if (source == null) continue;
|
|
1061
|
+
_lastSentBatch.Add(CloneScoreData(source));
|
|
1076
1062
|
}
|
|
1077
1063
|
|
|
1078
|
-
|
|
1064
|
+
// Serialize the full array of scores.
|
|
1065
|
+
// Normal ticks: array of 1. Offline backlog flush: array of N.
|
|
1066
|
+
string rawScoreJson = JsonConvert.SerializeObject(scores);
|
|
1079
1067
|
var compressedScoreData = JsonCompressor.CompressJson(rawScoreJson);
|
|
1080
1068
|
string scoreJson = compressedScoreData.MinifiedJson;
|
|
1081
1069
|
string encryptedData = EncryptScore(scoreJson);
|
|
1082
1070
|
|
|
1083
1071
|
if (string.IsNullOrEmpty(encryptedData))
|
|
1084
1072
|
{
|
|
1085
|
-
int lastScore =
|
|
1086
|
-
HyperDebug.LogError($"[Score] Encryption failed for
|
|
1073
|
+
int lastScore = scores[scores.Count - 1].cumulativeScore;
|
|
1074
|
+
HyperDebug.LogError($"[Score] Encryption failed for score submission. LastCumulativeScore={lastScore} [ErrorType=EncryptionError]");
|
|
1087
1075
|
return;
|
|
1088
1076
|
}
|
|
1089
1077
|
|
|
1090
1078
|
var message = new WebSocketScoreMessage(_cachedSessionId, _csrfToken, encryptedData);
|
|
1091
1079
|
string json = JsonConvert.SerializeObject(message);
|
|
1092
1080
|
int csrfLength = string.IsNullOrEmpty(_csrfToken) ? 0 : _csrfToken.Length;
|
|
1093
|
-
int
|
|
1094
|
-
HyperDebug.Log($"[Score] Sending
|
|
1081
|
+
int lastCumulativeScore = scores[scores.Count - 1].cumulativeScore;
|
|
1082
|
+
HyperDebug.Log($"[Score] Sending score batch. LastCumulativeScore={lastCumulativeScore}, BatchSize={scores.Count}, SessionId={_cachedSessionId}, CsrfLength={csrfLength}");
|
|
1095
1083
|
SendWebSocketMessage(json);
|
|
1096
1084
|
}
|
|
1097
1085
|
catch (Exception ex)
|
|
@@ -1107,6 +1095,51 @@ namespace Hyper.Internal.Managers
|
|
|
1107
1095
|
}
|
|
1108
1096
|
}
|
|
1109
1097
|
|
|
1098
|
+
/// <summary>
|
|
1099
|
+
/// Creates a deep clone of a ScoreData instance.
|
|
1100
|
+
/// Used to create standalone copies that are not affected by the DataManager's object pooling.
|
|
1101
|
+
/// </summary>
|
|
1102
|
+
private static ScoreData CloneScoreData(ScoreData source)
|
|
1103
|
+
{
|
|
1104
|
+
if (source == null) return null;
|
|
1105
|
+
|
|
1106
|
+
var cloned = new ScoreData
|
|
1107
|
+
{
|
|
1108
|
+
cumulativeScore = source.cumulativeScore,
|
|
1109
|
+
isFinal = source.isFinal,
|
|
1110
|
+
timestampUtc = source.timestampUtc
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
if (source.scoreSnapshot != null)
|
|
1114
|
+
{
|
|
1115
|
+
var snapshot = source.scoreSnapshot;
|
|
1116
|
+
var clonedSnapshot = new ScoreSnapshot
|
|
1117
|
+
{
|
|
1118
|
+
scoreTime = snapshot.scoreTime,
|
|
1119
|
+
scoreDelta = snapshot.scoreDelta
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
if (snapshot.gameplayModifiers != null)
|
|
1123
|
+
{
|
|
1124
|
+
var mods = snapshot.gameplayModifiers;
|
|
1125
|
+
var clonedMods = new GameplayModifiers
|
|
1126
|
+
{
|
|
1127
|
+
comboModifiers = mods.comboModifiers,
|
|
1128
|
+
globalModifiers = mods.globalModifiers,
|
|
1129
|
+
activePowerUps = mods.activePowerUps != null
|
|
1130
|
+
? new List<string>(mods.activePowerUps)
|
|
1131
|
+
: null
|
|
1132
|
+
};
|
|
1133
|
+
|
|
1134
|
+
clonedSnapshot.gameplayModifiers = clonedMods;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
cloned.scoreSnapshot = clonedSnapshot;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return cloned;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1110
1143
|
[Serializable]
|
|
1111
1144
|
private class WebSocketScoreMessage
|
|
1112
1145
|
{
|
|
@@ -1244,7 +1277,7 @@ namespace Hyper.Internal.Managers
|
|
|
1244
1277
|
|
|
1245
1278
|
string eventName = eventType switch
|
|
1246
1279
|
{
|
|
1247
|
-
ConsoleEventType.
|
|
1280
|
+
ConsoleEventType.GameStarted => "GameStarted",
|
|
1248
1281
|
ConsoleEventType.GameResumed => "GameResumed",
|
|
1249
1282
|
ConsoleEventType.GamePaused => "GamePaused",
|
|
1250
1283
|
ConsoleEventType.GameEnd => "GameEnd",
|
|
@@ -1255,27 +1288,22 @@ namespace Hyper.Internal.Managers
|
|
|
1255
1288
|
_ => string.Empty
|
|
1256
1289
|
};
|
|
1257
1290
|
|
|
1258
|
-
bool
|
|
1259
|
-
eventType == ConsoleEventType.PracticeGameEnd;
|
|
1260
|
-
|
|
1261
|
-
ConsoleEvent logEntry;
|
|
1291
|
+
bool stopGameOnLog = true;
|
|
1262
1292
|
|
|
1263
|
-
|
|
1293
|
+
switch (eventType)
|
|
1264
1294
|
{
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
else
|
|
1275
|
-
{
|
|
1276
|
-
logEntry = new ConsoleEvent { name = eventName };
|
|
1295
|
+
case ConsoleEventType.GameStarted:
|
|
1296
|
+
stopGameOnLog = false;
|
|
1297
|
+
break;
|
|
1298
|
+
case ConsoleEventType.GameResumed:
|
|
1299
|
+
stopGameOnLog = false;
|
|
1300
|
+
break;
|
|
1301
|
+
case ConsoleEventType.GamePaused:
|
|
1302
|
+
stopGameOnLog = false;
|
|
1303
|
+
break;
|
|
1277
1304
|
}
|
|
1278
1305
|
|
|
1306
|
+
ConsoleEvent logEntry = new ConsoleEvent { name = eventName };
|
|
1279
1307
|
string jsonLog = JsonUtility.ToJson(logEntry);
|
|
1280
1308
|
|
|
1281
1309
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
@@ -1283,7 +1311,7 @@ namespace Hyper.Internal.Managers
|
|
|
1283
1311
|
#endif
|
|
1284
1312
|
|
|
1285
1313
|
#if UNITY_EDITOR
|
|
1286
|
-
UnityEditor.EditorApplication.isPlaying = false;
|
|
1314
|
+
if (stopGameOnLog) UnityEditor.EditorApplication.isPlaying = false;
|
|
1287
1315
|
#endif
|
|
1288
1316
|
}
|
|
1289
1317
|
|
|
@@ -429,21 +429,10 @@ namespace Hyper
|
|
|
429
429
|
{
|
|
430
430
|
#region Public Data Structures
|
|
431
431
|
|
|
432
|
-
[Serializable]
|
|
433
|
-
public class ConsoleData
|
|
434
|
-
{
|
|
435
|
-
public int score;
|
|
436
|
-
public double time;
|
|
437
|
-
public string gameId;
|
|
438
|
-
public string currentPlatform;
|
|
439
|
-
public string recommendedPlatform;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
432
|
[Serializable]
|
|
443
433
|
public class ConsoleEvent
|
|
444
434
|
{
|
|
445
435
|
public string name;
|
|
446
|
-
public ConsoleData data;
|
|
447
436
|
}
|
|
448
437
|
|
|
449
438
|
#endregion
|
|
@@ -67,7 +67,7 @@ namespace Hyper.Internal.Managers
|
|
|
67
67
|
internal void InvokeGameStartEvent()
|
|
68
68
|
{
|
|
69
69
|
OnGameStart?.Invoke();
|
|
70
|
-
HyperRuntime.BackendManager.LogEvent(ConsoleEventType.
|
|
70
|
+
HyperRuntime.BackendManager.LogEvent(ConsoleEventType.GameStarted);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
internal void InvokeSoundChangedEvent(bool sound) => OnSoundChanged?.Invoke(sound);
|