diff --git a/BoneSync/MelonLoaderMod.cs b/BoneSync/MelonLoaderMod.cs index 686cb68..c7dfb6d 100644 --- a/BoneSync/MelonLoaderMod.cs +++ b/BoneSync/MelonLoaderMod.cs @@ -7,6 +7,7 @@ using UnityEngine; using BoneSync.Player; using BoneSync.Sync; using Facepunch.Steamworks; +using System.Reflection.Emit; namespace BoneSync { @@ -52,6 +53,7 @@ namespace BoneSync public override void OnSceneWasInitialized(int buildIndex, string sceneName) { //MelonLogger.Msg("OnLevelWasInitialized: " + sceneName); + SceneSync.OnSceneInit(buildIndex); } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) @@ -76,11 +78,6 @@ namespace BoneSync } } - public override void OnLevelWasInitialized(int level) - { - SceneSync.OnSceneInit(level); - } - public override void BONEWORKS_OnLoadingScreen() { base.BONEWORKS_OnLoadingScreen(); diff --git a/BoneSync/Networking/Messages/DiscardSyncableMessage.cs b/BoneSync/Networking/Messages/DiscardSyncableMessage.cs index f845296..1cde343 100644 --- a/BoneSync/Networking/Messages/DiscardSyncableMessage.cs +++ b/BoneSync/Networking/Messages/DiscardSyncableMessage.cs @@ -5,12 +5,14 @@ using System.Text; using System.Threading.Tasks; using BoneSync.Sync; using BoneSync.Sync.Components; +using MelonLoader; namespace BoneSync.Networking.Messages { public struct DiscardSyncableMessageData { public ushort syncId; + public bool despawn; } [PacketType(PacketType.DiscardSyncable), PacketReliability(PacketReliability.ReliableFast)] internal class DiscardSyncableMessage : NetworkMessage @@ -21,20 +23,26 @@ namespace BoneSync.Networking.Messages { _data = discardSyncableMessageData; byteEncoder.WriteUShort(_data.syncId); + byteEncoder.WriteBool(_data.despawn); } public DiscardSyncableMessage(Packet packet) { byteEncoder.WriteBytes(packet.Data); _data.syncId = byteEncoder.ReadUShort(); + _data.despawn = byteEncoder.ReadBool(); } public override void Execute() { Syncable syncable = ObjectSyncCache.GetSyncable(_data.syncId); + if (syncable != null) { - syncable.DiscardSyncable(true); + syncable.DiscardSyncable(true, _data.despawn); + } else + { + MelonLogger.Warning(senderId + " tried to discard a syncable that doesn't exist (syncId: " + _data.syncId + ")"); } } diff --git a/BoneSync/Networking/Messages/ObjectAttributeMessage.cs b/BoneSync/Networking/Messages/ObjectAttributeMessage.cs index 214eae5..5dbb0df 100644 --- a/BoneSync/Networking/Messages/ObjectAttributeMessage.cs +++ b/BoneSync/Networking/Messages/ObjectAttributeMessage.cs @@ -16,6 +16,8 @@ namespace BoneSync.Networking.Messages public bool[] _attributes; public ObjectMagazineData? magazineData; } + + [PacketType(PacketType.ObjectAttribute), PacketReliability(PacketReliability.ReliableFast)] internal class ObjectAttributeMessage : NetworkMessage { private ObjectAttributeMessageData _data; @@ -26,8 +28,8 @@ namespace BoneSync.Networking.Messages _data = objectAttributeMessageData; byteEncoder.WriteUShort(_data.syncId); - attributes.Add(_data.magazineData != null); - if (_data.magazineData != null) + attributes.Add(_data.magazineData.HasValue); + if (_data.magazineData.HasValue) { byteEncoder.WriteByte(_data.magazineData.Value.ammoCount); } diff --git a/BoneSync/Networking/PacketTypes.cs b/BoneSync/Networking/PacketTypes.cs index 18c1843..3706946 100644 --- a/BoneSync/Networking/PacketTypes.cs +++ b/BoneSync/Networking/PacketTypes.cs @@ -16,5 +16,6 @@ namespace BoneSync.Networking ObjectEvent = 5, ObjectOwnership = 6, DiscardSyncable = 7, + ObjectAttribute = 8, } } diff --git a/BoneSync/Patching/CallPatchedMethod.cs b/BoneSync/Patching/CallPatchedMethod.cs index 1b58945..cfe2ac9 100644 --- a/BoneSync/Patching/CallPatchedMethod.cs +++ b/BoneSync/Patching/CallPatchedMethod.cs @@ -31,6 +31,13 @@ namespace BoneSync.Patching allowPatchedMethodCall = false; } + public static void Despawn(Poolee poolee) + { + allowPatchedMethodCall = true; + poolee.Despawn(); + allowPatchedMethodCall = false; + } + public static Poolee InstantiatePoolee(Pool pool, Vector3 position, Quaternion rotation, Vector3 scale) { allowPatchedMethodCall = true; diff --git a/BoneSync/Patching/GripPatches.cs b/BoneSync/Patching/GripPatches.cs index 707c59c..14c05ae 100644 --- a/BoneSync/Patching/GripPatches.cs +++ b/BoneSync/Patching/GripPatches.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using BoneSync.Sync; +using BoneSync.Sync.Components; using HarmonyLib; using StressLevelZero.Interaction; namespace BoneSync.Patching @@ -15,7 +16,13 @@ namespace BoneSync.Patching public static void OnEnablePatch(ForcePullGrip __instance) { MelonLoader.MelonLogger.Msg("ForcePullGrip.Pull: " + __instance.name); - ObjectSync.MakeOrGetSyncable(__instance.gameObject); + InteractableHost interactableHost = __instance.GetComponent(); + if (interactableHost == null) return; + Syncable syncable = ObjectSync.MakeOrGetSyncable(interactableHost); + if (syncable) + { + syncable.RegisterSyncable(); + } } } diff --git a/BoneSync/Patching/PoolPatches.cs b/BoneSync/Patching/PoolPatches.cs index 3ef0c0e..86829f5 100644 --- a/BoneSync/Patching/PoolPatches.cs +++ b/BoneSync/Patching/PoolPatches.cs @@ -79,6 +79,7 @@ namespace BoneSync.Patching if (PoolBlacklist.isBlacklistedPool(__instance)) return; MelonLogger.Msg("Patched Instantiating object in pool: " + __instance.name); __result.onSpawnDelegate = Il2CppSystem.Delegate.Combine(__result.onSpawnDelegate, (Il2CppSystem.Action)((g) => { PooleePatches.OnSpawnPatchPost(__result); })).Cast>(); + __result.onDespawnDelegate = Il2CppSystem.Delegate.Combine(__result.onDespawnDelegate, (Il2CppSystem.Action)((g) => { PooleePatches.OnDespawnPatchPost(__result); })).Cast>(); } @@ -140,6 +141,22 @@ namespace BoneSync.Patching } + public static void OnDespawnPatchPost(Poolee __instance) + { + if (CallPatchedMethods.allowPatchedMethodCall) return; + if (!BoneSync.lobby.IsConnected()) return; + + MelonLogger.Msg("Poolee.OnDespawn: " + __instance.gameObject.transform.GetPath()); + + //Syncable syncable = ObjectSync.MakeOrGetSyncable(__instance); + + //if (syncable == null) return; + + //if (syncable.isOwner) return; + + //syncable.DiscardSyncable(true); + } + public static IEnumerator OnSpawnClient(Poolee poolee) { GameObject go = poolee.gameObject; diff --git a/BoneSync/Sync/Components/SyncableBase.cs b/BoneSync/Sync/Components/SyncableBase.cs index 68e60b2..1e15e39 100644 --- a/BoneSync/Sync/Components/SyncableBase.cs +++ b/BoneSync/Sync/Components/SyncableBase.cs @@ -31,6 +31,28 @@ namespace BoneSync.Sync.Components return "/" + current.name; return current.parent.GetPath() + "/" + current.name; } + + public static Transform TransformFromPath(string path) + { + if (path.StartsWith("/")) + { + path = path.Substring(1); + } + string[] pathParts = path.Split('/'); + Transform current = null; + foreach (string part in pathParts) + { + if (current == null) + { + current = GameObject.Find(part).transform; + } + else + { + current = current.Find(part); + } + } + return current; + } } [RegisterTypeInIl2Cpp] @@ -92,15 +114,20 @@ namespace BoneSync.Sync.Components private SpawnFragment spawnFragment; - private IEnumerator _CheckAutoSyncCo() + private void CheckAutoSync() { - yield return null; bool shouldAutoSync = CheckIfShouldAutoSync(); if (shouldAutoSync && (BoneSync.lobby.IsHost || ClientSpawningAllowed())) { MelonLogger.Msg("AutoSyncing: " + transform.GetPath()); RegisterSyncable(); } + } + private IEnumerator _OnEnableCo() + { + yield return null; // wait a frame to make sure all components are initialized, I hate this but it works + FindComponents(); + CheckAutoSync(); yield break; } @@ -110,11 +137,15 @@ namespace BoneSync.Sync.Components FindComponents(); - MelonCoroutines.Start(_CheckAutoSyncCo()); + MelonCoroutines.Start(_OnEnableCo()); } public bool CheckIfShouldAutoSync() { + if (transform.GetPath().StartsWith("/Pool Manager/")) + { + return false; + } if (poolee && poolee.pool) { return true; } @@ -129,7 +160,7 @@ namespace BoneSync.Sync.Components } if (interactableHost || interactableManager) { - return transform.GetPath().Replace(" ", "[]"); + return transform.GetPath(); } return ""; } @@ -153,6 +184,13 @@ namespace BoneSync.Sync.Components ObjectSyncCache.AddSyncable(this); } + private void ResetSyncStatus() + { + _ownerId = 0; + _syncId = 0; + _attemptedRegister = false; + SetKinematic(false); + } public bool CanBeSynced() { if (spawnFragment) return false; // if has spawn fragment, don't sync @@ -161,25 +199,39 @@ namespace BoneSync.Sync.Components return false; } - - private void _DiscardSyncable(bool force) + + private void _DiscardSyncable(bool force, bool despawn) { - if (Registered) + bool isRegistered = Registered; + MelonLogger.Msg("Discarding syncable: " + transform.GetPath() + " force: " + force + " isRegistered: " + isRegistered + " despawn: " + despawn); + if (isRegistered) { - MelonLogger.Warning("Discarding registered syncable: " + transform.GetPath() + " force: " + force); + //MelonLogger.Warning("Discarding registered syncable: " + transform.GetPath() + " force: " + force); bool isowner = isOwner; if (isowner) { - _SendDiscard(); // owner sends discard message + _SendDiscard(true); // owner sends discard message } if (!isowner && !force) return; // only owner can discard } - syncablesCache.Remove(gameObject); + + if (gameObject) syncablesCache.Remove(gameObject); + ObjectSyncCache.RemoveSyncable(this); - Destroy(this); // delete the component + + ResetSyncStatus(); + + DestroyImmediate(this); // delete the component + + if (despawn) + { + gameObject?.SetActive(false); + } + + } public void OnDestroy() @@ -188,19 +240,25 @@ namespace BoneSync.Sync.Components { MelonLogger.Warning("Destroying registered syncable: " + transform.GetPath()); } - _DiscardSyncable(true); + DiscardSyncableImmediate(true, Registered); //MelonLogger.Msg("Syncable destroyed: " + transform.GetPath()); } - private IEnumerator _FlagForDiscardCo(bool force) + private IEnumerator _FlagForDiscardCo(bool force, bool despawn) { yield return null; _DiscardSyncable(force, despawn); } // delay discard to prevent silly behavior + + public void DiscardSyncable(bool force = false, bool despawn = false) { - yield return null; - _DiscardSyncable(force); + if (Registered && isOwner) + { + DiscardSyncableImmediate(force, despawn); + return; + } + MelonCoroutines.Start(_FlagForDiscardCo(force, despawn)); } - public void DiscardSyncable(bool force = false) + public void DiscardSyncableImmediate(bool force = false, bool despawn = false) { - MelonCoroutines.Start(_FlagForDiscardCo(force)); + _DiscardSyncable(true, despawn); } public void OnDisable() @@ -209,10 +267,9 @@ namespace BoneSync.Sync.Components { MelonLogger.Warning("tried to disable non-owner syncable: " + transform.GetPath()); gameObject.SetActive(true); - } else - { - DiscardSyncable(); + return; } + DiscardSyncable(); } public void RegisterSyncable() diff --git a/BoneSync/Sync/Components/SyncableNetworking.cs b/BoneSync/Sync/Components/SyncableNetworking.cs index 4cf50f0..9d1f704 100644 --- a/BoneSync/Sync/Components/SyncableNetworking.cs +++ b/BoneSync/Sync/Components/SyncableNetworking.cs @@ -56,11 +56,13 @@ namespace BoneSync.Sync.Components SetSyncId(ObjectSync.SendRegisterSyncableMessage(this)); } - private void _SendDiscard() + private void _SendDiscard(bool despawn = false) { + MelonLogger.Msg("Sending discard for " + _syncId + " despawn: " + despawn); DiscardSyncableMessageData discardSyncableMessageData = new DiscardSyncableMessageData() { - syncId = _syncId + syncId = _syncId, + despawn = despawn }; DiscardSyncableMessage discardSyncableMessage = new DiscardSyncableMessage(discardSyncableMessageData); discardSyncableMessage.Broadcast(); @@ -85,7 +87,7 @@ namespace BoneSync.Sync.Components public void TryBecomeOwner() { - if (!isOwner) + if (Registered && !isOwner) { MelonLogger.Msg("Attempting to become owner of " + _syncId); OwnershipTransferMessageData data = new OwnershipTransferMessageData() diff --git a/BoneSync/Sync/ObjectSync.cs b/BoneSync/Sync/ObjectSync.cs index 839afc5..b3a0a19 100644 --- a/BoneSync/Sync/ObjectSync.cs +++ b/BoneSync/Sync/ObjectSync.cs @@ -105,12 +105,6 @@ namespace BoneSync.Sync //Scene scene = gameObject.scene; //MelonLogger.Msg("Making or getting syncable for: " + gameObject.name + " in scene: " + scene.name); - Syncable parentSyncable = gameObject.transform.parent.GetComponentInParent(); - if (parentSyncable != null) - { - return parentSyncable; - } - Syncable syncable = gameObject.GetComponent(); // delete all sub syncables @@ -210,6 +204,12 @@ namespace BoneSync.Sync } } + if (info.id == 0) + { + MelonLogger.Warning("Received register sync message with id 0"); + return; + } + bool hasCallback = info.callbackId != 0; if (hasCallback) { @@ -250,6 +250,9 @@ namespace BoneSync.Sync { ObjectSyncMessageData data = objectSyncMessage.objectSyncMessageData; ushort objectId = data.objectId; + + if (objectId == 0) return; + if (objectId >= _nextSyncableId && !BoneSync.lobby.IsHost) { _nextSyncableId = (ushort)(objectId + 1); @@ -257,7 +260,7 @@ namespace BoneSync.Sync Syncable syncable = ObjectSyncCache.GetSyncable(objectId); if (syncable == null) { - MelonLogger.Msg("SyncEvent: Syncable not found for id: " + objectId); + //MelonLogger.Msg("SyncEvent: Syncable not found for id: " + objectId); return; } syncable.ApplyObjectSyncTransforms(data.objectSyncTransforms); diff --git a/BoneSync/Sync/ObjectSyncCache.cs b/BoneSync/Sync/ObjectSyncCache.cs index 26e51cf..07aa461 100644 --- a/BoneSync/Sync/ObjectSyncCache.cs +++ b/BoneSync/Sync/ObjectSyncCache.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using UnityEngine; namespace BoneSync.Sync { @@ -86,6 +87,11 @@ namespace BoneSync.Sync { return _pathToSyncable[path]; } + Transform transform = TransformExtensions.TransformFromPath(path); + if (transform) + { + return transform.GetComponent(); + } return null; } diff --git a/BoneSync/Sync/SceneSync.cs b/BoneSync/Sync/SceneSync.cs index 4590b8c..b302c8e 100644 --- a/BoneSync/Sync/SceneSync.cs +++ b/BoneSync/Sync/SceneSync.cs @@ -1,9 +1,12 @@ -using MelonLoader; +using BoneSync.Sync.Components; +using MelonLoader; +using StressLevelZero.Interaction; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using UnityEngine; using UnityEngine.SceneManagement; namespace BoneSync.Sync @@ -27,12 +30,45 @@ namespace BoneSync.Sync return sceneName; } + } + + public static void RenameDuplicateSceneTransforms(Scene scene) + { + Dictionary seenTransformNames = new Dictionary(); + uint total = 0; + foreach (GameObject go in scene.GetRootGameObjects()) + { + foreach (InteractableHost host in go.GetComponentsInChildren(true)) + { + Transform t = host.transform; + string path = t.GetPath(); + if (seenTransformNames.ContainsKey(path)) + { + seenTransformNames[path]++; + t.name = t.name + " (BoneSync." + seenTransformNames[path] + ")"; + total++; + //MelonLogger.Msg("Renamed duplicate transform: " + path); + } + else + { + seenTransformNames[path] = 1; + } + } + } + + MelonLogger.Msg("Renamed " + total + " duplicate transforms in " + scene.name); + + + + } public static void OnSceneInit(int buildIndex) { - string SceneName = SceneManager.GetSceneByBuildIndex(buildIndex).name; + Scene scene = SceneManager.GetSceneByBuildIndex(buildIndex); + string SceneName = scene.name; _currentSceneName = SceneName; MelonLogger.Msg("Scene initialized: " + SceneName); + RenameDuplicateSceneTransforms(scene); } public static void Initialize()