app.hypergames.hypersdk 1.11.0-beta.24 → 1.11.0-beta.26

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.11.0-beta.26](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.25...v1.11.0-beta.26) (2026-09-07)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **sample:** refine replay integration template ([5a345df](https://github.com/mvmhyper/hyper-sdk/commit/5a345df87d19a6df11af4117fbeed1e21e8e9f9a))
7
+
8
+ # [1.11.0-beta.25](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.24...v1.11.0-beta.25) (2026-09-04)
9
+
10
+
11
+ ### Features
12
+
13
+ * **replay:** include final score metadata ([463dcad](https://github.com/mvmhyper/hyper-sdk/commit/463dcadd4df5e5684f3be328b0ec361f25f26f2a))
14
+
1
15
  # [1.11.0-beta.24](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.23...v1.11.0-beta.24) (2026-09-04)
2
16
 
3
17
 
@@ -448,7 +448,11 @@ namespace Hyper.Internal
448
448
  return false;
449
449
  }
450
450
 
451
- ReplaySessionState.SetMetadata(replay.PlayerName, replay.AvatarUrl, replay.Seed);
451
+ ReplaySessionState.SetMetadata(
452
+ replay.PlayerName,
453
+ replay.AvatarUrl,
454
+ replay.Seed,
455
+ replay.FinalScore);
452
456
  ReplaySessionState.SetReplayData(replay.ActivityNdjson);
453
457
  HyperDebug.Log($"Replay loaded from {path}");
454
458
  return true;
@@ -179,7 +179,8 @@ namespace Hyper.Internal.Managers
179
179
  ReplaySessionState.SetMetadata(
180
180
  replay.PlayerName,
181
181
  replay.AvatarUrl,
182
- replay.Seed);
182
+ replay.Seed,
183
+ replay.FinalScore);
183
184
  ReplaySessionState.SetReplayData(replay.ActivityNdjson);
184
185
  HyperDebug.Log($"Replay downloaded for entry {entryId}.");
185
186
  onSuccess?.Invoke();
@@ -289,9 +290,10 @@ namespace Hyper.Internal.Managers
289
290
  string seedLine = reader.ReadLine();
290
291
  string nameLine = reader.ReadLine();
291
292
  string avatarLine = reader.ReadLine();
293
+ string scoreLine = reader.ReadLine();
292
294
  string activities = reader.ReadToEnd();
293
295
 
294
- if (seedLine == null || nameLine == null || avatarLine == null)
296
+ if (seedLine == null || nameLine == null || avatarLine == null || scoreLine == null)
295
297
  {
296
298
  error = "The replay response is missing required metadata.";
297
299
  return false;
@@ -302,6 +304,7 @@ namespace Hyper.Internal.Managers
302
304
  JArray seedData = JArray.Parse(seedLine);
303
305
  JArray nameData = JArray.Parse(nameLine);
304
306
  JArray avatarData = JArray.Parse(avatarLine);
307
+ JArray scoreData = JArray.Parse(scoreLine);
305
308
 
306
309
  if (seedData.Count != 1 ||
307
310
  seedData[0].Type != JTokenType.Integer ||
@@ -327,6 +330,18 @@ namespace Hyper.Internal.Managers
327
330
  return false;
328
331
  }
329
332
 
333
+ if (scoreData.Count != 1 ||
334
+ scoreData[0].Type != JTokenType.Integer ||
335
+ !int.TryParse(
336
+ scoreData[0].ToString(),
337
+ NumberStyles.Integer,
338
+ CultureInfo.InvariantCulture,
339
+ out int finalScore))
340
+ {
341
+ error = "Replay metadata contains an invalid final score.";
342
+ return false;
343
+ }
344
+
330
345
  if (string.IsNullOrWhiteSpace(activities))
331
346
  {
332
347
  error = "The replay response contains no gameplay activities.";
@@ -337,6 +352,7 @@ namespace Hyper.Internal.Managers
337
352
  seed,
338
353
  nameData[0].Value<string>() ?? string.Empty,
339
354
  avatarData[0].Value<string>() ?? string.Empty,
355
+ finalScore,
340
356
  activities);
341
357
 
342
358
  return true;
@@ -354,13 +370,20 @@ namespace Hyper.Internal.Managers
354
370
  internal long Seed { get; }
355
371
  internal string PlayerName { get; }
356
372
  internal string AvatarUrl { get; }
373
+ internal int FinalScore { get; }
357
374
  internal string ActivityNdjson { get; }
358
375
 
359
- internal ReplayResponse(long seed, string playerName, string avatarUrl, string activityNdjson)
376
+ internal ReplayResponse(
377
+ long seed,
378
+ string playerName,
379
+ string avatarUrl,
380
+ int finalScore,
381
+ string activityNdjson)
360
382
  {
361
383
  Seed = seed;
362
384
  PlayerName = playerName;
363
385
  AvatarUrl = avatarUrl;
386
+ FinalScore = finalScore;
364
387
  ActivityNdjson = activityNdjson;
365
388
  }
366
389
  }
@@ -375,6 +375,7 @@ namespace Hyper.Internal.Managers
375
375
  new object[] { HyperRandom.CurrentSeed }));
