From c1946b7e84220b0a950a4488c9841876a907c3ee Mon Sep 17 00:00:00 2001 From: starfish <50672801+starfi5h@users.noreply.github.com> Date: Sat, 23 Mar 2024 11:41:42 +0800 Subject: [PATCH] Add IsClosestPlayer check before sending out mecha drones --- .../ConstructionModuleComponent_Patch.cs | 14 +++++ .../ConstructionSystem_Transpiler.cs | 63 +++++++++++++++++++ NebulaWorld/Player/DroneManager.cs | 26 ++++++++ 3 files changed, 103 insertions(+) create mode 100644 NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs diff --git a/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs b/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs index 5b88e9bcc..b84ad05a8 100644 --- a/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs +++ b/NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs @@ -3,6 +3,7 @@ using HarmonyLib; using NebulaModel.Packets.Players; using NebulaWorld; +using NebulaWorld.Player; #endregion @@ -25,4 +26,17 @@ public static void EjectMechaDrone_Prefix(PlanetFactory factory, Player player, var packet = new PlayerEjectMechaDronePacket(playerId, planetId, targetObjectId, next1ObjectId, next2ObjectId, next3ObjectId, priority); Multiplayer.Session.Network.SendPacketToLocalStar(packet); } + + [HarmonyPrefix] + [HarmonyPatch(nameof(ConstructionModuleComponent.InsertTmpBuildTarget))] + public static bool InsertTmpBuildTarget_Prefix(ConstructionModuleComponent __instance, int objectId, float value) + { + if (__instance.entityId != 0 || value < DroneManager.MinSqrDistance) return true; // BAB, or distance is very close + if (!Multiplayer.IsActive) return true; + + // Only send out mecha drones if local player is the closest player to the target prebuild + if (GameMain.localPlanet == null) return true; + var sqrDistToOtherPlayer = Multiplayer.Session.Drones.GetClosestRemotePlayerSqrDistance(GameMain.localPlanet.factory.prebuildPool[objectId].pos); + return value <= sqrDistToOtherPlayer; + } } diff --git a/NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs b/NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs new file mode 100644 index 000000000..5d588f021 --- /dev/null +++ b/NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs @@ -0,0 +1,63 @@ +#region + +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using NebulaModel.Logger; +using NebulaModel.Packets.Factory; +using NebulaWorld; +using NebulaWorld.Player; +using UnityEngine; + +#endregion + +namespace NebulaPatcher.Patches.Transpilers; + +[HarmonyPatch(typeof(ConstructionSystem))] +internal class ConstructionSystem_Transpiler +{ + [HarmonyTranspiler] + [HarmonyPatch(nameof(ConstructionSystem.AddBuildTargetToModules))] + public static IEnumerable AddBuildTargetToModules_Transpiler(IEnumerable instructions) + { + try + { + /* Sync Prebuild.itemRequired changes by player, insert local method call after player.package.TakeTailItems + Replace: if (num8 <= num) { this.player.mecha.constructionModule.InsertBuildTarget ... } + With: if (num8 <= num && IsClosestPlayer(ref pos)) { this.player.mecha.constructionModule.InsertBuildTarget ... } + */ + + var codeMatcher = new CodeMatcher(instructions) + .MatchForward(true, + new CodeMatch(OpCodes.Ldloc_S), + new CodeMatch(OpCodes.Ldloc_0), + new CodeMatch(OpCodes.Bgt_Un) + ); + Log.Info(codeMatcher.IsValid); + var sqrDist = codeMatcher.InstructionAt(-2).operand; + var skipLabel = codeMatcher.Operand; + codeMatcher.Advance(1) + .Insert( + new CodeInstruction(OpCodes.Ldloc_S, sqrDist), + new CodeInstruction(OpCodes.Ldarg_2), //ref Vector3 pos + new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(ConstructionSystem_Transpiler), nameof(IsClosestPlayer))), + new CodeInstruction(OpCodes.Brfalse_S, skipLabel) + ); + + return codeMatcher.InstructionEnumeration(); + } + catch (System.Exception e) + { + Log.Error("Transpiler ConstructionSystem.AddBuildTargetToModules failed."); + Log.Error(e); + return instructions; + } + } + + static bool IsClosestPlayer(float sqrDist, ref Vector3 pos) + { + if (!Multiplayer.IsActive || sqrDist < DroneManager.MinSqrDistance) return true; + return sqrDist <= Multiplayer.Session.Drones.GetClosestRemotePlayerSqrDistance(pos); + } +} diff --git a/NebulaWorld/Player/DroneManager.cs b/NebulaWorld/Player/DroneManager.cs index de6ba54fa..ff0a77717 100644 --- a/NebulaWorld/Player/DroneManager.cs +++ b/NebulaWorld/Player/DroneManager.cs @@ -15,7 +15,11 @@ namespace NebulaWorld.Player; public class DroneManager : IDisposable { + public const float MinSqrDistance = 225.0f; + private readonly Dictionary cachedPositions = []; + private Vector3[] localPlayerPos = new Vector3[2]; + private int localPlayerCount = 0; private long lastCheckedTick = 0; private readonly List crafts = []; private readonly Stack craftRecyleIds = []; @@ -68,6 +72,8 @@ public void RefreshCachedPositions() if (GameMain.gameTick != lastCheckedTick) { lastCheckedTick = GameMain.gameTick; + localPlayerCount = 0; + var currentLocalPlanetId = GameMain.localPlanet?.id ?? int.MinValue; //CachedPositions.Clear(); using (Multiplayer.Session.World.GetRemotePlayersModels(out var remotePlayersModels)) @@ -89,11 +95,31 @@ public void RefreshCachedPositions() { cachedPositions.Add(model.Movement.PlayerID, new PlayerPosition(ejectPos, model.Movement.localPlanetId)); } + + if (currentLocalPlanetId != localPlanetId) continue; + if (localPlayerCount >= localPlayerPos.Length) + { + var newArray = new Vector3[localPlayerPos.Length * 2]; + Array.Copy(localPlayerPos, newArray, localPlayerPos.Length); + localPlayerPos = newArray; + } + localPlayerPos[localPlayerCount++] = playerPos; } } } } + public float GetClosestRemotePlayerSqrDistance(Vector3 pos) + { + var result = float.MaxValue; + for (var i = 0; i < localPlayerCount; i++) + { + var sqrMagnitude = (pos - localPlayerPos[i]).sqrMagnitude; + if (sqrMagnitude < result) result = sqrMagnitude; + } + return result; + } + public Vector3 GetPlayerEjectPosition(ushort playerId) { return cachedPositions.TryGetValue(playerId, out var value) ? value.Position : GameMain.mainPlayer.position;