gg.easy.airship 0.1.2127 → 0.1.2128

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.
Files changed (111) hide show
  1. package/Editor/TypescriptServices/Compiler/TypescriptCompilationService.cs +3 -0
  2. package/Runtime/Code/AirshipConst.cs +2 -2
  3. package/Runtime/Code/LuauAPI/Bridge.cs +27 -0
  4. package/Runtime/Code/Network/Simulation/AirshipNetworkedObject.cs +81 -0
  5. package/Runtime/Code/Network/Simulation/AirshipNetworkedObject.cs.meta +3 -0
  6. package/Runtime/Code/Network/Simulation/AirshipOfflineRigidbody.cs +55 -0
  7. package/Runtime/Code/Network/Simulation/AirshipOfflineRigidbody.cs.meta +3 -0
  8. package/Runtime/Code/Network/Simulation/AirshipSimulationManager.cs +349 -0
  9. package/Runtime/Code/Network/Simulation/AirshipSimulationManager.cs.meta +3 -0
  10. package/Runtime/Code/Network/Simulation/History.cs +255 -0
  11. package/Runtime/Code/Network/Simulation/History.cs.meta +3 -0
  12. package/Runtime/Code/Network/Simulation.meta +3 -0
  13. package/Runtime/Code/Network/StateSystem/AirshipNetworkedStateManager.cs +1091 -0
  14. package/Runtime/Code/Network/StateSystem/AirshipNetworkedStateManager.cs.meta +3 -0
  15. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestMovement.cs +106 -0
  16. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestMovement.cs.meta +3 -0
  17. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestMovementInput.cs +26 -0
  18. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestMovementInput.cs.meta +3 -0
  19. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestMovementState.cs +43 -0
  20. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestMovementState.cs.meta +3 -0
  21. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestNetworkedStateManager.cs +43 -0
  22. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem/TestNetworkedStateManager.cs.meta +3 -0
  23. package/Runtime/Code/Network/StateSystem/Implementations/TestMovementSystem.meta +3 -0
  24. package/Runtime/Code/Network/StateSystem/Implementations.meta +3 -0
  25. package/Runtime/Code/Network/StateSystem/NetworkedStateSystem.cs +87 -0
  26. package/Runtime/Code/Network/StateSystem/NetworkedStateSystem.cs.meta +3 -0
  27. package/Runtime/Code/Network/StateSystem/Structures/InputCommand.cs +24 -0
  28. package/Runtime/Code/Network/StateSystem/Structures/InputCommand.cs.meta +3 -0
  29. package/Runtime/Code/Network/StateSystem/Structures/StateSnapshot.cs +35 -0
  30. package/Runtime/Code/Network/StateSystem/Structures/StateSnapshot.cs.meta +3 -0
  31. package/Runtime/Code/Network/StateSystem/Structures.meta +3 -0
  32. package/Runtime/Code/Network/StateSystem.meta +3 -0
  33. package/Runtime/Code/Network/SyncedBlob.cs +26 -0
  34. package/Runtime/Code/Network/SyncedBlob.cs.meta +3 -0
  35. package/Runtime/Code/Player/Character/Animation/CharacterAnimationHelper.cs +317 -246
  36. package/Runtime/Code/Player/Character/Animation/CharacterAnimationHelper.cs.meta +2 -2
  37. package/Runtime/Code/Player/Character/Animation/ClipReplacer/AnimatorClipReplacer.cs +0 -1
  38. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterAnimationSyncData.cs +41 -0
  39. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterAnimationSyncData.cs.meta +3 -0
  40. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterMovement.cs +1447 -0
  41. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterMovement.cs.meta +3 -0
  42. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterMovementSettings.cs +175 -0
  43. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterMovementSettings.cs.meta +3 -0
  44. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterNetworkedStateManager.cs +43 -0
  45. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterNetworkedStateManager.cs.meta +3 -0
  46. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterPhysics.cs +441 -0
  47. package/Runtime/Code/Player/Character/MovementSystems/Character/CharacterPhysics.cs.meta +3 -0
  48. package/Runtime/Code/Player/Character/MovementSystems/Character/Structures/CharacterInputData.cs +47 -0
  49. package/Runtime/Code/Player/Character/MovementSystems/Character/Structures/CharacterInputData.cs.meta +3 -0
  50. package/Runtime/Code/Player/Character/MovementSystems/Character/Structures/CharacterSnapshotData.cs +189 -0
  51. package/Runtime/Code/Player/Character/MovementSystems/Character/Structures/CharacterSnapshotData.cs.meta +2 -0
  52. package/Runtime/Code/Player/Character/MovementSystems/Character/Structures.meta +3 -0
  53. package/Runtime/Code/Player/Character/MovementSystems/Character.meta +3 -0
  54. package/Runtime/Code/Player/Character/MovementSystems.meta +3 -0
  55. package/Runtime/Code/Player/Entity/Animation/Editor/EntityDebugAnimator.cs +1 -0
  56. package/Runtime/Code/Player/PlayerInfo.cs +1 -2
  57. package/Runtime/Code/Player/PlayerManagerBridge.cs +1 -1
  58. package/Runtime/Code/PreventCodeStripping.cs +1 -1
  59. package/Runtime/Code/TSCodeGen/Editor/CsToTs/TypeScript/Helper.cs +21 -12
  60. package/Runtime/Code/TSCodeGen/TypeGenerator.cs +5 -5
  61. package/Runtime/Code/VoiceChat/AirshipUniVoiceNetwork.cs +24 -11
  62. package/Runtime/Code/VoxelWorld/VoxelWorld.cs +20 -0
  63. package/Runtime/Code/VoxelWorld/VoxelWorldChunk.cs +11 -0
  64. package/Runtime/Code/VoxelWorld/VoxelWorldCollision.cs +60 -2
  65. package/Runtime/Prefabs/Network.prefab +5 -5
  66. package/Runtime/Scenes/CoreScene.unity +350 -375
  67. package/ThirdParty/Mirror/Core/NetworkConnectionToClient.cs +1 -1
  68. package/URP/AirshipURPAsset.asset +1 -1
  69. package/package.json +1 -1
  70. package/Runtime/Code/Player/Character/API/CharacterAction.cs +0 -5
  71. package/Runtime/Code/Player/Character/API/CharacterAction.cs.meta +0 -3
  72. package/Runtime/Code/Player/Character/API/CharacterMovementData.cs +0 -188
  73. package/Runtime/Code/Player/Character/API/CharacterMovementData.cs.meta +0 -3
  74. package/Runtime/Code/Player/Character/API/CharacterPhysics.cs +0 -322
  75. package/Runtime/Code/Player/Character/API/CharacterPhysics.cs.meta +0 -3
  76. package/Runtime/Code/Player/Character/API.meta +0 -3
  77. package/Runtime/Code/Player/Character/MovementSystem/CharacterAnimationSyncData.cs +0 -36
  78. package/Runtime/Code/Player/Character/MovementSystem/CharacterAnimationSyncData.cs.meta +0 -2
  79. package/Runtime/Code/Player/Character/MovementSystem/CharacterMovement.cs +0 -1437
  80. package/Runtime/Code/Player/Character/MovementSystem/CharacterMovement.cs.meta +0 -14
  81. package/Runtime/Code/Player/Character/MovementSystem/CharacterMovementTests.cs +0 -68
  82. package/Runtime/Code/Player/Character/MovementSystem/CharacterMovementTests.cs.meta +0 -2
  83. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictedController.cs +0 -825
  84. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictedController.cs.meta +0 -2
  85. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictedState.cs +0 -23
  86. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictedState.cs.meta +0 -2
  87. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictionManager.cs +0 -431
  88. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictionManager.cs.meta +0 -2
  89. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictionRPC.cs +0 -39
  90. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictionRPC.cs.meta +0 -2
  91. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictionRigidbodyController.cs +0 -91
  92. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/AirshipPredictionRigidbodyController.cs.meta +0 -2
  93. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/CharacterMovement/AirshipPredictedCharacterMovement.cs +0 -313
  94. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/CharacterMovement/AirshipPredictedCharacterMovement.cs.meta +0 -2
  95. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/CharacterMovement/AirshipPredictionCharacterMovementRPC.cs +0 -19
  96. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/CharacterMovement/AirshipPredictionCharacterMovementRPC.cs.meta +0 -2
  97. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/CharacterMovement/CharacterMovementState.cs +0 -87
  98. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/CharacterMovement/CharacterMovementState.cs.meta +0 -2
  99. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/CharacterMovement.meta +0 -8
  100. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/Rigidbody/AirshipPredictedRigidbody.cs +0 -188
  101. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/Rigidbody/AirshipPredictedRigidbody.cs.meta +0 -2
  102. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/Rigidbody/AirshipPredictedRigidbodyState.cs +0 -32
  103. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/Rigidbody/AirshipPredictedRigidbodyState.cs.meta +0 -2
  104. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction/Rigidbody.meta +0 -8
  105. package/Runtime/Code/Player/Character/MovementSystem/ClientPrediction.meta +0 -8
  106. package/Runtime/Code/Player/Character/MovementSystem.meta +0 -8
  107. package/Runtime/Code/Player/Character/Net/MoveInputData.cs +0 -42
  108. package/Runtime/Code/Player/Character/Net/MoveInputData.cs.meta +0 -3
  109. package/Runtime/Code/Player/Character/Net.meta +0 -3
  110. /package/Runtime/Code/Player/Character/{CharacterRig.cs → Animation/CharacterRig.cs} +0 -0
  111. /package/Runtime/Code/Player/Character/{CharacterRig.cs.meta → Animation/CharacterRig.cs.meta} +0 -0
