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: __HyperSDK_BuildMarker_639114648052637778
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
- if (!_gameManager.IsPaused)
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 = true;
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
- // Only proceed if connected
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.9",
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",