From eff3e5cce58ec45b2a047b5a2edc6279cafad76a Mon Sep 17 00:00:00 2001 From: Aristeas <94058548+ari-steas@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:02:30 -0600 Subject: [PATCH] Actually networked fusion radiator animation --- .../Aristeas copy to DS and client.bat | 6 +- .../Communication/ModularDefinitionSender.cs | 14 +- .../ExtendableRadiators/ExtendableRadiator.cs | 82 ++++++--- .../ExtendableRadiators/RadiatorAnimation.cs | 9 - .../Networking/BlockPacket.cs | 68 +++++++ .../Networking/HeartNetwork.cs | 168 ++++++++++++++++++ .../Networking/PacketBase.cs | 15 ++ .../Generic.csproj.DotSettings | 4 + 8 files changed, 326 insertions(+), 40 deletions(-) create mode 100644 Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/BlockPacket.cs create mode 100644 Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/HeartNetwork.cs create mode 100644 Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/PacketBase.cs create mode 100644 Utility Mods/MoA Fusion Systems/Generic.csproj.DotSettings diff --git a/Utility Mods/MoA Fusion Systems/Aristeas copy to DS and client.bat b/Utility Mods/MoA Fusion Systems/Aristeas copy to DS and client.bat index f0433c823..0dae10c91 100644 --- a/Utility Mods/MoA Fusion Systems/Aristeas copy to DS and client.bat +++ b/Utility Mods/MoA Fusion Systems/Aristeas copy to DS and client.bat @@ -10,7 +10,7 @@ rem 2. Edit this variable if applicable (do not add quotes or end with backslash set STEAM_PATH=C:\Program Files (x86)\Steam rem 3. Edit this with your mod's workshop id. -set WORKSHOP_ID=3130656484 +set WORKSHOP_ID=3365316959 rem Now you can run it every time you want to update the mod on DS and client. @@ -26,6 +26,4 @@ rmdir "%CLIENT_PATH%" /S /Q rmdir "%DS_PATH%" /S /Q robocopy.exe .\ "%DS_PATH%" *.* /S /xd .git bin obj .vs ignored /xf *.lnk *.git* *.bat *.zip *.7z *.blend* *.md *.log *.sln *.csproj *.csproj.user *.ruleset modinfo.sbmi -robocopy.exe "%DS_PATH%" "%CLIENT_PATH%" *.* /S - -pause \ No newline at end of file +robocopy.exe "%DS_PATH%" "%CLIENT_PATH%" *.* /S \ No newline at end of file diff --git a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Communication/ModularDefinitionSender.cs b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Communication/ModularDefinitionSender.cs index 2cf873fc8..ae9f5b989 100644 --- a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Communication/ModularDefinitionSender.cs +++ b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Communication/ModularDefinitionSender.cs @@ -1,14 +1,16 @@ -using VRage.Game.Components; +using Epstein_Fusion_DS.Networking; +using VRage.Game.Components; using VRage.Utils; using static Epstein_Fusion_DS.Communication.DefinitionDefs; namespace Epstein_Fusion_DS.Communication { - [MySessionComponentDescriptor(MyUpdateOrder.Simulation, int.MinValue)] + [MySessionComponentDescriptor(MyUpdateOrder.AfterSimulation, int.MinValue)] internal class ModularDefinitionSender : MySessionComponentBase { internal ModularDefinitionContainer StoredDef; + public override void LoadData() { MyLog.Default.WriteLineAndConsole( @@ -19,10 +21,18 @@ public override void LoadData() // Send definitions over as soon as the API loads, and create the API before anything else can init. Epstein_Fusion_DS.ModularDefinition.ModularApi.Init(ModContext, SendDefinitions); + + new HeartNetwork().LoadData(); + } + + public override void UpdateAfterSimulation() + { + HeartNetwork.I.Update(); } protected override void UnloadData() { + HeartNetwork.I.UnloadData(); Epstein_Fusion_DS.ModularDefinition.ModularApi.UnloadData(); } diff --git a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/ExtendableRadiator.cs b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/ExtendableRadiator.cs index 302d7062d..37fd4bd47 100644 --- a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/ExtendableRadiator.cs +++ b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/ExtendableRadiator.cs @@ -1,23 +1,24 @@ using System; using System.Collections.Generic; -using System.Runtime.InteropServices; -using Epstein_Fusion_DS.Communication; using Epstein_Fusion_DS.HudHelpers; +using Epstein_Fusion_DS.Networking; using ProtoBuf; -using Sandbox.Game.Entities; using Sandbox.Game.EntityComponents; using Sandbox.ModAPI; using VRage.Game; using VRage.Game.Components; using VRage.Game.ModAPI; +using VRage.Game.ModAPI.Network; using VRage.ModAPI; +using VRage.Network; using VRage.ObjectBuilders; +using VRage.Sync; using VRageMath; namespace Epstein_Fusion_DS.HeatParts.ExtendableRadiators { [MyEntityComponentDescriptor(typeof(MyObjectBuilder_TerminalBlock), false, "ExtendableRadiatorBase")] - internal class ExtendableRadiator : MyGameLogicComponent + internal class ExtendableRadiator : MyGameLogicComponent, IMyEventProxy { public static readonly Guid RadiatorGuid = new Guid("e6b87818-5fd8-47a6-a480-3365e20214e1"); public static readonly string[] ValidPanelSubtypes = @@ -30,7 +31,7 @@ internal class ExtendableRadiator : MyGameLogicComponent internal StoredRadiator[] StoredRadiators = Array.Empty(); internal RadiatorAnimation Animation; - private bool _isExtended = true; + private MySync _isExtended; public bool IsExtended { get @@ -42,11 +43,7 @@ public bool IsExtended if (Animation.IsActive) return; - if (value) - ExtendPanels(); - else - RetractPanels(); - _isExtended = value; + _isExtended.Value = value; } } @@ -67,6 +64,9 @@ public override void UpdateOnceBeforeFrame() if (Block?.CubeGrid?.Physics == null) return; + if (MyAPIGateway.Session.IsServer) + _isExtended.Value = true; + LoadSettings(); try @@ -81,6 +81,13 @@ public override void UpdateOnceBeforeFrame() Animation = new RadiatorAnimation(this); NeedsUpdate |= MyEntityUpdateEnum.EACH_FRAME; //NeedsUpdate |= MyEntityUpdateEnum.EACH_100TH_FRAME; + + _isExtended.ValueChanged += SyncValueChanged; + } + + public override void MarkForClose() + { + _isExtended.ValueChanged -= SyncValueChanged; } public override void UpdateAfterSimulation() @@ -90,8 +97,6 @@ public override void UpdateAfterSimulation() // This is stupid, but prevents the mod profiler cost from being incurred every tick per block when inactive if (Animation.IsActive) Animation.UpdateTick(); - else - MakePanelsVisible(false); } public override bool IsSerialized() @@ -108,6 +113,17 @@ public override bool IsSerialized() return base.IsSerialized(); } + private void SyncValueChanged(MySync sync) + { + if (Animation.IsActive) + return; + + if (sync.Value) + ExtendPanels(); + else + RetractPanels(); + } + internal void SaveSettings() { if (Block == null) @@ -139,7 +155,11 @@ internal void LoadSettings() var loadedSettings = MyAPIGateway.Utilities.SerializeFromBinary(Convert.FromBase64String(rawData)) ?? Array.Empty(); StoredRadiators = loadedSettings; - _isExtended = StoredRadiators.Length == 0; + if (MyAPIGateway.Session.IsServer) + { + _isExtended.Value = StoredRadiators.Length == 0; + HeartNetwork.SendToEveryoneInSync(new BlockPacket(StoredRadiators, Block.CubeGrid, Block, null), Block.GetPosition()); + } for (int i = 0; i < StoredRadiators.Length; i++) { @@ -166,7 +186,7 @@ internal void LoadSettings() public void ExtendPanels() { - if (_isExtended || Animation.IsActive) + if (Animation.IsActive || !MyAPIGateway.Session.IsServer) return; Vector3I nextPosition = Block.Position; @@ -182,11 +202,13 @@ public void ExtendPanels() { MyAPIGateway.Utilities.ShowNotification("Block already exists at position!"); DebugDraw.AddGridPoint(nextPosition, Block.CubeGrid, Color.Red, 4); - _isExtended = false; + _isExtended.Value = false; return; } } + HeartNetwork.SendToEveryoneInSync(new BlockPacket(StoredRadiators, Block.CubeGrid, Block, true), Block.GetPosition()); + for (int i = 0; i < StoredRadiators.Length; i++) { StoredRadiators[i].ObjectBuilder.Name = null; @@ -209,7 +231,7 @@ public void ExtendPanels() /// /// Panels start invisible for the animation to play. This makes them visible again. /// - public void MakePanelsVisible(bool clearStored = true) + public void MakePanelsVisible() { IMyCubeBlock nextBlock; int idx = 1; @@ -222,13 +244,13 @@ public void MakePanelsVisible(bool clearStored = true) idx++; } - if (clearStored) - StoredRadiators = Array.Empty(); + StoredRadiators = Array.Empty(); + HeartNetwork.SendToEveryoneInSync(new BlockPacket(StoredRadiators, Block.CubeGrid, Block, null), Block.GetPosition()); } public void RetractPanels() { - if (!_isExtended) + if (Animation.IsActive || !MyAPIGateway.Session.IsServer) return; IMyCubeBlock nextBlock; @@ -237,18 +259,15 @@ public void RetractPanels() while (GetNextPanel(idx, out nextBlock)) { - var builder = nextBlock.GetObjectBuilderCubeBlock(true); - - builder.BlockOrientation = nextBlock.Orientation; - - Matrix matrix; - builders.Add(new StoredRadiator(builder, nextBlock.LocalMatrix, nextBlock.CalculateCurrentModel(out matrix), Block.LocalMatrix)); + var storedRad = new StoredRadiator(nextBlock, Block); + builders.Add(storedRad); nextBlock.CubeGrid.RemoveBlock(nextBlock.SlimBlock, true); idx++; } StoredRadiators = builders.ToArray(); + HeartNetwork.SendToEveryoneInSync(new BlockPacket(StoredRadiators, Block.CubeGrid, Block, false), Block.GetPosition()); Animation.StartRetraction(); } @@ -282,6 +301,19 @@ public StoredRadiator(MyObjectBuilder_CubeBlock objectBuilder, Matrix localMatri Model = model; BaseLocalMatrix = baseLocalMatrix; } + + public StoredRadiator(IMyCubeBlock block, IMyCubeBlock referenceBlock) + { + var builder = block.GetObjectBuilderCubeBlock(true); + + builder.BlockOrientation = block.Orientation; + + Matrix matrix; + ObjectBuilder = builder; + LocalMatrix = block.LocalMatrix; + Model = block.CalculateCurrentModel(out matrix); + BaseLocalMatrix = referenceBlock.LocalMatrix; + } } } } diff --git a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/RadiatorAnimation.cs b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/RadiatorAnimation.cs index f152477a5..3d8a1324e 100644 --- a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/RadiatorAnimation.cs +++ b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/HeatParts/ExtendableRadiators/RadiatorAnimation.cs @@ -1,18 +1,9 @@ using System.Collections.Generic; -using System.Drawing; using System.Linq; -using Epstein_Fusion_DS.HudHelpers; -using Sandbox.Engine.Physics; using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using VRage.Game.Components; using VRage.Game.Entity; -using VRage.Game.ModAPI; using VRage.ModAPI; -using VRage.ObjectBuilders; using VRageMath; -using CollisionLayers = Sandbox.Engine.Physics.MyPhysics.CollisionLayers; -using Color = VRageMath.Color; namespace Epstein_Fusion_DS.HeatParts.ExtendableRadiators { diff --git a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/BlockPacket.cs b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/BlockPacket.cs new file mode 100644 index 000000000..a89b7cdb2 --- /dev/null +++ b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/BlockPacket.cs @@ -0,0 +1,68 @@ +using Epstein_Fusion_DS.HeatParts.ExtendableRadiators; +using ProtoBuf; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; +using static Epstein_Fusion_DS.HeatParts.ExtendableRadiators.ExtendableRadiator; + +namespace Epstein_Fusion_DS.Networking +{ + [ProtoContract] + internal class BlockPacket : PacketBase + { + private static void LogInfo(string text) => ModularDefinition.ModularApi.Log(text); + + [ProtoMember(1)] public StoredRadiator[] Stored; + [ProtoMember(2)] public long CubeGridId; + [ProtoMember(3)] public long RadiatorBlockId; + [ProtoMember(5)] public bool? IsExtending; + + private BlockPacket() + { + } + + public BlockPacket(StoredRadiator[] stored, IMyCubeGrid grid, IMyCubeBlock radiatorBlock, bool? isExtending) + { + Stored = stored; + CubeGridId = grid.EntityId; + RadiatorBlockId = radiatorBlock.EntityId; + IsExtending = isExtending; + + //LogInfo("Sending " + ToString()); + } + + public override void Received(ulong SenderSteamId) + { + if (MyAPIGateway.Session.IsServer) + return; + + //LogInfo("Received " + ToString()); + + foreach (var radiator in Stored) + { + var block = (MyAPIGateway.Entities.GetEntityById(CubeGridId) as IMyCubeGrid)?.AddBlock(radiator.ObjectBuilder, true); + if (block?.FatBlock != null) + block.FatBlock.Visible = false; + } + + var logic = (MyAPIGateway.Entities.GetEntityById(RadiatorBlockId) as IMyCubeBlock)?.GameLogic + ?.GetAs(); + if (logic != null) + { + logic.StoredRadiators = Stored; + + if (IsExtending.HasValue) + { + if (IsExtending.Value) + logic.Animation?.StartExtension(); + else + logic.Animation?.StartRetraction(); + } + } + } + + public override string ToString() + { + return $"{CubeGridId}::{RadiatorBlockId}\n{Stored.Length}"; + } + } +} diff --git a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/HeartNetwork.cs b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/HeartNetwork.cs new file mode 100644 index 000000000..d8ba37b02 --- /dev/null +++ b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/HeartNetwork.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; +using VRage.Network; +using VRageMath; + +namespace Epstein_Fusion_DS.Networking +{ + internal class HeartNetwork + { + public const ushort NetworkId = 7828; + public static HeartNetwork I; + private static void LogInfo(string text) => ModularDefinition.ModularApi.Log(text); + private static void LogException(Exception ex) => ModularDefinition.ModularApi.Log(ex.ToString()); + + public int NetworkLoadTicks = 240; + public int TotalNetworkLoad { get; private set; } = 0; + private int _bufferNetworkLoad = 0; + + private Dictionary> _packetQueue = new Dictionary>(); + + private int _networkLoadUpdate = 0; + + + public void LoadData() + { + I = this; + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(NetworkId, ReceivedPacket); + + LogInfo("Initialized HeartNetwork."); + } + + public void UnloadData() + { + MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(NetworkId, ReceivedPacket); + I = null; + LogInfo("Closed HeartNetwork."); + } + + public void Update() + { + foreach (var queuePair in _packetQueue) + { + if (queuePair.Value.Count == 0) + continue; + + MyAPIGateway.Multiplayer.SendMessageTo(NetworkId, MyAPIGateway.Utilities.SerializeToBinary(queuePair.Value.ToArray()), queuePair.Key); + queuePair.Value.Clear(); + } + + _networkLoadUpdate--; + if (_networkLoadUpdate <= 0) + { + _networkLoadUpdate = NetworkLoadTicks; + TotalNetworkLoad = _bufferNetworkLoad; + _bufferNetworkLoad = 0; + + TotalNetworkLoad /= (NetworkLoadTicks / 60); // Average per-second + } + + if (MyAPIGateway.Session.IsServer && _networkLoadUpdate % 10 == 0) + { + Players.Clear(); // KEEN DOESN'T. CLEAR. THE LIST. AUTOMATICALLY. AUGH. -aristeas + MyAPIGateway.Multiplayer.Players.GetPlayers(Players); + } + } + + private void ReceivedPacket(ushort channelId, byte[] serialized, ulong senderSteamId, bool isSenderServer) + { + try + { + PacketBase[] packets = MyAPIGateway.Utilities.SerializeFromBinary(serialized); + _bufferNetworkLoad += serialized.Length; + foreach (var packet in packets) + { + HandlePacket(packet, senderSteamId); + } + } + catch (Exception ex) + { + LogException(ex); + } + } + + private void HandlePacket(PacketBase packet, ulong senderSteamId) + { + packet.Received(senderSteamId); + } + + + + + + + public static void SendToPlayer(PacketBase packet, ulong playerSteamId) => + I?.SendToPlayerInternal(packet, playerSteamId); + public static void SendToEveryone(PacketBase packet) => + I?.SendToEveryoneInternal(packet); + public static void SendToEveryoneInSync(PacketBase packet, Vector3D position) => + I?.SendToEveryoneInSyncInternal(packet, position); + public static void SendToServer(PacketBase packet) => + I?.SendToServerInternal(packet); + + + private void SendToPlayerInternal(PacketBase packet, ulong playerSteamId) + { + if (playerSteamId == MyAPIGateway.Multiplayer.ServerId || playerSteamId == 0) + { + if (!MyAPIGateway.Utilities.IsDedicated) + packet.Received(0); + return; + } + + if (!_packetQueue.ContainsKey(playerSteamId)) + _packetQueue[playerSteamId] = new HashSet(); + _packetQueue[playerSteamId].Add(packet); + } + + private void SendToEveryoneInternal(PacketBase packet) + { + _tempPlayers.Clear(); + MyAPIGateway.Players.GetPlayers(_tempPlayers); + + foreach (IMyPlayer p in _tempPlayers) + { + // skip sending to self (server player) or back to sender + if (p.SteamUserId == MyAPIGateway.Multiplayer.ServerId || p.SteamUserId == 0) + { + if (!MyAPIGateway.Utilities.IsDedicated) + packet.Received(0); + continue; + } + + if (!_packetQueue.ContainsKey(p.SteamUserId)) + _packetQueue[p.SteamUserId] = new HashSet(); + _packetQueue[p.SteamUserId].Add(packet); + } + + _tempPlayers.Clear(); + } + + private void SendToEveryoneInSyncInternal(PacketBase packet, Vector3D position) + { + List toSend = new List(); + foreach (var player in Players) + if (Vector3D.DistanceSquared(player.GetPosition(), position) <= SyncRangeSq) // TODO: Sync this based on camera position + toSend.Add(player.SteamUserId); + + if (toSend.Count == 0) + return; + + foreach (var clientSteamId in toSend) + SendToPlayerInternal(packet, clientSteamId); + } + + private void SendToServerInternal(PacketBase packet) + { + MyAPIGateway.Multiplayer.SendMessageToServer(NetworkId, MyAPIGateway.Utilities.SerializeToBinary(new[] {packet})); + } + + + private readonly List _tempPlayers = new List(); + public readonly List Players = new List(); + public int SyncRangeSq => MyAPIGateway.Session.SessionSettings.SyncDistance * MyAPIGateway.Session.SessionSettings.SyncDistance; + } +} diff --git a/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/PacketBase.cs b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/PacketBase.cs new file mode 100644 index 000000000..f737235dc --- /dev/null +++ b/Utility Mods/MoA Fusion Systems/Data/Scripts/ModularAssemblies/Networking/PacketBase.cs @@ -0,0 +1,15 @@ +using ProtoBuf; + +namespace Epstein_Fusion_DS.Networking +{ + [ProtoContract(UseProtoMembersOnly = true)] + [ProtoInclude(101, typeof(BlockPacket))] + public abstract class PacketBase + { + /// + /// Called whenever your packet is recieved. + /// + /// + public abstract void Received(ulong SenderSteamId); + } +} diff --git a/Utility Mods/MoA Fusion Systems/Generic.csproj.DotSettings b/Utility Mods/MoA Fusion Systems/Generic.csproj.DotSettings new file mode 100644 index 000000000..047c6be8c --- /dev/null +++ b/Utility Mods/MoA Fusion Systems/Generic.csproj.DotSettings @@ -0,0 +1,4 @@ + + True + True + True \ No newline at end of file