376
376
  _editorReplay.AppendLine(JsonConvert.SerializeObject(new object[] { "hypergamer" }));
377
377
  _editorReplay.AppendLine(JsonConvert.SerializeObject(new object[] { string.Empty }));
378
+ _editorReplayMetadataLength = _editorReplay.Length;
378
379
  #endif
379
380
  _replayRecorder?.StartSession();
380
381
  }
@@ -426,6 +427,7 @@ namespace Hyper.Internal.Managers
426
427
 
427
428
  #if UNITY_EDITOR
428
429
  private readonly StringBuilder _editorReplay = new StringBuilder(4096);
430
+ private int _editorReplayMetadataLength;
429
431
 
430
432
  private void CaptureEditorReplayActivity(object[] activity)
431
433
  {
@@ -434,6 +436,8 @@ namespace Hyper.Internal.Managers
434
436
 
435
437
  private void SaveEditorReplay()
436
438
  {
439
+ string finalScoreLine = JsonConvert.SerializeObject(new object[] { CurrentScore }) + Environment.NewLine;
440
+ _editorReplay.Insert(_editorReplayMetadataLength, finalScoreLine);
437
441
  try
438
442
  {
439
443
  File.WriteAllText(ReplaySessionState.EditorReplayFilePath, _editorReplay.ToString());
@@ -443,6 +447,10 @@ namespace Hyper.Internal.Managers
443
447
  {
444
448
  HyperDebug.LogWarning($"Unable to save Editor replay: {exception.Message}");
445
449
  }
450
+ finally
451
+ {
452
+ _editorReplay.Remove(_editorReplayMetadataLength, finalScoreLine.Length);
453
+ }
446
454
  }
447
455
  #endif
448
456
 
@@ -14,6 +14,7 @@ namespace Hyper.Internal
14
14
  public static string AvatarUrl { get; private set; }
15
15
  public static string EntryId { get; private set; }
16
16
  public static string ReplayNdjson { get; private set; }
17
+ public static int? FinalScore { get; private set; }
17
18
  public static Texture2D AvatarTexture { get; private set; }
18
19
  public static bool HasReplayData => !string.IsNullOrWhiteSpace(ReplayNdjson);
19
20
  public static long OriginalRandomSeed { get; private set; }
@@ -48,6 +49,7 @@ namespace Hyper.Internal
48
49
  AvatarUrl = avatarUrl ?? string.Empty;
49
50
  EntryId = entryId ?? string.Empty;
50
51
  ReplayNdjson = string.Empty;
52
+ FinalScore = null;
51
53
  ReleaseAvatarTexture();
52
54
  OriginalRandomSeed = originalRandomSeed;
53
55
  Progress01 = 0f;
@@ -61,13 +63,18 @@ namespace Hyper.Internal
61
63
  GameplayStartProgress01 = 0f;
62
64
  }
63
65
 
64
- public static void SetMetadata(string playerName, string avatarUrl, long originalRandomSeed)
66
+ public static void SetMetadata(
67
+ string playerName,
68
+ string avatarUrl,
69
+ long originalRandomSeed,
70
+ int finalScore)
65
71
  {
66
72
  if (!IsActive) return;
67
73
 
68
74
  PlayerName = playerName ?? string.Empty;
69
75
  AvatarUrl = avatarUrl ?? string.Empty;
70
76
  OriginalRandomSeed = originalRandomSeed;
77
+ FinalScore = finalScore;
71
78
  }
72
79
 
73
80
  public static void SetReplayData(string ndjson)
@@ -193,6 +200,7 @@ namespace Hyper.Internal
193
200
  AvatarUrl = string.Empty;
194
201
  EntryId = string.Empty;
195
202
  ReplayNdjson = string.Empty;
203
+ FinalScore = null;
196
204
  ReleaseAvatarTexture();
197
205
  OriginalRandomSeed = 0;
198
206
  Progress01 = 0f;
@@ -1,3 +1,4 @@
1
+ using System.Globalization;
1
2
  using TMPro;
2
3
  using UnityEngine;
3
4
  using UnityEngine.UI;
@@ -38,7 +39,7 @@ namespace Hyper.Internal
38
39
 
39
40
  if (scoreValue != null)
40
41
  {
41
- scoreValue.text = (HyperRuntime.DataManager?.CurrentScore ?? 0).ToString("N0");
42
+ scoreValue.text = FormatFinalScore(ReplaySessionState.FinalScore);
42
43
  }
43
44
 
44
45
  if (scoreOwnerName != null)
@@ -99,6 +100,13 @@ namespace Hyper.Internal
99
100
  : $"@{normalizedName.ToUpperInvariant()}'S SCORE";
100
101
  }
101
102
 
103
+ internal static string FormatFinalScore(int? finalScore)
104
+ {
105
+ return finalScore.HasValue
106
+ ? finalScore.Value.ToString("N0", CultureInfo.InvariantCulture)
107
+ : "--";
108
+ }
109
+
102
110
  private void UpdateSoundPresentation()
