From 149cf791498cfe7edb9d575de4c42b078c549655 Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Sat, 17 Feb 2024 00:12:29 +0800 Subject: [PATCH 1/6] Remove previous drone implementation --- .../Players/NewMechaDroneOrderPacket.cs | 23 -- .../Players/RemoveDroneOrdersPacket.cs | 15 - .../Players/NewDroneOrderProcessor.cs | 192 ----------- .../Players/RemoveDroneOrdersProcessor.cs | 129 ------- NebulaNetwork/Server.cs | 18 - .../ConstructionModuleComponent_Patch.cs | 38 --- .../Patches/Dynamic/DroneComponent_Patch.cs | 56 --- .../Patches/Dynamic/PlanetFactory_Patch.cs | 7 +- .../ConstructionModuleComponent_Transpiler.cs | 319 ------------------ .../ConstructionSystem_Transpiler.cs | 235 ------------- .../Transpilers/DroneComponent_Transpiler.cs | 87 ----- NebulaWorld/Player/DroneManager.cs | 283 ---------------- 12 files changed, 1 insertion(+), 1401 deletions(-) delete mode 100644 NebulaModel/Packets/Players/NewMechaDroneOrderPacket.cs delete mode 100644 NebulaModel/Packets/Players/RemoveDroneOrdersPacket.cs delete mode 100644 NebulaNetwork/PacketProcessors/Players/NewDroneOrderProcessor.cs delete mode 100644 NebulaNetwork/PacketProcessors/Players/RemoveDroneOrdersProcessor.cs delete mode 100644 NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs delete mode 100644 NebulaPatcher/Patches/Dynamic/DroneComponent_Patch.cs delete mode 100644 NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs delete mode 100644 NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs delete mode 100644 NebulaPatcher/Patches/Transpilers/DroneComponent_Transpiler.cs diff --git a/NebulaModel/Packets/Players/NewMechaDroneOrderPacket.cs b/NebulaModel/Packets/Players/NewMechaDroneOrderPacket.cs deleted file mode 100644 index 82c075fab..000000000 --- a/NebulaModel/Packets/Players/NewMechaDroneOrderPacket.cs +++ /dev/null @@ -1,23 +0,0 @@ -#region - -#endregion - -namespace NebulaModel.Packets.Players; - -public class NewMechaDroneOrderPacket -{ - public NewMechaDroneOrderPacket() { } - - public NewMechaDroneOrderPacket(int planetId, int entityId, ushort playerId, bool priority) - { - PlanetId = planetId; - EntityId = entityId; - PlayerId = playerId; - Priority = priority; - } - - public int PlanetId { get; set; } - public int EntityId { get; set; } - public ushort PlayerId { get; set; } - public bool Priority { get; set; } -} diff --git a/NebulaModel/Packets/Players/RemoveDroneOrdersPacket.cs b/NebulaModel/Packets/Players/RemoveDroneOrdersPacket.cs deleted file mode 100644 index 1e9517f87..000000000 --- a/NebulaModel/Packets/Players/RemoveDroneOrdersPacket.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace NebulaModel.Packets.Players; - -public class RemoveDroneOrdersPacket -{ - public RemoveDroneOrdersPacket() { } - - public RemoveDroneOrdersPacket(int[] queuedEntityIds, int planetId) - { - QueuedEntityIds = queuedEntityIds; - PlanetId = planetId; - } - - public int[] QueuedEntityIds { get; set; } - public int PlanetId { get; set; } -} diff --git a/NebulaNetwork/PacketProcessors/Players/NewDroneOrderProcessor.cs b/NebulaNetwork/PacketProcessors/Players/NewDroneOrderProcessor.cs deleted file mode 100644 index be7ab9e2e..000000000 --- a/NebulaNetwork/PacketProcessors/Players/NewDroneOrderProcessor.cs +++ /dev/null @@ -1,192 +0,0 @@ -#region - -using System.Net.Sockets; -using NebulaAPI.Packets; -using NebulaModel.Networking; -using NebulaModel.Packets; -using NebulaModel.Packets.Players; -using NebulaWorld; -using NebulaWorld.Player; -using UnityEngine; - -#endregion - -namespace NebulaNetwork.PacketProcessors.Players; - -[RegisterPacketProcessor] -internal class NewDroneOrderProcessor : PacketProcessor -{ - protected override void ProcessPacket(NewMechaDroneOrderPacket packet, NebulaConnection conn) - { - if (IsHost) - { - // Host needs to determine who is closest and who should send out drones. - // clients only send out construction drones in response to this packet. - - DroneManager.RefreshCachedPositions(); - Vector3 initialVector = getInitialVector(packet); - - var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; - if (factory == null) - { - return; - } - var entityPos = factory.constructionSystem._obj_hpos(packet.EntityId, ref initialVector); - var closestPlayer = DroneManager.GetClosestPlayerTo(packet.PlanetId, ref entityPos); - var elected = closestPlayer == Multiplayer.Session.LocalPlayer.Id; - - // only one drone per building allowed - if (DroneManager.IsPendingBuildRequest(packet.EntityId)) - { - return; - } - if (elected && - GameMain.mainPlayer.mecha.constructionModule.droneIdleCount > 0 && - GameMain.mainPlayer.mecha.constructionModule.droneEnabled && - (DroneManager.IsPrebuildGreen(factory, packet.EntityId) || DroneManager.PlayerHasEnoughItemsForConstruction(factory, packet.EntityId)) && - GameMain.mainPlayer.mecha.CheckEjectConstructionDroneCondition()) - { - informAndEjectLocalDrones(packet, factory, closestPlayer); - } - else if (elected) - { - // we are closest one but we do not have enough drones or they are disabled, or we just have not enough items to make the constructon green, so search next closest player - var nextClosestPlayer = - DroneManager.GetNextClosestPlayerToAfter(packet.PlanetId, closestPlayer, ref entityPos); - if (nextClosestPlayer == closestPlayer) - { - // there is no other one to ask so wait and do nothing. - return; - } - - informAndEjectRemoteDrones(packet, factory, nextClosestPlayer); - } - else if (!elected) - { - informAndEjectRemoteDrones(packet, factory, closestPlayer); - } - } - else - { - var elected = packet.PlayerId == Multiplayer.Session.LocalPlayer.Id; - var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; - var playerCanBuildIt = factory == null ? false : (DroneManager.IsPrebuildGreen(factory, packet.EntityId) || DroneManager.PlayerHasEnoughItemsForConstruction(factory, packet.EntityId)); - var ejectConstructionConditionsGiven = GameMain.mainPlayer.mecha.CheckEjectConstructionDroneCondition(); - - switch (elected) - { - case false: - { - // remove drone order request if not elected. - DroneManager.RemoveBuildRequest(packet.EntityId); - - // only render other drones when on same planet - if (packet.PlanetId != GameMain.mainPlayer.planetId) - { - return; - } - - // now spawn drones of other player, this is only visual to avoid having buildings popping up without any construction drone. - if (factory != null) - { - DroneManager.EjectDronesOfOtherPlayer(packet.PlayerId, packet.PlanetId, packet.EntityId); - // TODO(0.10.29.21869) - // factory.constructionSystem.constructServing.Add(packet.EntityId); - } - break; - } - case true when GameMain.mainPlayer.mecha.constructionModule.droneIdleCount > 0 && GameMain.mainPlayer.mecha.constructionModule.droneEnabled && playerCanBuildIt && ejectConstructionConditionsGiven: - { - // we should send out drones, so do it. - - if (factory != null) - { - // TODO(0.10.29.21869) - /* - factory.constructionSystem.TakeEnoughItemsFromPlayer(packet.EntityId); - GameMain.mainPlayer.mecha.constructionModule.EjectMechaDrone(factory, GameMain.mainPlayer, - packet.EntityId, - packet.Priority); - - factory.constructionSystem.constructServing.Add(packet.EntityId); - */ - } - break; - } - case true when GameMain.mainPlayer.mecha.constructionModule.droneIdleCount <= 0 || !GameMain.mainPlayer.mecha.constructionModule.droneEnabled || !playerCanBuildIt || !ejectConstructionConditionsGiven: - // remove drone order request if we cant handle it - DroneManager.RemoveBuildRequest(packet.EntityId); - - // others need to remove drones that are rendered for us. - Multiplayer.Session.Network.SendPacketToLocalPlanet(new RemoveDroneOrdersPacket([packet.EntityId], packet.PlanetId)); - break; - } - - // TODO: what about these from IdleDroneProcedure() - - /* - ref EntityData ptr = ref factory.entityPool[num2]; - CombatStat[] buffer = factory.skillSystem.combatStats.buffer; - int combatStatId = ptr.combatStatId; - buffer[combatStatId].repairerCount = buffer[combatStatId].repairerCount + 1; - */ - } - } - - private Vector3 getInitialVector(NewMechaDroneOrderPacket packet) - { - Vector3 vector; - - if (GameMain.mainPlayer.planetId == packet.PlanetId) - { - vector = GameMain.mainPlayer.position.normalized * (GameMain.mainPlayer.position.magnitude + 2.8f); - } - else - { - var playerPos = DroneManager.GetPlayerPosition(packet.PlayerId); - - vector = playerPos.normalized * (playerPos.magnitude + 2.8f); - } - - return vector; - } - - private void informAndEjectRemoteDrones(NewMechaDroneOrderPacket packet, PlanetFactory factory, ushort closestPlayerId) - { - DroneManager.AddBuildRequest(packet.EntityId); - DroneManager.AddPlayerDronePlan(closestPlayerId, packet.EntityId); - - // tell players to send out drones - Multiplayer.Session.Network.SendPacketToPlanet( - new NewMechaDroneOrderPacket(packet.PlanetId, packet.EntityId, closestPlayerId, packet.Priority), - packet.PlanetId); - // TODO(0.10.29.21869) - // factory.constructionSystem.constructServing.Add(packet.EntityId); - - // only render other drones when on same planet - if (packet.PlanetId == GameMain.mainPlayer.planetId) - { - // dont turn white prebuilds green here as drone plans can be rewoked by remote players and then we cant tell if it was a green or white prebuild. - DroneManager.EjectDronesOfOtherPlayer(closestPlayerId, packet.PlanetId, packet.EntityId); - } - } - - private void informAndEjectLocalDrones(NewMechaDroneOrderPacket packet, PlanetFactory factory, ushort closestPlayerId) - { - DroneManager.AddBuildRequest(packet.EntityId); - DroneManager.AddPlayerDronePlan(closestPlayerId, packet.EntityId); - - // tell players to send out drones - Multiplayer.Session.Network.SendPacketToPlanet( - new NewMechaDroneOrderPacket(packet.PlanetId, packet.EntityId, closestPlayerId, packet.Priority), - packet.PlanetId); - - // TODO(0.10.29.21869) - /* - factory.constructionSystem.TakeEnoughItemsFromPlayer(packet.EntityId); - GameMain.mainPlayer.mecha.constructionModule.EjectMechaDrone(factory, GameMain.mainPlayer, packet.EntityId, - packet.Priority); - factory.constructionSystem.constructServing.Add(packet.EntityId); - */ - } -} diff --git a/NebulaNetwork/PacketProcessors/Players/RemoveDroneOrdersProcessor.cs b/NebulaNetwork/PacketProcessors/Players/RemoveDroneOrdersProcessor.cs deleted file mode 100644 index f2cdd0c4e..000000000 --- a/NebulaNetwork/PacketProcessors/Players/RemoveDroneOrdersProcessor.cs +++ /dev/null @@ -1,129 +0,0 @@ -#region - -using System.Linq; -using NebulaAPI.Packets; -using NebulaModel.Networking; -using NebulaModel.Packets; -using NebulaModel.Packets.Players; -using NebulaWorld; -using NebulaWorld.Player; -using UnityEngine; - -#endregion - -namespace NebulaNetwork.PacketProcessors.Players; - -[RegisterPacketProcessor] -internal class RemoveDroneOrdersProcessor : PacketProcessor -{ - protected override void ProcessPacket(RemoveDroneOrdersPacket packet, NebulaConnection conn) - { - if (packet.QueuedEntityIds == null) - { - return; - } - - if (IsHost) - { - // host needs to remove targets from DroneManager - // but he also needs to RecycleDrone any rendered drone of this player - // and as clients only send this when they are unable to handle a NewDroneOrder the host should search for the next closest player to ask for construction. - var player = Players.Get(conn); - var factory = GameMain.galaxy.PlanetById(player.Data.LocalPlanetId)?.factory; - Vector3 vector; - - DroneManager.RefreshCachedPositions(); // refresh position cache - if (GameMain.mainPlayer.planetId == player.Data.LocalPlanetId) - { - vector = GameMain.mainPlayer.position.normalized * (GameMain.mainPlayer.position.magnitude + 2.8f); - } - else - { - var playerPos = DroneManager.GetPlayerPosition(player.Id); - - vector = playerPos.normalized * (playerPos.magnitude + 2.8f); - } - - foreach (var targetObjectId in packet.QueuedEntityIds) - { - DroneManager.RemoveBuildRequest(targetObjectId); - DroneManager.RemovePlayerDronePlan(player.Id, targetObjectId); - if (factory == null) - { - continue; - } - // TODO(0.10.29.21869) - // factory.constructionSystem.constructServing.Remove(targetObjectId); // in case it was a construction drone. - - if (GameMain.mainPlayer.planetId == player.Data.LocalPlanetId) - { - for (var i = 1; i < factory.constructionSystem.drones.cursor; i++) - { - ref var drone = ref factory.constructionSystem.drones.buffer[i]; - if (drone.owner < 0 && packet.QueuedEntityIds.Contains(drone.targetObjectId)) - { - GameMain.mainPlayer.mecha.constructionModule.RecycleDrone(factory, ref drone); - } - } - } - - var entityPos = factory.constructionSystem._obj_hpos(targetObjectId, ref vector); - var nextClosestPlayer = - DroneManager.GetNextClosestPlayerToAfter(player.Data.LocalPlanetId, player.Id, ref entityPos); - - if (nextClosestPlayer == player.Id) - { - continue; - } - DroneManager.AddBuildRequest(targetObjectId); - DroneManager.AddPlayerDronePlan(nextClosestPlayer, targetObjectId); - - // tell players to send out drones - Multiplayer.Session.Network.SendPacketToPlanet( - new NewMechaDroneOrderPacket(player.Data.LocalPlanetId, targetObjectId, - nextClosestPlayer, /*TODO: rip*/true), player.Data.LocalPlanetId); - - // TODO(0.10.29.21869) - //factory.constructionSystem.constructServing.Add(targetObjectId); - - // only render other drones when on same planet - if (player.Data.LocalPlanetId == GameMain.mainPlayer.planetId) - { - DroneManager.EjectDronesOfOtherPlayer(nextClosestPlayer, player.Data.LocalPlanetId, targetObjectId); - } - } - } - else - { - // check if there are any drones on the current planet and match the targets from this packet. - // if so recycle them. overflown drones are handled by RecycleDrone_Postfix - var factory = GameMain.mainPlayer.factory; - - if (factory == null || GameMain.mainPlayer.planetId != packet.PlanetId) - { - return; - } - for (var i = 1; i < factory.constructionSystem.drones.cursor; i++) - { - ref var drone = ref factory.constructionSystem.drones.buffer[i]; - switch (drone.owner) - { - case <= 0 when packet.QueuedEntityIds.Contains(drone.targetObjectId): - GameMain.mainPlayer.mecha.constructionModule.RecycleDrone(factory, ref drone); - break; - case > 0 when packet.QueuedEntityIds.Contains(drone.targetObjectId): - { - var battleBaseComponent = factory.defenseSystem.battleBases.buffer[drone.owner]; - battleBaseComponent.constructionModule.RecycleDrone(factory, ref drone); - break; - } - } - - DroneManager.RemoveBuildRequest(drone.targetObjectId); - // TODO(0.10.29.21869) - //factory.constructionSystem.constructServing - // .Remove(drone.targetObjectId); // in case it was a construction drone. - } - } - } -} diff --git a/NebulaNetwork/Server.cs b/NebulaNetwork/Server.cs index b5b4b2a01..1768882e1 100644 --- a/NebulaNetwork/Server.cs +++ b/NebulaNetwork/Server.cs @@ -26,7 +26,6 @@ using NebulaNetwork.Messaging; using NebulaNetwork.Ngrok; using NebulaWorld; -using NebulaWorld.Player; using NebulaWorld.SocialIntegration; using Open.Nat; using UnityEngine; @@ -161,23 +160,6 @@ internal void OnSocketDisconnection(INebulaConnection conn) Multiplayer.Session.Statistics.UnRegisterPlayer(player.Id); Multiplayer.Session.DysonSpheres.UnRegisterPlayer(conn); - //Notify players about queued building plans for drones - var DronePlans = DroneManager.GetPlayerDronePlans(player.Id); - if (DronePlans is { Length: > 0 } && player.Data.LocalPlanetId > 0) - { - SendPacketToPlanet(new RemoveDroneOrdersPacket(DronePlans, player.Data.LocalPlanetId), - player.Data.LocalPlanetId); - //Remove it also from host queue, if host is on the same planet - if (GameMain.mainPlayer.planetId == player.Data.LocalPlanetId) - { - //todo:replace - //foreach (var t in DronePlans) - //{ - // GameMain.mainPlayer.mecha.droneLogic.serving.Remove(t); - //} - } - } - // Note: using Keys or Values directly creates a readonly snapshot at the moment of call, as opposed to enumerating the dict. var syncCount = Players.Syncing.Count; if (conn.ConnectionStatus is not EConnectionStatus.Syncing || syncCount != 0) diff --git a/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs b/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs deleted file mode 100644 index e329529d9..000000000 --- a/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs +++ /dev/null @@ -1,38 +0,0 @@ -using HarmonyLib; -using NebulaWorld; - -namespace NebulaPatcher.Patches.Dynamic; - -[HarmonyPatch(typeof(ConstructionModuleComponent))] -internal class ConstructionModuleComponent_Patch -{ - // dont give back idle construction drones to player if it was a drone owned by a remote player - [HarmonyPostfix] - [HarmonyPatch(nameof(ConstructionModuleComponent.RecycleDrone))] - public static void RecycleDrone_Postfix(ConstructionModuleComponent __instance, ref DroneComponent drone) - { - if (!Multiplayer.IsActive) - { - return; - } - - if (drone.owner < 0 && drone.owner * -1 != Multiplayer.Session.LocalPlayer.Id || __instance.droneIdleCount > __instance.droneCount) - { - __instance.droneIdleCount--; - } - } - - // clients should skip the procedure for BattleBases. The host will tell them when to eject drones. - // TODO(0.10.29.21869) - //[HarmonyPrefix] - //[HarmonyPatch(nameof(ConstructionModuleComponent.IdleDroneProcedure))] - public static bool IdleDroneProcedure_Prefix(ConstructionModuleComponent __instance) - { - if (!Multiplayer.IsActive) - { - return true; - } - - return !(Multiplayer.Session.LocalPlayer.IsClient && __instance.entityId > 0); - } -} diff --git a/NebulaPatcher/Patches/Dynamic/DroneComponent_Patch.cs b/NebulaPatcher/Patches/Dynamic/DroneComponent_Patch.cs deleted file mode 100644 index 4c725e249..000000000 --- a/NebulaPatcher/Patches/Dynamic/DroneComponent_Patch.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Reflection; -using HarmonyLib; -using NebulaWorld; -using NebulaWorld.Player; -using UnityEngine; - -namespace NebulaPatcher.Patches.Dynamic; - -[HarmonyPatch] -internal class DroneComponent_Patch -{ - [HarmonyPatch] - class Get_InternalUpdate - { - [HarmonyTargetMethod] - public static MethodBase GetTargetMethod() - { - return AccessTools.Method( - typeof(DroneComponent), - "InternalUpdate", - [ - typeof(CraftData).MakeByRefType(), - typeof(PlanetFactory), - typeof(Vector3).MakeByRefType(), - typeof(float), - typeof(float), - typeof(double).MakeByRefType(), - typeof(double).MakeByRefType(), - typeof(double), - typeof(double), - typeof(float).MakeByRefType() - ]); - } - - // Update the position of the start/return point of construction drones that are owned by other players. The game would default to the local player position if not patched. - [HarmonyPrefix] - public static void InternalUpdate(DroneComponent __instance, ref Vector3 ejectPos, out float energyRatio) - { - energyRatio = 1f; // original method does this at the beginning anyways. - - if (!Multiplayer.IsActive) - { - return; - } - - if (__instance.owner >= 0) - { - return; - } - // very inefficient, better update this in the background elsewhere - DroneManager.RefreshCachedPositions(); - ejectPos = DroneManager.GetPlayerPosition((ushort)(__instance.owner * -1)); // player id is stored in owner to retrieve the current position when drones are updated. - ejectPos = ejectPos.normalized * (ejectPos.magnitude + 2.8f); // drones return to the head of mecha not feet - } - } -} \ No newline at end of file diff --git a/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs b/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs index 0ccb3f1bb..61ac939bd 100644 --- a/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs @@ -22,7 +22,6 @@ using NebulaModel.Packets.Planet; using NebulaWorld; using NebulaWorld.Factory; -using NebulaWorld.Player; #endregion @@ -56,16 +55,12 @@ public static bool BuildFinally_Prefix(PlanetFactory __instance, int prebuildId) return true; } - DroneManager.RemoveBuildRequest(-prebuildId); - if (Multiplayer.Session.LocalPlayer.IsHost) { - DroneManager.RemovePlayerDronePlan(-prebuildId); if (!Multiplayer.Session.Factories.ContainsPrebuildRequest(__instance.planetId, prebuildId)) { // This prevents duplicating the entity when multiple players trigger the BuildFinally for the same entity at the same time. - // If it occurs in any other circumstances, it means that we have some desynchronization between clients and host prebuilds buffers. - Log.Warn( + Log.Debug( $"BuildFinally was called without having a corresponding PrebuildRequest for the prebuild {prebuildId} on the planet {__instance.planetId}"); return false; } diff --git a/NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs deleted file mode 100644 index 8c24a3599..000000000 --- a/NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs +++ /dev/null @@ -1,319 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using HarmonyLib; -using NebulaModel.Logger; -using NebulaModel.Packets.Factory.BattleBase; -using NebulaModel.Packets.Players; -using NebulaWorld; -using NebulaWorld.Player; - -namespace NebulaPatcher.Patches.Transpilers; - -internal delegate bool EjectMechaDroneLocalOrRemote(ConstructionModuleComponent constructionModuleComponent, - PlanetFactory factory, Player player, int targetConstructionObjectId, int targetRepairObjectId, bool priority); - -internal delegate void BroadcastEjectBattleBaseDrone(ConstructionModuleComponent _this, PlanetFactory factory, - ref DroneComponent drone, ref CraftData cData, int targetId); - -internal delegate bool UseThisTarget(bool alreadyContained, int targetConstructionObjectId); - -[HarmonyPatch(typeof(ConstructionModuleComponent))] -internal class ConstructionModuleComponent_Transpiler -{ - // TODO(0.10.29.21869) - //[HarmonyTranspiler] - //[HarmonyPatch(nameof(ConstructionModuleComponent.IdleDroneProcedure))] - public static IEnumerable IdleDroneProcedure_Transpiler(IEnumerable instructions, - ILGenerator il) - { - var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); - var matcher = new CodeMatcher(codeInstructions, il); - - matcher - .MatchForward(true, - new CodeMatch(OpCodes.Add), - new CodeMatch(OpCodes.Conv_I4), - new CodeMatch(OpCodes.Stloc_S), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Clt), - new CodeMatch(OpCodes.Stloc_S), - new CodeMatch(OpCodes.Ldloc_S)); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionModuleComponent_Transpiler.IdleDroneProcedure_Transpiler 1 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - matcher.CreateLabel(out var jmpToOriginalCode); - - /* - * What this does: - if (player.mecha.CheckEjectConstructionDroneCondition()) - { - int num3 = this.droneCount - this.droneIdleCount; - int num4 = (int)((double)((float)this.droneCount * this.dronePriorConstructRatio) + 0.5); - bool flag2 = num3 < num4; - // inject code here, asking host if we can eject drones or if someone else will do so. - */ - matcher - .InsertAndAdvance( - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldarg_1), - new CodeInstruction(OpCodes.Ldarg_2), - new CodeInstruction(OpCodes.Ldloc_1), - new CodeInstruction(OpCodes.Ldloc_2), - new CodeInstruction(OpCodes.Ldloc_S, 5), - HarmonyLib.Transpilers.EmitDelegate( - (constructionModuleComponent, factory, player, targetConstructionObjectId, targetRepairObjectId, - priority) => - { - if (!Multiplayer.IsActive) - { - return true; - } - - // one of these must be != 0 because original method checks for it. - var targetObjectId = 0; - var constructing = false; - if (targetConstructionObjectId != 0) - { - targetObjectId = targetConstructionObjectId; - constructing = true; - } - if (targetRepairObjectId != 0) - { - targetObjectId = targetRepairObjectId; - } - - if (DroneManager.IsPendingBuildRequest(targetObjectId)) - { - return true; - } - - // clients need to ask host if they want to send out drones. drones will only be send out in response to that, but not here. - // clients use the DroneManager.PendingBuildRequests to remember which targetObjectId were already asked about. - if (Multiplayer.Session.LocalPlayer.IsClient) - { - // decrease idle drones to avoid sending more requests as we have drones. They are replenished once receiving the response. - //GameMain.mainPlayer.mecha.constructionModule.droneIdleCount--; - DroneManager.AddBuildRequest(targetObjectId); - - Multiplayer.Session.Network.SendPacket(new NewMechaDroneOrderPacket(GameMain.mainPlayer.planetId, - targetObjectId, Multiplayer.Session.LocalPlayer.Id, priority)); - } - else - { - // if we are the host we can directly determine in here who should send out drones. - DroneManager.RefreshCachedPositions(); // refresh position cache - - var vector = GameMain.mainPlayer.position.normalized * - (GameMain.mainPlayer.position.magnitude + 2.8f); - var entityPos = factory.constructionSystem._obj_hpos(targetObjectId, ref vector); - var closestPlayer = DroneManager.GetClosestPlayerTo(GameMain.mainPlayer.planetId, ref entityPos); - - // send out drones if we are closest - if (closestPlayer != Multiplayer.Session.LocalPlayer.Id) - { - return false; - } - DroneManager.AddBuildRequest(targetObjectId); - DroneManager.AddPlayerDronePlan(closestPlayer, targetObjectId); - - // tell players to send out drones only if we are the closest one. otherwise players will ask themselve in case they are able to send out drones. - Multiplayer.Session.Network.SendPacketToPlanet( - new NewMechaDroneOrderPacket(GameMain.mainPlayer.planetId, targetObjectId, closestPlayer, - priority), GameMain.mainPlayer.planetId); - - // TODO(0.10.29.21869) - /* - if (constructing) - { - factory.constructionSystem.TakeEnoughItemsFromPlayer(targetObjectId); - } - GameMain.mainPlayer.mecha.constructionModule.EjectMechaDrone(factory, GameMain.mainPlayer, - targetObjectId, priority); - factory.constructionSystem.constructServing.Add(targetObjectId); - */ - } - - return false; - }), - new CodeInstruction(OpCodes.Brtrue, jmpToOriginalCode), - new CodeInstruction(OpCodes.Ret)); - - // Now search for the parts where BattleBases eject their drones. - // The host must sync this to clients. (only host runs this because of a prefix patch that skips it for clients. - // add code before each 'this.EjectBaseDrone(factory, ref ptr3, ref ptr4, num/num2);' to broadcast to planets - // do this here to distinguish construction and repairing - - var pos = matcher.Pos; - - // construction - matcher - .MatchForward(true, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldarg_1), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldloc_1), // this is construction - new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "EjectBaseDrone")); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionModuleComponent_Transpiler.IdleDroneProcedure_Transpiler 2 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - matcher - .Repeat(matcher => - { - matcher - .InsertAndAdvance( - HarmonyLib.Transpilers.EmitDelegate( - (ConstructionModuleComponent _this, PlanetFactory factory, ref DroneComponent drone, - ref CraftData cData, int targetId) => - { - if (!Multiplayer.IsActive) - { - // TODO(0.10.29.21869) - // _this.EjectBaseDrone(factory, ref drone, ref cData, targetId); - return; - } - if (DroneManager.IsPendingBuildRequest(targetId)) - { - return; - } - DroneManager.AddBuildRequest( - targetId); // so clients will not receive any mecha drone order for this entity - Multiplayer.Session.Network.SendPacketToPlanet( - new NewBattleBaseDroneOrderPacket(factory.planetId, targetId, _this.id, true), - factory.planetId); - DroneManager.TakeEnoughItemsFromBattleBase(factory, targetId, _this); - // TODO(0.10.29.21869) - // _this.EjectBaseDrone(factory, ref drone, ref cData, targetId); - // factory.constructionSystem.constructServing.Add(targetId); - })) - .Set(OpCodes.Nop, null); // remove original call - }); - - // because matcher.Back() did not find the code for some reason... - while (matcher.Pos != pos) - { - matcher.Advance(-1); - } - - // repairment - matcher - .MatchForward(true, - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldarg_1), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Ldloc_2), // this is repair - new CodeMatch(i => i.opcode == OpCodes.Call && ((MethodInfo)i.operand).Name == "EjectBaseDrone")); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionModuleComponent_Transpiler.IdleDroneProcedure_Transpiler 3 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - matcher - .Repeat(matcher => - { - matcher - .InsertAndAdvance( - HarmonyLib.Transpilers.EmitDelegate( - (ConstructionModuleComponent _this, PlanetFactory factory, ref DroneComponent drone, - ref CraftData cData, int targetId) => - { - if (!Multiplayer.IsActive) - { - // TODO(0.10.29.21869) - //_this.EjectBaseDrone(factory, ref drone, ref cData, targetId); - return; - } - if (!DroneManager.IsPendingBuildRequest(targetId)) - { - DroneManager.AddBuildRequest( - targetId); // so clients will not receive any mecha drone order for this entity - Multiplayer.Session.Network.SendPacketToPlanet( - new NewBattleBaseDroneOrderPacket(factory.planetId, targetId, _this.id, false), - factory.planetId); - // TODO(0.10.29.21869) - //_this.EjectBaseDrone(factory, ref drone, ref cData, targetId); - } - })) - .Set(OpCodes.Nop, null); // remove original call - }); - - return matcher.InstructionEnumeration(); - } - - // skip targets that we already asked the host about, but only ask as much as we can handle with our idle drones. - // replace: if (!factory.constructionSystem.constructServing.Contains(num12)) - // with the checks from below - - // TODO(0.10.29.21869) - //[HarmonyTranspiler] - //[HarmonyPatch(nameof(ConstructionModuleComponent.SearchForNewTargets))] - public static IEnumerable SearchForNewTargets_Transpiler(IEnumerable instructions) - { - var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); - var matcher = new CodeMatcher(codeInstructions); - - matcher - .MatchForward(true, - new CodeMatch(OpCodes.Ldarg_1), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "constructionSystem"), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "constructServing"), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "Contains"), - new CodeMatch(OpCodes.Brtrue)); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionModuleComponent_Transpiler.SearchForNewTargets_Transpiler 1 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - matcher - .InsertAndAdvance( - new CodeInstruction(OpCodes.Ldloc_S, 28), - HarmonyLib.Transpilers.EmitDelegate((alreadyContained, targetConstructionObjectId) => - { - if (!Multiplayer.IsActive) - { - return alreadyContained; - } - - var myDronePlansCount = DroneManager.GetPlayerDronePlansCount(Multiplayer.Session.LocalPlayer.Id); - - var isPendingBuildRequest = DroneManager.IsPendingBuildRequest(targetConstructionObjectId); - var clientNoIdleDrones = Multiplayer.Session.LocalPlayer.IsClient && - DroneManager.CountPendingBuildRequest() > - GameMain.mainPlayer.mecha.constructionModule.droneCount; - var hostNoIdleDrones = Multiplayer.Session.LocalPlayer.IsHost && myDronePlansCount > - GameMain.mainPlayer.mecha.constructionModule.droneCount; - - if (!alreadyContained && isPendingBuildRequest && Multiplayer.Session.LocalPlayer.IsHost) - { - // this seems to be a deadlock desync, but a should be the correct value - DroneManager.RemoveBuildRequest(targetConstructionObjectId); - } - - // returning true here means the targetConstructionObjectId will not be selected as a valid next target - return alreadyContained || isPendingBuildRequest || clientNoIdleDrones || hostNoIdleDrones; - })); - - return matcher.InstructionEnumeration(); - } -} diff --git a/NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs deleted file mode 100644 index 5990b66fb..000000000 --- a/NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using HarmonyLib; -using NebulaModel.Logger; -using NebulaWorld; - -namespace NebulaPatcher.Patches.Transpilers; - -internal delegate bool RemoveExtraSpawnedDrones(ConstructionSystem _this, ref DroneComponent droneComponent); - -internal delegate bool IsOwnerAMecha(int owner); - -internal delegate bool OwnerAndIdMatchMecha(ConstructionModuleComponent constructionModuleComponent, int owner); -internal delegate bool AreDronesEnabled(bool droneEnabled, ref DroneComponent drone); - -[HarmonyPatch(typeof(ConstructionSystem))] -internal class ConstructionSystem_Transpiler -{ - // TODO(0.10.29.21869): runtime error - //[HarmonyTranspiler] - //[HarmonyPatch(nameof(ConstructionSystem.UpdateDrones))] - public static IEnumerable UpdateDrones_Transpiler1(IEnumerable instructions) - { - /* - * Poor Fix: Clients spawn with the right count of construction drones but for some reason extra drones spawn somewhere on the planet and come back to the mecha, - * draining core energy while doing so and increasing the drone count above the maximum when they arrive. - * - * This patch aims to RecycleDrone() these to remove them as soon as possible. - */ - - // find the continue jmp to skip one iteration of the for loop in case we needed to RecycleDrone() - var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); - var matcher = new CodeMatcher(codeInstructions) - .MatchForward(true, - new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "RecycleDrone"), - new CodeMatch(OpCodes.Br)); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionSystem_Transpiler.UpdateDrones_Transpiler 1 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - var continueJump = matcher.Operand; - - matcher - .MatchBack(true, - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "owner"), - new CodeMatch(OpCodes.Ldc_I4_0), - new CodeMatch(OpCodes.Ceq), - new CodeMatch(OpCodes.Stloc_S)); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionSystem_Transpiler.UpdateDrones_Transpiler 2 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - /* - * from: - ref DroneComponent ptr = ref this.drones.buffer[i]; - if (ptr.id == i) - { - if (ptr.stage != 0) - { - bool flag = ptr.owner == 0; - ConstructionModuleComponent constructionModuleComponent = (flag ? this.player.mecha.constructionModule : this.constructionModules.buffer[ptr.owner]); - * to: - ref DroneComponent ptr = ref this.drones.buffer[i]; - if (ptr.id == i) - { - if (ptr.stage != 0) - { - bool flag = ptr.owner == 0; - bool droneIsOwnedByMecha = ptr.owner == 0; - if (Multiplayer.IsActive && Multiplayer.Session.LocalPlayer.IsClient && droneIsOwnedByMecha) - { - int idleCount = _this.player.mecha.constructionModule.droneIdleCount; - int droneCount = _this.player.mecha.constructionModule.droneCount; - if (idleCount == droneCount) - { - this.player.mecha.constructionModule.RecycleDrone(this.factory, ref ptr); - this.player.mecha.constructionModule.droneIdleCount--; - continue; - } - } - ConstructionModuleComponent constructionModuleComponent = (flag ? this.player.mecha.constructionModule : this.constructionModules.buffer[ptr.owner]); - */ - matcher - .Advance(1) - .InsertAndAdvance( - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldloc_S, 8), - HarmonyLib.Transpilers.EmitDelegate( - (ConstructionSystem _this, ref DroneComponent droneComponent) => - { - var droneIsOwnedByMecha = droneComponent.owner == 0; - if (!Multiplayer.IsActive || !Multiplayer.Session.LocalPlayer.IsClient || !droneIsOwnedByMecha) - { - return false; - } - var idleCount = _this.player.mecha.constructionModule.droneIdleCount; - var droneCount = _this.player.mecha.constructionModule.droneCount; - if (idleCount != droneCount) - { - return false; - } - _this.player.mecha.constructionModule.RecycleDrone(_this.factory, ref droneComponent); - _this.player.mecha.constructionModule.droneIdleCount--; // because the above call increases it by one. - return true; - }), - new CodeInstruction(OpCodes.Brtrue, continueJump)); - - return matcher.InstructionEnumeration(); - } - - // still update rendering of other player drones, make sure this happens here. - - // TODO(0.10.29.21869): runtime error - //[HarmonyTranspiler] - //[HarmonyPatch(nameof(ConstructionSystem.UpdateDrones))] - public static IEnumerable UpdateDrones_Transpiler2(IEnumerable instructions, - ILGenerator generator) - { - var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); - var matcher = new CodeMatcher(codeInstructions, generator); - - // flag must be true too if ptr.owner <= 0 as we set owner to negative values in ConstructionModuleComponent_Transpiler to mark drones from other players. - matcher - .MatchForward(true, - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "owner"), - new CodeMatch(OpCodes.Ldc_I4_0), - new CodeMatch(OpCodes.Ceq)); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionSystem_Transpiler.UpdateDrones_Transpiler 3 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - // change: bool flag = ptr.owner == 0; - // to: bool flag = ptr.owner <= 0; - matcher - .SetInstruction(new CodeInstruction(OpCodes.Pop)) // remove the pushed 0 - .Advance(1) - .InsertAndAdvance( - HarmonyLib.Transpilers.EmitDelegate(owner => - { - if (!Multiplayer.IsActive) - { - return owner == 0; - } - return - owner <= 0; // we set owner to negative values in ConstructionModuleComponent_Transpiler to mark drones from other players. - })); - matcher - .MatchForward(false, - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "id"), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "owner"), - new CodeMatch(OpCodes.Bne_Un)); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionSystem_Transpiler.UpdateDrones_Transpiler 4 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - // change: if (constructionModuleComponent.id != ptr.owner || ptr2.id != ptr.craftId) - // to: if (ptr2.id != ptr.craftId) - matcher - .Advance(1) - .Set(OpCodes.Nop, null) // dont grab id but pass whole object, as it can be null in certain situations - .Advance(3) - .InsertAndAdvance( - HarmonyLib.Transpilers.EmitDelegate((constructionModuleComponent, owner) => - { - if (!Multiplayer.IsActive) - { - return constructionModuleComponent.id != owner; // game does exit when id does not match owner, so we do too when multiplayer is inactive - } - if (constructionModuleComponent == null) - { - // this might happen when battle bases are removed while drones are still around - return true; // this will stop rendering of those drones - } - - return false; // this might be a bit too open but will render any drone regardless of the games checks. - })); - var jmpOut = matcher.Operand; - matcher.SetInstruction(new CodeInstruction(OpCodes.Brtrue, jmpOut)); - - // now we also need to make sure that drones of other players still get rendered when the local player has his drones disabled. - // so set: bool droneEnabled = constructionModuleComponent.droneEnabled; - // to: bool droneEnabled = ptr.owner < 0 || constructionModuleComponent.droneEnabled - // but only when multiplayer is active ofc - - matcher - .MatchForward(true, - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "droneEnabled")); - - if (matcher.IsInvalid) - { - Log.Error( - "ConstructionSystem_Transpiler.UpdateDrones_Transpiler 5 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - matcher - .Advance(1) - .InsertAndAdvance( - new CodeInstruction(OpCodes.Ldloc_S, 8), - HarmonyLib.Transpilers.EmitDelegate((bool droneEnabled, ref DroneComponent drone) => - { - if (!Multiplayer.IsActive) - { - return droneEnabled; - } - - return droneEnabled || drone.owner < 0; - })); - - return matcher.InstructionEnumeration(); - } -} diff --git a/NebulaPatcher/Patches/Transpilers/DroneComponent_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/DroneComponent_Transpiler.cs deleted file mode 100644 index b5991ac43..000000000 --- a/NebulaPatcher/Patches/Transpilers/DroneComponent_Transpiler.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using HarmonyLib; -using NebulaModel.Logger; -using NebulaWorld; -using UnityEngine; - -namespace NebulaPatcher.Patches.Transpilers; - -internal delegate double patchEnergyChangeIfNeeded(double mechaValue, double subValue, int owner); - -[HarmonyPatch(typeof(DroneComponent))] -internal class DroneComponent_Transpiler -{ - [HarmonyPatch] - private class Get_InternalUpdate - { - [HarmonyTargetMethod] - public static MethodBase GetTargetMethod() - { - return AccessTools.Method( - typeof(DroneComponent), - "InternalUpdate", - [ - typeof(CraftData).MakeByRefType(), - typeof(PlanetFactory), - typeof(Vector3).MakeByRefType(), - typeof(float), - typeof(float), - typeof(double).MakeByRefType(), - typeof(double).MakeByRefType(), - typeof(double), - typeof(double), - typeof(float).MakeByRefType() - ]); - } - - // we need to make sure drones fro other players do not drain energy from our mecha core. - // replace each var in 'mechaEnergyChange -= var' and 'mechaEnergy -= var' with 0 if we need to. - [HarmonyTranspiler] - public static IEnumerable InternalUpdate_Transpiler(IEnumerable instructions) - { - var codeInstructions = instructions as CodeInstruction[] ?? instructions.ToArray(); - var matcher = new CodeMatcher(codeInstructions); - - matcher - .MatchForward(true, - new CodeMatch(OpCodes.Ldarg_S), - new CodeMatch(OpCodes.Ldind_R8), - new CodeMatch(OpCodes.Ldloc_S), - new CodeMatch(OpCodes.Sub)); - - if (matcher.IsInvalid) - { - Log.Error( - "DroneComponent_Transpiler.InternalUpdate_Transpiler 1 failed. Mod version not compatible with game version."); - return codeInstructions; - } - - matcher - .Repeat(matcher => matcher - .InsertAndAdvance( - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldfld, AccessTools.Field(typeof(DroneComponent), "owner")), - HarmonyLib.Transpilers.EmitDelegate((mechaValue, subValue, owner) => - { - if (!Multiplayer.IsActive) - { - return mechaValue - subValue; - } - - if (owner < 0) - { - // drone does not belong to us (that would be 0, > 0 would be battlebase) - return mechaValue; - } - return mechaValue - subValue; - })) - .Set(OpCodes.Nop, null) // remove original Sub - ); - - return matcher.InstructionEnumeration(); - } - } -} diff --git a/NebulaWorld/Player/DroneManager.cs b/NebulaWorld/Player/DroneManager.cs index 0a2112438..e49806bb0 100644 --- a/NebulaWorld/Player/DroneManager.cs +++ b/NebulaWorld/Player/DroneManager.cs @@ -15,147 +15,22 @@ namespace NebulaWorld.Player; public class DroneManager : IDisposable { - private static Dictionary> PlayerDroneBuildingPlans = []; - private static Dictionary PendingBuildRequests = []; private static Dictionary CachedPositions = []; private static long lastCheckedTick = 0; public DroneManager() { - PlayerDroneBuildingPlans = []; - PendingBuildRequests = []; CachedPositions = []; lastCheckedTick = 0; } public void Dispose() { - PlayerDroneBuildingPlans = null; - PendingBuildRequests = null; CachedPositions = null; lastCheckedTick = 0; GC.SuppressFinalize(this); } - public static void AddPlayerDronePlan(ushort playerId, int entityId) - { - if (!PlayerDroneBuildingPlans.TryGetValue(playerId, out var value)) - { - value = []; - PlayerDroneBuildingPlans.Add(playerId, value); - } - - if (!value.Contains(entityId)) - { - value.Add(entityId); - } - } - - public static void RemovePlayerDronePlan(ushort playerId, int entityId) - { - if (PlayerDroneBuildingPlans.TryGetValue(playerId, out var value)) - { - value.Remove(entityId); - } - } - - public static void RemovePlayerDronePlan(int entityId) - { - foreach (var kvp in PlayerDroneBuildingPlans.Where(kvp => kvp.Value.Contains(entityId))) - { - RemovePlayerDronePlan(kvp.Key, entityId); - return; - } - } - - public static void RemovePlayerDronePlans(ushort playerId) - { - PlayerDroneBuildingPlans.Remove(playerId); - } - - public static int[] GetPlayerDronePlans(ushort playerId) - { - return PlayerDroneBuildingPlans.TryGetValue(playerId, out var plan) ? [.. plan] : null; - } - public static int GetPlayerDronePlansCount(ushort playerId) - { - return PlayerDroneBuildingPlans.TryGetValue(playerId, out var plan) ? plan.Count : 0; - } - - // intended to be called on the host only - public static void RemoveOrphanDronePlans(List allPlayerIds) - { - foreach (var kv in PlayerDroneBuildingPlans) - { - if (allPlayerIds.Contains(kv.Key) || !CachedPositions.ContainsKey(kv.Key)) - { - continue; - } - var factory = GameMain.galaxy.PlanetById(CachedPositions[kv.Key].PlanetId).factory; - var dronePlans = GetPlayerDronePlans(kv.Key); - if (dronePlans.Length > 0) - { - var player = Multiplayer.Session.Server.Players.Get(kv.Key); - Multiplayer.Session.Network.SendPacketToPlanet(new RemoveDroneOrdersPacket(dronePlans, CachedPositions[kv.Key].PlanetId), - player.Data.LocalPlanetId); - - for (var i = 1; i < factory.constructionSystem.drones.cursor; i++) - { - ref var drone = ref factory.constructionSystem.drones.buffer[i]; - if (!dronePlans.Contains(drone.targetObjectId)) - { - continue; - } - // recycle drones from other player, removing them visually - // RecycleDrone_Postfix takes care of removing drones from mecha that do not belong to us - RemoveBuildRequest(drone.targetObjectId); - GameMain.mainPlayer.mecha.constructionModule.RecycleDrone(factory, ref drone); - } - } - - PlayerDroneBuildingPlans.Remove(kv.Key); - CachedPositions.Remove(kv.Key); - } - } - - public static void AddBuildRequest(int entityId) - { - if (!PendingBuildRequests.ContainsKey(entityId)) - { - PendingBuildRequests.Add(entityId, GameMain.gameTick); - } - } - - public static bool IsPendingBuildRequest(int entityId) - { - var isPresent = PendingBuildRequests.ContainsKey(entityId); - if (!isPresent || !Multiplayer.Session.LocalPlayer.IsClient) - { - return isPresent; - } - // clients can run in a situation where they have requested sending out drones but never received an answer, potentially leading to a deadlock - // thus we need to free up requests after a specific amount of time if there was no response yet. - if (GameMain.gameTick - PendingBuildRequests[entityId] <= 250) - { - return true; - } - Multiplayer.Session.Network.SendPacket(new RemoveDroneOrdersPacket(new[] { entityId }, GameMain.mainPlayer.planetId)); - RemoveBuildRequest(entityId); - // TODO(0.10.29.21869) - // GameMain.galaxy.PlanetById(GameMain.mainPlayer.planetId)?.factory?.constructionSystem.constructServing.Remove(entityId); - return false; - } - - public static int CountPendingBuildRequest() - { - return PendingBuildRequests.Count; - } - - public static void RemoveBuildRequest(int entityId) - { - PendingBuildRequests.Remove(entityId); - } - public static void EjectDronesOfOtherPlayer(ushort playerId, int planetId, int targetObjectId) { RefreshCachedPositions(); @@ -207,166 +82,8 @@ public static void RefreshCachedPositions() } } - public static ushort GetClosestPlayerTo(int planetId, ref Vector3 entityPos) - { - if (!Multiplayer.IsActive) - { - return 0; - } - - var shortestDistance = 0.0f; - ushort nearestPlayer = 0; - - var factory = GameMain.galaxy.PlanetById(planetId)?.factory; - if (factory == null) - { - return nearestPlayer; - } - - var sqrMinBuildAlt = factory.constructionSystem.sqrMinBuildAlt; - var buildArea = GameMain.mainPlayer.mecha.buildArea; // this should be same for every player - - if (entityPos.sqrMagnitude < sqrMinBuildAlt) - { - return nearestPlayer; - } - // this is from game code - buildArea *= buildArea; - if (factory.planet.type == EPlanetType.Gas) - { - buildArea *= 10f; - } - - // host is not in cache so check separately as long as its not a dedicated server. - if (GameMain.mainPlayer.planetId == planetId && (GameMain.mainPlayer.position - entityPos).sqrMagnitude <= buildArea && !Multiplayer.IsDedicated) - { - nearestPlayer = 1; // this is host - shortestDistance = (GameMain.mainPlayer.position - entityPos).sqrMagnitude; - } - - foreach (var playerPosition in CachedPositions.Where(playerPosition => - playerPosition.Value.PlanetId == planetId)) - { - var dist = (playerPosition.Value.Position - entityPos).sqrMagnitude; - - if (nearestPlayer == 0 && dist <= buildArea) - { - nearestPlayer = playerPosition.Key; - shortestDistance = dist; - - continue; - } - - if (dist < shortestDistance && dist <= buildArea) - { - shortestDistance = dist; - nearestPlayer = playerPosition.Key; - } - } - return nearestPlayer; - } - - public static ushort GetNextClosestPlayerToAfter(int planetId, ushort afterPlayerId, ref Vector3 entityPos) - { - if (!Multiplayer.IsActive) - { - return 0; - } - - var afterPlayerDistance = (GetPlayerPosition(afterPlayerId) - entityPos).sqrMagnitude; - var nextShortestDistance = afterPlayerDistance; - var nearestPlayer = afterPlayerId; - - var factory = GameMain.galaxy.PlanetById(planetId)?.factory; - if (factory == null) - { - return nearestPlayer; - } - - var sqrMinBuildAlt = factory.constructionSystem.sqrMinBuildAlt; - var buildArea = GameMain.mainPlayer.mecha.buildArea; // this should be same for every player - - if (entityPos.sqrMagnitude < sqrMinBuildAlt) - { - return nearestPlayer; - } - - // this is from game code - buildArea *= buildArea; - if (factory.planet.type == EPlanetType.Gas) - { - buildArea *= 10f; - } - - // host is not in cache so check separately as long as its not a dedicated server. - var distHost = (GameMain.mainPlayer.position - entityPos).sqrMagnitude; - if (GameMain.mainPlayer.planetId == planetId && distHost <= buildArea && distHost > afterPlayerDistance && !Multiplayer.IsDedicated) - { - nearestPlayer = 1; // this is host - nextShortestDistance = distHost; - } - - foreach (var playerPosition in CachedPositions.Where(playerPosition => - playerPosition.Value.PlanetId == planetId)) - { - var dist = (playerPosition.Value.Position - entityPos).sqrMagnitude; - - if (nextShortestDistance == afterPlayerDistance && dist > nextShortestDistance && dist <= buildArea) - { - nextShortestDistance = dist; - nearestPlayer = playerPosition.Key; - } - else if (nextShortestDistance != afterPlayerDistance && dist > afterPlayerDistance && dist < nextShortestDistance && dist <= buildArea) - { - nextShortestDistance = dist; - nearestPlayer = playerPosition.Key; - } - } - - return nearestPlayer; - } - public static Vector3 GetPlayerPosition(ushort playerId) { return CachedPositions.TryGetValue(playerId, out var value) ? value.Position : GameMain.mainPlayer.position; } - - public static bool IsPrebuildGreen(PlanetFactory factory, int targetObjectId) - { - return factory.prebuildPool[-targetObjectId].itemRequired <= 0; - } - public static bool PlayerHasEnoughItemsForConstruction(PlanetFactory factory, int targetObjectId) - { - ref PrebuildData prebuildData = ref factory.prebuildPool[-targetObjectId]; - return prebuildData.itemRequired <= GameMain.mainPlayer.package.GetItemCount((int)prebuildData.protoId); - } - public static bool TakeEnoughItemsFromBattleBase(PlanetFactory factory, int targetObjectId, ConstructionModuleComponent battleBaseCMC) - { - ref PrebuildData prebuildData = ref factory.prebuildPool[-targetObjectId]; - StorageComponent sc = factory.defenseSystem.battleBases.buffer[battleBaseCMC.battleBaseId].storage.topStorage; - - if (prebuildData.itemRequired > 0) - { - int protoId = (int)prebuildData.protoId; - int itemRequired = prebuildData.itemRequired; - int inc; - - sc.TakeTailItems(ref protoId, ref itemRequired, out inc, false); - while (itemRequired == 0 && sc.previousStorage != null) - { - protoId = (int)prebuildData.protoId; - itemRequired = prebuildData.itemRequired; - sc = sc.previousStorage; - sc.TakeTailItems(ref protoId, ref itemRequired, out inc, false); - } - - prebuildData.itemRequired = prebuildData.itemRequired - itemRequired; - if (factory.planet.factoryLoaded || factory.planet.factingCompletedStage >= 3) - { - factory.AlterPrebuildModelState(-targetObjectId, false); - } - } - - return prebuildData.itemRequired == 0; - } } From 85c7f846ff16f1e58d8330a4f0442a10435f9abe Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Sat, 17 Feb 2024 04:16:52 +0800 Subject: [PATCH 2/6] Sync Prebuild.itemRequired changes by player --- .../Factory/PrebuildItemRequiredUpdate.cs | 17 +++++ .../Packets/Players/PlayerGiveItemPacket.cs | 17 +++++ .../Entity/BuildEntityRequestProcessor.cs | 3 +- .../PrebuildItemRequiredUpdateProcessor.cs | 51 +++++++++++++ .../Players/PlayerGiveItemProcessor.cs | 19 +++++ .../ConstructionModuleComponent_Transpiler.cs | 71 +++++++++++++++++++ 6 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 NebulaModel/Packets/Factory/PrebuildItemRequiredUpdate.cs create mode 100644 NebulaModel/Packets/Players/PlayerGiveItemPacket.cs create mode 100644 NebulaNetwork/PacketProcessors/Factory/Entity/PrebuildItemRequiredUpdateProcessor.cs create mode 100644 NebulaNetwork/PacketProcessors/Players/PlayerGiveItemProcessor.cs create mode 100644 NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs diff --git a/NebulaModel/Packets/Factory/PrebuildItemRequiredUpdate.cs b/NebulaModel/Packets/Factory/PrebuildItemRequiredUpdate.cs new file mode 100644 index 000000000..dc1a5bf4a --- /dev/null +++ b/NebulaModel/Packets/Factory/PrebuildItemRequiredUpdate.cs @@ -0,0 +1,17 @@ +namespace NebulaModel.Packets.Factory; + +public class PrebuildItemRequiredUpdate +{ + public PrebuildItemRequiredUpdate() { } + + public PrebuildItemRequiredUpdate(int planetId, int prebuildId, int itemCount) + { + PlanetId = planetId; + PrebuildId = prebuildId; + ItemCount = itemCount; + } + + public int PlanetId { get; set; } + public int PrebuildId { get; set; } + public int ItemCount { get; set; } +} diff --git a/NebulaModel/Packets/Players/PlayerGiveItemPacket.cs b/NebulaModel/Packets/Players/PlayerGiveItemPacket.cs new file mode 100644 index 000000000..8b26a2a3c --- /dev/null +++ b/NebulaModel/Packets/Players/PlayerGiveItemPacket.cs @@ -0,0 +1,17 @@ +namespace NebulaModel.Packets.Players; + +public class PlayerGiveItemPacket +{ + public PlayerGiveItemPacket() { } + + public PlayerGiveItemPacket(int itemId, int itemCount, int itemInc) + { + ItemId = itemId; + ItemCount = itemCount; + ItemInc = itemInc; + } + + public int ItemId { get; set; } + public int ItemCount { get; set; } + public int ItemInc { get; set; } +} diff --git a/NebulaNetwork/PacketProcessors/Factory/Entity/BuildEntityRequestProcessor.cs b/NebulaNetwork/PacketProcessors/Factory/Entity/BuildEntityRequestProcessor.cs index 49bb575a0..015be3f6c 100644 --- a/NebulaNetwork/PacketProcessors/Factory/Entity/BuildEntityRequestProcessor.cs +++ b/NebulaNetwork/PacketProcessors/Factory/Entity/BuildEntityRequestProcessor.cs @@ -22,8 +22,7 @@ protected override void ProcessPacket(BuildEntityRequest packet, NebulaConnectio { if (IsHost && !Multiplayer.Session.Factories.ContainsPrebuildRequest(packet.PlanetId, packet.PrebuildId)) { - Log.Warn( - $"BuildEntityRequest received does not have a corresponding PrebuildRequest with the id {packet.PrebuildId} for the planet {packet.PlanetId}"); + // Prebuild has already been removed, so skip it. return; } diff --git a/NebulaNetwork/PacketProcessors/Factory/Entity/PrebuildItemRequiredUpdateProcessor.cs b/NebulaNetwork/PacketProcessors/Factory/Entity/PrebuildItemRequiredUpdateProcessor.cs new file mode 100644 index 000000000..49e4fa120 --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Factory/Entity/PrebuildItemRequiredUpdateProcessor.cs @@ -0,0 +1,51 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Factory; +using NebulaModel.Packets.Players; +using NebulaWorld; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Factory.Entity; + +[RegisterPacketProcessor] +public class PrebuildItemRequiredUpdateProcessor : PacketProcessor +{ + protected override void ProcessPacket(PrebuildItemRequiredUpdate packet, NebulaConnection conn) + { + var planet = GameMain.galaxy.PlanetById(packet.PlanetId); + if (planet.factory == null || packet.PrebuildId >= planet.factory.prebuildCursor) + { + return; + } + var factory = planet.factory; + + ref var ptr = ref factory.prebuildPool[packet.PrebuildId]; + if (ptr.id != packet.PrebuildId || ptr.itemRequired == 0) + { + //Prebuild not exist or the prebuild has satisfied the item requirement (green) + if (IsHost) + { + //Refund the item back to the player + var itemId = ptr.protoId; + conn.SendPacket(new PlayerGiveItemPacket(itemId, packet.ItemCount, 0)); + } + return; + } + + using (Multiplayer.Session.Factories.IsIncomingRequest.On()) + { + //From ConstructionModuleComponent.PlaceItems + ptr.itemRequired = 0; + factory.NotifyPrebuildChange(ptr.id, 3); + if (factory.planet.factoryLoaded || factory.planet.factingCompletedStage >= 3) + { + factory.AlterPrebuildModelState(ptr.id, false); + } + factory.constructionSystem.AddBuildTargetToModules(ptr.id, ref ptr.pos); + } + } +} diff --git a/NebulaNetwork/PacketProcessors/Players/PlayerGiveItemProcessor.cs b/NebulaNetwork/PacketProcessors/Players/PlayerGiveItemProcessor.cs new file mode 100644 index 000000000..7f65d6b59 --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Players/PlayerGiveItemProcessor.cs @@ -0,0 +1,19 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Players; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Players; + +[RegisterPacketProcessor] +public class PlayerGiveItemProcessor : PacketProcessor +{ + protected override void ProcessPacket(PlayerGiveItemPacket packet, NebulaConnection conn) + { + GameMain.mainPlayer.TryAddItemToPackage(packet.ItemId, packet.ItemCount, packet.ItemInc, true); + } +} diff --git a/NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs new file mode 100644 index 000000000..7d4e456c0 --- /dev/null +++ b/NebulaPatcher/Patches/Transpilers/ConstructionModuleComponent_Transpiler.cs @@ -0,0 +1,71 @@ +#region + +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using NebulaModel.Logger; +using NebulaModel.Packets.Factory; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Transpilers; + +[HarmonyPatch(typeof(ConstructionModuleComponent))] +internal class ConstructionModuleComponent_Transpiler +{ + [HarmonyTranspiler, HarmonyPriority(Priority.High)] + [HarmonyPatch(nameof(ConstructionModuleComponent.PlaceItems))] + public static IEnumerable PlaceItems_Transpiler(IEnumerable instructions) + { + try + { + /* Sync Prebuild.itemRequired changes by player, insert local method call after player.package.TakeTailItems + After: player.package.TakeTailItems(ref itemId, ref count, out inc, false); + Insert: SendPacket(factory, ptr3, count); + Before: Assert.True(count == ptr3.itemRequired); + */ + + var codeMatcher = new CodeMatcher(instructions) + .MatchForward(true, + new CodeMatch(OpCodes.Ldarg_2), + new CodeMatch(OpCodes.Callvirt, AccessTools.DeclaredPropertyGetter(typeof(Player), nameof(Player.package))), + new CodeMatch(OpCodes.Ldloca_S), + new CodeMatch(OpCodes.Ldloca_S), + new CodeMatch(OpCodes.Ldloca_S), + new CodeMatch(OpCodes.Ldc_I4_0), + new CodeMatch(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand).Name == "TakeTailItems"), + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(PrebuildData), nameof(PrebuildData.itemRequired))) + ) + .Repeat( + matcher => matcher + .Advance(-2) + .InsertAndAdvance( + new CodeInstruction(OpCodes.Ldarg_1), + new CodeInstruction(OpCodes.Ldloc_S, matcher.InstructionAt(1).operand), + new CodeInstruction(OpCodes.Ldloc_S, matcher.InstructionAt(-4).operand), + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(ConstructionModuleComponent_Transpiler), nameof(SendPacket))) + ) + ); + + return codeMatcher.InstructionEnumeration(); + } + catch (System.Exception e) + { + Log.Error("Transpiler ConstructionModuleComponent.PlaceItems failed."); + Log.Error(e); + return instructions; + } + } + + private static void SendPacket(PlanetFactory factory, ref PrebuildData prebuild, int itemCount) + { + if (!Multiplayer.IsActive) return; + + var packet = new PrebuildItemRequiredUpdate(factory.planetId, prebuild.id, itemCount); + Multiplayer.Session.Network.SendPacketToLocalStar(packet); + } +} From 74e318d9a2823194ba51f3ffb0ef85575359cd69 Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Sat, 17 Feb 2024 10:53:34 +0800 Subject: [PATCH 3/6] Manage remote mecha drones in separated pool in Drone Mananger --- .../Players/PlayerEjectMechaDronePacket.cs | 26 ++ .../Players/PlayerEjectMechaDroneProcessor.cs | 23 ++ .../ConstructionModuleComponent_Patch.cs | 28 ++ .../Dynamic/ConstructionSystem_Patch.cs | 23 ++ .../Patches/Dynamic/GameData_Patch.cs | 4 + NebulaWorld/MultiplayerSession.cs | 2 +- NebulaWorld/Player/DroneManager.cs | 258 ++++++++++++++++-- 7 files changed, 335 insertions(+), 29 deletions(-) create mode 100644 NebulaModel/Packets/Players/PlayerEjectMechaDronePacket.cs create mode 100644 NebulaNetwork/PacketProcessors/Players/PlayerEjectMechaDroneProcessor.cs create mode 100644 NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs create mode 100644 NebulaPatcher/Patches/Dynamic/ConstructionSystem_Patch.cs diff --git a/NebulaModel/Packets/Players/PlayerEjectMechaDronePacket.cs b/NebulaModel/Packets/Players/PlayerEjectMechaDronePacket.cs new file mode 100644 index 000000000..c35207c64 --- /dev/null +++ b/NebulaModel/Packets/Players/PlayerEjectMechaDronePacket.cs @@ -0,0 +1,26 @@ +namespace NebulaModel.Packets.Players; + +public class PlayerEjectMechaDronePacket +{ + public PlayerEjectMechaDronePacket() { } + + public PlayerEjectMechaDronePacket(ushort playerId, int planetId, int targetObjectId, + int next1ObjectId, int next2ObjectId, int next3ObjectId, int dronePriority) + { + PlayerId = playerId; + PlanetId = planetId; + TargetObjectId = targetObjectId; + Next1ObjectId = next1ObjectId; + Next2ObjectId = next2ObjectId; + Next3ObjectId = next3ObjectId; + DronePriority = dronePriority; + } + + public ushort PlayerId { get; set; } + public int PlanetId { get; set; } + public int TargetObjectId { get; set; } + public int Next1ObjectId { get; set; } + public int Next2ObjectId { get; set; } + public int Next3ObjectId { get; set; } + public int DronePriority { get; set; } +} diff --git a/NebulaNetwork/PacketProcessors/Players/PlayerEjectMechaDroneProcessor.cs b/NebulaNetwork/PacketProcessors/Players/PlayerEjectMechaDroneProcessor.cs new file mode 100644 index 000000000..4314ce4aa --- /dev/null +++ b/NebulaNetwork/PacketProcessors/Players/PlayerEjectMechaDroneProcessor.cs @@ -0,0 +1,23 @@ +#region + +using NebulaAPI.Packets; +using NebulaModel.Networking; +using NebulaModel.Packets; +using NebulaModel.Packets.Players; +using NebulaWorld; + +#endregion + +namespace NebulaNetwork.PacketProcessors.Players; + +[RegisterPacketProcessor] +public class PlayerEjectMechaDroneProcessor : PacketProcessor +{ + protected override void ProcessPacket(PlayerEjectMechaDronePacket packet, NebulaConnection conn) + { + var factory = GameMain.galaxy.PlanetById(packet.PlanetId)?.factory; + if (factory == null) return; + + Multiplayer.Session.Drones.EjectMechaDroneFromOtherPlayer(packet); + } +} diff --git a/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs b/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs new file mode 100644 index 000000000..5b88e9bcc --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs @@ -0,0 +1,28 @@ +#region + +using HarmonyLib; +using NebulaModel.Packets.Players; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(ConstructionModuleComponent))] +internal class ConstructionModuleComponent_Patch +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(ConstructionModuleComponent.EjectMechaDrone))] + public static void EjectMechaDrone_Prefix(PlanetFactory factory, Player player, int targetObjectId, + int next1ObjectId, int next2ObjectId, int next3ObjectId) + { + if (!Multiplayer.IsActive) return; + + // Notify other players for eject of mecha drone + var playerId = Multiplayer.Session.LocalPlayer.Id; + var planetId = factory.planetId; + var priority = player.mecha.constructionModule.dronePriority; + var packet = new PlayerEjectMechaDronePacket(playerId, planetId, targetObjectId, next1ObjectId, next2ObjectId, next3ObjectId, priority); + Multiplayer.Session.Network.SendPacketToLocalStar(packet); + } +} diff --git a/NebulaPatcher/Patches/Dynamic/ConstructionSystem_Patch.cs b/NebulaPatcher/Patches/Dynamic/ConstructionSystem_Patch.cs new file mode 100644 index 000000000..49c860756 --- /dev/null +++ b/NebulaPatcher/Patches/Dynamic/ConstructionSystem_Patch.cs @@ -0,0 +1,23 @@ +#region + +using HarmonyLib; +using NebulaWorld; + +#endregion + +namespace NebulaPatcher.Patches.Dynamic; + +[HarmonyPatch(typeof(ConstructionSystem))] +internal class ConstructionSystem_Patch +{ + [HarmonyPrefix] + [HarmonyPatch(nameof(ConstructionSystem.UpdateDrones))] + public static void UpdateDrones(ConstructionSystem __instance, ObjectRenderer[] renderers, bool sync_gpu_inst, float dt, long time) + { + if (!Multiplayer.IsActive) return; + + // Update remote drones from other players + var factory = __instance.factory; + Multiplayer.Session.Drones.UpdateDrones(factory, renderers, sync_gpu_inst, dt, time); + } +} diff --git a/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs b/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs index 6af842cdf..6b868c12e 100644 --- a/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs @@ -382,6 +382,10 @@ public static void LeaveStar_Prefix(GameData __instance) } if (!Multiplayer.Session.IsInLobby) { + if (Multiplayer.Session.IsClient) + { + Multiplayer.Session.Drones.ClearAllRemoteDrones(); + } Multiplayer.Session.Network.SendPacket(new PlayerUpdateLocalStarId(-1)); } } diff --git a/NebulaWorld/MultiplayerSession.cs b/NebulaWorld/MultiplayerSession.cs index 15f1f9c0e..a26dd0496 100644 --- a/NebulaWorld/MultiplayerSession.cs +++ b/NebulaWorld/MultiplayerSession.cs @@ -64,7 +64,7 @@ public MultiplayerSession(INetworkProvider networkProvider) public PowerTowerManager PowerTowers { get; set; } public BeltManager Belts { get; set; } public BuildToolManager BuildTools { get; set; } - private DroneManager Drones { get; set; } + public DroneManager Drones { get; set; } public GameDataHistoryManager History { get; set; } private GameStatesManager State { get; set; } public CourierManager Couriers { get; set; } diff --git a/NebulaWorld/Player/DroneManager.cs b/NebulaWorld/Player/DroneManager.cs index e49806bb0..4c82f3689 100644 --- a/NebulaWorld/Player/DroneManager.cs +++ b/NebulaWorld/Player/DroneManager.cs @@ -2,12 +2,12 @@ using System; using System.Collections.Generic; -using System.Linq; using NebulaAPI.DataStructures; using NebulaModel.DataStructures; using NebulaModel.Packets.Players; using UnityEngine; using Random = UnityEngine.Random; +#pragma warning disable IDE1006 // Naming Styles #endregion @@ -15,49 +15,59 @@ namespace NebulaWorld.Player; public class DroneManager : IDisposable { - private static Dictionary CachedPositions = []; - private static long lastCheckedTick = 0; + private readonly Dictionary cachedPositions = []; + private long lastCheckedTick = 0; + private readonly List crafts = []; + private readonly Stack craftRecyleIds = []; + private readonly DataPool drones = new(); public DroneManager() { - CachedPositions = []; lastCheckedTick = 0; } public void Dispose() { - CachedPositions = null; lastCheckedTick = 0; GC.SuppressFinalize(this); } - public static void EjectDronesOfOtherPlayer(ushort playerId, int planetId, int targetObjectId) + public void EjectMechaDroneFromOtherPlayer(PlayerEjectMechaDronePacket packet) { RefreshCachedPositions(); - var ejectPos = GetPlayerPosition(playerId); - ejectPos = ejectPos.normalized * (ejectPos.magnitude + 2.8f); - var factory = GameMain.galaxy.PlanetById(planetId).factory; - var targetPos = factory.constructionSystem._obj_hpos(targetObjectId, ref ejectPos); - var vector3 = ejectPos + ejectPos.normalized * 4.5f + + var ejectPos = GetPlayerEjectPosition(packet.PlayerId); + var factory = GameMain.galaxy.PlanetById(packet.PlanetId).factory; + var targetPos = factory.constructionSystem._obj_hpos(packet.TargetObjectId, ref ejectPos); + var initialVector = ejectPos + ejectPos.normalized * 4.5f + ((targetPos - ejectPos).normalized + Random.insideUnitSphere) * 1.5f; - - ref var ptr = - ref GameMain.mainPlayer.mecha.constructionModule.CreateDrone(factory, ejectPos, Quaternion.LookRotation(vector3), - Vector3.zero); + // Use custom CreateDrone to store in separate pool + ref var ptr = ref CreateDrone(factory, ejectPos, Quaternion.LookRotation(initialVector), Vector3.zero, packet.PlayerId); ptr.stage = 1; - ptr.targetObjectId = targetObjectId; + ptr.priority = packet.DronePriority; + ptr.targetObjectId = packet.TargetObjectId; + ptr.nextTarget1ObjectId = packet.Next1ObjectId; + ptr.nextTarget2ObjectId = packet.Next2ObjectId; + ptr.nextTarget3ObjectId = packet.Next3ObjectId; ptr.targetPos = targetPos; - ptr.initialVector = vector3; + ptr.initialVector = initialVector; ptr.progress = 0f; - ptr.priority = 1; - ptr.owner = playerId * - -1; // to prevent the ConstructionSystem_Transpiler.UpdateDrones_Transpiler() to remove them. Must be negative, positive ones are owned by battle bases. Store playerId in here. + ptr.owner = packet.PlanetId; // Use drone.owner field to store planetId + + if (packet.TargetObjectId > 0) // Repair + { + ref var entity = ref factory.entityPool[packet.TargetObjectId]; + if (entity.id != packet.TargetObjectId || entity.constructStatId == 0) + { + return; + } + factory.constructionSystem.constructStats.buffer[entity.constructStatId].repairerCount++; + } } - public static void RefreshCachedPositions() + public void RefreshCachedPositions() { - if (GameMain.gameTick - lastCheckedTick > 10) + if (GameMain.gameTick != lastCheckedTick) { lastCheckedTick = GameMain.gameTick; //CachedPositions.Clear(); @@ -67,23 +77,215 @@ public static void RefreshCachedPositions() // host needs it for all players since they can build on other planets too. foreach (var model in remotePlayersModels.Values) { + var playerPos = model.Movement.GetLastPosition().LocalPlanetPosition.ToVector3(); + var ejectPos = playerPos.normalized * (playerPos.magnitude + 2.8f); + var localPlanetId = model.Movement.localPlanetId; + // Cache players positions - if (!CachedPositions.ContainsKey(model.Movement.PlayerID)) + if (cachedPositions.TryGetValue(model.Movement.PlayerID, out var playerPosition)) { - CachedPositions.Add(model.Movement.PlayerID, new PlayerPosition(model.Movement.GetLastPosition().LocalPlanetPosition.ToVector3(), model.Movement.localPlanetId)); + playerPosition.Position = ejectPos; + playerPosition.PlanetId = localPlanetId; } else { - CachedPositions[model.Movement.PlayerID].Position = model.Movement.GetLastPosition().LocalPlanetPosition.ToVector3(); - CachedPositions[model.Movement.PlayerID].PlanetId = model.Movement.localPlanetId; + cachedPositions.Add(model.Movement.PlayerID, new PlayerPosition(ejectPos, model.Movement.localPlanetId)); } } } } } - public static Vector3 GetPlayerPosition(ushort playerId) + public Vector3 GetPlayerEjectPosition(ushort playerId) + { + return cachedPositions.TryGetValue(playerId, out var value) ? value.Position : GameMain.mainPlayer.position; + } + + public void ClearAllRemoteDrones() { - return CachedPositions.TryGetValue(playerId, out var value) ? value.Position : GameMain.mainPlayer.position; + crafts.Clear(); + craftRecyleIds.Clear(); + drones.Reset(); } + + public void UpdateDrones(PlanetFactory factory, ObjectRenderer[] renderers, bool sync_gpu_inst, float dt, long time) + { + // Mimic from ConstructionSystem.UpdateDrones + RefreshCachedPositions(); + var entityPool = factory.entityPool; + var constructionDroneSpeed = factory.gameData.history.constructionDroneSpeed; + var planetId = factory.planetId; + + for (var droneId = 1; droneId < drones.cursor; droneId++) + { + ref var ptr = ref drones.buffer[droneId]; + if (ptr.owner != planetId) //ptr.owner is planetId in the custom pool + { + continue; + } + var craftData = crafts[ptr.craftId]; + var playerId = (ushort)craftData.owner; + if (!cachedPositions.TryGetValue(playerId, out var playerPosition) || playerPosition.PlanetId != planetId) + { + // If the owner leave the planet, recycle the drone + RecycleDrone(factory, ref ptr, droneId); + continue; + } + + // Update drone stage and craft position + var flag2 = false; + var ejectPos = playerPosition.Position; + var meachEnerey = (double)float.MaxValue; // Dummy value for remote drones + var mecahEnergyChange = 0.0; + var result = ptr.InternalUpdate(ref craftData, factory, ref ejectPos, constructionDroneSpeed, dt, + ref meachEnerey, ref mecahEnergyChange, 0, 0, out _); + crafts[ptr.craftId] = craftData; + + if (ptr.stage == 3) // Hovering state? + { + if (ptr.targetObjectId > 0) // Repair entity + { + result = (factory.constructionSystem.Repair(ptr.targetObjectId, 1.0f, time) ? 1 : 0); // Assume energy ratio is 1.0f + if (result == 1) + { + ptr.targetObjectId = 0; + } + } + else if (result == 0) // Mod: Do not wait for prebuild to finished, just advance to the next target + { + result = 1; + } + } + if (result == 1 && ptr.stage == 4) + { + RecycleDrone(factory, ref ptr, droneId); + } + if (result != 0 && (ptr.stage == 2 || ptr.stage == 3 || ptr.stage == 4)) + { + ptr.movement--; + if (ptr.movement <= 0 || flag2) + { + ptr.movement = 0; + ptr.stage = 4; + ptr.targetObjectId = 0; + } + else if (ptr.nextTarget1ObjectId != 0) + { + ptr.stage = 2; + ptr.targetObjectId = ptr.nextTarget1ObjectId; + ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget1ObjectId); + ptr.nextTarget1ObjectId = 0; + } + else if (ptr.nextTarget2ObjectId != 0) + { + ptr.stage = 2; + ptr.targetObjectId = ptr.nextTarget2ObjectId; + ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget2ObjectId); + ptr.nextTarget2ObjectId = 0; + } + else if (ptr.nextTarget3ObjectId != 0) + { + ptr.stage = 2; + ptr.targetObjectId = ptr.nextTarget3ObjectId; + ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget3ObjectId); + ptr.nextTarget3ObjectId = 0; + } + else if (factory.constructionSystem.FindNextRepair(0, craftData.pos, out var foundPos, out var targetId)) + { + ptr.stage = 2; + ptr.targetObjectId = targetId; + ptr.targetPos = foundPos; + var buffer = factory.constructionSystem.constructStats.buffer; + var constructStatId = entityPool[targetId].constructStatId; + buffer[constructStatId].repairerCount++; + } + else + { + ptr.stage = 4; + ptr.targetObjectId = 0; + } + } + + if (sync_gpu_inst) + { + if (craftData.modelId > 0 && renderers[craftData.modelIndex] is DynamicRenderer dynamicRenderer) + { + var instPool = dynamicRenderer.instPool; + var modelId = craftData.modelId; + instPool[modelId].posx = (float)craftData.pos.x; + instPool[modelId].posy = (float)craftData.pos.y; + instPool[modelId].posz = (float)craftData.pos.z; + instPool[modelId].rotx = craftData.rot.x; + instPool[modelId].roty = craftData.rot.y; + instPool[modelId].rotz = craftData.rot.z; + instPool[modelId].rotw = craftData.rot.w; + dynamicRenderer.extraPool[craftData.modelId].x = ptr.stage; + } + } + } + } + + private ref DroneComponent CreateDrone(PlanetFactory factory, Vector3 pos, Quaternion rot, Vector3 vel, ushort playerId) + { + int craftId; + if (craftRecyleIds.Count > 0) + { + craftId = craftRecyleIds.Pop(); + } + else + { + crafts.Add(default); + craftId = crafts.Count - 1; + } + + var craftData = default(CraftData); + craftData.id = craftId; + craftData.protoId = 0; + craftData.modelIndex = 454; + craftData.astroId = factory.planetId; + craftData.owner = playerId; // Use craft.owner field to store playerId + craftData.port = 0; + craftData.prototype = EPrototype.ConstructionDrone; + craftData.dynamic = true; + craftData.isSpace = false; + craftData.pos = pos; + craftData.rot = rot; + craftData.vel = vel; + var planet = factory.planet; + if (planet.factoryLoaded || planet.factingCompletedStage >= 5) + { + craftData.modelId = GameMain.gpuiManager.AddModel(454, craftId, pos, rot, true); + } + crafts[craftId] = craftData; + + ref var drone = ref drones.Add(); + drone.craftId = craftId; + + return ref drone; + } + + private void RecycleDrone(PlanetFactory factory, ref DroneComponent dronePtr, int droneId) + { + if (dronePtr.targetObjectId > 0) + { + ref var entity = ref factory.entityPool[dronePtr.targetObjectId]; + if (entity.id == dronePtr.targetObjectId && entity.constructStatId > 0) + { + ref var constructStat = ref factory.constructionSystem.constructStats.buffer[entity.constructStatId]; + constructStat.repairerCount--; + if (constructStat.repairerCount < 0) constructStat.repairerCount = 0; + } + } + + var craftId = dronePtr.craftId; + var modelId = crafts[craftId].modelId; + if (modelId != 0 && GameMain.gpuiManager.activeFactory == factory) + { + GameMain.gpuiManager.RemoveModel(454, modelId, true); + } + crafts[craftId].SetEmpty(); + craftRecyleIds.Push(craftId); + drones.Remove(droneId); + } + } From 2ef04c65072178995a9ab4b3565d2b36a2d99502 Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:31:18 +0800 Subject: [PATCH 4/6] Refactor DroneManager.UpdateDrones for readability --- NebulaWorld/Player/DroneManager.cs | 163 +++++++++++++++-------------- 1 file changed, 86 insertions(+), 77 deletions(-) diff --git a/NebulaWorld/Player/DroneManager.cs b/NebulaWorld/Player/DroneManager.cs index 4c82f3689..59d0c18be 100644 --- a/NebulaWorld/Player/DroneManager.cs +++ b/NebulaWorld/Player/DroneManager.cs @@ -112,7 +112,6 @@ public void UpdateDrones(PlanetFactory factory, ObjectRenderer[] renderers, bool { // Mimic from ConstructionSystem.UpdateDrones RefreshCachedPositions(); - var entityPool = factory.entityPool; var constructionDroneSpeed = factory.gameData.history.constructionDroneSpeed; var planetId = factory.planetId; @@ -128,12 +127,11 @@ public void UpdateDrones(PlanetFactory factory, ObjectRenderer[] renderers, bool if (!cachedPositions.TryGetValue(playerId, out var playerPosition) || playerPosition.PlanetId != planetId) { // If the owner leave the planet, recycle the drone - RecycleDrone(factory, ref ptr, droneId); + RecycleDrone(factory, ref ptr); continue; } // Update drone stage and craft position - var flag2 = false; var ejectPos = playerPosition.Position; var meachEnerey = (double)float.MaxValue; // Dummy value for remote drones var mecahEnergyChange = 0.0; @@ -141,90 +139,102 @@ public void UpdateDrones(PlanetFactory factory, ObjectRenderer[] renderers, bool ref meachEnerey, ref mecahEnergyChange, 0, 0, out _); crafts[ptr.craftId] = craftData; - if (ptr.stage == 3) // Hovering state? + // Repair or find the next target + UpdateDroneStageAndTarget(factory, ref ptr, in craftData, result, time); + + if (sync_gpu_inst) { - if (ptr.targetObjectId > 0) // Repair entity - { - result = (factory.constructionSystem.Repair(ptr.targetObjectId, 1.0f, time) ? 1 : 0); // Assume energy ratio is 1.0f - if (result == 1) - { - ptr.targetObjectId = 0; - } - } - else if (result == 0) // Mod: Do not wait for prebuild to finished, just advance to the next target + UpdateGpuInstance(renderers, in craftData, ptr.stage); + } + } + } + + private void UpdateDroneStageAndTarget(PlanetFactory factory, ref DroneComponent ptr, in CraftData craftData, int result, long time) + { + // DroneComponent.stage [1]:eject from player [2]:going to target [3]:on target [4]:back to player + if (ptr.stage == 3) + { + if (ptr.targetObjectId > 0) // Repair entity + { + result = (factory.constructionSystem.Repair(ptr.targetObjectId, 1.0f, time) ? 1 : 0); // Assume energy ratio is 1.0f + if (result == 1) { - result = 1; + ptr.targetObjectId = 0; } } - if (result == 1 && ptr.stage == 4) + else if (result == 0) // Mod: Do not wait for prebuild to finished, just advance to the next target { - RecycleDrone(factory, ref ptr, droneId); + result = 1; } - if (result != 0 && (ptr.stage == 2 || ptr.stage == 3 || ptr.stage == 4)) + } + if (result == 1 && ptr.stage == 4) + { + RecycleDrone(factory, ref ptr); + } + if (result != 0 && (ptr.stage == 2 || ptr.stage == 3 || ptr.stage == 4)) + { + ptr.movement--; + if (ptr.movement <= 0) { - ptr.movement--; - if (ptr.movement <= 0 || flag2) - { - ptr.movement = 0; - ptr.stage = 4; - ptr.targetObjectId = 0; - } - else if (ptr.nextTarget1ObjectId != 0) - { - ptr.stage = 2; - ptr.targetObjectId = ptr.nextTarget1ObjectId; - ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget1ObjectId); - ptr.nextTarget1ObjectId = 0; - } - else if (ptr.nextTarget2ObjectId != 0) - { - ptr.stage = 2; - ptr.targetObjectId = ptr.nextTarget2ObjectId; - ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget2ObjectId); - ptr.nextTarget2ObjectId = 0; - } - else if (ptr.nextTarget3ObjectId != 0) - { - ptr.stage = 2; - ptr.targetObjectId = ptr.nextTarget3ObjectId; - ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget3ObjectId); - ptr.nextTarget3ObjectId = 0; - } - else if (factory.constructionSystem.FindNextRepair(0, craftData.pos, out var foundPos, out var targetId)) - { - ptr.stage = 2; - ptr.targetObjectId = targetId; - ptr.targetPos = foundPos; - var buffer = factory.constructionSystem.constructStats.buffer; - var constructStatId = entityPool[targetId].constructStatId; - buffer[constructStatId].repairerCount++; - } - else - { - ptr.stage = 4; - ptr.targetObjectId = 0; - } + ptr.movement = 0; + ptr.stage = 4; + ptr.targetObjectId = 0; } - - if (sync_gpu_inst) + else if (ptr.nextTarget1ObjectId != 0) { - if (craftData.modelId > 0 && renderers[craftData.modelIndex] is DynamicRenderer dynamicRenderer) - { - var instPool = dynamicRenderer.instPool; - var modelId = craftData.modelId; - instPool[modelId].posx = (float)craftData.pos.x; - instPool[modelId].posy = (float)craftData.pos.y; - instPool[modelId].posz = (float)craftData.pos.z; - instPool[modelId].rotx = craftData.rot.x; - instPool[modelId].roty = craftData.rot.y; - instPool[modelId].rotz = craftData.rot.z; - instPool[modelId].rotw = craftData.rot.w; - dynamicRenderer.extraPool[craftData.modelId].x = ptr.stage; - } + ptr.stage = 2; + ptr.targetObjectId = ptr.nextTarget1ObjectId; + ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget1ObjectId); + ptr.nextTarget1ObjectId = 0; + } + else if (ptr.nextTarget2ObjectId != 0) + { + ptr.stage = 2; + ptr.targetObjectId = ptr.nextTarget2ObjectId; + ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget2ObjectId); + ptr.nextTarget2ObjectId = 0; + } + else if (ptr.nextTarget3ObjectId != 0) + { + ptr.stage = 2; + ptr.targetObjectId = ptr.nextTarget3ObjectId; + ptr.targetPos = factory.constructionSystem._obj_hpos(ptr.nextTarget3ObjectId); + ptr.nextTarget3ObjectId = 0; + } + else if (factory.constructionSystem.FindNextRepair(0, craftData.pos, out var foundPos, out var targetId)) + { + ptr.stage = 2; + ptr.targetObjectId = targetId; + ptr.targetPos = foundPos; + var buffer = factory.constructionSystem.constructStats.buffer; + var constructStatId = factory.entityPool[targetId].constructStatId; + buffer[constructStatId].repairerCount++; + } + else + { + ptr.stage = 4; + ptr.targetObjectId = 0; } } } + private static void UpdateGpuInstance(ObjectRenderer[] renderers, in CraftData craftData, int droneStage) + { + if (craftData.modelId > 0 && renderers[craftData.modelIndex] is DynamicRenderer dynamicRenderer) + { + var instPool = dynamicRenderer.instPool; + var modelId = craftData.modelId; + instPool[modelId].posx = (float)craftData.pos.x; + instPool[modelId].posy = (float)craftData.pos.y; + instPool[modelId].posz = (float)craftData.pos.z; + instPool[modelId].rotx = craftData.rot.x; + instPool[modelId].roty = craftData.rot.y; + instPool[modelId].rotz = craftData.rot.z; + instPool[modelId].rotw = craftData.rot.w; + dynamicRenderer.extraPool[craftData.modelId].x = droneStage; + } + } + private ref DroneComponent CreateDrone(PlanetFactory factory, Vector3 pos, Quaternion rot, Vector3 vel, ushort playerId) { int craftId; @@ -264,7 +274,7 @@ private ref DroneComponent CreateDrone(PlanetFactory factory, Vector3 pos, Quate return ref drone; } - private void RecycleDrone(PlanetFactory factory, ref DroneComponent dronePtr, int droneId) + private void RecycleDrone(PlanetFactory factory, ref DroneComponent dronePtr) { if (dronePtr.targetObjectId > 0) { @@ -285,7 +295,6 @@ private void RecycleDrone(PlanetFactory factory, ref DroneComponent dronePtr, in } crafts[craftId].SetEmpty(); craftRecyleIds.Push(craftId); - drones.Remove(droneId); + drones.Remove(dronePtr.id); } - } From 236aa38865eb07c0afd87cc43e35f92001db28c2 Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:47:49 +0800 Subject: [PATCH 5/6] RefreshCachedPositions only if there are remote drones --- NebulaWorld/Player/DroneManager.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/NebulaWorld/Player/DroneManager.cs b/NebulaWorld/Player/DroneManager.cs index 59d0c18be..de6ba54fa 100644 --- a/NebulaWorld/Player/DroneManager.cs +++ b/NebulaWorld/Player/DroneManager.cs @@ -23,12 +23,10 @@ public class DroneManager : IDisposable public DroneManager() { - lastCheckedTick = 0; } public void Dispose() { - lastCheckedTick = 0; GC.SuppressFinalize(this); } @@ -110,8 +108,7 @@ public void ClearAllRemoteDrones() public void UpdateDrones(PlanetFactory factory, ObjectRenderer[] renderers, bool sync_gpu_inst, float dt, long time) { - // Mimic from ConstructionSystem.UpdateDrones - RefreshCachedPositions(); + // Mimic from ConstructionSystem.UpdateDrones var constructionDroneSpeed = factory.gameData.history.constructionDroneSpeed; var planetId = factory.planetId; @@ -124,6 +121,7 @@ public void UpdateDrones(PlanetFactory factory, ObjectRenderer[] renderers, bool } var craftData = crafts[ptr.craftId]; var playerId = (ushort)craftData.owner; + RefreshCachedPositions(); if (!cachedPositions.TryGetValue(playerId, out var playerPosition) || playerPosition.PlanetId != planetId) { // If the owner leave the planet, recycle the drone From ac018e6cc2fd69e1c83d8c8cfc817b0e6ca863d0 Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Sun, 18 Feb 2024 07:31:54 +0800 Subject: [PATCH 6/6] Fix merge conflict with ongoing branches --- NebulaPatcher/Patches/Dynamic/GameData_Patch.cs | 5 +---- NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs b/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs index 6b868c12e..50696de68 100644 --- a/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/GameData_Patch.cs @@ -363,6 +363,7 @@ public static void LeaveStar_Prefix(GameData __instance) { return; } + Multiplayer.Session.Drones.ClearAllRemoteDrones(); using (Multiplayer.Session.Ships.PatchLockILS.On()) { for (var i = 0; i < __instance.localStar.planetCount; i++) @@ -382,10 +383,6 @@ public static void LeaveStar_Prefix(GameData __instance) } if (!Multiplayer.Session.IsInLobby) { - if (Multiplayer.Session.IsClient) - { - Multiplayer.Session.Drones.ClearAllRemoteDrones(); - } Multiplayer.Session.Network.SendPacket(new PlayerUpdateLocalStarId(-1)); } } diff --git a/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs b/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs index 61ac939bd..de0b7cad7 100644 --- a/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/PlanetFactory_Patch.cs @@ -22,6 +22,7 @@ using NebulaModel.Packets.Planet; using NebulaWorld; using NebulaWorld.Factory; +using NebulaWorld.Player; #endregion