From 476d6382da43802cce88c063048ddd616114dd75 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Wed, 11 Aug 2021 17:50:14 -0400 Subject: [PATCH] Add client-side terrain replacement when terrain add is rejected --- CLre/API/App/ClientEngines.cs | 9 - CLre/API/Characters/Character.cs | 3 - CLre/API/Engines/FrontEndEngines.cs | 16 +- CLre/API/Engines/GameEngines.cs | 8 +- CLre/API/Synergy/ClientHandshakeEngine.cs | 9 +- CLre/API/Synergy/ClientMessagingEngine.cs | 3 - .../SerializedCLreTerrainModifyRejection.cs | 75 ++++++ CLre/API/Utility/Reflection.cs | 7 + CLre/CLre.cs | 7 +- CLre/Fixes/TerrainModifyReset.cs | 244 ++++++++++++++++++ .../SerializedCLreTerrainModifyRejection.cs | 27 +- CLre_server/API/Tools/AccessToolsWarnings.cs | 16 ++ CLre_server/API/Utility/Reflection.cs | 5 +- .../TerrainModificationExclusionZone.cs | 43 ++- 14 files changed, 427 insertions(+), 45 deletions(-) create mode 100644 CLre/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs create mode 100644 CLre/Fixes/TerrainModifyReset.cs diff --git a/CLre/API/App/ClientEngines.cs b/CLre/API/App/ClientEngines.cs index b699fad..173df17 100644 --- a/CLre/API/App/ClientEngines.cs +++ b/CLre/API/App/ClientEngines.cs @@ -14,9 +14,6 @@ namespace CLre.API.App if (gameEngineReady != null) gameEngineReady(this, new GameReady { }); } - public override IEntitiesDB entitiesDB { get; set; } - public override IEntityFactory entityFactory { get; set; } - public void OnFrameworkInitialized() { // TODO framework init event @@ -33,9 +30,6 @@ namespace CLre.API.App { } - public override IEntitiesDB entitiesDB { get; set; } - public override IEntityFactory entityFactory { get; set; } - public void OnFrameworkInitialized() { if (gameFrameworkReady != null) gameFrameworkReady(this, new GameReady { }); @@ -55,8 +49,5 @@ namespace CLre.API.App { if (menuEngineReady != null) menuEngineReady(this, new MenuReady { }); } - - public override IEntitiesDB entitiesDB { get; set; } - public override IEntityFactory entityFactory { get; set; } } } \ No newline at end of file diff --git a/CLre/API/Characters/Character.cs b/CLre/API/Characters/Character.cs index a16c05b..02e83e1 100644 --- a/CLre/API/Characters/Character.cs +++ b/CLre/API/Characters/Character.cs @@ -54,9 +54,6 @@ namespace CLre.API.Characters { } - public override IEntitiesDB entitiesDB { get; set; } - public override IEntityFactory entityFactory { get; set; } - public bool GetGodModeRequested() { if (entitiesDB == null) return false; diff --git a/CLre/API/Engines/FrontEndEngines.cs b/CLre/API/Engines/FrontEndEngines.cs index f066e6b..cd68bd1 100644 --- a/CLre/API/Engines/FrontEndEngines.cs +++ b/CLre/API/Engines/FrontEndEngines.cs @@ -21,8 +21,8 @@ namespace CLre.API.Engines } public abstract void Ready(); - public abstract IEntitiesDB entitiesDB { get; set; } - public abstract IEntityFactory entityFactory { get; set; } + public IEntitiesDB entitiesDB { get; set; } + public IEntityFactory entityFactory { get; set; } } /// @@ -41,8 +41,8 @@ namespace CLre.API.Engines } public abstract void Ready(); - public abstract IEntitiesDB entitiesDB { get; set; } - public abstract IEntityFactory entityFactory { get; set; } + public IEntitiesDB entitiesDB { get; set; } + public IEntityFactory entityFactory { get; set; } } /// @@ -60,8 +60,8 @@ namespace CLre.API.Engines } public abstract void Ready(); - public abstract IEntitiesDB entitiesDB { get; set; } - public abstract IEntityFactory entityFactory { get; set; } + public IEntitiesDB entitiesDB { get; set; } + public IEntityFactory entityFactory { get; set; } } /// @@ -79,8 +79,8 @@ namespace CLre.API.Engines } public abstract void Ready(); - public abstract IEntitiesDB entitiesDB { get; set; } - public abstract IEntityFactory entityFactory { get; set; } + public IEntitiesDB entitiesDB { get; set; } + public IEntityFactory entityFactory { get; set; } } [HarmonyPatch(typeof(FrontEnd.MainFrontEnd), "BuildEngines")] diff --git a/CLre/API/Engines/GameEngines.cs b/CLre/API/Engines/GameEngines.cs index ebb26d2..7a5a6a3 100644 --- a/CLre/API/Engines/GameEngines.cs +++ b/CLre/API/Engines/GameEngines.cs @@ -12,8 +12,8 @@ namespace CLre.API.Engines } public abstract void Ready(); - public abstract IEntitiesDB entitiesDB { get; set; } - public abstract IEntityFactory entityFactory { get; set; } + public IEntitiesDB entitiesDB { get; set; } + public IEntityFactory entityFactory { get; set; } } public abstract class GameObsoleteEnginePostBuild : ICLreEngine @@ -24,8 +24,8 @@ namespace CLre.API.Engines } public abstract void Ready(); - public abstract IEntitiesDB entitiesDB { get; set; } - public abstract IEntityFactory entityFactory { get; set; } + public IEntitiesDB entitiesDB { get; set; } + public IEntityFactory entityFactory { get; set; } } [HarmonyPatch(typeof(GameFramework.MainLevel), "BuildDeprecatedEngines")] diff --git a/CLre/API/Synergy/ClientHandshakeEngine.cs b/CLre/API/Synergy/ClientHandshakeEngine.cs index 06a4da6..545afa2 100644 --- a/CLre/API/Synergy/ClientHandshakeEngine.cs +++ b/CLre/API/Synergy/ClientHandshakeEngine.cs @@ -3,6 +3,7 @@ using CLre.API.Engines; using GameNetworkLayer.Shared; using HarmonyLib; using Svelto.ECS; +using VoxelFarm.GameServer; namespace CLre.API.Synergy { @@ -40,9 +41,6 @@ namespace CLre.API.Synergy Utility.Logging.MetaLog($"Received CLre handshake! {p}"); } - public override IEntitiesDB entitiesDB { get; set; } - public override IEntityFactory entityFactory { get; set; } - public IEnumerator Sender(SerializedCLreHandshake payload) { yield return null; @@ -72,12 +70,15 @@ namespace CLre.API.Synergy internal static object netMessageListener; internal static object netMessageSender; + + internal static TerrainModelClientServer tmcs; [HarmonyPostfix] - public static void AfterMethodCall(object ____netMessageListener, object ____netMessageSender) + public static void AfterMethodCall(object ____netMessageListener, object ____netMessageSender, TerrainModelClientServer ____terrainModelServerPrediction) { netMessageListener = ____netMessageListener; netMessageSender = ____netMessageSender; + tmcs = ____terrainModelServerPrediction; } } } \ No newline at end of file diff --git a/CLre/API/Synergy/ClientMessagingEngine.cs b/CLre/API/Synergy/ClientMessagingEngine.cs index e240b7c..1bbb632 100644 --- a/CLre/API/Synergy/ClientMessagingEngine.cs +++ b/CLre/API/Synergy/ClientMessagingEngine.cs @@ -54,9 +54,6 @@ namespace CLre.API.Synergy }); } - public override IEntitiesDB entitiesDB { get; set; } - public override IEntityFactory entityFactory { get; set; } - public ClientMessagingEngine(): base() { App.Client.GameJoin += (_, __) => { MessageSender().Run(); }; diff --git a/CLre/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs b/CLre/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs new file mode 100644 index 0000000..20c127d --- /dev/null +++ b/CLre/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; +using Game.Handhelds; +using NetworkFramework.Shared; +using UnityEngine; + +namespace CLre.API.Synergy.Tweaks +{ + public struct SerializedCLreTerrainModifyRejection: ISerializedNetData + { + public RejectionFlag Flags; + + public uint resourceId; + + public Vector3 hit; + + public string toolKey; + + public ToolModeType toolMode; + + public byte[] Serialize() + { + using (MemoryStream stream = new MemoryStream()) + { + using (BinaryWriter writer = new BinaryWriter(stream)) + { + writer.Write((byte)Flags); + writer.Write(resourceId); + writer.Write(hit.x); + writer.Write(hit.y); + writer.Write(hit.z); + writer.Write(toolKey); + writer.Write((byte)toolMode); + return stream.ToArray(); + } + } + } + + public void Deserialize(byte[] data) + { + using (MemoryStream stream = new MemoryStream(data)) + { + using (BinaryReader reader = new BinaryReader(stream)) + { + Flags = (RejectionFlag)reader.ReadByte(); + resourceId = reader.ReadUInt32(); + float x = reader.ReadSingle(); + float y = reader.ReadSingle(); + float z = reader.ReadSingle(); + hit = new Vector3(x, y, z); + toolKey = reader.ReadString(); + toolMode = (ToolModeType)reader.ReadByte(); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Ok() + { + return (Flags & RejectionFlag.Rejection) == RejectionFlag.None; + } + } + + [Flags] + public enum RejectionFlag : byte + { + None = 0, + Rejection = 1, + Proximity = 1 << 1, + Permission = 1 << 2, + AccountNotFound = 1 << 3, + InitError = 1 << 4, + } +} diff --git a/CLre/API/Utility/Reflection.cs b/CLre/API/Utility/Reflection.cs index 6c2d49b..7ebf278 100644 --- a/CLre/API/Utility/Reflection.cs +++ b/CLre/API/Utility/Reflection.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.Reflection; +using Game.DataLoader; using GameNetworkLayer.Client; using GameNetworkLayer.Shared; using HarmonyLib; using NetworkFramework.Shared; +using OpenCVForUnity; using Svelto.DataStructures; using Svelto.ECS; @@ -34,6 +37,10 @@ namespace CLre.API.Utility public delegate void INetMsgClientListener_RegisterListener(NetworkDispatcherCode code, NetCBClient proc) where T: struct, ISerializedNetData; + public delegate void DeprecatedDispatcher_Dispatch(ref T value); + + public delegate Dictionary IDataDB_GetValues(); + // useful reflection functions public static TFuncProto BuildDelegate(MethodInfo method) where TFuncProto : Delegate { diff --git a/CLre/CLre.cs b/CLre/CLre.cs index 9ce761a..cf90897 100644 --- a/CLre/CLre.cs +++ b/CLre/CLre.cs @@ -51,6 +51,7 @@ namespace CLre Fixes.InitLogSooner.Init(); Fixes.MiniScreenHelper.Init(); Fixes.UnderStructureCollider.Init(); + Fixes.TerrainModifyReset.Init(); // API init API.Synergy.ClientHandshakeEngine.Init(); @@ -106,7 +107,11 @@ namespace CLre API.App.Client.MenuReady += (_, __) => { API.Utility.Logging.MetaLog("Menu engine ready event fired!"); }; API.App.Client.GameReady += (_, __) => { API.Utility.Logging.MetaLog("Game engine ready event fired!"); }; - API.App.Client.GameFrameworkReady += (_, __) => { API.Utility.Logging.MetaLog("Game framework ready event fired!"); }; + API.App.Client.GameFrameworkReady += (_, __) => + { + API.Utility.Logging.MetaLog("Game framework ready event fired!"); + API.Utility.Logging.MetaLog($"PhotonChat Connection Protocol: {PhotonChatUI.Chat.Instance.ConnectionProtocol}"); + }; API.App.Client.GameFrameworkExit += (_, __) => { API.Utility.Logging.MetaLog("Game framework exit event fired!"); }; Character c = Character.Local(); diff --git a/CLre/Fixes/TerrainModifyReset.cs b/CLre/Fixes/TerrainModifyReset.cs new file mode 100644 index 0000000..9292eb8 --- /dev/null +++ b/CLre/Fixes/TerrainModifyReset.cs @@ -0,0 +1,244 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using CLre.API.Synergy; +using CLre.API.Utility; +using Game.DataLoader; +using Game.Handhelds; +using GameNetworkLayer.Shared; +using HarmonyLib; +using NetworkFramework.Shared; +using Svelto.ECS; +using UnityEngine; +using VoxelFarm.GameServer; + +namespace CLre.Fixes +{ + [Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.Initialiser, id = 8)] + public class TerrainModifyReset + { + private static TerrainModifyResetEngine _tmrEngine = null; + + public static void Init() + { + _tmrEngine = new TerrainModifyResetEngine(); + } + } + + [Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.Workaround, id = 8)] + public class TerrainModifyResetEngine : API.Engines.GameObsoleteEnginePostBuild, IDataAccess + { + private Reflection.INetMsgClientListener_RegisterListener _registerListener; + + public override void Ready() + { + _registerListener = + Reflection.MethodAsDelegate>( + "GameNetworkLayer.Client.NetMessageClientListener:RegisterListener", + generics: new [] {typeof(API.Synergy.Tweaks.SerializedCLreTerrainModifyRejection)}, + instance: MainLevel_BuildClasses_Patch.netMessageListener); + _registerListener(NetworkDispatcherCode.TerrainModificationFailed, OnMessageReceived); + } + + private void OnMessageReceived(ref API.Synergy.Tweaks.SerializedCLreTerrainModifyRejection data) + { + if (!data.Ok()) + { + // reset terrain visuals + // TODO optimise +#if DEBUG + API.Utility.Logging.MetaLog($"data.resourceId: {data.resourceId}"); +#endif + AddTerrain(ref data); + + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void GetTerrainRelativePosition(ref Vector3 pos) + { + // This uses decompiled code from VoxelFarm.Shared.VoxelFarmGameUtils:GetTerrainRelativePosition + // there's no point in calling that simple function when I have to jump through hoops with reflection + // + // there's also nothing particularly unique (ie copyrightable) about this code, + // so the lawyers can suck it (also suing a benevolent project is a shitty move) + pos.x /= 0.083333336f; + pos.y /= 0.041666668f; + pos.z /= 0.083333336f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void AddTerrain(ref API.Synergy.Tweaks.SerializedCLreTerrainModifyRejection data) + { + // This uses decompiled code from VoxelFarm.GameServer.TerrainModelClientServer:AddTerrain + // there's no point in calling that simple function when I have to jump through hoops with reflection + // to supply the parameters to it + // + // Also this is not unique functionality, and comes from the logic of how the placement modes work + switch (data.toolMode) + { + case ToolModeType.Block: + MainLevel_BuildClasses_Patch.tmcs.AddDisc(0, data.hit, (int)data.resourceId, 5, 5); + break; + case ToolModeType.Disc: + MainLevel_BuildClasses_Patch.tmcs.AddDisc(0, data.hit, (int)data.resourceId, 1, 5); + break; + case ToolModeType.Voxel: + MainLevel_BuildClasses_Patch.tmcs.AddSingleVoxel(0, data.hit, (int)data.resourceId); + break; + } + } + + public IDataDB dataDB { get; set; } + } + + [Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.HarmonyPatch, id = 8)] + [HarmonyPatch] + class TerrainPendingModificationEngineClient_Add_Patch + { + internal static object _terrainModifyInputNode = null; + + [HarmonyPrefix] + public static void BeforeMethodCall(object node) + { +#if DEBUG + API.Utility.Logging.MetaLog("Intercepting VoxelFarm.Client.TerrainPendingModificationEngineClient:Add()"); +#endif + _terrainModifyInputNode = node; + } + + [HarmonyTargetMethod] + public static MethodBase Target() + { + return AccessTools.Method("VoxelFarm.Client.TerrainPendingModificationEngineClient:Add", new []{ AccessTools.TypeByName("VoxelFarm.Shared.TerrainModifyInputNode")}); + } + } + + /*[Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.HarmonyPatch, id = 8)] + [HarmonyPatch] + class TerrainPendingModificationEngineClient_OnBlockRemoved_Patch + { + [HarmonyPrefix] + public static bool BeforeMethodCall(int sender, ref ISerializedNetData terrainModifyInputData) + { +#if DEBUG + API.Utility.Logging.MetaLog($"Intercepting VoxelFarm.Client.TerrainPendingModificationEngineClient:OnBlockRemoved({sender}, {terrainModifyInputData})"); +#endif + return false; + } + + [HarmonyTargetMethod] + public static MethodBase Target() + { + return AccessTools.Method("VoxelFarm.Client.TerrainPendingModificationEngineClient:OnBlockRemoved"); + } + }*/ + + [Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.Debug, id = 8)] + [HarmonyPatch] + class SpadeEngine_FinishDigging_Patch + { + internal static int CurrentMaterialId = 0; + [HarmonyPrefix] + public static void BeforeMethodCall(object toolNode, int ____currentMaterialId) + { +#if DEBUG + API.Utility.Logging.MetaLog($"Intercepting Game.Handhelds.SpadeEngine:FinishDigging:GetTerrainMaterial(...) material:{____currentMaterialId}"); +#endif + CurrentMaterialId = ____currentMaterialId; + } + + [HarmonyTargetMethod] + public static MethodBase Target() + { + return AccessTools.Method("Game.Handhelds.SpadeEngine:FinishDigging"); + } + } + + [Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.Debug, id = 8)] + [HarmonyPatch] + class TerrainModifyInputData_InjectValues_Patch + { + [HarmonyPrefix] + public static void BeforeMethodCall(ref uint resourceId) + { +#if DEBUG + API.Utility.Logging.MetaLog($"VoxelFarm.Shared.TerrainModifyInputData:InjectValues({resourceId}, ...)"); +#endif + if (resourceId == 0) resourceId = (uint) SpadeEngine_FinishDigging_Patch.CurrentMaterialId; + } + + [HarmonyTargetMethod] + public static MethodBase Target() + { + return AccessTools.Method("VoxelFarm.Shared.TerrainModifyInputData:InjectValues"); + } + } + + [Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.HarmonyPatch, id = 8)] + [HarmonyPatch] + class NetMessageClientListener_RegisterListener_Patch + { + [HarmonyPrefix] + public static bool BeforeMethodCall(NetworkDispatcherCode code) + { +#if DEBUG + API.Utility.Logging.MetaLog($"Intercepting GameNetworkLayer.Client.NetMessageClientListener:RegisterListener({code}, ...)"); +#endif + // don't allow for standard TerrainModificationFailed listener to be registered + // because it's a different type and that's illegal + return code != NetworkDispatcherCode.TerrainModificationFailed; + } + + [HarmonyTargetMethod] + public static MethodBase Target() + { + return AccessTools.Method("GameNetworkLayer.Client.NetMessageClientListener:RegisterListener", generics: new []{ typeof(SerializedEmptyNetData)}); + } + } + + // this disables terrain destruction + /*[Bugfix(name = "TerrainModificationFailedHandler", + description = "Actually handle TerrainModificationFailed network messages", + more = "https://trello.com/c/Pq5lcB1p/23-moderation-tools", + component = BugfixType.Debug, id = 8)] + [HarmonyPatch] + class TerrainModificationEngineServer_RemoveTerrainInput_Patch + { + [HarmonyPrefix] + public static bool BeforeMethodCall(int senderPlayerId, ref ISerializedNetData data) + { +#if DEBUG + API.Utility.Logging.MetaLog($"Intercepting client-side GameServer.VoxelFarm.Server.TerrainModificationEngineServer:RemoveTerrainInput({senderPlayerId}, {data})"); +#endif + return false; + } + + [HarmonyTargetMethod] + public static MethodBase Target() + { + return AccessTools.Method("GameServer.VoxelFarm.Server.TerrainModificationEngineServer:RemoveTerrainInput"); + } + }*/ +} \ No newline at end of file diff --git a/CLre_server/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs b/CLre_server/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs index e9ba82b..b4c4ea3 100644 --- a/CLre_server/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs +++ b/CLre_server/API/Synergy/Tweaks/SerializedCLreTerrainModifyRejection.cs @@ -1,7 +1,9 @@ using System; using System.IO; using System.Runtime.CompilerServices; +using Game.Handhelds; using NetworkFramework.Shared; +using UnityEngine; namespace CLre_server.API.Synergy.Tweaks { @@ -9,8 +11,14 @@ namespace CLre_server.API.Synergy.Tweaks { public RejectionFlag Flags; - public uint Cell; - + public uint resourceId; + + public Vector3 hit; + + public string toolKey; + + public ToolModeType toolMode; + public byte[] Serialize() { using (MemoryStream stream = new MemoryStream()) @@ -18,7 +26,12 @@ namespace CLre_server.API.Synergy.Tweaks using (BinaryWriter writer = new BinaryWriter(stream)) { writer.Write((byte)Flags); - writer.Write(Cell); + writer.Write(resourceId); + writer.Write(hit.x); + writer.Write(hit.y); + writer.Write(hit.z); + writer.Write(toolKey); + writer.Write((byte)toolMode); return stream.ToArray(); } } @@ -31,7 +44,13 @@ namespace CLre_server.API.Synergy.Tweaks using (BinaryReader reader = new BinaryReader(stream)) { Flags = (RejectionFlag)reader.ReadByte(); - Cell = reader.ReadUInt32(); + resourceId = reader.ReadUInt32(); + float x = reader.ReadSingle(); + float y = reader.ReadSingle(); + float z = reader.ReadSingle(); + hit = new Vector3(x, y, z); + toolKey = reader.ReadString(); + toolMode = (ToolModeType)reader.ReadByte(); } } } diff --git a/CLre_server/API/Tools/AccessToolsWarnings.cs b/CLre_server/API/Tools/AccessToolsWarnings.cs index 3cce05f..2eb1384 100644 --- a/CLre_server/API/Tools/AccessToolsWarnings.cs +++ b/CLre_server/API/Tools/AccessToolsWarnings.cs @@ -51,5 +51,21 @@ namespace CLre_server.API.Tools } } } + + [HarmonyPatch(typeof(AccessTools), "Method", + new Type[] {typeof(Type), typeof(string), typeof(Type[]), typeof(Type[])})] + class AccessTools_Method2_Patch + { + [HarmonyPostfix] + public static void AfterMethodCall(MethodInfo __result, Type type, string name) + { + if (!AccessToolsWarnings.isEnabled) return; + if (__result == null) + { + var method = (new StackTrace()).GetFrame(2).GetMethod(); + Utility.Logging.LogWarning($"[{method.DeclaringType.FullName}.{method.Name}] AccessTools.Method(\"{type}\", \"{name}\") returned null result"); + } + } + } } #endif \ No newline at end of file diff --git a/CLre_server/API/Utility/Reflection.cs b/CLre_server/API/Utility/Reflection.cs index 93427a7..b9fe7cb 100644 --- a/CLre_server/API/Utility/Reflection.cs +++ b/CLre_server/API/Utility/Reflection.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Reflection; +using Game.DataLoader; using GameNetworkLayer.Shared; using HarmonyLib; using NetworkFramework.Shared; @@ -33,7 +35,8 @@ namespace CLre_server.API.Utility public delegate void INetMsgServerListener_RegisterListener(NetworkDispatcherCode code, NetCBServer proc) where T: struct, ISerializedNetData; - + public delegate Dictionary IDataDB_GetValues(); + // useful reflection functions public static TFuncProto BuildDelegate(MethodInfo method) where TFuncProto : Delegate { diff --git a/CLre_server/Tweaks/TerrainModificationExclusionZone.cs b/CLre_server/Tweaks/TerrainModificationExclusionZone.cs index 107193f..86000e1 100644 --- a/CLre_server/Tweaks/TerrainModificationExclusionZone.cs +++ b/CLre_server/Tweaks/TerrainModificationExclusionZone.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using CLre_server.API.Synergy.Tweaks; +using CLre_server.API.Utility; using Game.DataLoader; using GameNetworkLayer.Shared; using HarmonyLib; @@ -12,6 +13,7 @@ using Svelto.DataStructures; using Svelto.ECS; using UnityEngine; using User.Server; +using voxelfarm; namespace CLre_server.Tweaks { @@ -104,8 +106,6 @@ namespace CLre_server.Tweaks #endif if (isPointInAABB) { - result.Cell = item; - if (!isOwner) { result.Flags = RejectionFlag.Proximity @@ -154,19 +154,24 @@ namespace CLre_server.Tweaks [HarmonyPatch] class TerrainModificationEngineServer_RemoveTerrainInput_Patch { - private static API.Utility.Reflection.INetMsgServerSender_SendMessage - _netMessageSend = null; + private static object _netMsgServerSender; + // reflection caching + private static API.Utility.Reflection.INetMsgServerSender_SendMessage + _netMessageSend_CLre = null; + [HarmonyPrefix] public static bool BeforeMethodCall(int senderPlayerId, ref ISerializedNetData data, object ____netMsgServerSender) { if (!CLre.Config.terrain_exclusion_zone) return true; - if (_netMessageSend == null) + _netMsgServerSender = ____netMsgServerSender; + if (_netMessageSend_CLre == null) { + // cache reflection operations on first run #if DEBUG API.Utility.Logging.MetaLog("Building SendMessage delegate optimisation"); #endif - _netMessageSend = API.Utility.Reflection + _netMessageSend_CLre = API.Utility.Reflection .MethodAsDelegate< API.Utility.Reflection.INetMsgServerSender_SendMessage >( @@ -185,12 +190,34 @@ namespace CLre_server.Tweaks #if DEBUG API.Utility.Logging.MetaLog("Rejecting terrain modification"); #endif + // TODO optimize + Traverse tmid = Traverse.Create(data); + modifyPayload.resourceId = tmid.Property("resourceId").Value; + modifyPayload.hit = location; + //modifyPayload.materialIndex = tmid.Property("materialIndex").Value; + modifyPayload.toolKey = tmid.Property("toolKey").Value; + modifyPayload.toolMode = tmid.Property("toolMode").Value; + switch (modifyPayload.toolMode) + { + case Game.Handhelds.ToolModeType.Block: + modifyPayload.hit.y -= 0.125f * 2; // each layer is 0.125 thick, block is 3 layers + break; + case Game.Handhelds.ToolModeType.Disc: + break; + case Game.Handhelds.ToolModeType.Voxel: + modifyPayload.toolMode = Game.Handhelds.ToolModeType.Disc; // voxels aren't replaced properly + break; + } // signal client that stuff failed - _netMessageSend(NetworkDispatcherCode.TerrainModificationFailed, ref modifyPayload, senderPlayerId); + _netMessageSend_CLre(NetworkDispatcherCode.TerrainModificationFailed, ref modifyPayload, senderPlayerId); + // build terrain data for terrain replacement +#if DEBUG + API.Utility.Logging.MetaLog("Filling hole left by terrain modification"); +#endif } return modifyPayload.Ok(); } - + [HarmonyTargetMethod] public static MethodBase Target() {