From 875d269781e78de485e67cb816bad22a22b5f54e Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 26 Aug 2022 18:21:13 -0400 Subject: [PATCH] Sync probe launcher rotation, allow one person to use at a time --- .../RotatingElementsVariableSyncer.cs | 2 +- QSB/QSBNetworkManager.cs | 5 + .../StationaryProbeLauncherMessage.cs | 11 ++ .../Patches/StationaryProbeLauncherPatches.cs | 27 +++++ .../StationaryProbeLauncherManager.cs | 15 +++ .../StationaryProbeLauncherTransformSync.cs | 35 ++++++ .../QSBStationaryProbeLauncher.cs | 108 ++++++++++++++++++ .../ProbeLauncherTool/ProbeLauncherManager.cs | 2 +- 8 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 QSB/StationaryProbeLauncherSync/Messages/StationaryProbeLauncherMessage.cs create mode 100644 QSB/StationaryProbeLauncherSync/Patches/StationaryProbeLauncherPatches.cs create mode 100644 QSB/StationaryProbeLauncherSync/StationaryProbeLauncherManager.cs create mode 100644 QSB/StationaryProbeLauncherSync/TransformSync/StationaryProbeLauncherTransformSync.cs create mode 100644 QSB/StationaryProbeLauncherSync/WorldObjects/QSBStationaryProbeLauncher.cs diff --git a/QSB/EchoesOfTheEye/RotatingElementsVariableSyncer.cs b/QSB/EchoesOfTheEye/RotatingElementsVariableSyncer.cs index c510a730..271bf807 100644 --- a/QSB/EchoesOfTheEye/RotatingElementsVariableSyncer.cs +++ b/QSB/EchoesOfTheEye/RotatingElementsVariableSyncer.cs @@ -8,7 +8,7 @@ using UnityEngine; namespace QSB.EchoesOfTheEye; -internal abstract class RotatingElementsVariableSyncer : BaseVariableSyncer, ILinkedNetworkBehaviour +public abstract class RotatingElementsVariableSyncer : BaseVariableSyncer, ILinkedNetworkBehaviour where TWorldObject : IWorldObject { public override void OnStartClient() diff --git a/QSB/QSBNetworkManager.cs b/QSB/QSBNetworkManager.cs index 78fb5359..e32a6cf9 100644 --- a/QSB/QSBNetworkManager.cs +++ b/QSB/QSBNetworkManager.cs @@ -24,6 +24,7 @@ using QSB.Player.TransformSync; using QSB.SaveSync; using QSB.ShipSync; using QSB.ShipSync.TransformSync; +using QSB.StationaryProbeLauncherSync.TransformSync; using QSB.Syncs.Occasional; using QSB.TimeSync; using QSB.Tools.ProbeTool.TransformSync; @@ -56,6 +57,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart public GameObject ShipModulePrefab { get; private set; } public GameObject ShipLegPrefab { get; private set; } public GameObject ModelShipPrefab { get; private set; } + public GameObject StationaryProbeLauncherPrefab { get; private set; } private string PlayerName { get; set; } private GameObject _probePrefab; @@ -149,6 +151,9 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart ModelShipPrefab = MakeNewNetworkObject(14, "NetworkModelShip", typeof(ModelShipTransformSync)); spawnPrefabs.Add(ModelShipPrefab); + StationaryProbeLauncherPrefab = MakeNewNetworkObject(15, "NetworkStationaryProbeLauncher", typeof(StationaryProbeLauncherTransformSync)); + spawnPrefabs.Add(StationaryProbeLauncherPrefab); + ConfigureNetworkManager(); } diff --git a/QSB/StationaryProbeLauncherSync/Messages/StationaryProbeLauncherMessage.cs b/QSB/StationaryProbeLauncherSync/Messages/StationaryProbeLauncherMessage.cs new file mode 100644 index 00000000..641e1f4d --- /dev/null +++ b/QSB/StationaryProbeLauncherSync/Messages/StationaryProbeLauncherMessage.cs @@ -0,0 +1,11 @@ +using QSB.Messaging; +using QSB.StationaryProbeLauncherSync.WorldObjects; + +namespace QSB.StationaryProbeLauncherSync.Messages; + +public class StationaryProbeLauncherMessage : QSBWorldObjectMessage +{ + public StationaryProbeLauncherMessage(bool inUse) : base(inUse) { } + + public override void OnReceiveRemote() => WorldObject.OnUseStateChanged(Data, From); +} diff --git a/QSB/StationaryProbeLauncherSync/Patches/StationaryProbeLauncherPatches.cs b/QSB/StationaryProbeLauncherSync/Patches/StationaryProbeLauncherPatches.cs new file mode 100644 index 00000000..51dec4c5 --- /dev/null +++ b/QSB/StationaryProbeLauncherSync/Patches/StationaryProbeLauncherPatches.cs @@ -0,0 +1,27 @@ +using HarmonyLib; +using QSB.Messaging; +using QSB.Patches; +using QSB.StationaryProbeLauncherSync.Messages; +using QSB.StationaryProbeLauncherSync.WorldObjects; +using QSB.WorldSync; + +namespace QSB.StationaryProbeLauncherSync.Patches; + +[HarmonyPatch] +public class StationaryProbeLauncherPatches : QSBPatch +{ + public override QSBPatchTypes Type => QSBPatchTypes.OnClientConnect; + + [HarmonyPostfix] + [HarmonyPatch(typeof(StationaryProbeLauncher), nameof(StationaryProbeLauncher.FinishExitSequence))] + public static void StationaryProbeLauncher_FinishExitSequence(Elevator __instance) + { + if (Remote) + { + return; + } + + var qsbStationaryProbe = __instance.GetWorldObject(); + qsbStationaryProbe.SendMessage(new StationaryProbeLauncherMessage(false)); + } +} diff --git a/QSB/StationaryProbeLauncherSync/StationaryProbeLauncherManager.cs b/QSB/StationaryProbeLauncherSync/StationaryProbeLauncherManager.cs new file mode 100644 index 00000000..515709ad --- /dev/null +++ b/QSB/StationaryProbeLauncherSync/StationaryProbeLauncherManager.cs @@ -0,0 +1,15 @@ +using Cysharp.Threading.Tasks; +using QSB.StationaryProbeLauncherSync.WorldObjects; +using QSB.Utility; +using QSB.WorldSync; +using System.Threading; + +namespace QSB.StationaryProbeLauncherSync; + +public class StationaryProbeLauncherManager : WorldObjectManager +{ + public override WorldObjectScene WorldObjectScene => WorldObjectScene.SolarSystem; + + public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) => + QSBWorldSync.Init(QSBWorldSync.GetUnityObjects().SortDeterministic()); +} diff --git a/QSB/StationaryProbeLauncherSync/TransformSync/StationaryProbeLauncherTransformSync.cs b/QSB/StationaryProbeLauncherSync/TransformSync/StationaryProbeLauncherTransformSync.cs new file mode 100644 index 00000000..f9095e2c --- /dev/null +++ b/QSB/StationaryProbeLauncherSync/TransformSync/StationaryProbeLauncherTransformSync.cs @@ -0,0 +1,35 @@ +using Mirror; +using QSB.EchoesOfTheEye; +using QSB.StationaryProbeLauncherSync.WorldObjects; +using UnityEngine; + +namespace QSB.StationaryProbeLauncherSync.TransformSync; + +public class StationaryProbeLauncherTransformSync : RotatingElementsVariableSyncer +{ + protected override Transform[] RotatingElements => new Transform[] { WorldObject.AttachedObject.transform }; + + protected override void Serialize(NetworkWriter writer) + { + base.Serialize(writer); + + var launcher = (WorldObject.AttachedObject as StationaryProbeLauncher); + + writer.Write(launcher.transform.localRotation); + writer.Write(launcher._degreesX); + writer.Write(launcher._degreesY); + writer.Write(launcher._audioSource.GetLocalVolume()); + } + + protected override void Deserialize(NetworkReader reader) + { + base.Deserialize(reader); + + var launcher = (WorldObject.AttachedObject as StationaryProbeLauncher); + + launcher.transform.localRotation = reader.Read(); + launcher._degreesX = reader.Read(); + launcher._degreesY = reader.Read(); + launcher._audioSource.SetLocalVolume(reader.Read()); + } +} diff --git a/QSB/StationaryProbeLauncherSync/WorldObjects/QSBStationaryProbeLauncher.cs b/QSB/StationaryProbeLauncherSync/WorldObjects/QSBStationaryProbeLauncher.cs new file mode 100644 index 00000000..8823a5d7 --- /dev/null +++ b/QSB/StationaryProbeLauncherSync/WorldObjects/QSBStationaryProbeLauncher.cs @@ -0,0 +1,108 @@ +using Cysharp.Threading.Tasks; +using Mirror; +using QSB.AuthoritySync; +using QSB.Messaging; +using QSB.Player; +using QSB.StationaryProbeLauncherSync.Messages; +using QSB.StationaryProbeLauncherSync.TransformSync; +using QSB.Tools.ProbeLauncherTool.WorldObjects; +using QSB.Utility.LinkedWorldObject; +using System.Threading; + +namespace QSB.StationaryProbeLauncherSync.WorldObjects; + +public class QSBStationaryProbeLauncher : QSBProbeLauncher, ILinkedWorldObject +{ + public StationaryProbeLauncherTransformSync NetworkBehaviour { get; private set; } + public void SetNetworkBehaviour(NetworkBehaviour networkBehaviour) => NetworkBehaviour = (StationaryProbeLauncherTransformSync)networkBehaviour; + + private bool _isInit; + private bool _isInUse; + private Shape _shape; + private StationaryProbeLauncher _stationaryProbeLauncher; + + public override async UniTask Init(CancellationToken ct) + { + _isInit = true; + + // This is implemented by inheriting LinkedWorldObject normally, however I want to inherit from QSBProbeLauncher + // Else we'd have to redo the sync for the effects + if (QSBCore.IsHost) + { + this.SpawnLinked(QSBNetworkManager.singleton.StationaryProbeLauncherPrefab, false); + } + else + { + await this.WaitForLink(ct); + } + + await base.Init(ct); + + _stationaryProbeLauncher = AttachedObject as StationaryProbeLauncher; + _shape = ((InteractZone)_stationaryProbeLauncher._interactVolume)._trigger._shape; + + _stationaryProbeLauncher._interactVolume.OnPressInteract += OnPressInteract; + + UpdateUse(); + } + + public override void OnRemoval() + { + _isInit = false; + + if (QSBCore.IsHost) + { + NetworkServer.Destroy(NetworkBehaviour.gameObject); + } + + base.OnRemoval(); + + _stationaryProbeLauncher._interactVolume.OnPressInteract -= OnPressInteract; + } + + private void OnPressInteract() + { + // Whoever is using it needs authority to be able to rotate it + // If this is a client they'll get authority from the host when the message is received otherwise give now + if (QSBCore.IsHost) NetworkBehaviour.netIdentity.SetAuthority(QSBPlayerManager.LocalPlayerId); + + _isInUse = true; + this.SendMessage(new StationaryProbeLauncherMessage(_isInUse)); + } + + public override void SendInitialState(uint to) + { + base.SendInitialState(to); + + this.SendMessage(new StationaryProbeLauncherMessage(_isInUse) { To = to }); + } + + private void UpdateUse() + { + // Stuff can be null when its sending the initial state info + if (!_isInit) return; + + // If somebody is using this we disable the interaction shape + _shape.enabled = !_isInUse; + + if (_isInUse) + { + _stationaryProbeLauncher._audioSource.SetLocalVolume(0f); + _stationaryProbeLauncher._audioSource.Start(); + } + else + { + _stationaryProbeLauncher._audioSource.Stop(); + } + } + + public void OnUseStateChanged(bool isInUse, uint from) + { + // Whoever is using it needs authority to be able to rotate it + if (QSBCore.IsHost) NetworkBehaviour.netIdentity.SetAuthority(from); + + _isInUse = isInUse; + + UpdateUse(); + } +} diff --git a/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs b/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs index cff58709..60ca8d8e 100644 --- a/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs +++ b/QSB/Tools/ProbeLauncherTool/ProbeLauncherManager.cs @@ -12,7 +12,7 @@ internal class ProbeLauncherManager : WorldObjectManager public override async UniTask BuildWorldObjects(OWScene scene, CancellationToken ct) { - QSBWorldSync.Init(typeof(PlayerProbeLauncher)); + QSBWorldSync.Init(typeof(PlayerProbeLauncher), typeof(StationaryProbeLauncher)); if (scene == OWScene.SolarSystem) { QSBWorldSync.Init(new[]