app.hypergames.hypersdk 1.4.21 → 1.4.22

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,10 @@
1
+ ## [1.4.22](https://github.com/mvmhyper/hyper-sdk/compare/v1.4.21...v1.4.22) (2026-01-26)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * timeout updates, remove app pause, RNG migrator UX improvements ([3529f50](https://github.com/mvmhyper/hyper-sdk/commit/3529f50fd22f25563f1852be56d7cef246c06008))
7
+
1
8
  ## [1.4.21](https://github.com/mvmhyper/hyper-sdk/compare/v1.4.20...v1.4.21) (2026-01-12)
2
9
 
3
10
 
@@ -24,13 +24,17 @@ namespace Hyper.Editor
24
24
  public bool IsSelected;
25
25
  public int UnityRandomCount;
26
26
  public int SystemRandomCount;
27
+ public List<int> UnityRandomLineNumbers;
28
+ public List<int> SystemRandomLineNumbers;
27
29
 
28
- public FileEntry(string path, int unityCount, int systemCount)
30
+ public FileEntry(string path, int unityCount, int systemCount, List<int> unityLines, List<int> systemLines)
29
31
  {
30
32
  Path = path;
31
33
  IsSelected = true;
32
34
  UnityRandomCount = unityCount;
33
35
  SystemRandomCount = systemCount;
36
+ UnityRandomLineNumbers = unityLines ?? new List<int>();
37
+ SystemRandomLineNumbers = systemLines ?? new List<int>();
34
38
  }
35
39
  }
36
40
 
@@ -141,7 +145,16 @@ namespace Hyper.Editor
141
145
  usageInfo = $"System: {scannedFiles[i].SystemRandomCount}";
142
146
  }
143
147
 
144
- EditorGUILayout.LabelField($"{fileName} ({usageInfo})", GUILayout.Width(350));
148
+ // Make the file name clickable
149
+ GUIStyle clickableStyle = new GUIStyle(EditorStyles.label);
150
+ ColorUtility.TryParseHtmlString("#93C7FF", out Color blueColor);
151
+ clickableStyle.hover.textColor = blueColor;
152
+
153
+ if (GUILayout.Button($"{fileName} ({usageInfo})", clickableStyle, GUILayout.Width(350), GUILayout.Height(18)))
154
+ {
155
+ OpenScriptAtFirstInstance(scannedFiles[i]);
156
+ }
157
+
145
158
  EditorGUILayout.LabelField(relativePath, EditorStyles.miniLabel);
146
159
 
147
160
  EditorGUILayout.EndHorizontal();
@@ -217,12 +230,15 @@ namespace Hyper.Editor
217
230
  (float)i / csFiles.Length);
218
231
 
219
232
  string content = File.ReadAllText(filePath);
220
- int unityUsageCount = CountUnityRandomUsage(content);
221
- int systemUsageCount = CountSystemRandomUsage(content);
233
+ string[] lines = content.Split('\n');
234
+ List<int> unityLines = FindUnityRandomLines(content, lines);
235
+ List<int> systemLines = FindSystemRandomLines(content, lines);
236
+ int unityUsageCount = unityLines.Count;
237
+ int systemUsageCount = systemLines.Count;
222
238
 
223
239
  if (unityUsageCount > 0 || systemUsageCount > 0)
224
240
  {
225
- scannedFiles.Add(new FileEntry(filePath, unityUsageCount, systemUsageCount));
241
+ scannedFiles.Add(new FileEntry(filePath, unityUsageCount, systemUsageCount, unityLines, systemLines));
226
242
  }
227
243
  }
228
244
 
@@ -245,6 +261,43 @@ namespace Hyper.Editor
245
261
  }
246
262
  }
247
263
 
264
+ private void OpenScriptAtFirstInstance(FileEntry fileEntry)
265
+ {
266
+ string relativePath = fileEntry.Path.Replace(Application.dataPath, "Assets");
267
+
268
+ // Find the first line number where Random is used
269
+ int firstLine = 0;
270
+ if (fileEntry.UnityRandomLineNumbers.Count > 0)
271
+ {
272
+ firstLine = fileEntry.UnityRandomLineNumbers[0];
273
+ }
274
+ else if (fileEntry.SystemRandomLineNumbers.Count > 0)
275
+ {
276
+ firstLine = fileEntry.SystemRandomLineNumbers[0];
277
+ }
278
+
279
+ // Load the MonoScript asset
280
+ MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>(relativePath);
281
+ if (script != null)
282
+ {
283
+ // Open the script at the specified line number (0 means no line specified)
284
+ if (firstLine > 0)
285
+ {
286
+ AssetDatabase.OpenAsset(script, firstLine);
287
+ }
288
+ else
289
+ {
290
+ AssetDatabase.OpenAsset(script);
291
+ }
292
+ }
293
+ else
294
+ {
295
+ // Fallback: just ping/select the file
296
+ EditorUtility.DisplayDialog("File Not Found",
297
+ $"Could not load script: {relativePath}\n\nThe file may not be in the AssetDatabase.", "OK");
298
+ }
299
+ }
300
+
248
301
  private bool ShouldExcludeFile(string filePath)
