gg.easy.airship 0.1.2194 → 0.1.2195
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/Editor/Settings/AirshipSettingsProvider.cs +11 -2
- package/Runtime/Code/AirshipConst.cs +3 -2
- package/Runtime/Code/Bootstrap/ServerBootstrap.cs +4 -0
- package/Runtime/Code/Bundles/SystemRoot.cs +11 -1
- package/Runtime/Code/CoreUI/Components/AirshipVersionOverlay.cs +5 -0
- package/Runtime/Code/Luau/LuauCore.cs +25 -1
- package/Runtime/Code/Luau/LuauCoreCallbacks.cs +6 -0
- package/Runtime/Code/Network/Net.cs +9 -3
- package/Runtime/Code/Network/Simulation/AirshipSimulationManager.cs +13 -11
- package/Runtime/Code/Network/StateSystem/AirshipNetworkedStateManager.cs +118 -109
- package/Runtime/Code/Player/Character/MovementSystems/Character/Structures/CharacterSnapshotData.cs +1 -1
- package/Runtime/Code/Quality/QualityManager.cs +5 -5
- package/Runtime/Code/UI/Canvas/CanvasUIEventInterceptor.cs +14 -14
- package/Runtime/Code/VoxelWorld/VoxelBlockDefinition.cs +1 -0
- package/Runtime/Code/VoxelWorld/VoxelBlockDefinitionList.cs +1 -0
- package/Runtime/Code/VoxelWorld/VoxelBlocks.cs +2 -0
- package/Runtime/Code/VoxelWorld/VoxelMeshProcessor.cs +11 -6
- package/Runtime/Code/VoxelWorld/VoxelWorld.cs +3 -0
- package/Runtime/Plugins/Android/libLuauPlugin.so +0 -0
- package/Runtime/Plugins/Linux/libLuauPlugin.so +0 -0
- package/Runtime/Plugins/Mac/LuauPlugin.bundle/Contents/Info.plist +7 -7
- package/Runtime/Plugins/Mac/LuauPlugin.bundle/Contents/MacOS/LuauPlugin +0 -0
- package/Runtime/Plugins/Windows/x64/LuauPlugin.dll +0 -0
- package/Runtime/Plugins/iOS/LuauPluginIos.a +0 -0
- package/Runtime/Scenes/CoreScene.unity +1 -1
- package/ThirdParty/Mirror/Core/NetworkClient.cs +9 -3
- package/ThirdParty/Mirror/Core/NetworkClient_TimeInterpolation.cs +1 -1
- package/ThirdParty/Mirror/Core/NetworkConnectionToClient.cs +6 -0
- package/ThirdParty/Mirror/Core/NetworkServer.cs +6 -4
- package/ThirdParty/Mirror/Core/Tools/SimpleMovingAverage.cs +42 -0
- package/ThirdParty/Mirror/Core/Tools/SimpleMovingAverage.cs.meta +3 -0
- package/URP/AirshipURPAsset.asset +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
using UnityEngine;
|
|
2
2
|
using UnityEditor;
|
|
3
3
|
using System.Collections.Generic;
|
|
4
|
+
using System.Diagnostics;
|
|
4
5
|
using System.IO;
|
|
5
6
|
using Airship.Editor;
|
|
6
7
|
using Code.Bootstrap;
|
|
@@ -186,7 +187,9 @@ public class AirshipSettingsProvider : SettingsProvider
|
|
|
186
187
|
|
|
187
188
|
if (newTimeout != EditorIntegrationsConfig.instance.luauScriptTimeout) {
|
|
188
189
|
EditorIntegrationsConfig.instance.luauScriptTimeout = newTimeout;
|
|
189
|
-
|
|
190
|
+
if (!Debugger.IsAttached) {
|
|
191
|
+
LuauPlugin.SetScriptTimeoutDuration(newTimeout);
|
|
192
|
+
}
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
if (GUI.changed) {
|
|
@@ -252,6 +255,12 @@ public class AirshipSettingsProvider : SettingsProvider
|
|
|
252
255
|
|
|
253
256
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
|
254
257
|
private static void OnReload() {
|
|
255
|
-
|
|
258
|
+
if (Debugger.IsAttached) {
|
|
259
|
+
// If the debugger is attached, just feed in a large delay to effectively disable the timeout.
|
|
260
|
+
// In the future, we could adjust the plugin to take something like -1 to disable it.
|
|
261
|
+
LuauPlugin.SetScriptTimeoutDuration(86400);
|
|
262
|
+
} else {
|
|
263
|
+
LuauPlugin.SetScriptTimeoutDuration(EditorIntegrationsConfig.instance.luauScriptTimeout);
|
|
264
|
+
}
|
|
256
265
|
}
|
|
257
266
|
}
|
|
@@ -6,17 +6,18 @@ using UnityEngine.Scripting;
|
|
|
6
6
|
namespace Code {
|
|
7
7
|
[LuauAPI][Preserve]
|
|
8
8
|
public static class AirshipConst {
|
|
9
|
-
public const int playerVersion =
|
|
9
|
+
public const int playerVersion = 23;
|
|
10
10
|
public static readonly IReadOnlyList<string> playerFlags = new string[] {
|
|
11
11
|
"SkipLoading",
|
|
12
12
|
"LagCompCheckIdIsInt",
|
|
13
13
|
"PlatformGearDownloadClassId",
|
|
14
|
+
"HasTransformMoveDirection", // True for versions that have access to CharacterMovement.TransformMoveDirection
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
/// <summary>
|
|
18
19
|
/// The server will kick clients that have a playerVersion lower than this value.
|
|
19
20
|
/// </summary>
|
|
20
|
-
public const int minAcceptedPlayerVersionOnServer =
|
|
21
|
+
public const int minAcceptedPlayerVersionOnServer = 23;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -66,6 +66,9 @@ public class ServerBootstrap : MonoBehaviour
|
|
|
66
66
|
private GameServer gameServer;
|
|
67
67
|
|
|
68
68
|
[NonSerialized] public bool isServerReady = false;
|
|
69
|
+
/// <summary>
|
|
70
|
+
/// Can be fired multiple times.
|
|
71
|
+
/// </summary>
|
|
69
72
|
public event Action OnStartLoadingGame;
|
|
70
73
|
public event Action OnServerReady;
|
|
71
74
|
public event Action OnStartupConfigReady;
|
|
@@ -373,6 +376,7 @@ public class ServerBootstrap : MonoBehaviour
|
|
|
373
376
|
var request = UnityWebRequestProxyHelper.ApplyProxySettings(new UnityWebRequest(url));
|
|
374
377
|
var gameConfigPath = Path.Combine(Application.persistentDataPath, "Games", startupConfig.GameBundleId, "gameConfig.json");
|
|
375
378
|
request.downloadHandler = new DownloadHandlerFile(gameConfigPath);
|
|
379
|
+
request.timeout = 7;
|
|
376
380
|
yield return request.SendWebRequest();
|
|
377
381
|
if (request.result != UnityWebRequest.Result.Success) {
|
|
378
382
|
Debug.LogError($"Failed to download gameConfig.json. url={url}, message={request.error}");
|
|
@@ -175,15 +175,25 @@ public class SystemRoot : Singleton<SystemRoot> {
|
|
|
175
175
|
var sw = Stopwatch.StartNew();
|
|
176
176
|
|
|
177
177
|
// Find packages we should UNLOAD
|
|
178
|
-
|
|
178
|
+
HashSet<string> unloadList = new();
|
|
179
179
|
foreach (var loadedPair in this.loadedAssetBundles) {
|
|
180
180
|
if (forceUnloadAll) {
|
|
181
181
|
unloadList.Add(loadedPair.Key);
|
|
182
182
|
continue;
|
|
183
183
|
}
|
|
184
|
+
|
|
184
185
|
var packageToLoad = packages.Find(p => p.id.ToLower() == loadedPair.Value.airshipPackage.id.ToLower());
|
|
186
|
+
// Todo: we can not unload asset bundles if there is only a code change.
|
|
185
187
|
if (packageToLoad == null || packageToLoad.assetVersion != loadedPair.Value.airshipPackage.assetVersion || packageToLoad.codeVersion != loadedPair.Value.airshipPackage.codeVersion) {
|
|
186
188
|
unloadList.Add(loadedPair.Key);
|
|
189
|
+
|
|
190
|
+
// If a package changes, then we need to unload everything because other packages & the game may depend on it.
|
|
191
|
+
if (packageToLoad != null && packageToLoad.packageType == AirshipPackageType.Package) {
|
|
192
|
+
foreach (var loaded in loadedAssetBundles) {
|
|
193
|
+
unloadList.Add(loaded.Key);
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
187
197
|
}
|
|
188
198
|
}
|
|
189
199
|
|
|
@@ -14,6 +14,11 @@ namespace Code.CoreUI.Components {
|
|
|
14
14
|
}
|
|
15
15
|
#endif
|
|
16
16
|
|
|
17
|
+
#if UNITY_IOS || UNITY_ANDROID
|
|
18
|
+
this.versionText.enabled = false;
|
|
19
|
+
return;
|
|
20
|
+
#endif
|
|
21
|
+
|
|
17
22
|
var hash = AirshipVersion.GetVersionHash();
|
|
18
23
|
this.versionText.text = Application.version + "-" + hash;
|
|
19
24
|
}
|
|
@@ -258,11 +258,35 @@ public partial class LuauCore : MonoBehaviour {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
#if UNITY_EDITOR
|
|
261
|
+
private PauseState _editorPauseState = PauseState.Unpaused;
|
|
262
|
+
private bool _appPaused = false;
|
|
263
|
+
private bool _pluginPaused = false;
|
|
264
|
+
|
|
265
|
+
private void OnApplicationOrEditorPauseChanged() {
|
|
266
|
+
var shouldPause = _appPaused || _editorPauseState == PauseState.Paused;
|
|
267
|
+
if (shouldPause == _pluginPaused) return;
|
|
268
|
+
|
|
269
|
+
_pluginPaused = shouldPause;
|
|
270
|
+
LuauPlugin.SetIsPaused(_pluginPaused);
|
|
271
|
+
}
|
|
272
|
+
|
|
261
273
|
private void OnPauseStateChanged(PauseState state) {
|
|
262
|
-
|
|
274
|
+
// Handle pauses in-editor triggered by the toolbar pause button
|
|
275
|
+
_editorPauseState = state;
|
|
276
|
+
OnApplicationOrEditorPauseChanged();
|
|
263
277
|
}
|
|
264
278
|
#endif
|
|
265
279
|
|
|
280
|
+
private void OnApplicationPause(bool pauseStatus) {
|
|
281
|
+
// Handle application pauses (e.g. when application loses focus on iOS)
|
|
282
|
+
#if UNITY_EDITOR
|
|
283
|
+
_appPaused = pauseStatus;
|
|
284
|
+
OnApplicationOrEditorPauseChanged();
|
|
285
|
+
#else
|
|
286
|
+
LuauPlugin.SetIsPaused(pauseStatus);
|
|
287
|
+
#endif
|
|
288
|
+
}
|
|
289
|
+
|
|
266
290
|
private void Start() {
|
|
267
291
|
Application.quitting += Quit;
|
|
268
292
|
LuauPlugin.unityMainThreadId = Thread.CurrentThread.ManagedThreadId;
|
|
@@ -717,6 +717,12 @@ public partial class LuauCore : MonoBehaviour {
|
|
|
717
717
|
}
|
|
718
718
|
|
|
719
719
|
private static unsafe void SetFieldValue<T>(object instance, T value, FieldInfo fieldInfo) where T : unmanaged {
|
|
720
|
+
// If declaring type is not blittable just use reflection
|
|
721
|
+
if (!UnsafeUtility.IsBlittable(fieldInfo.DeclaringType)) {
|
|
722
|
+
fieldInfo.SetValue(instance, value);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
|
|
720
726
|
if (fieldInfo.IsStatic) {
|
|
721
727
|
// Not sure how to do non-alloc static field sets, so just use reflection for now
|
|
722
728
|
// (these are relatively rare anyways)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
using System;
|
|
2
2
|
using System.Collections.Generic;
|
|
3
3
|
using Code.Luau;
|
|
4
|
+
using Code.Zstd;
|
|
4
5
|
using Mirror;
|
|
5
6
|
using UnityEngine;
|
|
6
7
|
using UnityEngine.Scripting;
|
|
@@ -75,11 +76,16 @@ namespace Assets.Luau.Network {
|
|
|
75
76
|
|
|
76
77
|
private void OnBroadcastFromClient(NetworkConnectionToClient conn, NetBroadcast msg) {
|
|
77
78
|
// Runs on the server, when the client broadcasts a message
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
try {
|
|
80
|
+
if ((ulong)msg.Blob.DecompressedDataSize >= MaxBytesAtOnce) {
|
|
81
|
+
Debug.LogWarning(
|
|
82
|
+
$"Dropping message from client connection {conn.connectionId} due to exceeding max data size.");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
} catch (ZstdException) { // TODO: temporary code to discard Zstd exceptions
|
|
80
86
|
return;
|
|
81
87
|
}
|
|
82
|
-
|
|
88
|
+
|
|
83
89
|
var now = Time.realtimeSinceStartup;
|
|
84
90
|
if (!_throttle.TryGetValue(conn.connectionId, out var throttle)) {
|
|
85
91
|
// If we don't have a throttle key, that means the client isn't connected anymore and we are processing an old message.
|
|
@@ -205,10 +205,18 @@ namespace Code.Network.Simulation
|
|
|
205
205
|
|
|
206
206
|
// Only resimulate once. Go back to the farthest back time that was requested.
|
|
207
207
|
if (resimBackTo != tick) this.PerformResimulation(resimBackTo);
|
|
208
|
+
|
|
209
|
+
// Perform the standard tick behavior
|
|
210
|
+
OnTick?.Invoke(tick, time, false);
|
|
211
|
+
// Debug.Log($"Simulate call. Main tick {tick}");
|
|
212
|
+
Physics.Simulate(Time.fixedDeltaTime);
|
|
213
|
+
OnCaptureSnapshot?.Invoke(tick, time, false);
|
|
208
214
|
|
|
209
215
|
// Process any lag compensation requests now that we have completed the ticking and snapshot creation
|
|
210
216
|
// Note: This process is placed after snapshot processing so that changes made to physics (like an impulse)
|
|
211
|
-
// are processed on the _next_ tick. This is safe because the server never resimulates.
|
|
217
|
+
// are processed on the _next_ tick. This is safe because the server never resimulates. If lag compensation
|
|
218
|
+
// is placed before OnTick, lag compensation may overwrite changes that occured during Update() or LateUpdate()
|
|
219
|
+
// since we roll back to the last snapshot which will have occured before Update() and LateUpdate().
|
|
212
220
|
var processedLagCompensation = false;
|
|
213
221
|
foreach (var entry in this.lagCompensationRequests)
|
|
214
222
|
{
|
|
@@ -216,9 +224,9 @@ namespace Code.Network.Simulation
|
|
|
216
224
|
{
|
|
217
225
|
processedLagCompensation = true;
|
|
218
226
|
// Debug.LogWarning("Server lag compensation rolling back for client " + entry.Key.connectionId);
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
OnLagCompensationCheck?.Invoke(entry.Key.connectionId,
|
|
227
|
+
// inputBufferTime is the additional time we queue commands locally on the server before processing them
|
|
228
|
+
// bufferTime is the observed player buffer size on the client
|
|
229
|
+
OnLagCompensationCheck?.Invoke(entry.Key.connectionId, tick, time, entry.Key.rtt / 2f, entry.Key.bufferTime + entry.Key.inputBufferTime);
|
|
222
230
|
|
|
223
231
|
var requests = entry.Value;
|
|
224
232
|
for (var i = 0; i < requests.Count; i++) {
|
|
@@ -236,7 +244,7 @@ namespace Code.Network.Simulation
|
|
|
236
244
|
{
|
|
237
245
|
// Debug.LogWarning("Server completed " + this.lagCompensationRequests.Count + " lag compensation requests. Resetting to current tick (" + previousTimes[^1] + ") and finalizing.");
|
|
238
246
|
// Reset back to the server view of the world at the current time.
|
|
239
|
-
OnSetSnapshot?.Invoke(
|
|
247
|
+
OnSetSnapshot?.Invoke(tick);
|
|
240
248
|
Physics.SyncTransforms();
|
|
241
249
|
// Invoke all of the callbacks for modifying physics that should be applied in the next tick.
|
|
242
250
|
foreach (var entry in this.lagCompensationRequests)
|
|
@@ -255,12 +263,6 @@ namespace Code.Network.Simulation
|
|
|
255
263
|
}
|
|
256
264
|
this.lagCompensationRequests.Clear();
|
|
257
265
|
}
|
|
258
|
-
|
|
259
|
-
// Perform the standard tick behavior
|
|
260
|
-
OnTick?.Invoke(tick, time, false);
|
|
261
|
-
// Debug.Log($"Simulate call. Main tick {tick}");
|
|
262
|
-
Physics.Simulate(Time.fixedDeltaTime);
|
|
263
|
-
OnCaptureSnapshot?.Invoke(tick, time, false);
|
|
264
266
|
|
|
265
267
|
// Add our completed tick time into our history
|
|
266
268
|
this.previousTicks.Add(tick);
|
|
@@ -68,16 +68,21 @@ namespace Code.Network.StateSystem
|
|
|
68
68
|
private int serverLastProcessedCommandNumber;
|
|
69
69
|
private int serverPredictedCommandCount = 0;
|
|
70
70
|
private SortedList<int, Input> serverCommandBuffer = new SortedList<int, Input>();
|
|
71
|
+
// Used to flag if we have exhaused the command buffer and need to wait for it to reach target fill
|
|
72
|
+
private bool serverCommandBufferWait = true;
|
|
73
|
+
private int serverNextBufferCheckTick = 0;
|
|
71
74
|
|
|
72
75
|
private int serverCommandBufferMaxSize = 0;
|
|
73
76
|
|
|
74
77
|
// How many commands we should generally have in the command buffer
|
|
75
|
-
private int serverCommandBufferTargetSize = 0;
|
|
76
78
|
private int serverCommandCatchUpRequired = 0;
|
|
79
|
+
private SimpleMovingAverage serverCommandBufferTargetSize;
|
|
80
|
+
private SimpleMovingAverage serverCommandBufferAvgSize;
|
|
77
81
|
|
|
78
82
|
// Non-auth server command tracking
|
|
79
83
|
// Note: we also re-use some of the above command buffer fields
|
|
80
84
|
private SortedList<int, State> serverReceivedStateBuffer = new SortedList<int, State>();
|
|
85
|
+
private int serverStatesReceivedThisFrame = 0;
|
|
81
86
|
// Last observed state from OnTick when running as a non-auth server.
|
|
82
87
|
private State serverLastObservedState;
|
|
83
88
|
|
|
@@ -203,10 +208,8 @@ namespace Code.Network.StateSystem
|
|
|
203
208
|
// The client should also stop sending commands after 1 second's worth of unconfirmed commands.
|
|
204
209
|
// This value is refreshed in auth server tick
|
|
205
210
|
this.serverCommandBufferMaxSize = (int)(1f/ Time.fixedUnscaledDeltaTime);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.serverCommandBufferTargetSize =Math.Min(this.serverCommandBufferMaxSize,
|
|
209
|
-
(int)Math.Ceiling(NetworkServer.sendInterval * bufferTimeMultiplier / Time.fixedUnscaledDeltaTime));
|
|
211
|
+
this.serverCommandBufferTargetSize = new SimpleMovingAverage(NetworkServer.sendRate);
|
|
212
|
+
this.serverCommandBufferAvgSize = new SimpleMovingAverage(NetworkServer.sendRate);
|
|
210
213
|
|
|
211
214
|
this.inputHistory = new(1);
|
|
212
215
|
this.stateHistory = new(1);
|
|
@@ -346,26 +349,44 @@ namespace Code.Network.StateSystem
|
|
|
346
349
|
this.Interpolate();
|
|
347
350
|
}
|
|
348
351
|
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
serverCommandCatchUpRequired = 0;
|
|
358
|
-
}
|
|
352
|
+
// Server states are sent one at a time instead of as a group, but we track how many we receive per frame so we can adjust our
|
|
353
|
+
// target buffer size if the client is slow. This is similar to what we do for received input groups.
|
|
354
|
+
if (serverStatesReceivedThisFrame != 0) {
|
|
355
|
+
// var jitterTime = Math.Sqrt(this.connectionToClient.rttVariance) / Time.fixedUnscaledDeltaTime;
|
|
356
|
+
this.serverCommandBufferTargetSize.Add(serverStatesReceivedThisFrame);
|
|
357
|
+
serverStatesReceivedThisFrame = 0;
|
|
359
358
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
359
|
+
|
|
360
|
+
if (AirshipSimulationManager.Instance.tick > this.serverNextBufferCheckTick) {
|
|
361
|
+
this.serverNextBufferCheckTick = AirshipSimulationManager.Instance.tick + NetworkServer.sendRate;
|
|
362
|
+
|
|
363
|
+
// We check this in Update so that we have finished processing all required fixedUpdates before checking
|
|
364
|
+
// if we are behind. Next time fixed update runs, we will process the additional amount we need to catch up.
|
|
365
|
+
if (isServer && serverAuth) {
|
|
366
|
+
if (serverCommandBuffer.Count > this.serverCommandBufferTargetSize.Value && Math.Floor(this.serverCommandBufferAvgSize.Value) > Math.Ceiling(this.serverCommandBufferTargetSize.Value)) {
|
|
367
|
+
serverCommandCatchUpRequired = (int) Math.Round(Math.Floor(this.serverCommandBufferAvgSize.Value) - Math.Ceiling(this.serverCommandBufferTargetSize.Value));
|
|
368
|
+
// print($"Command catchup required for {this.name}: {serverCommandCatchUpRequired}. {serverCommandBuffer.Count} in buffer {(int) Math.Round(this.serverCommandBufferTargetSize.Value)} target");
|
|
369
|
+
} else if (this.serverCommandBufferAvgSize.Value * 2 < this.serverCommandBufferTargetSize.Value) {
|
|
370
|
+
// This is a heuristic for if we are likely to have to predict more commands soon. Low buffer size means
|
|
371
|
+
// we have less of a chance to recover dropped packets. It's probably better for us to wait for additional
|
|
372
|
+
// inputs before we continue processing.
|
|
373
|
+
// print($"Command delay required for {this.name}: {serverCommandBuffer.Count} in buffer, {this.serverCommandBufferAvgSize.Value} ema, {this.serverCommandBufferTargetSize.Value} target");
|
|
374
|
+
this.serverCommandBufferWait = true;
|
|
375
|
+
} else {
|
|
376
|
+
serverCommandCatchUpRequired = 0;
|
|
377
|
+
}
|
|
366
378
|
}
|
|
367
|
-
|
|
368
|
-
|
|
379
|
+
|
|
380
|
+
// Same as above, but for client authoritative systems
|
|
381
|
+
if (isServer && !serverAuth) {
|
|
382
|
+
if (serverReceivedStateBuffer.Count > this.serverCommandBufferTargetSize.Value && this.serverCommandBufferAvgSize.Value > this.serverCommandBufferTargetSize.Value) {
|
|
383
|
+
serverCommandCatchUpRequired = (int) Math.Round(this.serverCommandBufferAvgSize.Value - this.serverCommandBufferTargetSize.Value);
|
|
384
|
+
// print($"State catchup required for {this.name}: {serverCommandCatchUpRequired}");
|
|
385
|
+
} else if (this.serverCommandBufferAvgSize.Value * 2 < this.serverCommandBufferTargetSize.Value) {
|
|
386
|
+
this.serverCommandBufferWait = true;
|
|
387
|
+
} else {
|
|
388
|
+
serverCommandCatchUpRequired = 0;
|
|
389
|
+
}
|
|
369
390
|
}
|
|
370
391
|
}
|
|
371
392
|
}
|
|
@@ -523,7 +544,7 @@ namespace Code.Network.StateSystem
|
|
|
523
544
|
|
|
524
545
|
#region Authoritative Server Functions
|
|
525
546
|
|
|
526
|
-
public void AuthServerTick(int tick, double time, bool replay)
|
|
547
|
+
public void AuthServerTick(int tick, double time, bool replay)
|
|
527
548
|
{
|
|
528
549
|
if (replay)
|
|
529
550
|
{
|
|
@@ -544,30 +565,20 @@ namespace Code.Network.StateSystem
|
|
|
544
565
|
|
|
545
566
|
// We must recalculate target size if the timescale has changed.
|
|
546
567
|
this.serverCommandBufferMaxSize = (int)( 1 / Time.fixedUnscaledDeltaTime);
|
|
547
|
-
this.serverCommandBufferTargetSize =
|
|
548
|
-
Math.Min(this.serverCommandBufferMaxSize,
|
|
549
|
-
(int)Math.Ceiling(NetworkServer.sendInterval * this.bufferTimeMultiplier / Time.fixedUnscaledDeltaTime));
|
|
550
568
|
// Optimal max is when we will start processing extra commands.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
//
|
|
554
|
-
if (this.maxServerCommandCatchup == 0)
|
|
555
|
-
{
|
|
556
|
-
var dropCount = 0;
|
|
557
|
-
|
|
558
|
-
while (serverCommandCatchUpRequired > 0 && dropCount < this.serverCommandBufferTargetSize)
|
|
559
|
-
{
|
|
560
|
-
this.serverCommandBuffer.RemoveAt(0);
|
|
561
|
-
dropCount++;
|
|
562
|
-
}
|
|
563
|
-
// print("Dropped " + dropCount + " command(s) from " + this.gameObject.name + " due to exceeding command buffer size.");
|
|
564
|
-
}
|
|
569
|
+
this.serverCommandBufferAvgSize.Add(this.serverCommandBuffer.Count);
|
|
570
|
+
this.connectionToClient.inputBufferTime = this.serverCommandBufferAvgSize.Value * Time.fixedUnscaledDeltaTime;
|
|
571
|
+
// print($"{this.name} has {serverCommandBuffer.Count} entries in the buffer. Target is {this.serverCommandBufferTargetSize.Value} Current Avg Fill: {this.serverCommandBufferAvgSize.Value}");
|
|
565
572
|
|
|
566
573
|
// Delay processing until we have at least one send interval worth of commands to process.
|
|
567
|
-
if (this.serverCommandBuffer.Count
|
|
574
|
+
if (this.serverCommandBufferWait && this.serverCommandBuffer.Count < this.serverCommandBufferTargetSize.Value || this.serverCommandBuffer.Count == 0) {
|
|
568
575
|
// Debug.Log($"Waiting for additional commands for {this.name}. There are {this.serverCommandBuffer.Count} commands in the buffer.");
|
|
569
576
|
this.stateSystem.Tick(null, tick, time, false);
|
|
570
577
|
return;
|
|
578
|
+
} else {
|
|
579
|
+
// Start processing commands from the beginning of the buffer after waiting
|
|
580
|
+
this.serverLastProcessedCommandNumber = this.serverCommandBuffer.Values[0].commandNumber - 1;
|
|
581
|
+
this.serverCommandBufferWait = false;
|
|
571
582
|
}
|
|
572
583
|
|
|
573
584
|
var commandsProcessed = 0;
|
|
@@ -575,69 +586,62 @@ namespace Code.Network.StateSystem
|
|
|
575
586
|
commandsProcessed++;
|
|
576
587
|
|
|
577
588
|
// Attempt to get a new command out of the buffer.
|
|
578
|
-
var
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
589
|
+
var canPredictCommand = this.serverPredictedCommandCount < Math.Ceiling(
|
|
590
|
+
this.maxServerCommandPrediction *
|
|
591
|
+
(NetworkServer.sendInterval /
|
|
592
|
+
Time.fixedUnscaledDeltaTime));
|
|
593
|
+
var expectedNextCommandNumber = this.serverLastProcessedCommandNumber + 1;
|
|
594
|
+
Input command = this.serverCommandBuffer.Count > 0 ? this.serverCommandBuffer.Values[0] : null;
|
|
595
|
+
|
|
596
|
+
// We have a valid command that is in sequence, or we reached our max predicted fill. Remove the next
|
|
597
|
+
// valid command from the buffer and process it.
|
|
598
|
+
if (command != null && (command.commandNumber == expectedNextCommandNumber || !canPredictCommand)) {
|
|
582
599
|
// Get the command to process
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
// (this.serverPredictedCommandCount + 1) + " command(s) so far.");
|
|
600
|
-
this.serverLastProcessedCommandNumber = expectedNextCommandNumber;
|
|
601
|
-
command = this.lastProcessedCommand;
|
|
602
|
-
command.commandNumber = expectedNextCommandNumber;
|
|
603
|
-
this.serverPredictedCommandCount++;
|
|
604
|
-
}
|
|
605
|
-
// We have a valid command that is in sequence, or we reached our max fill. Remove the next
|
|
606
|
-
// valid command from the buffer and process it.
|
|
607
|
-
else {
|
|
608
|
-
// Debug.Log("Ticking next command in sequence: " + command.commandNumber);
|
|
609
|
-
this.serverPredictedCommandCount = 0;
|
|
610
|
-
this.serverCommandBuffer.RemoveAt(0);
|
|
611
|
-
this.lastProcessedCommand = command;
|
|
612
|
-
this.serverLastProcessedCommandNumber = command.commandNumber;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
// tick command
|
|
616
|
-
command.tick = tick; // Correct tick to local timeline for ticking on the server.
|
|
617
|
-
this.stateSystem.Tick(command, tick, time, false);
|
|
600
|
+
command = this.serverCommandBuffer.Values[0];
|
|
601
|
+
this.serverCommandBuffer.RemoveAt(0);
|
|
602
|
+
|
|
603
|
+
this.serverPredictedCommandCount = 0;
|
|
604
|
+
this.lastProcessedCommand = command;
|
|
605
|
+
this.serverLastProcessedCommandNumber = command.commandNumber;
|
|
606
|
+
// We have an out of order command, or no command and we can predict the next command in sequence
|
|
607
|
+
} else if ((command != null && command.commandNumber != expectedNextCommandNumber ||
|
|
608
|
+
this.serverCommandBuffer.Count == 0) && this.lastProcessedCommand != null && canPredictCommand) {
|
|
609
|
+
command = this.lastProcessedCommand;
|
|
610
|
+
|
|
611
|
+
this.serverLastProcessedCommandNumber = expectedNextCommandNumber;
|
|
612
|
+
|
|
613
|
+
command.commandNumber = expectedNextCommandNumber;
|
|
614
|
+
this.serverPredictedCommandCount++;
|
|
615
|
+
// print("Reprocessing last command");
|
|
618
616
|
}
|
|
617
|
+
// we processed a command that never reached the server and we can't fill it or move on to a new command in the buffer.
|
|
618
|
+
// this means we've completely run out of inputs and we should wait for more.
|
|
619
619
|
else {
|
|
620
|
-
//
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
620
|
+
// print("No command available for ticking");
|
|
621
|
+
this.serverPredictedCommandCount = 0;
|
|
622
|
+
this.serverCommandBufferWait = true;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// tick command
|
|
626
|
+
// Ensure that we always tick the system even if there's no command to process.
|
|
627
|
+
if (command != null) {
|
|
628
|
+
command.tick = tick; // Correct tick to local timeline for ticking on the server.
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// We cannot tick more than once per FixedUpdate with our current implementation of character movement. A physics.simulate
|
|
632
|
+
// is required to apply the calculated velocities and move the character. We could work around this by pausing all other
|
|
633
|
+
// characters and then simulating forward once, but it would add a lot of complexity/processing power requirements.
|
|
634
|
+
// Instead we just drop the command and don't process it at all.
|
|
635
|
+
if (commandsProcessed == 1) {
|
|
636
|
+
this.stateSystem.Tick(command, tick, time, false);
|
|
628
637
|
}
|
|
629
638
|
|
|
630
639
|
if (commandsProcessed > 1) {
|
|
631
640
|
serverCommandCatchUpRequired--;
|
|
641
|
+
// print($"Processed additional command for catchup. {serverCommandCatchUpRequired} more required.");
|
|
632
642
|
}
|
|
633
643
|
|
|
634
|
-
} while (commandsProcessed < 1 + this.
|
|
635
|
-
// We add 1 to maxServerCommandCatchup because we always want to process at least 1 command per fixed update.
|
|
636
|
-
|
|
637
|
-
// if (commandsProcessed > 1)
|
|
638
|
-
// {
|
|
639
|
-
// print("Processed " + commandsProcessed + " commands for " + this.gameObject.name + $". There are now {this.serverCommandBuffer.Count} commands in the buffer.");
|
|
640
|
-
// }
|
|
644
|
+
} while (commandsProcessed < 1 + (this.serverCommandBufferTargetSize.Value / 2) && serverCommandCatchUpRequired > 0);
|
|
641
645
|
}
|
|
642
646
|
|
|
643
647
|
public void AuthServerCaptureSnapshot(int tick, double time, bool replay)
|
|
@@ -751,17 +755,18 @@ namespace Code.Network.StateSystem
|
|
|
751
755
|
}
|
|
752
756
|
|
|
753
757
|
this.serverCommandBufferMaxSize = (int)( 1 / Time.fixedUnscaledDeltaTime);
|
|
754
|
-
this.
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
// print($"{this.name} {serverReceivedStateBuffer.Count}/{serverCommandBufferMaxSize} target {serverCommandBufferTargetSize}");
|
|
758
|
+
this.serverCommandBufferAvgSize.Add(this.serverReceivedStateBuffer.Count);
|
|
759
|
+
this.connectionToClient.inputBufferTime = this.serverCommandBufferAvgSize.Value * Time.fixedUnscaledDeltaTime;
|
|
760
|
+
// print($"{this.name} {serverReceivedStateBuffer.Count}/{serverCommandBufferMaxSize} target {serverCommandBufferTargetSize.Value} avg {serverCommandBufferAvgSize.Value}");
|
|
758
761
|
|
|
759
762
|
// Delay processing until we have at least one send interval worth of commands to process.
|
|
760
|
-
if (this.serverReceivedStateBuffer.Count
|
|
763
|
+
if (this.serverCommandBufferWait && this.serverReceivedStateBuffer.Count < this.serverCommandBufferTargetSize.Value || this.serverReceivedStateBuffer.Count == 0) {
|
|
761
764
|
// Debug.Log($"Waiting for additional states for {this.name}. There are {this.serverReceivedStateBuffer.Count} states in the buffer.");
|
|
762
765
|
// no operation since there is no new state for us to use. Client authority means we use whatever the client sends us, even if that means
|
|
763
766
|
// seeing irregular physics movement.
|
|
764
767
|
return;
|
|
768
|
+
} else {
|
|
769
|
+
this.serverCommandBufferWait = false;
|
|
765
770
|
}
|
|
766
771
|
|
|
767
772
|
// Process the buffer of states that we've gotten from the authoritative client
|
|
@@ -781,14 +786,14 @@ namespace Code.Network.StateSystem
|
|
|
781
786
|
: null;
|
|
782
787
|
|
|
783
788
|
// If we have a new state to process, update our last processed command and then remove it.
|
|
789
|
+
// TODO: We could be a little more clever with gaps in received state. We could interpolate between last and next if one exists
|
|
784
790
|
if (latestState != null) {
|
|
785
791
|
// mark this as our latest state and remove it. We will do the real processing on the final
|
|
786
792
|
// state retrieved during this loop later.
|
|
787
793
|
this.serverLastProcessedCommandNumber = latestState.lastProcessedCommand;
|
|
788
794
|
this.serverReceivedStateBuffer.RemoveAt(0);
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
this.serverLastProcessedCommandNumber += 1;
|
|
795
|
+
} else {
|
|
796
|
+
this.serverCommandBufferWait = true;
|
|
792
797
|
}
|
|
793
798
|
|
|
794
799
|
if (statesProcessed > 1) {
|
|
@@ -797,7 +802,7 @@ namespace Code.Network.StateSystem
|
|
|
797
802
|
|
|
798
803
|
// If we don't have a new state to process, that's ok. It just means that the client hasn't sent us
|
|
799
804
|
// their updated state yet.
|
|
800
|
-
} while (statesProcessed < 1 + this.
|
|
805
|
+
} while (statesProcessed < 1 + (this.serverCommandBufferTargetSize.Value / 2) && serverCommandCatchUpRequired > 0);
|
|
801
806
|
|
|
802
807
|
// Commit the last processed snapshot to our state history
|
|
803
808
|
if (latestState != null)
|
|
@@ -1103,6 +1108,7 @@ namespace Code.Network.StateSystem
|
|
|
1103
1108
|
// interpolate over unscaledTime accurately. The remote timestamp is what the server was rendering
|
|
1104
1109
|
// at that time, which may be the same tick twice (especially with modified timescales)
|
|
1105
1110
|
if (!isOwned) {
|
|
1111
|
+
// print($"Frame {Time.frameCount} received state for {state.time}");
|
|
1106
1112
|
this.observerHistory.Set(state.time, state);
|
|
1107
1113
|
}
|
|
1108
1114
|
|
|
@@ -1220,8 +1226,10 @@ namespace Code.Network.StateSystem
|
|
|
1220
1226
|
this.serverCommandBuffer.Add(command.commandNumber, command);
|
|
1221
1227
|
}
|
|
1222
1228
|
|
|
1223
|
-
private void ServerReceiveInputCommand(Input[] commands)
|
|
1224
|
-
|
|
1229
|
+
private void ServerReceiveInputCommand(Input[] commands) {
|
|
1230
|
+
// var jitterTime = Math.Sqrt(this.connectionToClient.rttVariance) / Time.fixedUnscaledDeltaTime;
|
|
1231
|
+
serverCommandBufferTargetSize.Add(commands.Length);
|
|
1232
|
+
// print($"{commands.Length} + {jitterTime}");
|
|
1225
1233
|
foreach (var command in commands)
|
|
1226
1234
|
{
|
|
1227
1235
|
ProcessClientInputOnServer(command);
|
|
@@ -1229,8 +1237,9 @@ namespace Code.Network.StateSystem
|
|
|
1229
1237
|
|
|
1230
1238
|
}
|
|
1231
1239
|
|
|
1232
|
-
private void ServerReceiveSnapshot(State snapshot)
|
|
1233
|
-
|
|
1240
|
+
private void ServerReceiveSnapshot(State snapshot) {
|
|
1241
|
+
serverStatesReceivedThisFrame++;
|
|
1242
|
+
|
|
1234
1243
|
// Debug.Log("Server receive snapshot" + snapshot.lastProcessedCommand + " data: " + snapshot.ToString());
|
|
1235
1244
|
// This should only occur if the server is not authoritative.
|
|
1236
1245
|
if (serverAuth) return;
|
package/Runtime/Code/Player/Character/MovementSystems/Character/Structures/CharacterSnapshotData.cs
CHANGED
|
@@ -258,7 +258,7 @@ namespace Code.Player.Character.MovementSystems.Character
|
|
|
258
258
|
// we could not do this optimization since there would be no way to know where the next packet starts.
|
|
259
259
|
if (fullCustomData && other.customData != null) { // If we should send the full new data, send it, but only if we actually have data. null custom data shouldn't be sent.
|
|
260
260
|
writer.WriteBytes(other.customData.Data, 0, other.customData.Data.Length);
|
|
261
|
-
} else {
|
|
261
|
+
} else if (customDataDiff != null) {
|
|
262
262
|
writer.WriteBytes(customDataDiff, 0, customDataDiff.Length);
|
|
263
263
|
}
|
|
264
264
|
|
|
@@ -101,7 +101,7 @@ namespace Code.Quality {
|
|
|
101
101
|
private int GetRealTargetFPS() {
|
|
102
102
|
var targetFrameRate = Application.targetFrameRate;
|
|
103
103
|
if (targetFrameRate < 0 || targetFrameRate > Screen.currentResolution.refreshRateRatio.value)
|
|
104
|
-
targetFrameRate = (int)
|
|
104
|
+
targetFrameRate = (int) Screen.currentResolution.refreshRateRatio.value;
|
|
105
105
|
return targetFrameRate;
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -115,16 +115,16 @@ namespace Code.Quality {
|
|
|
115
115
|
var avgFrameTimings = GetRecentAverageFrameTimings();
|
|
116
116
|
|
|
117
117
|
// If our 5% is lower than 80% of target we should drop quality
|
|
118
|
-
if (currentFivePercent < 0.80 * targetFrameRate) {
|
|
118
|
+
if (currentFivePercent < 0.80 * Math.Min(targetFrameRate, 144)) {
|
|
119
119
|
frameHealth = FrameHealth.Unhealthy;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
// print($"[Quality Check] 5%: {currentFivePercent}, 0.5%: {pointFivePercent}, health: {frameHealth}");
|
|
123
123
|
|
|
124
124
|
if (this.tracer != null) {
|
|
125
|
-
this.tracer.SetMeasurement("
|
|
126
|
-
this.tracer.SetMeasurement("
|
|
127
|
-
this.tracer.SetMeasurement("
|
|
125
|
+
this.tracer.SetMeasurement("spf_5_percent", 1f / Math.Max(0.01, currentFivePercent));
|
|
126
|
+
this.tracer.SetMeasurement("spf_0.5_percent", 1f / Math.Max(0.01, pointFivePercent));
|
|
127
|
+
this.tracer.SetMeasurement("target_spf", 1f / Math.Max(0.01, targetFrameRate));
|
|
128
128
|
this.tracer.SetMeasurement("cpu_main_avg", avgFrameTimings.cpuMainAvg);
|
|
129
129
|
this.tracer.SetMeasurement("cpu_render_avg", avgFrameTimings.cpuRenderAvg);
|
|
130
130
|
this.tracer.SetMeasurement("gpu_avg", avgFrameTimings.gpuAvg);
|
|
@@ -6,35 +6,35 @@ using UnityEngine.EventSystems;
|
|
|
6
6
|
public class CanvasUIEventInterceptor : MonoBehaviour {
|
|
7
7
|
|
|
8
8
|
/** Generic pointer event. */
|
|
9
|
-
public event Action<
|
|
9
|
+
public event Action<int, int, int> PointerEvent;
|
|
10
10
|
|
|
11
11
|
/** Generic hover event. */
|
|
12
|
-
public event Action<
|
|
12
|
+
public event Action<int, int, PointerEventData> HoverEvent;
|
|
13
13
|
|
|
14
14
|
/** Params: InstanceId */
|
|
15
|
-
public event Action<
|
|
15
|
+
public event Action<int> SubmitEvent;
|
|
16
16
|
|
|
17
17
|
/** Params: InstanceId, string value */
|
|
18
|
-
public event Action<
|
|
18
|
+
public event Action<int, string> InputFieldSubmitEvent;
|
|
19
19
|
|
|
20
20
|
/** Params: InstanceId */
|
|
21
|
-
public event Action<
|
|
21
|
+
public event Action<int> SelectEvent;
|
|
22
22
|
|
|
23
23
|
/** Params: InstanceId */
|
|
24
|
-
public event Action<
|
|
24
|
+
public event Action<int> DeselectEvent;
|
|
25
25
|
|
|
26
|
-
public event Action<
|
|
26
|
+
public event Action<int> ClickEvent;
|
|
27
27
|
|
|
28
|
-
public event Action<
|
|
28
|
+
public event Action<int, float> ValueChangeEvent;
|
|
29
29
|
|
|
30
|
-
public event Action<
|
|
30
|
+
public event Action<int, bool> ToggleValueChangeEvent;
|
|
31
31
|
|
|
32
|
-
public event Action<
|
|
33
|
-
public event Action<
|
|
34
|
-
public event Action<
|
|
35
|
-
public event Action<
|
|
32
|
+
public event Action<int, PointerEventData> BeginDragEvent;
|
|
33
|
+
public event Action<int, PointerEventData> EndDragEvent;
|
|
34
|
+
public event Action<int, PointerEventData> DropEvent;
|
|
35
|
+
public event Action<int, PointerEventData> DragEvent;
|
|
36
36
|
|
|
37
|
-
public event Action<
|
|
37
|
+
public event Action<int, int> ScreenSizeChangeEvent;
|
|
38
38
|
|
|
39
39
|
/** Fires a pointer event for instance that corresponds to `instanceId`. Includes pointer button and direction. (up or down) */
|
|
40
40
|
public void FirePointerEvent(int instanceId, int direction, int button) {
|
|
@@ -201,6 +201,7 @@ public class VoxelBlocks : MonoBehaviour {
|
|
|
201
201
|
QuarterBlockTypes.DM, //UN
|
|
202
202
|
};
|
|
203
203
|
|
|
204
|
+
[LuauAPI]
|
|
204
205
|
public class LodSet {
|
|
205
206
|
public VoxelMeshCopy lod0;
|
|
206
207
|
public VoxelMeshCopy lod1;
|
|
@@ -208,6 +209,7 @@ public class VoxelBlocks : MonoBehaviour {
|
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
//The runtime version of VoxelBlockDefinition, after everything is loaded in
|
|
212
|
+
[LuauAPI]
|
|
211
213
|
public class BlockDefinition {
|
|
212
214
|
/// <summary>
|
|
213
215
|
/// The generated world id for this block
|
|
@@ -1193,6 +1193,9 @@ namespace VoxelWorldStuff {
|
|
|
1193
1193
|
handledBlockFaces[i] = new HashSet<int>();
|
|
1194
1194
|
}
|
|
1195
1195
|
|
|
1196
|
+
// List used in internals of face expansion functions to avoid repeated non-main-thread allocations
|
|
1197
|
+
var expandTempList = new List<int>();
|
|
1198
|
+
|
|
1196
1199
|
for (int x = 0; x < VoxelWorld.chunkSize; x++) {
|
|
1197
1200
|
for (int y = 0; y < VoxelWorld.chunkSize; y++) {
|
|
1198
1201
|
for (int z = 0; z < VoxelWorld.chunkSize; z++) {
|
|
@@ -1368,15 +1371,17 @@ namespace VoxelWorldStuff {
|
|
|
1368
1371
|
var zScale = 1;
|
|
1369
1372
|
// Only expand face in planes perpendicular to normal
|
|
1370
1373
|
var flooredNorm = Vector3Int.FloorToInt(faceNormal);
|
|
1374
|
+
Profiler.BeginSample("MeshFace");
|
|
1371
1375
|
if (Vector3.Dot(faceNormal, Vector3.right) == 0)
|
|
1372
1376
|
while (ExpandFaceX(localVoxel, flooredNorm, faceIndex, blockIndex, damage,
|
|
1373
1377
|
handledBlockFaces, xScale)) xScale++;
|
|
1374
1378
|
if (Vector3.Dot(faceNormal, Vector3.forward) == 0)
|
|
1375
1379
|
while (ExpandFaceZ(localVoxel, flooredNorm, faceIndex, blockIndex, damage,
|
|
1376
|
-
handledBlockFaces, new Vector3(xScale, yScale, zScale), zScale)) zScale++;
|
|
1380
|
+
handledBlockFaces, expandTempList, new Vector3(xScale, yScale, zScale), zScale)) zScale++;
|
|
1377
1381
|
if (Vector3.Dot(faceNormal, Vector3.up) == 0)
|
|
1378
1382
|
while (ExpandFaceY(localVoxel, flooredNorm, faceIndex, blockIndex, damage,
|
|
1379
|
-
handledBlockFaces, new Vector3(xScale, yScale, zScale), yScale)) yScale++;
|
|
1383
|
+
handledBlockFaces, expandTempList, new Vector3(xScale, yScale, zScale), yScale)) yScale++;
|
|
1384
|
+
Profiler.EndSample();
|
|
1380
1385
|
|
|
1381
1386
|
Rect uvRect = block.GetUvsForFace(faceIndex);
|
|
1382
1387
|
|
|
@@ -1545,10 +1550,10 @@ namespace VoxelWorldStuff {
|
|
|
1545
1550
|
return true;
|
|
1546
1551
|
}
|
|
1547
1552
|
|
|
1548
|
-
private bool ExpandFaceY(Vector3Int localVoxel, Vector3Int normal, int faceIndex, ushort blockIndex, float damage, HashSet<int>[] handledBlockFaces, Vector3 size, int y) {
|
|
1553
|
+
private bool ExpandFaceY(Vector3Int localVoxel, Vector3Int normal, int faceIndex, ushort blockIndex, float damage, HashSet<int>[] handledBlockFaces, List<int> expandChecks, Vector3 size, int y) {
|
|
1549
1554
|
if (y + localVoxel.y >= chunkSize) return false;
|
|
1550
1555
|
|
|
1551
|
-
|
|
1556
|
+
expandChecks.Clear();
|
|
1552
1557
|
for (var x = 0; x < size.x; x++) {
|
|
1553
1558
|
for (var z = 0; z < size.z; z++) {
|
|
1554
1559
|
var expandCheck = (ushort) ((localVoxel.x + x) + (localVoxel.y + y) * paddedChunkSize +
|
|
@@ -1579,10 +1584,10 @@ namespace VoxelWorldStuff {
|
|
|
1579
1584
|
return true;
|
|
1580
1585
|
}
|
|
1581
1586
|
|
|
1582
|
-
private bool ExpandFaceZ(Vector3Int localVoxel, Vector3Int normal, int faceIndex, ushort blockIndex, float damage, HashSet<int>[] handledBlockFaces, Vector3 size, int z) {
|
|
1587
|
+
private bool ExpandFaceZ(Vector3Int localVoxel, Vector3Int normal, int faceIndex, ushort blockIndex, float damage, HashSet<int>[] handledBlockFaces, List<int> expandChecks, Vector3 size, int z) {
|
|
1583
1588
|
if (z + localVoxel.z >= chunkSize) return false;
|
|
1584
1589
|
|
|
1585
|
-
|
|
1590
|
+
expandChecks.Clear();
|
|
1586
1591
|
for (var x = 0; x < size.x; x++) {
|
|
1587
1592
|
for (var y = 0; y < size.y; y++) {
|
|
1588
1593
|
var expandCheck = (ushort) ((localVoxel.x + x) + (localVoxel.y + y) * paddedChunkSize +
|
|
Binary file
|
|
Binary file
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<plist version="1.0">
|
|
4
4
|
<dict>
|
|
5
5
|
<key>BuildMachineOSBuild</key>
|
|
6
|
-
<string>
|
|
6
|
+
<string>24G325</string>
|
|
7
7
|
<key>CFBundleDevelopmentRegion</key>
|
|
8
8
|
<string>en</string>
|
|
9
9
|
<key>CFBundleExecutable</key>
|
|
@@ -27,19 +27,19 @@
|
|
|
27
27
|
<key>DTCompiler</key>
|
|
28
28
|
<string>com.apple.compilers.llvm.clang.1_0</string>
|
|
29
29
|
<key>DTPlatformBuild</key>
|
|
30
|
-
<string>
|
|
30
|
+
<string>25B74</string>
|
|
31
31
|
<key>DTPlatformName</key>
|
|
32
32
|
<string>macosx</string>
|
|
33
33
|
<key>DTPlatformVersion</key>
|
|
34
|
-
<string>26.
|
|
34
|
+
<string>26.1</string>
|
|
35
35
|
<key>DTSDKBuild</key>
|
|
36
|
-
<string>
|
|
36
|
+
<string>25B74</string>
|
|
37
37
|
<key>DTSDKName</key>
|
|
38
|
-
<string>macosx26.
|
|
38
|
+
<string>macosx26.1</string>
|
|
39
39
|
<key>DTXcode</key>
|
|
40
|
-
<string>
|
|
40
|
+
<string>2611</string>
|
|
41
41
|
<key>DTXcodeBuild</key>
|
|
42
|
-
<string>
|
|
42
|
+
<string>17B100</string>
|
|
43
43
|
<key>LSMinimumSystemVersion</key>
|
|
44
44
|
<string>13.0</string>
|
|
45
45
|
<key>NSHumanReadableCopyright</key>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1587,12 +1587,18 @@ namespace Mirror
|
|
|
1587
1587
|
// (we add this to the UnityEngine in NetworkLoop)
|
|
1588
1588
|
internal static void NetworkEarlyUpdate()
|
|
1589
1589
|
{
|
|
1590
|
+
// EASYMOD: Moved above transport early update. UpdateTimeInterpolation() updates NetworkTime.time, so
|
|
1591
|
+
// you would have NetworkTime.time one frame behind when processing snapshot updates compared to when FixedUpdate()/Update() runs.
|
|
1592
|
+
// This throws off the localTimeline clock drift adjustment as it's operating on the previous NetworkTime.time when adjusting
|
|
1593
|
+
// based on the received time snapshot. This causes you to jump ahead of the expected time when the timeline advances.
|
|
1594
|
+
// If you had a low framerate, you may end up eating up all of the buffer time on this jump and even go ahead of the predicted server time, causing stuttering
|
|
1595
|
+
// for observed objects that depend on NetworkTime.time being buffered to give extra time for packets to arrive.
|
|
1596
|
+
// time snapshot interpolation
|
|
1597
|
+
UpdateTimeInterpolation();
|
|
1598
|
+
|
|
1590
1599
|
// process all incoming messages first before updating the world
|
|
1591
1600
|
if (Transport.active != null)
|
|
1592
1601
|
Transport.active.ClientEarlyUpdate();
|
|
1593
|
-
|
|
1594
|
-
// time snapshot interpolation
|
|
1595
|
-
UpdateTimeInterpolation();
|
|
1596
1602
|
}
|
|
1597
1603
|
|
|
1598
1604
|
// NetworkLateUpdate called after any Update/FixedUpdate/LateUpdate
|
|
@@ -142,7 +142,7 @@ namespace Mirror
|
|
|
142
142
|
snapshotSettings.catchupPositiveThreshold,
|
|
143
143
|
ref deliveryTimeEma);
|
|
144
144
|
|
|
145
|
-
// Debug.Log($"inserted TimeSnapshot remote={snap.remoteTime:F2} local={snap.localTime:F2} total={snapshots.Count}");
|
|
145
|
+
// Debug.Log($"inserted TimeSnapshot frame={Time.frameCount} remote={snap.remoteTime:F2} local={snap.localTime:F2} pred={localTimeline} diff={localTimeline - snap.remoteTime} total={snapshots.Count}");
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
// call this from early update, so the timeline is safe to use in update
|
|
@@ -37,6 +37,9 @@ namespace Mirror
|
|
|
37
37
|
public double bufferTimeMultiplier = 2;
|
|
38
38
|
public double bufferTime => NetworkServer.sendInterval * bufferTimeMultiplier;
|
|
39
39
|
|
|
40
|
+
// EASYMOD: This is a hack for us to be able to adjust lag comp requests per connection to account for the character input buffer. This is not used by anything in mirror.
|
|
41
|
+
public double inputBufferTime = 0;
|
|
42
|
+
|
|
40
43
|
// <clienttime, snaps>
|
|
41
44
|
readonly SortedList<double, TimeSnapshot> snapshots = new SortedList<double, TimeSnapshot>();
|
|
42
45
|
|
|
@@ -50,6 +53,9 @@ namespace Mirror
|
|
|
50
53
|
|
|
51
54
|
/// <summary>Round trip time (in seconds) that it takes a message to go server->client->server.</summary>
|
|
52
55
|
public double rtt => _rtt.Value;
|
|
56
|
+
/// <Summary>Round trip time variance aka jitter, in seconds.</Summary>
|
|
57
|
+
// "rttVariance" instead of "rttVar" for consistency with older versions.
|
|
58
|
+
public double rttVariance => _rtt.Variance;
|
|
53
59
|
|
|
54
60
|
public NetworkConnectionToClient(int networkConnectionId, string clientAddress = "localhost")
|
|
55
61
|
: base(networkConnectionId)
|
|
@@ -2016,13 +2016,15 @@ namespace Mirror
|
|
|
2016
2016
|
fullUpdateDuration.Begin();
|
|
2017
2017
|
}
|
|
2018
2018
|
|
|
2019
|
-
//
|
|
2020
|
-
|
|
2021
|
-
Transport.active.ServerEarlyUpdate();
|
|
2022
|
-
|
|
2019
|
+
// EASYMOD: Moved ahead of transport early update for the same reason as
|
|
2020
|
+
// described in NetworkClient.NetworkEarlyUpdate()
|
|
2023
2021
|
// step each connection's local time interpolation in early update.
|
|
2024
2022
|
foreach (NetworkConnectionToClient connection in connections.Values)
|
|
2025
2023
|
connection.UpdateTimeInterpolation();
|
|
2024
|
+
|
|
2025
|
+
// process all incoming messages first before updating the world
|
|
2026
|
+
if (Transport.active != null)
|
|
2027
|
+
Transport.active.ServerEarlyUpdate();
|
|
2026
2028
|
|
|
2027
2029
|
if (active) earlyUpdateDuration.End();
|
|
2028
2030
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Simple Moving Average (SMA) implementation
|
|
2
|
+
// Keeps a sliding window of the last n entries and drops older data
|
|
3
|
+
// https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
|
|
4
|
+
using System;
|
|
5
|
+
using System.Collections.Generic;
|
|
6
|
+
|
|
7
|
+
namespace Mirror
|
|
8
|
+
{
|
|
9
|
+
public class SimpleMovingAverage
|
|
10
|
+
{
|
|
11
|
+
readonly int maxSamples;
|
|
12
|
+
readonly Queue<double> samples;
|
|
13
|
+
double sum;
|
|
14
|
+
|
|
15
|
+
public double Value => samples.Count > 0 ? sum / samples.Count : 0;
|
|
16
|
+
public int Count => samples.Count;
|
|
17
|
+
|
|
18
|
+
public SimpleMovingAverage(int n)
|
|
19
|
+
{
|
|
20
|
+
maxSamples = n;
|
|
21
|
+
samples = new Queue<double>(n + 1);
|
|
22
|
+
sum = 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public void Add(double newValue)
|
|
26
|
+
{
|
|
27
|
+
samples.Enqueue(newValue);
|
|
28
|
+
sum += newValue;
|
|
29
|
+
|
|
30
|
+
if (samples.Count > maxSamples)
|
|
31
|
+
{
|
|
32
|
+
sum -= samples.Dequeue();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public void Reset()
|
|
37
|
+
{
|
|
38
|
+
samples.Clear();
|
|
39
|
+
sum = 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -19,7 +19,7 @@ MonoBehaviour:
|
|
|
19
19
|
m_RendererDataList:
|
|
20
20
|
- {fileID: 11400000, guid: fd1b4d11df5dd4a378ed9d33fdae2dbf, type: 2}
|
|
21
21
|
m_DefaultRendererIndex: 0
|
|
22
|
-
m_RequireDepthTexture:
|
|
22
|
+
m_RequireDepthTexture: 1
|
|
23
23
|
m_RequireOpaqueTexture: 0
|
|
24
24
|
m_OpaqueDownsampling: 1
|
|
25
25
|
m_SupportsTerrainHoles: 1
|