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 +14 -0
- package/Runtime/Internal/Bootstrap/HyperLoader.cs +5 -1
- package/Runtime/Internal/Managers/BackendManager.cs +26 -3
- package/Runtime/Internal/Managers/DataManager.cs +8 -0
- package/Runtime/Internal/Replay/ReplaySessionState.cs +9 -1
- package/Runtime/Internal/UI/Modals/ReplayPausedModal.cs +9 -1
- package/Samples~/LaneRunner/Prefabs/DoubleScorePickup.prefab +1 -1
- package/Samples~/LaneRunner/Prefabs/Obstacle.prefab +1 -1
- package/Samples~/LaneRunner/README.md +19 -2
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerSpawner.cs +1 -1
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerTrackItem.cs +9 -2
- package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerTournamentAdapter.cs +1 -0
- package/package.json +1 -1
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(
|
|
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(
|
|
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(
|
|
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 = (
|
|
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;
|
|
@@ -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
|
-
|
|
17
|
-
|
|
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.
|
|
@@ -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
|
-
|
|
29
|
+
body.MovePosition(body.position + Vector3.left * distance);
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
private void OnTriggerEnter(Collider other)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "app.hypergames.hypersdk",
|
|
3
|
-
"version": "1.11.0-beta.
|
|
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",
|