249
302
  {
250
303
  string normalizedPath = filePath.Replace("\\", "/");
@@ -313,6 +366,70 @@ namespace Hyper.Editor
313
366
  return count;
314
367
  }
315
368
 
369
+ private List<int> FindUnityRandomLines(string content, string[] lines)
370
+ {
371
+ List<int> lineNumbers = new List<int>();
372
+ var patterns = GetUnityRandomPatterns();
373
+
374
+ for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
375
+ {
376
+ string line = lines[lineIndex];
377
+
378
+ // Check each pattern
379
+ foreach (var pattern in patterns.Keys)
380
+ {
381
+ if (Regex.IsMatch(line, pattern))
382
+ {
383
+ lineNumbers.Add(lineIndex + 1); // Unity line numbers are 1-based
384
+ break; // Only count each line once
385
+ }
386
+ }
387
+
388
+ // Check for namespace aliases
389
+ if (Regex.IsMatch(line, @"using\s+\w+\s*=\s*(?:global::)?UnityEngine\.Random\s*;", RegexOptions.Multiline))
390
+ {
391
+ lineNumbers.Add(lineIndex + 1);
392
+ }
393
+ }
394
+
395
+ return lineNumbers;
396
+ }
397
+
398
+ private List<int> FindSystemRandomLines(string content, string[] lines)
399
+ {
400
+ List<int> lineNumbers = new List<int>();
401
+
402
+ for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
403
+ {
404
+ string line = lines[lineIndex];
405
+ bool foundMatch = false;
406
+
407
+ // Check for System.Random instantiations
408
+ if (Regex.IsMatch(line, @"new\s+Random\s*\(") ||
409
+ Regex.IsMatch(line, @"new\s+System\.Random\s*\("))
410
+ {
411
+ lineNumbers.Add(lineIndex + 1);
412
+ foundMatch = true;
413
+ }
414
+
415
+ // Check for Random type declarations
416
+ if (!foundMatch && (Regex.IsMatch(line, @"\bRandom\s+\w+\s*=") ||
417
+ Regex.IsMatch(line, @"\bSystem\.Random\s+\w+")))
418
+ {
419
+ lineNumbers.Add(lineIndex + 1);
420
+ foundMatch = true;
421
+ }
422
+
423
+ // Check for namespace aliases
424
+ if (!foundMatch && Regex.IsMatch(line, @"using\s+\w+\s*=\s*(?:global::)?System\.Random\s*;", RegexOptions.Multiline))
425
+ {
426
+ lineNumbers.Add(lineIndex + 1);
427
+ }
428
+ }
429
+
430
+ return lineNumbers;
431
+ }
432
+
316
433
  private void MigrateFiles()
317
434
  {
318
435
  var selectedFiles = scannedFiles.Where(f => f.IsSelected).ToList();
package/HyperLoader.unity CHANGED
@@ -304,8 +304,8 @@ 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.20746, y: -280.1698}
308
- m_SizeDelta: {x: 1044.9, y: 239.347}
307
+ m_AnchoredPosition: {x: 489.10004, y: -280.1698}
308
+ m_SizeDelta: {x: 0, y: 239.347}
309
309
  m_Pivot: {x: 0.5, y: 0.5}
310
310
  --- !u!114 &751153944
311
311
  MonoBehaviour:
@@ -840,7 +840,7 @@ GameObject:
840
840
  m_Component:
841
841
  - component: {fileID: 1064166296}
842
842
  m_Layer: 0
