diff --git a/NebulaModel/Packets/Factory/PowerTower/PowerTowerChargerUpdate.cs b/NebulaModel/Packets/Factory/PowerTower/PowerTowerChargerUpdate.cs new file mode 100644 index 000000000..81b81f223 --- /dev/null +++ b/NebulaModel/Packets/Factory/PowerTower/PowerTowerChargerUpdate.cs @@ -0,0 +1,17 @@ +namespace NebulaModel.Packets.Factory.PowerTower; + +public class PowerTowerChargerUpdate +{ + public PowerTowerChargerUpdate() { } + + public PowerTowerChargerUpdate(int planetId, int nodeId, bool charging) + { + PlanetId = planetId; + NodeId = nodeId; + Charging = charging; + } + + public int PlanetId { get; set; } + public int NodeId { get; set; } + public bool Charging { get; set; } +} diff --git a/NebulaModel/Packets/Factory/PowerTower/PowerTowerUserLoadingRequest.cs b/NebulaModel/Packets/Factory/PowerTower/PowerTowerUserLoadingRequest.cs deleted file mode 100644 index 14e0a60e5..000000000 --- a/NebulaModel/Packets/Factory/PowerTower/PowerTowerUserLoadingRequest.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace NebulaModel.Packets.Factory.PowerTower; - -public class PowerTowerUserLoadingRequest -{ - public PowerTowerUserLoadingRequest() { } - - public PowerTowerUserLoadingRequest(int PlanetId, int NetId, int NodeId, int PowerAmount, bool Charging) - { - this.PlanetId = PlanetId; - this.NetId = NetId; - this.NodeId = NodeId; - this.PowerAmount = PowerAmount; - this.Charging = Charging; - } - - public int PlanetId { get; set; } - public int NetId { get; set; } - public int NodeId { get; set; } - public int PowerAmount { get; set; } - public bool Charging { get; set; } -} diff --git a/NebulaModel/Packets/Factory/PowerTower/PowerTowerUserLoadingResponse.cs b/NebulaModel/Packets/Factory/PowerTower/PowerTowerUserLoadingResponse.cs deleted file mode 100644 index 7ae1c8b43..000000000 --- a/NebulaModel/Packets/Factory/PowerTower/PowerTowerUserLoadingResponse.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace NebulaModel.Packets.Factory.PowerTower; - -public class PowerTowerUserLoadingResponse -{ - public PowerTowerUserLoadingResponse() { } - - public PowerTowerUserLoadingResponse(int PlanetId, int NetId, int NodeId, int PowerAmount, long EnergyCapacity, - long EnergyRequired, long EnergyServed, long EnergyAccumulated, long EnergyExchanged, bool Charging) - { - this.PlanetId = PlanetId; - this.NetId = NetId; - this.NodeId = NodeId; - this.PowerAmount = PowerAmount; - this.EnergyCapacity = EnergyCapacity; - this.EnergyRequired = EnergyRequired; - this.EnergyServed = EnergyServed; - this.EnergyAccumulated = EnergyAccumulated; - this.EnergyExchanged = EnergyExchanged; - this.Charging = Charging; - } - - public int PlanetId { get; set; } - public int NetId { get; set; } - public int NodeId { get; set; } - public int PowerAmount { get; set; } - public long EnergyCapacity { get; set; } - public long EnergyRequired { get; set; } - public long EnergyServed { get; set; } - public long EnergyAccumulated { get; set; } - public long EnergyExchanged { get; set; } - public bool Charging { get; set; } -} diff --git a/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerChargerUpdateProcessor.cs b/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerChargerUpdateProcessor.cs new file mode 100644 index 000000000..324d0bf6f --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerChargerUpdateProcessor.cs @@ -0,0 +1,41 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Factory.PowerTower; +using NebulaWorld; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Factory.PowerTower; + +[RegisterPacketProcessor] +internal class PowerTowerChargerUpdateProcessor : PacketProcessor +{ + protected override void ProcessPacket(PowerTowerChargerUpdate packet, NebulaConnection conn) + { + if (packet.PlanetId == -1) + { + // When a player disconnect, clear all records and restart + Multiplayer.Session.PowerTowers.LocalChargerIds.Clear(); + Multiplayer.Session.PowerTowers.RemoteChargerHashIds.Clear(); + return; + } + + var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; + if (factory is not { powerSystem: not null }) + { + return; + } + var hashId = ((long)packet.PlanetId << 32) | (long)packet.NodeId; + if (packet.Charging) + { + Multiplayer.Session.PowerTowers.RemoteChargerHashIds.Add(hashId); + } + else + { + Multiplayer.Session.PowerTowers.RemoteChargerHashIds.Remove(hashId); + } + } +} diff --git a/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerUserLoadRequestProcessor.cs b/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerUserLoadRequestProcessor.cs deleted file mode 100644 index 2ea087d9d..000000000 --- a/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerUserLoadRequestProcessor.cs +++ /dev/null @@ -1,53 +0,0 @@ -#region - -using NebulaAPI.Packets; -using NebulaModel.Networking; -using NebulaModel.Packets; -using NebulaModel.Packets.Factory.PowerTower; -using NebulaWorld; - -#endregion - -namespace NebulaNetwork.PacketProcessors.Factory.PowerTower; - -[RegisterPacketProcessor] -public class PowerTowerUserLoadingRequestProcessor : PacketProcessor -{ - protected override void ProcessPacket(PowerTowerUserLoadingRequest packet, NebulaConnection conn) - { - if (IsClient) - { - return; - } - - var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; - - if (factory?.powerSystem == null) - { - return; - } - var pNet = factory.powerSystem.netPool[packet.NetId]; - - if (packet.Charging) - { - Multiplayer.Session.PowerTowers.AddExtraDemand(packet.PlanetId, packet.NetId, packet.NodeId, - packet.PowerAmount); - } - else - { - Multiplayer.Session.PowerTowers.RemExtraDemand(packet.PlanetId, packet.NetId, packet.NodeId); - } - - Multiplayer.Session.Network.SendPacketToStar(new PowerTowerUserLoadingResponse(packet.PlanetId, - packet.NetId, - packet.NodeId, - packet.PowerAmount, - pNet.energyCapacity, - pNet.energyRequired, - pNet.energyServed, - pNet.energyAccumulated, - pNet.energyExchanged, - packet.Charging), - GameMain.galaxy.PlanetById(packet.PlanetId).star.id); - } -} diff --git a/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerUserLoadResponseProcessor.cs b/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerUserLoadResponseProcessor.cs deleted file mode 100644 index f5d66d993..000000000 --- a/NebulaNetwork/PacketProcessors/Factory/PowerTower/PowerTowerUserLoadResponseProcessor.cs +++ /dev/null @@ -1,68 +0,0 @@ -#region - -using NebulaAPI.Packets; -using NebulaModel.Networking; -using NebulaModel.Packets; -using NebulaModel.Packets.Factory.PowerTower; -using NebulaWorld; - -#endregion - -namespace NebulaNetwork.PacketProcessors.Factory.PowerTower; - -[RegisterPacketProcessor] -internal class PowerTowerUserLoadResponseProcessor : PacketProcessor -{ - protected override void ProcessPacket(PowerTowerUserLoadingResponse packet, NebulaConnection conn) - { - var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; - if (factory is not { powerSystem: not null }) - { - return; - } - var pNet = factory.powerSystem.netPool[packet.NetId]; - - if (packet.Charging) - { - Multiplayer.Session.PowerTowers.AddExtraDemand(packet.PlanetId, packet.NetId, packet.NodeId, - packet.PowerAmount); - if (IsClient) - { - if (Multiplayer.Session.PowerTowers.DidRequest(packet.PlanetId, packet.NetId, packet.NodeId)) - { - var baseDemand = factory.powerSystem.nodePool[packet.NodeId].workEnergyPerTick - - factory.powerSystem.nodePool[packet.NodeId].idleEnergyPerTick; - var mult = factory.powerSystem.networkServes[packet.NetId]; - Multiplayer.Session.PowerTowers.PlayerChargeAmount += (int)(mult * baseDemand); - } - } - } - else - { - Multiplayer.Session.PowerTowers.RemExtraDemand(packet.PlanetId, packet.NetId, packet.NodeId); - } - - if (IsHost) - { - Multiplayer.Session.Network.SendPacketToStar(new PowerTowerUserLoadingResponse(packet.PlanetId, - packet.NetId, - packet.NodeId, - packet.PowerAmount, - pNet.energyCapacity, - pNet.energyRequired, - pNet.energyServed, - pNet.energyAccumulated, - pNet.energyExchanged, - packet.Charging), - GameMain.galaxy.PlanetById(packet.PlanetId).star.id); - } - else - { - pNet.energyCapacity = packet.EnergyCapacity; - pNet.energyRequired = packet.EnergyRequired; - pNet.energyAccumulated = packet.EnergyAccumulated; - pNet.energyExchanged = packet.EnergyExchanged; - pNet.energyServed = packet.EnergyServed; - } - } -} diff --git a/NebulaNetwork/PlayerManager.cs b/NebulaNetwork/PlayerManager.cs index 54391a54f..ad1b840e8 100644 --- a/NebulaNetwork/PlayerManager.cs +++ b/NebulaNetwork/PlayerManager.cs @@ -315,6 +315,7 @@ public void PlayerDisconnected(INebulaConnection conn) { availablePlayerIds.Enqueue(player.Id); } + Multiplayer.Session.PowerTowers.OnClientDisconnect(); Multiplayer.Session.Statistics.UnRegisterPlayer(player.Id); Multiplayer.Session.DysonSpheres.UnRegisterPlayer(conn); diff --git a/NebulaPatcher/Patches/Dynamic/PowerSystem_Patch.cs b/NebulaPatcher/Patches/Dynamic/PowerSystem_Patch.cs index 80f4074aa..c6eb31792 100644 --- a/NebulaPatcher/Patches/Dynamic/PowerSystem_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/PowerSystem_Patch.cs @@ -21,23 +21,6 @@ public static void PowerSystem_GameTick_Prefix(long time, ref bool isActive) } } - [HarmonyPostfix] - [HarmonyPatch(nameof(PowerSystem.GameTick))] - public static void PowerSystem_GameTick_Postfix(PowerSystem __instance) - { - if (!Multiplayer.IsActive) - { - return; - } - for (var i = 1; i < __instance.netCursor; i++) - { - var pNet = __instance.netPool[i]; - pNet.energyRequired += Multiplayer.Session.PowerTowers.GetExtraDemand(__instance.planet.id, i); - } - Multiplayer.Session.PowerTowers.GivePlayerPower(); - Multiplayer.Session.PowerTowers.UpdateAllAnimations(__instance.planet.id); - } - [HarmonyPrefix] [HarmonyPatch(nameof(PowerSystem.RemoveNodeComponent))] public static bool RemoveNodeComponent(PowerSystem __instance, int id) @@ -47,9 +30,10 @@ public static bool RemoveNodeComponent(PowerSystem __instance, int id) return true; } // as the destruct is synced across players this event is too - // and as such we can safely remove power demand for every player - var pComp = __instance.nodePool[id]; - Multiplayer.Session.PowerTowers.RemExtraDemand(__instance.planet.id, pComp.networkId, id); + // and as such we can safely remove power demand for every player + Multiplayer.Session.PowerTowers.LocalChargerIds.Remove(id); + var hashId = (long)__instance.factory.planetId << 32 | (long)id; + Multiplayer.Session.PowerTowers.RemoteChargerHashIds.Remove(hashId); return true; } diff --git a/NebulaPatcher/Patches/Transpilers/PowerSystem_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/PowerSystem_Transpiler.cs index 0cbfd4f54..303985b47 100644 --- a/NebulaPatcher/Patches/Transpilers/PowerSystem_Transpiler.cs +++ b/NebulaPatcher/Patches/Transpilers/PowerSystem_Transpiler.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Reflection.Emit; using HarmonyLib; using NebulaModel.Logger; using NebulaModel.Packets.Factory.PowerTower; using NebulaWorld; +using UnityEngine; #endregion @@ -15,142 +17,196 @@ namespace NebulaPatcher.Patches.Transpilers; [HarmonyPatch(typeof(PowerSystem))] internal class PowerSystem_Transpiler { - //todo:replace - //[HarmonyTranspiler] - //[HarmonyPatch(nameof(PowerSystem.GameTick))] - public static IEnumerable PowerSystem_GameTick_Transpiler(IEnumerable instructions) + [HarmonyTranspiler] + [HarmonyPatch(nameof(PowerSystem.GameTick))] + public static IEnumerable PowerSystem_GameTick_Transpiler(IEnumerable instructions, ILGenerator iLGenerator) { var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); - var codeMatcher = new CodeMatcher(codeInstructions) + + // Get the variable of mecha power coefficient: + // num7 = Mathf.Pow(Mathf.Clamp01((float)(1.0 - mainPlayer.mecha.coreEnergy / mainPlayer.mecha.coreEnergyCap) * 10f), 0.75f); + var codeMatcher = new CodeMatcher(codeInstructions, iLGenerator) .MatchForward(true, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerSystem), nameof(PowerSystem.nodePool))), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldelema), - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerSystem), nameof(PowerSystem.nodePool))), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldelema), - new CodeMatch(OpCodes.Ldfld, - AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.workEnergyPerTick))), - new CodeMatch(OpCodes.Stfld, - AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.requiredEnergy)))); + new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(Mathf), nameof(Mathf.Pow))), + new CodeMatch(OpCodes.Stloc_S)); + var coreEnergyRatioCI = new CodeInstruction(OpCodes.Ldloc_S, codeMatcher.Operand); + /* Overwrite the logic that set the power charger requiredEnergy and replace with our own. + from: + if (this.nodePool[id].id == id && this.nodePool[id].isCharger) + { + if (this.nodePool[id].coverRadius <= 20f) + { + ... + } + else + { + this.nodePool[id].requiredEnergy = this.nodePool[id].idleEnergyPerTick; + } + long num21 = (long)this.nodePool[id].requiredEnergy; + num11 += num21; + num2 += num21; + } + to: + if (this.nodePool[id].id == id && this.nodePool[id].isCharger) + { + if (this.nodePool[id].coverRadius <= 20f) + { + SetChargerRequriePower(this, id, num7); //replace + } + else + { + this.nodePool[id].requiredEnergy = this.nodePool[id].idleEnergyPerTick; + } + long num21 = (long)this.nodePool[id].requiredEnergy; + num11 += num21; + num2 += num21; + } + */ + codeMatcher + .MatchForward(false, new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "MoveNext")) + .MatchBack(true, + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.idleEnergyPerTick))), + new CodeMatch(OpCodes.Stfld, AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.requiredEnergy))), + new CodeMatch(OpCodes.Ldarg_0)); if (codeMatcher.IsInvalid) { Log.Error("PowerSystem_GameTick_Transpiler 1 failed. Mod version not compatible with game version."); return codeInstructions; } + codeMatcher.CreateLabel(out var label); + var nodeIdCI = codeMatcher.InstructionAt(-4); + + codeMatcher + .MatchBack(true, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.isCharger)))) + .MatchForward(true, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.coverRadius)))); + if (codeMatcher.IsInvalid) + { + Log.Error("PowerSystem_GameTick_Transpiler 2 failed. Mod version not compatible with game version."); + return codeInstructions; + } + codeMatcher.Advance(3) + .Insert( + new CodeInstruction(OpCodes.Ldarg_0), + nodeIdCI, + coreEnergyRatioCI, + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PowerSystem_Transpiler), nameof(SetChargerRequiredPower))), + new CodeInstruction(OpCodes.Br_S, label) + ); + + // Check if chargers are local before adding the energy to the mecha + // from: if (this.nodePool[num75].id == num75) + // get: num75 (nodeId) + codeMatcher.End() + .MatchBack(true, + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.id))), + new CodeMatch(OpCodes.Ldloc_S)); + if (codeMatcher.IsInvalid) + { + Log.Error("PowerSystem_GameTick_Transpiler 3 failed. Mod version not compatible with game version."); + return codeInstructions; + } + var loadNodeIdInstruction = codeMatcher.InstructionAt(0); + + /* + from: + if (num77 <= 0 || entityAnimPool[entityId5].state != 2U) + to: + if (num77 <= 0 || entityAnimPool[entityId5].state != 2U || !IsNotLocal(this, num75)) + */ + codeMatcher.MatchForward(true, + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(AnimData), nameof(AnimData.state))), + new CodeMatch(OpCodes.Ldc_I4_2), + new CodeMatch(OpCodes.Bne_Un)); + if (codeMatcher.IsInvalid) + { + Log.Error("PowerSystem_GameTick_Transpiler 4 failed. Mod version not compatible with game version."); + return codeInstructions; + } + + codeMatcher + .Insert( + new CodeInstruction(OpCodes.Ldarg_0), + loadNodeIdInstruction, + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PowerSystem_Transpiler), nameof(IsNotLocal))), + new CodeInstruction(OpCodes.Or) + ); + + return codeMatcher.InstructionEnumeration(); + } - codeMatcher = codeMatcher - .Advance(1) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 59)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 22)) - .Insert(HarmonyLib.Transpilers.EmitDelegate((_this, powerNodeId, powerNetId) => + private static bool IsNotLocal(PowerSystem powerSystem, int nodeId) + { + if (!Multiplayer.IsActive) + return false; + + if (GameMain.mainPlayer.planetId != powerSystem.factory.planetId) + return true; + + return !Multiplayer.Session.PowerTowers.LocalChargerIds.Contains(nodeId); + } + +#pragma warning disable CA1868 + private static void SetChargerRequiredPower(PowerSystem powerSystem, int nodeId, float coreEnergyRatio) + { + ref var powerNode = ref powerSystem.nodePool[nodeId]; + var planetId = powerSystem.factory.planetId; + var isLocalPlanet = GameMain.mainPlayer.planetId == planetId; + + if (isLocalPlanet) + { + // Assume the game is multithread + var dist = Vector3.SqrMagnitude(powerNode.powerPoint - powerSystem.multithreadPlayerPos); + // vanilla code to make wireless charger range bigger + var maxDist = (powerNode.coverRadius + 2.01f) * (powerNode.coverRadius + 2.01f); + + if (dist <= maxDist && coreEnergyRatio > 0) { - if (!Multiplayer.IsActive) - { - return; - } - if (!Multiplayer.Session.LocalPlayer.IsHost) + if (Multiplayer.IsActive) { - _this.nodePool[powerNodeId].requiredEnergy = - _this.nodePool[powerNodeId] - .idleEnergyPerTick; // this gets added onto the known required energy by Multiplayer.Session.PowerTowers. and PowerSystem_Patch - if (Multiplayer.Session.PowerTowers.AddRequested(_this.planet.id, powerNetId, powerNodeId, true, false)) + if (!Multiplayer.Session.PowerTowers.LocalChargerIds.Contains(powerNode.id)) { - Multiplayer.Session.Network.SendPacket(new PowerTowerUserLoadingRequest(_this.planet.id, powerNetId, - powerNodeId, _this.nodePool[powerNodeId].workEnergyPerTick, true)); + // If player start requesting power and the node id hasn't been record, broadcast to other players + Multiplayer.Session.PowerTowers.LocalChargerIds.Add(powerNode.id); + Multiplayer.Session.Network.SendPacketToLocalStar(new PowerTowerChargerUpdate( + powerSystem.factory.planetId, + powerNode.id, + true)); } } - else + powerNode.requiredEnergy = powerNode.workEnergyPerTick; + } + else + { + if (Multiplayer.IsActive) { - var pNet = _this.netPool[powerNetId]; - if (!Multiplayer.Session.PowerTowers.AddRequested(_this.planet.id, powerNetId, powerNodeId, true, - false)) + if (powerNode.requiredEnergy > powerNode.idleEnergyPerTick && Multiplayer.Session.PowerTowers.LocalChargerIds.Contains(powerNode.id)) { - return; + // If player stop requesting power and the node id has been record, broadcast to other players + Multiplayer.Session.PowerTowers.LocalChargerIds.Remove(powerNode.id); + Multiplayer.Session.Network.SendPacketToLocalStar(new PowerTowerChargerUpdate( + powerSystem.factory.planetId, + powerNode.id, + false)); } - Multiplayer.Session.PowerTowers.AddExtraDemand(_this.planet.id, powerNetId, powerNodeId, - _this.nodePool[powerNodeId].workEnergyPerTick); - Multiplayer.Session.Network.SendPacketToLocalStar(new PowerTowerUserLoadingResponse(_this.planet.id, - powerNetId, powerNodeId, _this.nodePool[powerNodeId].workEnergyPerTick, - pNet.energyCapacity, - pNet.energyRequired, - pNet.energyServed, - pNet.energyAccumulated, - pNet.energyExchanged, - true)); } - })) - // now search for where its set back to idle after player leaves radius / has charged fully - .MatchForward(true, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerSystem), nameof(PowerSystem.nodePool))), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldelema), - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PowerSystem), nameof(PowerSystem.nodePool))), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldelema), - new CodeMatch(OpCodes.Ldfld, - AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.idleEnergyPerTick))), - new CodeMatch(OpCodes.Stfld, - AccessTools.Field(typeof(PowerNodeComponent), nameof(PowerNodeComponent.requiredEnergy)))); - - if (!codeMatcher.IsInvalid) + powerNode.requiredEnergy = powerNode.idleEnergyPerTick; + } + } + else { - return codeMatcher - .Repeat(matcher => - { - matcher - .Advance(1) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldarg_0)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 59)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_S, 22)) - .Insert(HarmonyLib.Transpilers.EmitDelegate((_this, powerNodeId, powerNetId) => - { - if (!Multiplayer.IsActive) - { - return; - } - if (!Multiplayer.Session.LocalPlayer.IsHost) - { - if (Multiplayer.Session.PowerTowers.AddRequested(_this.planet.id, powerNetId, powerNodeId, - false, false)) - { - Multiplayer.Session.Network.SendPacket(new PowerTowerUserLoadingRequest(_this.planet.id, - powerNetId, powerNodeId, _this.nodePool[powerNodeId].workEnergyPerTick, false)); - } - } - else - { - var pNet = _this.netPool[powerNetId]; - if (!Multiplayer.Session.PowerTowers.AddRequested(_this.planet.id, powerNetId, powerNodeId, - false, false)) - { - return; - } - Multiplayer.Session.PowerTowers.RemExtraDemand(_this.planet.id, powerNetId, - powerNodeId); - Multiplayer.Session.Network.SendPacketToLocalStar(new PowerTowerUserLoadingResponse( - _this.planet.id, powerNetId, powerNodeId, - _this.nodePool[powerNodeId].workEnergyPerTick, - pNet.energyCapacity, - pNet.energyRequired, - pNet.energyServed, - pNet.energyAccumulated, - pNet.energyExchanged, - false)); - } - })); - }) - .InstructionEnumeration(); + powerNode.requiredEnergy = powerNode.idleEnergyPerTick; } - Log.Error("PowerSystem_GameTick_Transpiler 2 failed. Mod version not compatible with game version."); - return codeMatcher.InstructionEnumeration(); + if (Multiplayer.IsActive) + { + var hashId = (long)planetId << 32 | (long)powerNode.id; + if (Multiplayer.Session.PowerTowers.RemoteChargerHashIds.Contains(hashId)) + { + // This charger is used by remote player + powerNode.requiredEnergy = powerNode.workEnergyPerTick; + } + } } [HarmonyTranspiler] @@ -183,6 +239,4 @@ public static IEnumerable PowerSystem_RequestDysonSpherePower_T return codeInstructions; } } - - private delegate void PlayerChargesAtTower(PowerSystem _this, int powerNodeId, int powerNetId); } diff --git a/NebulaWorld/Factory/PowerTowerManager.cs b/NebulaWorld/Factory/PowerTowerManager.cs index 32ff3b520..68e9f8a52 100644 --- a/NebulaWorld/Factory/PowerTowerManager.cs +++ b/NebulaWorld/Factory/PowerTowerManager.cs @@ -1,11 +1,8 @@ #region using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; -using System.Threading; -using NebulaModel.Logger; +using NebulaModel.Packets.Factory.PowerTower; #endregion @@ -13,352 +10,28 @@ namespace NebulaWorld.Factory; public class PowerTowerManager : IDisposable { - private int ChargerCount; - - private ConcurrentDictionary> Energy = new(); - public int PlayerChargeAmount; - private ConcurrentDictionary> RequestsSent = new(); + public HashSet LocalChargerIds = []; + public HashSet RemoteChargerHashIds = []; public void Dispose() { - Energy.Clear(); - Energy = null; + LocalChargerIds.Clear(); + LocalChargerIds = null; - RequestsSent.Clear(); - RequestsSent = null; + RemoteChargerHashIds.Clear(); + RemoteChargerHashIds = null; GC.SuppressFinalize(this); } - public void GivePlayerPower() - { - if (Multiplayer.Session.LocalPlayer.IsHost) - { - // host gets it anyways - return; - } - - if (PlayerChargeAmount <= 0) - { - return; - } - GameMain.mainPlayer.mecha.coreEnergy += PlayerChargeAmount; - GameMain.mainPlayer.mecha.MarkEnergyChange(2, PlayerChargeAmount); - if (GameMain.mainPlayer.mecha.coreEnergy > GameMain.mainPlayer.mecha.coreEnergyCap) - { - GameMain.mainPlayer.mecha.coreEnergy = GameMain.mainPlayer.mecha.coreEnergyCap; - } - } - - // return true if added or changed state, false if already known - public bool AddRequested(int PlanetId, int NetId, int NodeId, bool Charging, bool eventFromOtherPlayer) - { - if (RequestsSent.TryGetValue(PlanetId, out var requests)) - { - foreach (var t in requests.Where(t => t.NetId == NetId && t.NodeId == NodeId)) - { - if (t.Charging == Charging) - { - return false; - } - if (Charging == false) - { - var factory = GameMain.galaxy.PlanetById(PlanetId)?.factory; - - if (factory is { powerSystem: not null }) - { - var baseDemand = factory.powerSystem.nodePool[NodeId].workEnergyPerTick - - factory.powerSystem.nodePool[NodeId].idleEnergyPerTick; - var mult = factory.powerSystem.networkServes[NetId]; - - PlayerChargeAmount -= (int)(mult * baseDemand); - ChargerCount--; - - if (PlayerChargeAmount < 0) - { - PlayerChargeAmount = 0; - } - if (ChargerCount == 0) - { - PlayerChargeAmount = 0; - } - } - } - if (!eventFromOtherPlayer) - { - t.Charging = Charging; - } - return true; - } - - var req = new Requested { NetId = NetId, NodeId = NodeId }; - - requests.Add(req); - - return true; - } - - var list = new List(); - var req2 = new Requested { NetId = NetId, NodeId = NodeId }; - - list.Add(req2); - return RequestsSent.TryAdd(PlanetId, list); - } - - public bool DidRequest(int PlanetId, int NetId, int NodeId) - { - if (!RequestsSent.TryGetValue(PlanetId, out var requests)) - { - return false; - } - if (!requests.Any(t => t.NetId == NetId && t.NodeId == NodeId && t.Charging)) - { - return false; - } - ChargerCount++; - return true; - } - - public int GetExtraDemand(int PlanetId, int NetId) - { - if (!Energy.TryGetValue(PlanetId, out var mapping)) - { - return 0; - } - if (Monitor.TryEnter(mapping, 100)) - { - try - { - foreach (var t in mapping.Where(t => t.NetId == NetId)) - { - return t.ExtraPower; - } - } - finally - { - Monitor.Exit(mapping); - } - } - else - { - Log.Warn("PowerTower: cant wait longer for threading lock, PowerTowers will be desynced!"); - } - - return 0; - } - - public void RemExtraDemand(int PlanetId, int NetId, int NodeId) - { - if (!Energy.TryGetValue(PlanetId, out var mapping)) - { - return; - } - foreach (var t in mapping) - { - if (t.NetId != NetId) - { - continue; - } - var factory = GameMain.galaxy.PlanetById(PlanetId).factory; - var pSystem = factory?.powerSystem; - - if (Monitor.TryEnter(mapping, 100)) - { - try - { - for (var j = 0; j < t.NodeId.Count; j++) - { - if (t.NodeId[j] != NodeId) - { - continue; - } - if (factory != null && pSystem != null) - { - t.ExtraPower -= pSystem.nodePool[NodeId].workEnergyPerTick; - } - else - { - t.ExtraPower -= t.ExtraPower / t.NodeId.Count; - } - - t.Activated[j]--; - AddRequested(PlanetId, NetId, NodeId, false, true); - - break; - } - } - finally - { - Monitor.Exit(mapping); - } - } - else - { - Log.Warn("PowerTower: cant wait longer for threading lock, PowerTowers will be desynced!"); - } - - if (t.ExtraPower < 0) - { - t.ExtraPower = 0; - } - } - } - - public void AddExtraDemand(int PlanetId, int NetId, int NodeId, int PowerAmount) - { - while (true) - { - if (Energy.TryGetValue(PlanetId, out var mapping)) - { - foreach (var t in mapping) - { - if (Monitor.TryEnter(mapping, 100)) - { - try - { - if (t.NetId != NetId) - { - continue; - } - var foundNodeId = false; - for (var j = 0; j < t.NodeId.Count; j++) - { - if (t.NodeId[j] != NodeId) - { - continue; - } - foundNodeId = true; - t.Activated[j]++; - t.ExtraPower += PowerAmount; - break; - } - - if (foundNodeId) - { - return; - } - t.NodeId.Add(NodeId); - t.Activated.Add(1); - t.ExtraPower += PowerAmount; - - return; - } - finally - { - Monitor.Exit(mapping); - } - } - Log.Warn("PowerTower: cant wait longer for threading lock, PowerTowers will be desynced!"); - } - - if (Monitor.TryEnter(mapping, 100)) - { - try - { - var map = new EnergyMapping { NetId = NetId }; - map.NodeId.Add(NodeId); - map.Activated.Add(1); - map.ExtraPower = PowerAmount; - - mapping.Add(map); - } - finally - { - Monitor.Exit(mapping); - } - } - else - { - Log.Warn("PowerTower: cant wait longer for threading lock, PowerTowers will be desynced!"); - } - } - else - { - var mapping2 = new List(); - - var map = new EnergyMapping { NetId = NetId }; - map.NodeId.Add(NodeId); - map.Activated.Add(1); - map.ExtraPower = PowerAmount; - - mapping2.Add(map); - if (!Energy.TryAdd(PlanetId, mapping2)) - { - // if we failed to add then most likely because another thread was faster, so call this again to run the above part of the method. - continue; - } - } - break; - } - } - - public void UpdateAllAnimations(int PlanetId) - { - if (!Energy.TryGetValue(PlanetId, out var mapping)) - { - return; - } - if (Monitor.TryEnter(mapping, 100)) - { - try - { - foreach (var t in mapping) - { - for (var j = 0; j < t.Activated.Count; j++) - { - UpdateAnimation(PlanetId, t.NetId, t.NodeId[j], - t.Activated[j] > 0 ? 1 : 0); - } - } - } - finally - { - Monitor.Exit(mapping); - } - } - else - { - Log.Warn("PowerTower: cant wait longer for threading lock, PowerTowers will be desynced!"); - } - } - - private static void UpdateAnimation(int PlanetId, int NetId, int NodeId, int PowerAmount) - { - const float idkValue = 0.016666668f; - var factory = GameMain.galaxy.PlanetById(PlanetId)?.factory; - - if (factory is not { entityAnimPool: not null, powerSystem: not null }) - { - return; - } - var pComp = factory.powerSystem.nodePool[NodeId]; - var animPool = factory.entityAnimPool; - var entityId = pComp.entityId; - - if (pComp.coverRadius < 15f) - { - animPool[entityId].StepPoweredClamped(factory.powerSystem.networkServes[NetId], idkValue, - PowerAmount > 0 ? 2U : 1U); - } - else - { - animPool[entityId].StepPoweredClamped2(factory.powerSystem.networkServes[NetId], idkValue, - PowerAmount > 0 ? 2U : 1U); - } - } - - private class EnergyMapping - { - public readonly List Activated = []; - public readonly List NodeId = []; - public int ExtraPower; - public int NetId; - } - - private class Requested + public void OnClientDisconnect() { - public bool Charging; - public int NetId; - public int NodeId; + // Procast event to reset all + LocalChargerIds.Clear(); + RemoteChargerHashIds.Clear(); + Multiplayer.Session.Network.SendPacketToLocalStar(new PowerTowerChargerUpdate( + -1, + 0, + false)); } }