103
111
  {
104
112
  bool isSoundOn = HyperRuntime.DataManager == null || HyperRuntime.DataManager.GetSound() == 1;
@@ -134,7 +134,7 @@ Rigidbody:
134
134
  m_ImplicitTensor: 1
135
135
  m_UseGravity: 0
136
136
  m_IsKinematic: 1
137
- m_Interpolate: 0
137
+ m_Interpolate: 1
138
138
  m_Constraints: 0
139
139
  m_CollisionDetection: 0
140
140
  --- !u!114 &3724547471223867592
@@ -134,7 +134,7 @@ Rigidbody:
134
134
  m_ImplicitTensor: 1
135
135
  m_UseGravity: 0
136
136
  m_IsKinematic: 1
137
- m_Interpolate: 0
137
+ m_Interpolate: 1
138
138
  m_Constraints: 0
139
139
  m_CollisionDetection: 0
140
140
  --- !u!114 &6935381596637914183
@@ -13,8 +13,10 @@ deterministic spawning, a timed score multiplier, and replay integration.
13
13
  Assign `LaneRunnerSample` as the Game and Practice scene in Hyper Game Content
14
14
  Config, then use Hyper's Editor game-mode menu to run Practice, Battle, or Replay.
15
15
  Battle sessions record to `hyper-replay.ndjson` in `Application.persistentDataPath`.
16
- Enable Custom Restart Flow in Hyper SDK Settings to test in-place Watch Again and
17
- replay seeking without reloading the scene.
16
+ The sample supports custom replay restarts, but enable **Replay Custom Restart**
17
+ only after repeated Watch Again tests reproduce the original run. Enable
18
+ **Allow Replay Seeking** only after accelerated reconstruction is stable on
19
+ desktop and mobile WebGL.
18
20
 
19
21
  The sample uses only Unity's built-in `Default` layer, `Untagged`, and
20
22
  `MainCamera`. Its collider overrides and ground safety do not require changes to
@@ -40,3 +42,18 @@ HyperSDK.Replay.OnAction += HandleReplayAction;
40
42
 
41
43
  `RecordActivity` records only during active Battle gameplay. The SDK owns replay
42
44
  timestamps, file loading, scheduling, pause handling, progress, and completion.
45
+
46
+ ## Replay integration checklist
47
+
48
+ 1. Read physical input in `Update`, queue commands, and consume them in
49
+ `FixedUpdate` with gameplay and physics.
50
+ 2. Record only commands the game accepts, then route `HyperSDK.Replay.OnAction`
51
+ through the same command path.
52
+ 3. Use `HyperRandom` for gameplay randomness and interpolate moving Rigidbodies
53
+ so fixed simulation still renders smoothly.
54
+ 4. Verify the original replay and repeated Watch Again runs at every supported
55
+ speed on desktop and mobile WebGL.
56
+ 5. Leave **Replay Custom Restart** disabled unless the game clears all gameplay
57
+ state, spawned objects, coroutines, pools, and cached references correctly.
58
+ 6. Leave **Allow Replay Seeking** disabled for physics-heavy games unless their
59
+ accelerated reconstruction has been tested and remains deterministic.
@@ -47,7 +47,7 @@ namespace HyperSamples.LaneRunner
47
47
  }
48
48
 
49
49
  item.Advance(distance);
50
- if (item.transform.position.x < despawnX)
50
+ if (item.PositionX < despawnX)
51
51
  RemoveItemAt(i);
52
52
  }
53
53
 
@@ -2,14 +2,21 @@ using UnityEngine;
2
2
 
3
3
  namespace HyperSamples.LaneRunner
4
4
  {
5
- [RequireComponent(typeof(Collider))]
5
+ [RequireComponent(typeof(Collider), typeof(Rigidbody))]
6
6
  public sealed class LaneRunnerTrackItem : MonoBehaviour
7
7
  {
8
8
  [SerializeField] private bool doubleScorePickup;
9
9
 
10
10
  public bool IsCollected { get; private set; }
11
+ public float PositionX => body.position.x;
11
12
 
12
13
  private LaneRunnerGame game;
14
+ private Rigidbody body;
15
+
16
+ private void Awake()
17
+ {
18
+ body = GetComponent<Rigidbody>();
19
+ }
13
20
 
14
21
  public void Initialize(LaneRunnerGame owner)
15
22
  {
@@ -19,7 +26,7 @@ namespace HyperSamples.LaneRunner
19
26
 
20
27
  public void Advance(float distance)
21
28
  {
22
- transform.position += Vector3.left * distance;
29
+ body.MovePosition(body.position + Vector3.left * distance);
23
30
  }
24
31
 
25
32
  private void OnTriggerEnter(Collider other)
@@ -40,6 +40,7 @@ namespace HyperSamples.LaneRunner
40
40
  private void HandleCustomRestart()
41
41
  {
42
42
  game.Stop();
43
+ // The SDK rewinds replay state and RNG before OnGameStart rebuilds the run.
43
44
  HyperSDK.Game.CompleteCustomRestart();
44
45
  }
45
46
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "app.hypergames.hypersdk",
3
- "version": "1.11.0-beta.24",
3
+ "version": "1.11.0-beta.26",
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",