843
- m_Name: __HyperSDK_BuildMarker_639030030877789930
843
+ m_Name: __HyperSDK_BuildMarker_639047227933615465
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.20746, y: -133.2274}
1095
+ m_AnchoredPosition: {x: 489.10004, 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,7 +42,7 @@ namespace Hyper.Internal.Managers
42
42
 
43
43
  #region Public Properties (Internal API)
44
44
 
45
- public int scoreSubmissionTimeout { get; private set; } = 4;
45
+ public int scoreSubmissionTimeout { get; private set; } = 7;
46
46
  public int CurrentRetryCount => _currentRetryCount;
47
47
  public bool isSocketConnected { get; private set; }
48
48
  public bool tutorialFetchSuccess { get; private set; }
@@ -156,7 +156,7 @@ namespace Hyper.Internal.Managers
156
156
  entryId = _gameManager?.Config.EntryId,
157
157
  host = _gameManager?.Config.Host,
158
158
  time = _gameManager?.Config.TimeLimit,
159
- username = _gameManager?.Config.Username,
159
+ userName = _gameManager?.Config.Username,
160
160
  userId = _gameManager?.Config.UserId,
161
161
  pvp = _gameManager?.Config.isPVP,
162
162
  oldTournamentId = _gameManager?.Config.OldTournamentId
@@ -299,6 +299,7 @@ namespace Hyper.Internal.Managers
299
299
  public void StopWebSocket()
300
300
  {
301
301
  _isManuallyClosed = true;
302
+ isSocketConnected = false;
302
303
  CloseWebSocket();
303
304
  }
304
305
 
@@ -354,6 +355,14 @@ namespace Hyper.Internal.Managers
354
355
  yield break;
355
356
  }
356
357
 
358
+ // Don't send if socket is not connected - prevents wasting retry attempts
359
+ if (!isSocketConnected)
360
+ {
361
+ HyperDebug.LogWarning("Cannot send gameResumed: WebSocket not connected");
362
+ _sendGameResumedCoroutine = null;
363
+ yield break;
364
+ }
365
+
357
366
  _gameResumed = false;
358
367
 
359
368
  while (!_gameResumed && attempts < maxAttempts)
@@ -454,6 +463,11 @@ namespace Hyper.Internal.Managers
454
463
 
455
464
  public void OnSocketConnected()
456
465
  {
466
+ // Mark connected before any follow-up actions (resumeGame, UI updates, etc.)
467
+ isSocketConnected = true;
468
+ _isManuallyClosed = false;
469
+ _isSocketErrorCalled = false;
470
+
457
471
  if (_gameManager.HasGameStarted)
458
472
  {
459
473
  _gameManager?.ShowConnectionRestored();
@@ -463,20 +477,18 @@ namespace Hyper.Internal.Managers
463
477
  {
464
478
  _eventManager?.InvokeConnectionFoundEvent();
465
479
  }
466
-
467
- isSocketConnected = true;
468
- _isManuallyClosed = false;
469
- _isSocketErrorCalled = false;
470
480
  }
471
481
 
472
482
  public void OnSocketDisconnected()
473
483
  {
484
+ isSocketConnected = false;
485
+
474
486
  if (!_isManuallyClosed)
475
487
  {
476
488
  _gameManager?.ShowConnectionLost();
477
489
  }
478
490
 
479
- if (!_gameManager.HasGameStarted && isSocketConnected)
491
+ if (!_gameManager.HasGameStarted)
480
492
  {
481
493
  _isSocketErrorCalled = true;
482
494
  _gameManager?.ShowLoadingFailedModal(true);
@@ -530,10 +542,11 @@ namespace Hyper.Internal.Managers
530
542
  _eventManager?.InvokeWebSocketScoreSubmitted(_lastScoreData);
531
543
  }
532
544
 
533
- if (_isFinalScoreSubmission)
545
+ if (_isWaitingForFinalScoreResponse)
534
546
  {
535
547
  _finalScoreSubmitted = true;
536
- _isFinalScoreSubmission = false;
548
+ _isWaitingForFinalScoreResponse = false;
549
+ return;
537
550
  }
538
551
 
539
552
  TryUpdateCSRFToken(message);
@@ -663,11 +676,7 @@ namespace Hyper.Internal.Managers
663
676
 
664
677
  _finalScoreSubmissionCoroutine = null;
665
678
 
666
- #if UNITY_WEBGL && !UNITY_EDITOR
667
- _finalScoreSubmissionCoroutine = StartCoroutine(FinalScoreSubmissionCoroutine());
668
- #else
669
- _finalScoreSubmissionCoroutine = StartCoroutine(FinalScoreSubmissionCoroutineDemo());
670
- #endif
679
+ SubmitScore();
671
680
  }
672
681
 
673
682
  private IEnumerator FinalScoreSubmissionCoroutine()
@@ -727,7 +736,7 @@ namespace Hyper.Internal.Managers
727
736
 
728
737
  // Sending final score via WebSocket
729
738
  _finalScoreSubmitted = false;
730
- _isFinalScoreSubmission = true;
739
+ _isWaitingForFinalScoreResponse = true;
731
740
  _lastSentBatch = new List<ScoreData> { finalScoreData };
732
741
 
733
742
  bool sendFailed = false;
@@ -740,7 +749,8 @@ namespace Hyper.Internal.Managers
740
749
  catch (Exception ex)
741
750
  {
742
751
  HyperDebug.LogError($"Failed to send final score via WebSocket: {ex.Message}");
743
- _isFinalScoreSubmission = false;
752
+ // Keep _isWaitingForFinalScoreResponse = true since we're still in final score submission mode
753
+ // and will retry. We're still waiting for a final score response.
744
754
  sendFailed = true;
745
755
  }
746
756
 
@@ -765,7 +775,8 @@ namespace Hyper.Internal.Managers
765
775
  if (_hasSessionEnded)
766
776
  {
767
777
  _gameManager?.ShowRetryTimeExpiredModal();
768
- _isFinalScoreSubmission = false;
778
+ // Session ended - can't retry anymore, so set to false
779
+ _isWaitingForFinalScoreResponse = false;
769
780
  _finalScoreSubmissionCoroutine = null;
770
781
  yield break;
771
782
  }
@@ -783,13 +794,16 @@ namespace Hyper.Internal.Managers
783
794
  else if (_hasSessionEnded)
784
795
  {
785
796
  _gameManager?.ShowRetryTimeExpiredModal();
786
- _isFinalScoreSubmission = false;
797
+ // Session ended - can't retry anymore, so set to false
798
+ _isWaitingForFinalScoreResponse = false;
787
799
  _finalScoreSubmissionCoroutine = null;
788
800
  yield break;
789
801
  }
790
802
  else
791
803
  {
792
- _isFinalScoreSubmission = false;
804
+ // Timeout occurred, but keep _isWaitingForFinalScoreResponse = true
805
+ // because we're still in final score submission mode and will retry.
806
+ // If backend responds during retry wait, we'll catch it.
793
807
  _currentRetryCount++;
794
808
  _gameManager?.GameOverModalComponent?.UpdateAttemptCount(_currentRetryCount, MAX_RETRY_COUNT);
795
809
 
@@ -800,7 +814,7 @@ namespace Hyper.Internal.Managers
800
814
  }
801
815
  }