@@ -21,6 +21,7 @@ using Newtonsoft.Json.Linq;
21
21
  using ParrelSync;
22
22
  using UnityEditor;
23
23
  using UnityEngine;
24
+ using UnityEngine.Android;
24
25
  using UnityEngine.PlayerLoop;
25
26
  using UnityEngine.Serialization;
26
27
  using Debug = UnityEngine.Debug;
@@ -55,6 +56,7 @@ using Object = UnityEngine.Object;
55
56
  /// </summary>
56
57
  // [InitializeOnLoad]
57
58
  public static class TypescriptCompilationService {
59
+ private const int ExitCodeKill = 137;
58
60
  private const string TsCompilerService = "Typescript Compilation Service";
59
61
 
60
62
  /// <summary>
@@ -768,6 +770,7 @@ using Object = UnityEngine.Object;
768
770
 
769
771
  proc.Exited += (_, _) => {
770
772
  if (proc.ExitCode <= 0) return;
773
+ if (proc.ExitCode == ExitCodeKill) return;
771
774
 
772
775
  Debug.Log("Compiler process exited with code " + proc.ExitCode);
773
776
  var progressId = TypescriptProjectsService.Project!.ProgressId;
@@ -1,11 +1,11 @@
1
1
  // ReSharper disable InconsistentNaming
2
2
  namespace Code {
3
3
  public static class AirshipConst {
4
- public const int playerVersion = 4;
4
+ public const int playerVersion = 5;
5
5
 
6
6
  /// <summary>
7
7
  /// The server will kick clients that have a playerVersion lower than this value.
8
8
  /// </summary>
9
- public const int minAcceptedPlayerVersionOnServer = 4;
9
+ public const int minAcceptedPlayerVersionOnServer = 5;
10
10
  }
11
11
  }
@@ -389,6 +389,33 @@ public static class Bridge {
389
389
  public static void UnloadGlobalSceneByName(string sceneName) {
390
390
  // InstanceFinder.SceneManager.UnloadGlobalScenes(new SceneUnloadData(sceneName));
391
391
  }
392
+
393
+ [LuauAPI(LuauContext.Protected)]
394
+ public static bool IsLowEndDevice() {
395
+ // CPU check
396
+ string cpu = SystemInfo.processorType.ToLower();
397
+ if (cpu.Contains("celeron") || cpu.Contains("pentium") || cpu.Contains("atom")) {
398
+ return true;
399
+ }
400
+
401
+ // GPU check
402
+ string gpu = SystemInfo.graphicsDeviceName.ToLower();
403
+ if (gpu.Contains("intel") || gpu.Contains("uhd") || gpu.Contains("hd graphics")) {
404
+ return true;
405
+ }
406
+
407
+ // RAM check
408
+ if (SystemInfo.systemMemorySize < 8000) { // Less than 8GB RAM
409
+ return true;
410
+ }
411
+
412
+ // GPU memory check
413
+ if (SystemInfo.graphicsMemorySize < 2000) { // Less than 2GB VRAM
414
+ return true;
415
+ }
416
+
417
+ return false;
418
+ }
392
419
 
393
420
  public static void MoveGameObjectToScene(GameObject gameObject, Scene scene) {
394
421
  if (LuauCore.IsProtectedScene(scene) && LuauCore.CurrentContext == LuauContext.Game) {
@@ -0,0 +1,81 @@
1
+ using System;
2
+ using Mirror;
3
+ using UnityEngine;
4
+
5
+ namespace Code.Network.Simulation
6
+ {
7
+ struct TransformSnapshot
8
+ {
9
+ public Vector3 position;
10
+ public Quaternion rotation;
11
+ }
12
+
13
+ /**
14
+ * This component is used to allow lag compensation, prediction, and other networked state systems to
15
+ * work with a networked object controlled by the server.
16
+ *
17
+ * When this component is placed on a object being networked with Mirror, the server can include it
18
+ * in lag compensation and clients can resimulate their predictions more accurately.
19
+ */
20
+ public class AirshipNetworkedObject : NetworkBehaviour
21
+ {
22
+ private History<TransformSnapshot> history;
23
+
24
+ private void Start()
25
+ {
26
+ if (isServer && authority)
27
+ {
28
+ history = new History<TransformSnapshot>(NetworkServer.sendRate);
29
+ AirshipSimulationManager.Instance.OnCaptureSnapshot += this.CaptureSnapshot;
30
+ AirshipSimulationManager.Instance.OnSetSnapshot += this.SetSnapshot;
31
+ AirshipSimulationManager.Instance.OnLagCompensationCheck += this.LagCompensationCheck;
32
+ }
33
+
34
+ if (isClient && !authority)
35
+ {
36
+ history = new History<TransformSnapshot>(NetworkClient.sendRate);
37
+ AirshipSimulationManager.Instance.OnCaptureSnapshot += this.CaptureSnapshot;
38
+ AirshipSimulationManager.Instance.OnSetSnapshot += this.SetSnapshot;
39
+ }
40
+ }
41
+
42
+ private void OnDestroy()
43
+ {
44
+ AirshipSimulationManager.Instance.OnCaptureSnapshot -= this.CaptureSnapshot;
45
+ AirshipSimulationManager.Instance.OnSetSnapshot -= this.SetSnapshot;
46
+ AirshipSimulationManager.Instance.OnLagCompensationCheck -= this.LagCompensationCheck;
47
+ }
48
+
49
+ private void CaptureSnapshot(double time, bool replay)
50
+ {
51
+ if (replay)
52
+ {
53
+ var state = this.history.Get(time);
54
+ this.transform.position = state.position;
55
+ this.transform.rotation = state.rotation;
56
+ return;
57
+ }
58
+
59
+ this.history.Add(time, new TransformSnapshot()
60
+ {
61
+ position = this.transform.position,
62
+ rotation = this.transform.rotation
63
+ });
64
+ }
65
+
66
+ private void SetSnapshot(object objTime)
67
+ {
68
+ if (objTime is double time) {
69
+ var snapshot = this.history.Get(time);
70
+ this.transform.position = snapshot.position;
71
+ this.transform.rotation = snapshot.rotation;
72
+ }
73
+ }
74
+
75
+ private void LagCompensationCheck(int clientId, double time, double latency)
76
+ {
77
+ var bufferedTime = time - latency - NetworkClient.bufferTime;
78
+ this.SetSnapshot(bufferedTime);
79
+ }
80
+ }
81
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 06ffda6049064b9aa9449f9b169ff389
3
+ timeCreated: 1739323845
@@ -0,0 +1,55 @@
1
+ using System;
2
+ using UnityEngine;
3
+
4
+ namespace Code.Network.Simulation
5
+ {
6
+ /**
7
+ * Disables a rigidbodies physics (sets it to kinematic) when resimulation or lag compensation is taking place.
8
+ */
9
+ public class AirshipOfflineRigidbody : MonoBehaviour
10
+ {
11
+ public Rigidbody rigidbody;
12
+ private bool kinematicSetting = false;
13
+
14
+ private Vector3 position;
15
+ private Quaternion rotation;
16
+ private Vector3 linearVelocity;
17
+ private Vector3 angularVelocity;
18
+
19
+ public void Start()
20
+ {
21
+ this.rigidbody = this.GetComponent<Rigidbody>();
22
+ AirshipSimulationManager.Instance.OnSetPaused += OnPause;
23
+ }
24
+
25
+ private void OnDestroy()
26
+ {
27
+ AirshipSimulationManager.Instance.OnSetPaused -= OnPause;
28
+ }
29
+
30
+ private void OnPause(bool paused)
31
+ {
32
+ if (this.rigidbody == null) return;
33
+ if (paused)
34
+ {
35
+ this.kinematicSetting = this.rigidbody.isKinematic;
36
+ this.rigidbody.isKinematic = true;
37
+ this.position = this.rigidbody.position;
38
+ this.rotation = this.rigidbody.rotation;
39
+ this.linearVelocity = this.rigidbody.linearVelocity;
40
+ this.angularVelocity = this.rigidbody.angularVelocity;
41
+ }
42
+ else
43
+ {
44
+ this.rigidbody.isKinematic = this.kinematicSetting;
45
+ this.rigidbody.position = this.position;
46
+ this.rigidbody.rotation = this.rotation;
47
+ if (!this.kinematicSetting)
48
+ {
49
+ this.rigidbody.linearVelocity = this.linearVelocity;
50
+ this.rigidbody.angularVelocity = this.angularVelocity;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: e81927b37e89412490521c8e87aadcd0
3
+ timeCreated: 1739512084
@@ -0,0 +1,349 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using Mirror;
5
+ using UnityEngine;
6
+ using UnityEngine.Serialization;
7
+
8
+ namespace Code.Network.Simulation
9
+ {
10
+ /**
11
+ * Callback used to check the world state at the time requested. You should consider the physics world
12
+ * to be read only while this function executes. Changes to the physics state will be overwritten after
13
+ * your function returns. Use the RollbackComplete callback to modify the physics world based on the
14
+ * results of your check.
15
+ */
16
+ public delegate void CheckWorld();
17
+
18
+ /**
19
+ * Callback used to modify physics in the next server tick. The physics world is set to the most recent
20
+ * server tick, and can be modified freely. These modifications will be reconciled to the clients as part
21
+ * of the next server tick.
22
+ *
23
+ * Use this callback to do things like add impulses to hit characters, move them, or anything else that
24
+ * changes physics results.
25
+ */
26
+ public delegate void RollbackComplete();
27
+
28
+ /**
29
+ * Requests a simulation based on the provided time. Requesting a simulation will roll back the physics
30
+ * world to the snapshot just before or at the base time provided. Calling the returned tick function
31
+ * will advance the simulation and re-simulate the calls to OnPerformTick, Physics.Simulate(), and OnCaptureSnapshot
32
+ *
33
+ * When this call completes, the world will be at the last completed tick.
34
+ */
35
+ public delegate void PerformResimulate(double baseTime);
36
+
37
+ /**
38
+ * Function that will be run when the simulation manager is ready to perform a resimulation. Remember that
39
+ * the simulation base time provided to the resimulate function is the **local time** you wish to resimulate from.
40
+ * The local time should be one provided by the SimulationManager during a tick.
41
+ * Do not pass NetworkTime.time or a similar server derived time to this resimulate function.
42
+ *
43
+ * This function should not be used on the server.
44
+ */
45
+ public delegate void PerformResimulationCallback(PerformResimulate simulateFunction);
46
+
47
+ struct LagCompensationRequest
48
+ {
49
+ public CheckWorld check;
50
+ public RollbackComplete complete;
51
+ public NetworkConnectionToClient client;
52
+ }
53
+
54
+ struct ResimulationRequest
55
+ {
56
+ public PerformResimulationCallback callback;
57
+ }
58
+
59
+ /**
60
+ * The simulation manager is responsible for calling Physics.Simulate and providing generic hooks for other systems to use.
61
+ * Server authoritative networking uses the simulation manager to perform resimulations of its client predictions.
62
+ */
63
+ [LuauAPI]
64
+ public class AirshipSimulationManager : Singleton<AirshipSimulationManager>
65
+ {
66
+ /**
67
+ * This function notifies all watching components that a re-simulation
68
+ * is about to occur. The boolean parameter will be true if a re-simulation
69
+ * is about to occur, and will be false if the re-simulation has finished.
70
+ *
71
+ * Most components watching this will want to set their rigidbodies to
72
+ * kinematic if they do not wish to take part in the re-simulation. Physics
73
+ * will be ticked during re-simulation.
74
+ */
75
+ public event Action<bool> OnSetPaused;
76
+
77
+ /**
78
+ * This action notifies all watching components that they need to set their
79
+ * state to be based on the snapshot captured just before or on the provided
80
+ * time. Components should expect a PerformTick() call sometime after this
81
+ * function completes.
82
+ */
83
+ public event Action<object> OnSetSnapshot;
84
+
85
+ /**
86
+ * This action notifies listeners that we are performing a lag compensation check.
87
+ * This action is only ever invoked on the server. Components listening to this
88
+ * action should set their state to be what the client would have seen at the provided
89
+ * tick time. Keep in mind, this means that any components the client would have been
90
+ * observing (ie. other player characters) should be rolled back an additional amount to account for the client
91
+ * interpolation. You can convert a time to an exact tick time using
92
+ * GetLastSimulationTime() to find the correct tick time for any given time in the
93
+ * last 1 second.
94
+ *
95
+ * After a lag compensation check is completed, OnSetSnapshot will be called to correct
96
+ * the physics world to it's current state.
97
+ *
98
+ * clientId - The connectionId of the client we are simulating the view of
99
+ * currentTime - The tick time that triggered this compensation check
100
+ * rtt - The estimated time it takes for a message to reach the client and then be returned to the server (aka. ping) (rtt / 2 = latency)
101
+ */
102
+ public event Action<int, double, double> OnLagCompensationCheck;
103
+
104
+ /**
105
+ * This action tells all watching components that they need to perform a tick.
106
+ * A Physics.Simulate() call will be made after PerformTick completes.
107
+ */
108
+ public event Action<double, bool> OnPerformTick;
109
+
110
+ public event Action<object, object> OnTick;
111
+
112
+ /**
113
+ * Informs all watching components that the simulation tick has been performed
114
+ * and that a new snapshot of the resulting Physics.Simulate() should be captured.
115
+ * This snapshot should be the state for the provided tick number in history.
116
+ */
117
+ public event Action<double, bool> OnCaptureSnapshot;
118
+
119
+ /**
120
+ * Fired when a tick leaves local history and will never be referenced again. You can use this
121
+ * event to clean up any data that is no longer required.
122
+ */
123
+ public event Action<object> OnHistoryLifetimeReached;
124
+
125
+ [NonSerialized] public bool replaying = false;
126
+
127
+ private bool isActive = false;
128
+ private List<double> tickTimes = new List<double>();
129
+ private List<LagCompensationRequest> lagCompensationRequests = new();
130
+ private Queue<ResimulationRequest> resimulationRequests = new();
131
+
132
+ public void ActivateSimulationManager()
133
+ {
134
+ if (isActive) return;
135
+ Physics.simulationMode = SimulationMode.Script;
136
+ this.isActive = true;
137
+ }
138
+
139
+ public void FixedUpdate()
140
+ {
141
+ // Clients use their own timelines for physics. Do not compare times generated on a client with a time generated
142
+ // on the server. The Server should estimate when a client created a command using it's own timeline and ping calculations
143
+ // and a client should convert server authoritative state received to its own timeline by interpolating with NetworkTime.time
144
+ // and capturing snapshots of the interpolated state on its own timeline.
145
+ var time = NetworkServer.active ? NetworkTime.time : Time.unscaledTimeAsDouble;
146
+
147
+ if (!isActive) return;
148
+ if (Physics.simulationMode != SimulationMode.Script) return;
149
+
150
+ // Before running any commands, we perform any resimulation requests that were made during
151
+ // the last tick. This ensures that resimulations don't affect command processing and
152
+ // that all commands run on the most up to date predictions.
153
+ var resimBackTo = time;
154
+ while (this.resimulationRequests.TryDequeue(out ResimulationRequest request))
155
+ {
156
+ try
157
+ {
158
+ request.callback((requestedTime) =>
159
+ {
160
+ if (resimBackTo > requestedTime) resimBackTo = requestedTime;
161
+ });
162
+ }
163
+ catch (Exception e)
164
+ {
165
+ Debug.LogError(e);
166
+ }
167
+ }
168
+
169
+ // Only resimulate once. Go back to the farthest back time that was requested.
170
+ if (resimBackTo != time) this.PerformResimulation(resimBackTo);
171
+
172
+ // Perform the standard tick behavior
173
+ OnPerformTick?.Invoke(time, false);
174
+ OnTick?.Invoke(time, false);
175
+ // Debug.Log("Simulate call. Main Tick: " + NetworkTime.time);
176
+ Physics.Simulate(Time.fixedDeltaTime);
177
+ OnCaptureSnapshot?.Invoke(time, false);
178
+
179
+ // Process any lag compensation requests now that we have completed the ticking and snapshot creation
180
+ // Note: This process is placed after snapshot processing so that changes made to physics (like an impulse)
181
+ // are processed on the _next_ tick. This is safe because the server never resimulates.
182
+ var processedLagCompensation = false;
183
+ foreach (var request in this.lagCompensationRequests)
184
+ {
185
+ processedLagCompensation = true;
186
+ try
187
+ {
188
+ // Debug.LogWarning("Server lag compensation rolling back for client " + request.client.connectionId);
189
+ OnLagCompensationCheck?.Invoke(request.client.connectionId, time,
190
+ request.client.rtt);
191
+ request.check();
192
+ }
193
+ catch (Exception e)
194
+ {
195
+ Debug.LogError(e);
196
+ }
197
+ }
198
+
199
+ // If we processed lag compensation, we have some additional work to do
200
+ if (processedLagCompensation)
201
+ {
202
+ // Debug.LogWarning("Server completed " + this.lagCompensationRequests.Count + " lag compensation requests. Resetting to current tick (" + time + ") and finalizing.");
203
+ // Reset back to the server view of the world at the current time.
204
+ OnSetSnapshot?.Invoke(time);
205
+ // Invoke all of the callbacks for modifying physics that should be applied in the next tick.
206
+ while (this.lagCompensationRequests.Count > 0)
207
+ {
208
+ this.lagCompensationRequests[0].complete();
209
+ this.lagCompensationRequests.RemoveAt(0);
210
+ }
211
+ }
212
+
213
+ // Add our completed tick time into our history
214
+ this.tickTimes.Add(time);
215
+ // Keep the tick history around only for 1 second. This limits our lag compensation amount.
216
+ while (this.tickTimes.Count > 0 && time - this.tickTimes[0] > 1)
217
+ {
218
+ OnHistoryLifetimeReached?.Invoke(this.tickTimes[0]);
219
+ this.tickTimes.RemoveAt(0);
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Submits callbacks to be run later that will be able to view the physics world as the client
225
+ * would have seen it at the current tick. This allows you to confirm if a clients input would
226
+ * have hit a target from their point of view.
227
+ *
228
+ * This uses the clients estimated round trip time to determine what tick the client was likely
229
+ * seeing and rolls back Physics to that tick. Once physics is rolled back, the callback function
230
+ * is executed.
231
+ */
232
+ public void ScheduleLagCompensation(NetworkConnectionToClient client, CheckWorld checkCallback,
233
+ RollbackComplete completeCallback)
234
+ {
235
+ this.lagCompensationRequests.Add(new LagCompensationRequest()
236
+ {
237
+ check = checkCallback,
238
+ complete = completeCallback,
239
+ client = client,
240
+ });
241
+ }
242
+
243
+ /**
244
+ * Schedules a resimulation to occur on the next tick. This allows correcting predicted history on a non authoritative client.
245
+ * The callback provided will be called when the resimulation should occur. The callback will be passed a resimulate
246
+ * function to trigger a resimulation of all ticks from the provided base time back to the present time.
247
+ */
248
+ public void ScheduleResimulation(PerformResimulationCallback callback)
249
+ {
250
+ this.resimulationRequests.Enqueue(new ResimulationRequest() { callback = callback });
251
+ }
252
+
253
+ /**
254
+ * Allows typescript to request a resimulation from the provided time.
255
+ */
256
+ public void RequestResimulation(double time)
257
+ {
258
+ this.ScheduleResimulation((resim => resim(time)));
259
+ }
260
+
261
+ /**
262
+ * Requests a simulation based on the provided time. Requesting a simulation will roll back the physics
263
+ * world to the snapshot just before or at the base time provided. Calling the returned tick function
264
+ * will advance the simulation and re-simulate the calls to OnPerformTick, Physics.Simulate(), and OnCaptureSnapshot
265
+ *
266
+ * This function is used internally to implement the scheduled resimulations.
267
+ */
268
+ private void PerformResimulation(double baseTime)
269
+ {
270
+ Debug.Log($"T:{Time.unscaledTimeAsDouble} Resimulating from {baseTime}");
271
+
272
+ if (replaying)
273
+ {
274
+ Debug.LogWarning("Resim already active");
275
+ throw new ApplicationException(
276
+ "Re-simulation requested while a re-simulation is already active. Report this.");
277
+ }
278
+
279
+ // If the base time further in the past that our history goes, we reset to the oldest history we have (0) instead.
280
+ int tickIndex = this.CalculateIndexBeforeTime(baseTime);
281
+
282
+ this.replaying = true;
283
+ try
284
+ {
285
+ OnSetPaused?.Invoke(true);
286
+ OnSetSnapshot?.Invoke(this.tickTimes[tickIndex]);
287
+ Physics.SyncTransforms();
288
+ // Advance the tick so that we are re-processing the next tick after the base time provided.
289
+ tickIndex++;
290
+
291
+ while (tickIndex < this.tickTimes.Count)
292
+ {
293
+ OnPerformTick?.Invoke(this.tickTimes[tickIndex], true);
294
+ // Debug.Log("Simulate call. Replay Tick: " + this.tickTimes[tickIndex]);
295
+ Physics.Simulate(Time.fixedDeltaTime);
296
+ OnCaptureSnapshot?.Invoke(this.tickTimes[tickIndex], true);
297
+ tickIndex++;
298
+ }
299
+
300
+ OnSetPaused?.Invoke(false);
301
+ }
302
+ catch (Exception e)
303
+ {
304
+ Debug.LogError(e);
305
+ }
306
+ finally
307
+ {
308
+ this.replaying = false;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Gets the exact time of the last simulation provided a given time less than 1 second ago.
314
+ * Does not get times into the future.
315
+ */
316
+ public double GetLastSimulationTime(double time)
317
+ {
318
+ var index = this.CalculateIndexBeforeTime(time);
319
+ return this.tickTimes[index];
320
+ }
321
+
322
+ /**
323
+ * Calculates the index of the tick time just before or exactly at the time provided.
324
+ */
325
+ private int CalculateIndexBeforeTime(double baseTime)
326
+ {
327
+ if (this.tickTimes.Count == 0)
328
+ {
329
+ throw new ApplicationException("Resimulation requested before any ticks have occured. Report this.");
330
+ }
331
+
332
+ int afterIndex = this.tickTimes.FindIndex((time) =>
333
+ {
334
+ if (time > baseTime) return true;
335
+ return false;
336
+ });
337
+ if (afterIndex == -1)
338
+ {
339
+ Debug.LogWarning("Time calculation request used a base time of " + baseTime +
340
+ ", but the last tick time was " + this.tickTimes[^1] +
341
+ ". Current time is: " + NetworkTime.time + ". Is your network ok?");
342
+ return this.tickTimes.Count - 1;
343
+ }
344
+
345
+ // If the base time further in the past that our history goes, we reset to the oldest history we have (0) instead.
346
+ return afterIndex == 0 ? 0 : afterIndex - 1;
347
+ }
348
+ }
349
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 2847f64b42a24ab3bf97d62a494a2afa
3
+ timeCreated: 1738895079