app.hypergames.hypersdk 1.8.9 → 1.8.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.8.11](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.10...v1.8.11) (2026-04-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* normalize score ordering and prevent duplicate final score delta ([ad47380](https://github.com/mvmhyper/hyper-sdk/commit/ad47380e7af6d3c6323a53732bc56d2c0fd545fa))
|
|
7
|
+
|
|
8
|
+
## [1.8.10](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.9...v1.8.10) (2026-04-22)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* hold score batches until startGame ack ([bf49863](https://github.com/mvmhyper/hyper-sdk/commit/bf498637b2c7a14e9d8eba5f62345fc97b59d6aa))
|
|
14
|
+
|
|
1
15
|
## [1.8.9](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.8...v1.8.9) (2026-04-17)
|
|
2
16
|
|
|
3
17
|
|
package/HyperLoader.unity
CHANGED
|
@@ -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_639122897878079609
|
|
844
844
|
m_TagString: Untagged
|
|
845
845
|
m_Icon: {fileID: 0}
|
|
846
846
|
m_NavMeshLayer: 0
|
|
@@ -294,6 +294,9 @@ namespace Hyper.Internal.Managers
|
|
|
294
294
|
private Coroutine _sendGameResumedCoroutine;
|
|
295
295
|
private bool _gameStarted;
|
|
296
296
|
private bool _gameResumed;
|
|
297
|
+
private bool _canFlushScoresAfterGameStartAck;
|
|
298
|
+
private bool _hasLoggedWaitingForGameStartAck;
|
|
299
|
+
private bool _hasLoggedFirstFlushAfterGameStartAck;
|
|
297
300
|
private bool _isManuallyClosed;
|
|
298
301
|
private bool _isSocketErrorCalled;
|
|
299
302
|
|
|
@@ -361,6 +364,10 @@ namespace Hyper.Internal.Managers
|
|
|
361
364
|
|
|
362
365
|
public void SendGameStarted()
|
|
363
366
|
{
|
|
367
|
+
_canFlushScoresAfterGameStartAck = false;
|
|
368
|
+
_hasLoggedWaitingForGameStartAck = false;
|
|
369
|
+
_hasLoggedFirstFlushAfterGameStartAck = false;
|
|
370
|
+
|
|
364
371
|
if (_sendGameStartedCoroutine == null)
|
|
365
372
|
{
|
|
366
373
|
_sendGameStartedCoroutine = StartCoroutine(SendGameStartedCoroutine());
|
|
@@ -583,7 +590,14 @@ namespace Hyper.Internal.Managers
|
|
|
583
590
|
{
|
|
584
591
|
_gameManager?.ShowConnectionRestored();
|
|
585
592
|
|
|
586
|
-
|
|
593
|
+
// If gameplay started locally but backend never acknowledged startGame,
|
|
594
|
+
// reconnect must bootstrap the backend session with startGame first.
|
|
595
|
+
if (!_gameStarted)
|
|
596
|
+
{
|
|
597
|
+
HyperDebug.Log("[Reconnect] Game has started locally but startGame ack not received yet. Sending startGame on reconnect.");
|
|
598
|
+
SendGameStarted();
|
|
599
|
+
}
|
|
600
|
+
else if (!_gameManager.IsPaused)
|
|
587
601
|
{
|
|
588
602
|
SendGameResumed();
|
|
589
603
|
}
|
|
@@ -633,6 +647,8 @@ namespace Hyper.Internal.Managers
|
|
|
633
647
|
public void OnSocketGameStarted(string message)
|
|
634
648
|
{
|
|
635
649
|
_gameStarted = true;
|
|
650
|
+
_canFlushScoresAfterGameStartAck = true;
|
|
651
|
+
_hasLoggedWaitingForGameStartAck = false;
|
|
636
652
|
|
|
637
653
|
if (_sendGameStartedCoroutine != null)
|
|
638
654
|
{
|
|
@@ -642,6 +658,8 @@ namespace Hyper.Internal.Managers
|
|
|
642
658
|
|
|
643
659
|
HyperDebug.Log("[WebSocket] Received gameStarted event from server.");
|
|
644
660
|
TryUpdateCSRFToken(message, "gameStarted");
|
|
661
|
+
int pendingCount = _dataManager?.PendingScores?.Count ?? 0;
|
|
662
|
+
HyperDebug.Log($"[Score] gameStarted acknowledged by backend. Score flush gate opened. PendingCount={pendingCount}");
|
|
645
663
|
}
|
|
646
664
|
|
|
647
665
|
public void OnSocketGameResumed(string message)
|
|
@@ -649,6 +667,14 @@ namespace Hyper.Internal.Managers
|
|
|
649
667
|
_gameResumed = true;
|
|
650
668
|
HyperDebug.Log("[WebSocket] Received gameResumed event from server.");
|
|
651
669
|
|
|
670
|
+
if (!_canFlushScoresAfterGameStartAck)
|
|
671
|
+
{
|
|
672
|
+
_canFlushScoresAfterGameStartAck = true;
|
|
673
|
+
_hasLoggedWaitingForGameStartAck = false;
|
|
674
|
+
int pendingCount = _dataManager?.PendingScores?.Count ?? 0;
|
|
675
|
+
HyperDebug.Log($"[Score] gameResumed acknowledged before gameStarted ack. Opening score flush gate. PendingCount={pendingCount}");
|
|
676
|
+
}
|
|
677
|
+
|
|
652
678
|
TryUpdateCSRFToken(message, "gameResumed");
|
|
653
679
|
ResumeScoreSending();
|
|
654
680
|
}
|
|
@@ -902,24 +928,6 @@ namespace Hyper.Internal.Managers
|
|
|
902
928
|
yield break;
|
|
903
929
|
}
|
|
904
930
|
|
|
905
|
-
var finalScoreData = _dataManager?.CurrentScoreData;
|
|
906
|
-
finalScoreData.isFinal = true;
|
|
907
|
-
|
|
908
|
-
// For consistency with per-second and backlog submissions, always send
|
|
909
|
-
// the final score as an array of ScoreData.
|
|
910
|
-
var finalScoreList = new List<ScoreData>(1) { finalScoreData };
|
|
911
|
-
string rawScoreJson = JsonConvert.SerializeObject(finalScoreList);
|
|
912
|
-
var compressedScoreData = JsonCompressor.CompressJson(rawScoreJson);
|
|
913
|
-
string scoreJson = compressedScoreData.MinifiedJson;
|
|
914
|
-
string encryptedScoreData = EncryptScore(scoreJson);
|
|
915
|
-
|
|
916
|
-
if (string.IsNullOrEmpty(encryptedScoreData))
|
|
917
|
-
{
|
|
918
|
-
LastSubmissionError = SubmissionErrorType.EncryptionError;
|
|
919
|
-
HyperDebug.LogError("Encryption failed [ENCRYPTION_ERROR]");
|
|
920
|
-
yield break;
|
|
921
|
-
}
|
|
922
|
-
|
|
923
931
|
// Reset gameResumed flag for this attempt
|
|
924
932
|
_gameResumed = false;
|
|
925
933
|
|
|
@@ -1016,6 +1024,39 @@ namespace Hyper.Internal.Managers
|
|
|
1016
1024
|
_finalScoreSubmitted = false;
|
|
1017
1025
|
_isWaitingForFinalScoreResponse = true;
|
|
1018
1026
|
|
|
1027
|
+
float currentScoreDataScoreTime = _dataManager.CurrentScoreData.cumulativeScore + _dataManager.CurrentScoreData.scoreSnapshot.scoreTime;
|
|
1028
|
+
float lastCapturedScoreDataScoreTime = _lastSentBatch.Last().cumulativeScore + _lastSentBatch.Last().scoreSnapshot.scoreTime;
|
|
1029
|
+
ScoreData finalScoreData;
|
|
1030
|
+
|
|
1031
|
+
if (currentScoreDataScoreTime > lastCapturedScoreDataScoreTime)
|
|
1032
|
+
{
|
|
1033
|
+
finalScoreData = _dataManager.CurrentScoreData;
|
|
1034
|
+
finalScoreData.isFinal = true;
|
|
1035
|
+
}
|
|
1036
|
+
else
|
|
1037
|
+
{
|
|
1038
|
+
finalScoreData = _lastSentBatch.Last();
|
|
1039
|
+
finalScoreData.isFinal = true;
|
|
1040
|
+
finalScoreData.timestampUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
|
|
1041
|
+
finalScoreData.scoreSnapshot.scoreDelta = 0;
|
|
1042
|
+
finalScoreData.scoreSnapshot.gameplayModifiers = new GameplayModifiers();
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// For consistency with per-second and backlog submissions, always send
|
|
1046
|
+
// the final score as an array of ScoreData.
|
|
1047
|
+
var finalScoreList = new List<ScoreData>(1) { finalScoreData };
|
|
1048
|
+
string rawScoreJson = JsonConvert.SerializeObject(finalScoreList);
|
|
1049
|
+
var compressedScoreData = JsonCompressor.CompressJson(rawScoreJson);
|
|
1050
|
+
string scoreJson = compressedScoreData.MinifiedJson;
|
|
1051
|
+
string encryptedScoreData = EncryptScore(scoreJson);
|
|
1052
|
+
|
|
1053
|
+
if (string.IsNullOrEmpty(encryptedScoreData))
|
|
1054
|
+
{
|
|
1055
|
+
LastSubmissionError = SubmissionErrorType.EncryptionError;
|
|
1056
|
+
HyperDebug.LogError("Encryption failed [ENCRYPTION_ERROR]");
|
|
1057
|
+
yield break;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1019
1060
|
_lastSentBatch.Clear();
|
|
1020
1061
|
if (finalScoreData != null)
|
|
1021
1062
|
{
|
|
@@ -1173,8 +1214,25 @@ namespace Hyper.Internal.Managers
|
|
|
1173
1214
|
// Scores will be flushed when the connection is restored.
|
|
1174
1215
|
if (!isSocketConnected)
|
|
1175
1216
|
{
|
|
1217
|
+
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
1176
1218
|
int pendingCount = _dataManager?.PendingScores?.Count ?? 0;
|
|
1177
1219
|
HyperDebug.Log($"[Score] Socket disconnected - accumulating scores for offline batch. PendingCount={pendingCount}");
|
|
1220
|
+
#endif
|
|
1221
|
+
_isWaitingForTokenLogEmitted = false;
|
|
1222
|
+
_hasLoggedWaitingForGameStartAck = false;
|
|
1223
|
+
yield return _scoreSendInterval;
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
if (!_canFlushScoresAfterGameStartAck)
|
|
1228
|
+
{
|
|
1229
|
+
int pendingCount = _dataManager?.PendingScores?.Count ?? 0;
|
|
1230
|
+
if (!_hasLoggedWaitingForGameStartAck)
|
|
1231
|
+
{
|
|
1232
|
+
HyperDebug.LogWarning($"[Score] Waiting for gameStarted acknowledgement before flushing score batches. PendingCount={pendingCount}");
|
|
1233
|
+
_hasLoggedWaitingForGameStartAck = true;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1178
1236
|
_isWaitingForTokenLogEmitted = false;
|
|
1179
1237
|
yield return _scoreSendInterval;
|
|
1180
1238
|
continue;
|
|
@@ -1211,6 +1269,11 @@ namespace Hyper.Internal.Managers
|
|
|
1211
1269
|
scoresToSend.AddRange(_dataManager?.PendingScores);
|
|
1212
1270
|
_dataManager?.PendingScores.Clear();
|
|
1213
1271
|
int count = scoresToSend.Count;
|
|
1272
|
+
if (!_hasLoggedFirstFlushAfterGameStartAck)
|
|
1273
|
+
{
|
|
1274
|
+
HyperDebug.Log($"[Score] First score flush after gameStarted acknowledgement. BatchSize={count}");
|
|
1275
|
+
_hasLoggedFirstFlushAfterGameStartAck = true;
|
|
1276
|
+
}
|
|
1214
1277
|
HyperDebug.Log($"[Score] Preparing to send {count} score sample(s) over WebSocket. LastPendingCumulativeScore={scoresToSend[count - 1].cumulativeScore}");
|
|
1215
1278
|
|
|
1216
1279
|
// Send the entire batch as an array payload.
|
|
@@ -66,7 +66,7 @@ namespace Hyper.Internal.Managers
|
|
|
66
66
|
|
|
67
67
|
CurrentScoreData.cumulativeScore = CurrentScore;
|
|
68
68
|
CurrentScoreData.timestampUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
|
|
69
|
-
CurrentScoreData.isFinal =
|
|
69
|
+
CurrentScoreData.isFinal = false;
|
|
70
70
|
|
|
71
71
|
var snapshot = CurrentScoreData.scoreSnapshot;
|
|
72
72
|
snapshot.scoreTime = currentGameSecond;
|
|
@@ -283,12 +283,13 @@ namespace Hyper.Internal
|
|
|
283
283
|
_dataManager.UpdateScore(0);
|
|
284
284
|
HyperRuntime.Timer.StartActiveGameTimer();
|
|
285
285
|
|
|
286
|
-
//
|
|
286
|
+
_dataManager.StartCapturingScores(); // Start capture/sender immediately so offline starts still buffer score samples.
|
|
287
|
+
_backendManager.StartSendingScores(); // Sender loop already pauses actual submissions while disconnected or token-gated.
|
|
288
|
+
|
|
289
|
+
// startGame handshake can only be emitted when socket is connected.
|
|
287
290
|
if (_backendManager.isSocketConnected)
|
|
288
291
|
{
|
|
289
292
|
_backendManager.SendGameStarted();
|
|
290
|
-
_dataManager.StartCapturingScores();
|
|
291
|
-
_backendManager.StartSendingScores();
|
|
292
293
|
}
|
|
293
294
|
|
|
294
295
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "app.hypergames.hypersdk",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.11",
|
|
4
4
|
"displayName": "HyperSDK",
|
|
5
5
|
"description": "Official Unity SDK for Hyper: multiplayer battles, leaderboards, deterministic RNG, session management, tutorials, and WebGL optimizations for production-ready games.",
|
|
6
6
|
"unity": "6000.0",
|