802
816
 
803
- _isFinalScoreSubmission = false;
817
+ _isWaitingForFinalScoreResponse = false;
804
818
  _gameManager?.GameOverModalComponent?.AllAttemptsHaveFailed();
805
819
  _finalScoreSubmissionCoroutine = null;
806
820
  }
@@ -831,7 +845,7 @@ namespace Hyper.Internal.Managers
831
845
  private List<ScoreData> _lastSentBatch = new List<ScoreData>();
832
846
  private ScoreData _lastScoreData;
833
847
  private readonly WaitForSeconds _scoreSendInterval = new WaitForSeconds(1f);
834
- private bool _isFinalScoreSubmission;
848
+ private bool _isWaitingForFinalScoreResponse;
835
849
  private bool _finalScoreSubmitted;
836
850
 
837
851
  public void StartSendingScores()
@@ -883,6 +897,14 @@ namespace Hyper.Internal.Managers
883
897
  {
884
898
  if (scores == null || scores.Count == 0) return;
885
899
 
900
+ // Don't send if socket is not connected - prevents sending with expired tokens
901
+ // and ensures we wait for reconnection to get fresh token via SendGameResumed()
902
+ if (!isSocketConnected)
903
+ {
904
+ HyperDebug.LogWarning("Cannot send scores: WebSocket not connected");
905
+ return;
906
+ }
907
+
886
908
  try
887
909
  {
888
910
  _lastSentBatch = new List<ScoreData>(scores);
@@ -190,11 +190,6 @@ namespace Hyper.Internal.Managers
190
190
  HandlePauseState(!hasFocus);
191
191
  }
192
192
 
193
- void OnApplicationPause(bool pauseStatus)
194
- {
195
- HandlePauseState(pauseStatus);
196
- }
197
-
198
193
  void HandlePauseState(bool shouldPause)
199
194
  {
200
195
  if (shouldPause && !_isPaused)
@@ -104,7 +104,7 @@ namespace Hyper.Internal
104
104
  // Called from the SlideRight script once the animation is finished
105
105
  public void StartAction()
106
106
  {
107
- StartLerpingSlider(0.7f, HyperRuntime.BackendManager.scoreSubmissionTimeout);
107
+ StartLerpingSlider(0.75f, HyperRuntime.BackendManager.scoreSubmissionTimeout);
108
108
  }
109
109
 
110
110
  public void ScoreHasBeenSubmitted()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "app.hypergames.hypersdk",
3
- "version": "1.4.21",
3
+ "version": "1.4.22",
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",