From cb6b811002f6354c6ef0f443291670755a562fb7 Mon Sep 17 00:00:00 2001
From: _nebula <41904486+misternebula@users.noreply.github.com>
Date: Wed, 14 Feb 2024 21:40:11 +0000
Subject: [PATCH] move determinstic stuff to separate folder, remove WO dumps
---
.../AlarmTotemSync/AlarmTotemManager.cs | 2 +-
QSB/EchoesOfTheEye/Ghosts/GhostManager.cs | 2 +-
.../LightSensorSync/LightSensorManager.cs | 2 +-
QSB/ItemSync/ItemManager.cs | 1 +
QSB/PoolSync/PoolManager.cs | 2 +-
QSB/QSBCore.cs | 1 +
QSB/Syncs/Occasional/OccasionalManager.cs | 1 +
.../ProbeLauncherTool/ProbeLauncherManager.cs | 2 +-
QSB/Utility/DebugSettings.cs | 3 -
.../Deterministic/DeterministicManager.cs | 80 +++++
.../Deterministic/OWRigidbodyPatches.cs | 217 +++++++++++++
QSB/Utility/DeterministicManager.cs | 305 ------------------
QSB/WorldSync/MiscManager.cs | 2 +-
QSB/WorldSync/QSBWorldSync.cs | 1 +
14 files changed, 307 insertions(+), 314 deletions(-)
create mode 100644 QSB/Utility/Deterministic/DeterministicManager.cs
create mode 100644 QSB/Utility/Deterministic/OWRigidbodyPatches.cs
delete mode 100644 QSB/Utility/DeterministicManager.cs
diff --git a/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs b/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs
index 992f79ee1..c59956598 100644
--- a/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs
+++ b/QSB/EchoesOfTheEye/AlarmTotemSync/AlarmTotemManager.cs
@@ -1,6 +1,6 @@
using Cysharp.Threading.Tasks;
using QSB.EchoesOfTheEye.AlarmTotemSync.WorldObjects;
-using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System.Linq;
using System.Threading;
diff --git a/QSB/EchoesOfTheEye/Ghosts/GhostManager.cs b/QSB/EchoesOfTheEye/Ghosts/GhostManager.cs
index 64f5dbcf6..3cfdec966 100644
--- a/QSB/EchoesOfTheEye/Ghosts/GhostManager.cs
+++ b/QSB/EchoesOfTheEye/Ghosts/GhostManager.cs
@@ -1,6 +1,6 @@
using Cysharp.Threading.Tasks;
using QSB.EchoesOfTheEye.Ghosts.WorldObjects;
-using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
diff --git a/QSB/EchoesOfTheEye/LightSensorSync/LightSensorManager.cs b/QSB/EchoesOfTheEye/LightSensorSync/LightSensorManager.cs
index 4fa61dc63..f65565ca6 100644
--- a/QSB/EchoesOfTheEye/LightSensorSync/LightSensorManager.cs
+++ b/QSB/EchoesOfTheEye/LightSensorSync/LightSensorManager.cs
@@ -1,6 +1,6 @@
using Cysharp.Threading.Tasks;
using QSB.EchoesOfTheEye.LightSensorSync.WorldObjects;
-using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System.Linq;
using System.Threading;
diff --git a/QSB/ItemSync/ItemManager.cs b/QSB/ItemSync/ItemManager.cs
index afa1a8eea..f429acaa4 100644
--- a/QSB/ItemSync/ItemManager.cs
+++ b/QSB/ItemSync/ItemManager.cs
@@ -4,6 +4,7 @@
using QSB.ItemSync.WorldObjects.Items;
using QSB.ItemSync.WorldObjects.Sockets;
using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System.Linq;
using System.Threading;
diff --git a/QSB/PoolSync/PoolManager.cs b/QSB/PoolSync/PoolManager.cs
index 39a48f586..7b5c1c7ee 100644
--- a/QSB/PoolSync/PoolManager.cs
+++ b/QSB/PoolSync/PoolManager.cs
@@ -1,5 +1,5 @@
using Cysharp.Threading.Tasks;
-using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System.Threading;
diff --git a/QSB/QSBCore.cs b/QSB/QSBCore.cs
index 486e004b5..e033123f8 100644
--- a/QSB/QSBCore.cs
+++ b/QSB/QSBCore.cs
@@ -25,6 +25,7 @@
using UnityEngine;
using UnityEngine.InputSystem;
using Random = System.Random;
+using QSB.Utility.Deterministic;
/*
Copyright (C) 2020 - 2023
diff --git a/QSB/Syncs/Occasional/OccasionalManager.cs b/QSB/Syncs/Occasional/OccasionalManager.cs
index 1b0ffb9d7..b60fdda3f 100644
--- a/QSB/Syncs/Occasional/OccasionalManager.cs
+++ b/QSB/Syncs/Occasional/OccasionalManager.cs
@@ -1,6 +1,7 @@
using Cysharp.Threading.Tasks;
using Mirror;
using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System.Collections.Generic;
using System.Linq;
diff --git a/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs b/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs
index 3902e0a3c..8f98a005e 100644
--- a/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs
+++ b/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs
@@ -1,6 +1,6 @@
using Cysharp.Threading.Tasks;
using QSB.Tools.ProbeLauncherTool.WorldObjects;
-using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.WorldSync;
using System.Linq;
using System.Threading;
diff --git a/QSB/Utility/DebugSettings.cs b/QSB/Utility/DebugSettings.cs
index 0d0fdb802..ee4b4569c 100644
--- a/QSB/Utility/DebugSettings.cs
+++ b/QSB/Utility/DebugSettings.cs
@@ -5,9 +5,6 @@ namespace QSB.Utility;
[JsonObject(MemberSerialization.OptIn)]
public class DebugSettings
{
- [JsonProperty("dumpWorldObjects")]
- public bool DumpWorldObjects;
-
[JsonProperty("logQSBMessages")]
public bool LogQSBMessages;
diff --git a/QSB/Utility/Deterministic/DeterministicManager.cs b/QSB/Utility/Deterministic/DeterministicManager.cs
new file mode 100644
index 000000000..d4bd4b015
--- /dev/null
+++ b/QSB/Utility/Deterministic/DeterministicManager.cs
@@ -0,0 +1,80 @@
+using HarmonyLib;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+
+namespace QSB.Utility.Deterministic;
+
+///
+/// TODO make this only do cache clearing on pre scene load when HOSTING instead of just all the time
+///
+public static class DeterministicManager
+{
+ private static readonly Harmony _harmony = new(typeof(DeterministicManager).FullName);
+ private static bool _patched;
+
+ public static readonly Dictionary Cache = new();
+
+ public static void Init() =>
+ QSBSceneManager.OnPreSceneLoad += (_, _) =>
+ {
+ DebugLog.DebugWrite("cleared cache");
+ Cache.Clear();
+
+ if (!_patched)
+ {
+ _harmony.PatchAll(typeof(OWRigidbodyPatches));
+ _patched = true;
+ }
+ };
+
+ public static void OnWorldObjectsAdded()
+ {
+ //DebugLog.DebugWrite($"cleared cache of {_cache.Count} entries");
+ //_cache.Clear();
+
+ if (_patched)
+ {
+ _harmony.UnpatchSelf();
+ _patched = false;
+ }
+ }
+
+ ///
+ /// only call this before world objects added
+ ///
+ public static string DeterministicPath(this Component component)
+ {
+ var sb = new StringBuilder();
+ var transform = component.transform;
+ while (true)
+ {
+ if (!Cache.TryGetValue(transform, out var data))
+ {
+ data = (transform.GetSiblingIndex(), transform.parent);
+ Cache.Add(transform, data);
+ }
+
+ if (!data.Parent)
+ {
+ break;
+ }
+
+ sb.Append(transform.name);
+ sb.Append(' ');
+ sb.Append(data.SiblingIndex);
+ sb.Append(' ');
+ transform = data.Parent;
+ }
+
+ sb.Append(transform.name);
+ return sb.ToString();
+ }
+
+ ///
+ /// only call this before world objects added
+ ///
+ public static IEnumerable SortDeterministic(this IEnumerable components) where T : Component
+ => components.OrderBy(DeterministicPath);
+}
\ No newline at end of file
diff --git a/QSB/Utility/Deterministic/OWRigidbodyPatches.cs b/QSB/Utility/Deterministic/OWRigidbodyPatches.cs
new file mode 100644
index 000000000..3005321e3
--- /dev/null
+++ b/QSB/Utility/Deterministic/OWRigidbodyPatches.cs
@@ -0,0 +1,217 @@
+using HarmonyLib;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace QSB.Utility.Deterministic;
+
+[HarmonyPatch(typeof(OWRigidbody))]
+public static class OWRigidbodyPatches
+{
+ private static readonly Dictionary _setParentQueue = new();
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.Awake))]
+ private static bool Awake(OWRigidbody __instance)
+ {
+ __instance._transform = __instance.transform;
+
+ // ADDED
+ DeterministicManager.Cache.Add(__instance._transform, (__instance._transform.GetSiblingIndex(), __instance._transform.parent));
+
+ if (!__instance._scaleRoot)
+ {
+ __instance._scaleRoot = __instance._transform;
+ }
+
+ CenterOfTheUniverse.TrackRigidbody(__instance);
+ __instance._offsetApplier = __instance.gameObject.GetAddComponent();
+ __instance._offsetApplier.Init(__instance);
+ if (__instance._simulateInSector)
+ {
+ __instance._simulateInSector.OnSectorOccupantsUpdated += __instance.OnSectorOccupantsUpdated;
+ }
+
+ __instance._origParent = __instance._transform.parent;
+ __instance._origParentBody = __instance._origParent ? __instance._origParent.GetAttachedOWRigidbody() : null;
+
+ // ADDED
+ if (__instance._transform.parent)
+ {
+ _setParentQueue[__instance] = null;
+ }
+
+ // REMOVED
+ /*if (__instance._transform.parent != null)
+ {
+ __instance._transform.parent = null;
+ }*/
+
+ __instance._rigidbody = __instance.GetRequiredComponent();
+ __instance._rigidbody.interpolation = RigidbodyInterpolation.None;
+
+ if (!__instance._autoGenerateCenterOfMass)
+ {
+ __instance._rigidbody.centerOfMass = __instance._centerOfMass;
+ }
+
+ if (__instance.IsSimulatedKinematic())
+ {
+ __instance.EnableKinematicSimulation();
+ }
+
+ __instance._origCenterOfMass = __instance.RunningKinematicSimulation() ? __instance._kinematicRigidbody.centerOfMass : __instance._rigidbody.centerOfMass;
+ __instance._referenceFrame = new ReferenceFrame(__instance);
+
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.Start))]
+ private static void Start(OWRigidbody __instance)
+ {
+ if (_setParentQueue.TryGetValue(__instance, out var parent))
+ {
+ __instance._transform.parent = parent;
+ _setParentQueue.Remove(__instance);
+ }
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.OnDestroy))]
+ private static void OnDestroy(OWRigidbody __instance)
+ {
+ DeterministicManager.Cache.Remove(__instance._transform);
+ _setParentQueue.Remove(__instance);
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.Suspend), typeof(Transform), typeof(OWRigidbody))]
+ private static bool Suspend(OWRigidbody __instance, Transform suspensionParent, OWRigidbody suspensionBody)
+ {
+ if (!__instance._suspended || __instance._unsuspendNextUpdate)
+ {
+ __instance._suspensionBody = suspensionBody;
+ var direction = __instance.GetVelocity() - suspensionBody.GetPointVelocity(__instance._transform.position);
+ __instance._cachedRelativeVelocity = suspensionBody.transform.InverseTransformDirection(direction);
+ __instance._cachedAngularVelocity = __instance.RunningKinematicSimulation() ? __instance._kinematicRigidbody.angularVelocity : __instance._rigidbody.angularVelocity;
+ __instance.enabled = false;
+ __instance._offsetApplier.enabled = false;
+
+ if (__instance.RunningKinematicSimulation())
+ {
+ __instance._kinematicRigidbody.enabled = false;
+ }
+ else
+ {
+ __instance.MakeKinematic();
+ }
+
+ // ADDED
+ if (_setParentQueue.ContainsKey(__instance))
+ {
+ _setParentQueue[__instance] = suspensionParent;
+ }
+ else
+ {
+ __instance._transform.parent = suspensionParent;
+ }
+
+ // REMOVED
+ // __instance._transform.parent = suspensionParent;
+
+ __instance._suspended = true;
+ __instance._unsuspendNextUpdate = false;
+ if (!Physics.autoSyncTransforms)
+ {
+ Physics.SyncTransforms();
+ }
+
+ if (__instance._childColliders == null)
+ {
+ __instance._childColliders = __instance.GetComponentsInChildren();
+
+ // CLEANED
+ foreach (var childCollider in __instance._childColliders)
+ {
+ childCollider.gameObject.GetAddComponent().ListenForParentBodySuspension();
+ }
+ }
+
+ __instance.RaiseEvent(nameof(__instance.OnSuspendOWRigidbody), __instance);
+ }
+
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.ChangeSuspensionBody))]
+ private static bool ChangeSuspensionBody(OWRigidbody __instance, OWRigidbody newSuspensionBody)
+ {
+ if (__instance._suspended)
+ {
+ __instance._cachedRelativeVelocity = Vector3.zero;
+ __instance._suspensionBody = newSuspensionBody;
+
+ // ADDED
+ if (_setParentQueue.ContainsKey(__instance))
+ {
+ _setParentQueue[__instance] = newSuspensionBody.transform;
+ }
+ else
+ {
+ __instance._transform.parent = newSuspensionBody.transform;
+ }
+
+ // REMOVED
+ // __instance._transform.parent = newSuspensionBody.transform;
+ }
+
+ return false;
+ }
+
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(OWRigidbody.UnsuspendImmediate))]
+ private static bool UnsuspendImmediate(OWRigidbody __instance, bool restoreCachedVelocity)
+ {
+ if (__instance._suspended)
+ {
+ if (__instance.RunningKinematicSimulation())
+ {
+ __instance._kinematicRigidbody.enabled = true;
+ }
+ else
+ {
+ __instance.MakeNonKinematic();
+ }
+
+ __instance.enabled = true;
+
+ // ADDED
+ if (_setParentQueue.ContainsKey(__instance))
+ {
+ _setParentQueue[__instance] = null;
+ }
+ else
+ {
+ __instance._transform.parent = null;
+ }
+
+ // REMOVED
+ // __instance._transform.parent = null;
+
+ if (!Physics.autoSyncTransforms)
+ {
+ Physics.SyncTransforms();
+ }
+
+ var cachedVelocity = restoreCachedVelocity ? __instance._suspensionBody.transform.TransformDirection(__instance._cachedRelativeVelocity) : Vector3.zero;
+ __instance.SetVelocity(__instance._suspensionBody.GetPointVelocity(__instance._transform.position) + cachedVelocity);
+ __instance.SetAngularVelocity(restoreCachedVelocity ? __instance._cachedAngularVelocity : Vector3.zero);
+ __instance._suspended = false;
+ __instance._suspensionBody = null;
+ __instance.RaiseEvent(nameof(__instance.OnUnsuspendOWRigidbody), __instance);
+ }
+
+ return false;
+ }
+}
diff --git a/QSB/Utility/DeterministicManager.cs b/QSB/Utility/DeterministicManager.cs
deleted file mode 100644
index d6a71aef7..000000000
--- a/QSB/Utility/DeterministicManager.cs
+++ /dev/null
@@ -1,305 +0,0 @@
-using HarmonyLib;
-using QSB.WorldSync;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using UnityEngine;
-
-namespace QSB.Utility;
-
-///
-/// TODO make this only do cache clearing on pre scene load when HOSTING instead of just all the time
-///
-public static class DeterministicManager
-{
- private static readonly Harmony _harmony = new(typeof(DeterministicManager).FullName);
- private static bool _patched;
-
- private static readonly Dictionary _cache = new();
-
- public static void Init() =>
- QSBSceneManager.OnPreSceneLoad += (_, _) =>
- {
- DebugLog.DebugWrite("cleared cache");
- _cache.Clear();
-
- if (!_patched)
- {
- _harmony.PatchAll(typeof(OWRigidbodyPatches));
- _patched = true;
- }
- };
-
- public static void OnWorldObjectsAdded()
- {
- if (QSBCore.DebugSettings.DumpWorldObjects)
- {
- using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, $"[{DebugLog.ProcessInstanceId}] world objects.csv")))
- {
- file.WriteLine("world object,deterministic path");
- foreach (var worldObject in QSBWorldSync.GetWorldObjects())
- {
- file.Write('"');
- file.Write(worldObject);
- file.Write('"');
- file.Write(',');
- file.Write('"');
- file.Write(worldObject.AttachedObject.DeterministicPath().Replace("\"", "\"\""));
- file.Write('"');
- file.WriteLine();
- }
- }
-
- using (var file = File.CreateText(Path.Combine(QSBCore.Helper.Manifest.ModFolderPath, $"[{DebugLog.ProcessInstanceId}] cache.csv")))
- {
- file.WriteLine("name,instance id,sibling index,parent,parent instance id");
- foreach (var (transform, (siblingIndex, parent)) in _cache)
- {
- file.Write('"');
- file.Write(transform.name.Replace("\"", "\"\""));
- file.Write('"');
- file.Write(',');
- file.Write(transform.GetInstanceID());
- file.Write(',');
- file.Write(siblingIndex);
- file.Write(',');
- file.Write('"');
- file.Write(parent ? parent.name.Replace("\"", "\"\"") : default);
- file.Write('"');
- file.Write(',');
- file.Write(parent ? parent.GetInstanceID() : default);
- file.WriteLine();
- }
- }
- }
-
- DebugLog.DebugWrite($"cleared cache of {_cache.Count} entries");
- _cache.Clear();
-
- if (_patched)
- {
- _harmony.UnpatchSelf();
- _patched = false;
- }
- }
-
- [HarmonyPatch(typeof(OWRigidbody))]
- private static class OWRigidbodyPatches
- {
- private static readonly Dictionary _setParentQueue = new();
-
- [HarmonyPrefix]
- [HarmonyPatch(nameof(OWRigidbody.Awake))]
- private static bool Awake(OWRigidbody __instance)
- {
- __instance._transform = __instance.transform;
- _cache.Add(__instance._transform, (__instance._transform.GetSiblingIndex(), __instance._transform.parent));
- if (!__instance._scaleRoot)
- {
- __instance._scaleRoot = __instance._transform;
- }
-
- CenterOfTheUniverse.TrackRigidbody(__instance);
- __instance._offsetApplier = __instance.gameObject.GetAddComponent();
- __instance._offsetApplier.Init(__instance);
- if (__instance._simulateInSector)
- {
- __instance._simulateInSector.OnSectorOccupantsUpdated += __instance.OnSectorOccupantsUpdated;
- }
-
- __instance._origParent = __instance._transform.parent;
- __instance._origParentBody = __instance._origParent ? __instance._origParent.GetAttachedOWRigidbody() : null;
- if (__instance._transform.parent)
- {
- _setParentQueue[__instance] = null;
- }
-
- __instance._rigidbody = __instance.GetRequiredComponent();
- __instance._rigidbody.interpolation = RigidbodyInterpolation.None;
- if (!__instance._autoGenerateCenterOfMass)
- {
- __instance._rigidbody.centerOfMass = __instance._centerOfMass;
- }
-
- if (__instance.IsSimulatedKinematic())
- {
- __instance.EnableKinematicSimulation();
- }
-
- __instance._origCenterOfMass = __instance.RunningKinematicSimulation() ? __instance._kinematicRigidbody.centerOfMass : __instance._rigidbody.centerOfMass;
- __instance._referenceFrame = new ReferenceFrame(__instance);
- return false;
- }
-
- [HarmonyPrefix]
- [HarmonyPatch(nameof(OWRigidbody.Start))]
- private static void Start(OWRigidbody __instance)
- {
- if (_setParentQueue.TryGetValue(__instance, out var parent))
- {
- __instance._transform.parent = parent;
- _setParentQueue.Remove(__instance);
- }
- }
-
- [HarmonyPrefix]
- [HarmonyPatch(nameof(OWRigidbody.OnDestroy))]
- private static void OnDestroy(OWRigidbody __instance)
- {
- _cache.Remove(__instance._transform);
- _setParentQueue.Remove(__instance);
- }
-
- [HarmonyPrefix]
- [HarmonyPatch(nameof(OWRigidbody.Suspend), typeof(Transform), typeof(OWRigidbody))]
- private static bool Suspend(OWRigidbody __instance, Transform suspensionParent, OWRigidbody suspensionBody)
- {
- if (!__instance._suspended || __instance._unsuspendNextUpdate)
- {
- __instance._suspensionBody = suspensionBody;
- var direction = __instance.GetVelocity() - suspensionBody.GetPointVelocity(__instance._transform.position);
- __instance._cachedRelativeVelocity = suspensionBody.transform.InverseTransformDirection(direction);
- __instance._cachedAngularVelocity = __instance.RunningKinematicSimulation() ? __instance._kinematicRigidbody.angularVelocity : __instance._rigidbody.angularVelocity;
- __instance.enabled = false;
- __instance._offsetApplier.enabled = false;
- if (__instance.RunningKinematicSimulation())
- {
- __instance._kinematicRigidbody.enabled = false;
- }
- else
- {
- __instance.MakeKinematic();
- }
-
- if (_setParentQueue.ContainsKey(__instance))
- {
- _setParentQueue[__instance] = suspensionParent;
- }
- else
- {
- __instance._transform.parent = suspensionParent;
- }
-
- __instance._suspended = true;
- __instance._unsuspendNextUpdate = false;
- if (!Physics.autoSyncTransforms)
- {
- Physics.SyncTransforms();
- }
-
- if (__instance._childColliders == null)
- {
- __instance._childColliders = __instance.GetComponentsInChildren();
- foreach (var childCollider in __instance._childColliders)
- {
- childCollider.gameObject.GetAddComponent().ListenForParentBodySuspension();
- }
- }
-
- __instance.RaiseEvent(nameof(__instance.OnSuspendOWRigidbody), __instance);
- }
-
- return false;
- }
-
- [HarmonyPrefix]
- [HarmonyPatch(nameof(OWRigidbody.ChangeSuspensionBody))]
- private static bool ChangeSuspensionBody(OWRigidbody __instance, OWRigidbody newSuspensionBody)
- {
- if (__instance._suspended)
- {
- __instance._cachedRelativeVelocity = Vector3.zero;
- __instance._suspensionBody = newSuspensionBody;
- if (_setParentQueue.ContainsKey(__instance))
- {
- _setParentQueue[__instance] = newSuspensionBody.transform;
- }
- else
- {
- __instance._transform.parent = newSuspensionBody.transform;
- }
- }
-
- return false;
- }
-
- [HarmonyPrefix]
- [HarmonyPatch(nameof(OWRigidbody.UnsuspendImmediate))]
- private static bool UnsuspendImmediate(OWRigidbody __instance, bool restoreCachedVelocity)
- {
- if (__instance._suspended)
- {
- if (__instance.RunningKinematicSimulation())
- {
- __instance._kinematicRigidbody.enabled = true;
- }
- else
- {
- __instance.MakeNonKinematic();
- }
-
- __instance.enabled = true;
- if (_setParentQueue.ContainsKey(__instance))
- {
- _setParentQueue[__instance] = null;
- }
- else
- {
- __instance._transform.parent = null;
- }
-
- if (!Physics.autoSyncTransforms)
- {
- Physics.SyncTransforms();
- }
-
- var cachedVelocity = restoreCachedVelocity ? __instance._suspensionBody.transform.TransformDirection(__instance._cachedRelativeVelocity) : Vector3.zero;
- __instance.SetVelocity(__instance._suspensionBody.GetPointVelocity(__instance._transform.position) + cachedVelocity);
- __instance.SetAngularVelocity(restoreCachedVelocity ? __instance._cachedAngularVelocity : Vector3.zero);
- __instance._suspended = false;
- __instance._suspensionBody = null;
- __instance.RaiseEvent(nameof(__instance.OnUnsuspendOWRigidbody), __instance);
- }
-
- return false;
- }
- }
-
- ///
- /// only call this before world objects added
- ///
- public static string DeterministicPath(this Component component)
- {
- var sb = new StringBuilder();
- var transform = component.transform;
- while (true)
- {
- if (!_cache.TryGetValue(transform, out var data))
- {
- data = (transform.GetSiblingIndex(), transform.parent);
- _cache.Add(transform, data);
- }
-
- if (!data.Parent)
- {
- break;
- }
-
- sb.Append(transform.name);
- sb.Append(' ');
- sb.Append(data.SiblingIndex);
- sb.Append(' ');
- transform = data.Parent;
- }
-
- sb.Append(transform.name);
- return sb.ToString();
- }
-
- ///
- /// only call this before world objects added
- ///
- public static IEnumerable SortDeterministic(this IEnumerable components) where T : Component
- => components.OrderBy(DeterministicPath);
-}
\ No newline at end of file
diff --git a/QSB/WorldSync/MiscManager.cs b/QSB/WorldSync/MiscManager.cs
index 5d69b7b9e..800101ff9 100644
--- a/QSB/WorldSync/MiscManager.cs
+++ b/QSB/WorldSync/MiscManager.cs
@@ -1,5 +1,5 @@
using Cysharp.Threading.Tasks;
-using QSB.Utility;
+using QSB.Utility.Deterministic;
using System.Linq;
using System.Threading;
diff --git a/QSB/WorldSync/QSBWorldSync.cs b/QSB/WorldSync/QSBWorldSync.cs
index ef87cfb9d..38702de5b 100644
--- a/QSB/WorldSync/QSBWorldSync.cs
+++ b/QSB/WorldSync/QSBWorldSync.cs
@@ -8,6 +8,7 @@
using QSB.Player.TransformSync;
using QSB.TriggerSync.WorldObjects;
using QSB.Utility;
+using QSB.Utility.Deterministic;
using QSB.Utility.LinkedWorldObject;
using QSB.WorldSync.Messages;
using System;