Decompiled source of BuildTweaks v1.0.1

BuildTweaks.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BuildTweaks")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BuildTweaks")]
[assembly: AssemblyTitle("BuildTweaks")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BuildTweaks
{
	[BepInPlugin("local.buildtweaks", "Build Tweaks", "1.0.0")]
	public class BuildTweaksPlugin : BaseUnityPlugin
	{
		private class SyncData
		{
			public bool allowToggle;

			public bool extIgnoreCollision;

			public float extRange;

			public bool forceValid;
		}

		private class OrigExt
		{
			public float dist;
		}

		private static BuildTweaksPlugin I;

		private const string RpcName = "LocalBuildTweaks_Cfg";

		private const int ProtocolVersion = 3;

		private ConfigEntry<KeyboardShortcut> toggleKey;

		private ConfigEntry<bool> cfgAllowToggle;

		private ConfigEntry<bool> cfgExtIgnoreCollision;

		private ConfigEntry<float> cfgExtRange;

		private ConfigEntry<bool> cfgForceValid;

		private ConfigEntry<bool> cfgDebug;

		private static SyncData fromServer;

		private bool collisionOff;

		private volatile bool reloadFlag;

		private float nextReload;

		private static readonly Dictionary<StationExtension, OrigExt> Orig = new Dictionary<StationExtension, OrigExt>();

		private static readonly FieldInfo FDist = AccessTools.Field(typeof(StationExtension), "m_maxStationDistance");

		private static readonly FieldInfo FStatus = AccessTools.Field(typeof(Player), "m_placementStatus");

		private static readonly FieldInfo FGhost = AccessTools.Field(typeof(Player), "m_placementGhost");

		private static readonly MethodInfo MSetGhostValid = AccessTools.Method(typeof(Player), "SetPlacementGhostValid", (Type[])null, (Type[])null);

		private static readonly MethodInfo MTestClip = AccessTools.Method(typeof(Player), "TestGhostClipping", (Type[])null, (Type[])null);

		private static bool inRawClipTest;

		private static string lastDebug = "";

		private static bool EffAllowToggle => (fromServer != null) ? fromServer.allowToggle : I.cfgAllowToggle.Value;

		private static bool EffExtIgnoreCollision => (fromServer != null) ? fromServer.extIgnoreCollision : I.cfgExtIgnoreCollision.Value;

		private static float EffExtRange => (fromServer != null) ? fromServer.extRange : I.cfgExtRange.Value;

		private static bool EffForceValid => (fromServer != null) ? fromServer.forceValid : I.cfgForceValid.Value;

		private void Awake()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			I = this;
			toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "ToggleBuildCollision", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Кнопка включения и выключения коллизии построек (только у вас, не синхронизируется)");
			cfgAllowToggle = ((BaseUnityPlugin)this).Config.Bind<bool>("Synced", "AllowCollisionToggle", true, "[Синхр.] Разрешить игрокам отключать коллизию построек по кнопке");
			cfgExtIgnoreCollision = ((BaseUnityPlugin)this).Config.Bind<bool>("Synced", "ExtensionsIgnoreCollision", true, "[Синхр.] Убрать ограничение «нужно больше места» для улучшений крафт-станций: их можно ставить вплотную и внахлёст друг с другом и с другими постройками");
			cfgExtRange = ((BaseUnityPlugin)this).Config.Bind<float>("Synced", "ExtensionRange", 0f, new ConfigDescription("[Синхр.] Максимальное расстояние (м) от крафт-станции до её улучшений. 0 = как в оригинале (обычно 5)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			cfgForceValid = ((BaseUnityPlugin)this).Config.Bind<bool>("Synced", "ForceValidWhenCollisionOff", true, "[Синхр.] Пока коллизия отключена кнопкой, любая установка, которую игра считает недопустимой, становится допустимой. Варды, зоны запрета строительства и привязка улучшений к станции продолжают работать");
			cfgDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogPlacementStatus", true, "Писать в лог BepInEx статус установки заблокированных построек, пока коллизия выключена, а также для улучшений станций (только у вас)");
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				ApplyAllExtensions();
				Broadcast();
			};
			WatchConfigFile();
			Harmony h = new Harmony("local.buildtweaks");
			TryPatch(h, typeof(Player), "TestGhostClipping", "ClipPrefix", prefix: true);
			TryPatch(h, typeof(Player), "UpdatePlacementGhost", "PlacementPostfix", prefix: false);
			TryPatch(h, typeof(StationExtension), "Awake", "ExtAwakePostfix", prefix: false);
			TryPatch(h, typeof(ZNet), "OnNewConnection", "NewConnPostfix", prefix: false);
			TryPatch(h, typeof(ZNet), "RPC_PeerInfo", "PeerInfoPostfix", prefix: false);
			TryPatch(h, typeof(ZNet), "OnDestroy", "NetDestroyPostfix", prefix: false);
		}

		private static void TryPatch(Harmony h, Type t, string method, string patch, bool prefix)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(t, method, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				((BaseUnityPlugin)I).Logger.LogWarning((object)("Method " + t.Name + "." + method + " not found - patch skipped"));
				return;
			}
			HarmonyMethod val = new HarmonyMethod(typeof(BuildTweaksPlugin).GetMethod(patch, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
			try
			{
				if (prefix)
				{
					h.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else
				{
					h.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)I).Logger.LogWarning((object)("Patch " + t.Name + "." + method + " failed: " + ex.Message));
			}
		}

		private void WatchConfigFile()
		{
			try
			{
				FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath));
				fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
				fileSystemWatcher.Changed += delegate
				{
					reloadFlag = true;
				};
				fileSystemWatcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Config watcher not started: " + ex.Message));
			}
		}

		private void Update()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (reloadFlag && Time.unscaledTime >= nextReload)
			{
				reloadFlag = false;
				nextReload = Time.unscaledTime + 1f;
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)ex.Message);
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				collisionOff = false;
			}
			else
			{
				if (Typing())
				{
					return;
				}
				KeyboardShortcut value = toggleKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					if (!EffAllowToggle)
					{
						((Character)localPlayer).Message((MessageType)2, "Build collision toggle is disabled by the server", 0, (Sprite)null, false);
						return;
					}
					collisionOff = !collisionOff;
					((Character)localPlayer).Message((MessageType)2, collisionOff ? "Build collision: OFF" : "Build collision: ON", 0, (Sprite)null, false);
				}
			}
		}

		private static bool Typing()
		{
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return true;
			}
			if (Console.IsVisible())
			{
				return true;
			}
			if (Menu.IsVisible())
			{
				return true;
			}
			return false;
		}

		private static bool WantSkip(GameObject ghost)
		{
			if ((Object)(object)I == (Object)null)
			{
				return false;
			}
			if (I.collisionOff && EffAllowToggle)
			{
				return true;
			}
			return EffExtIgnoreCollision && (Object)(object)ghost != (Object)null && (Object)(object)ghost.GetComponent<StationExtension>() != (Object)null;
		}

		private static void DebugLog(string msg)
		{
			if (!((Object)(object)I == (Object)null) && I.cfgDebug.Value && !(msg == lastDebug))
			{
				lastDebug = msg;
				((BaseUnityPlugin)I).Logger.LogInfo((object)("[placement] " + msg));
			}
		}

		private static bool ClipPrefix(GameObject __0, ref bool __result)
		{
			if (inRawClipTest)
			{
				return true;
			}
			if (WantSkip(__0))
			{
				__result = false;
				return false;
			}
			return true;
		}

		private static void PlacementPostfix(Player __instance)
		{
			if ((Object)(object)I == (Object)null || FStatus == null || FGhost == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			object? value = FGhost.GetValue(__instance);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val == (Object)null || !val.activeSelf || !WantSkip(val))
			{
				return;
			}
			string name = Enum.GetName(FStatus.FieldType, FStatus.GetValue(__instance));
			if (name == null || name == "Valid")
			{
				return;
			}
			bool flag = I.collisionOff && EffAllowToggle;
			bool flag2 = false;
			if (name == "MoreSpace")
			{
				flag2 = true;
			}
			else if (name == "Invalid")
			{
				if (flag && EffForceValid)
				{
					flag2 = true;
				}
				else if (MTestClip != null)
				{
					inRawClipTest = true;
					try
					{
						flag2 = (bool)MTestClip.Invoke(__instance, new object[2] { val, 0.2f });
					}
					catch
					{
					}
					finally
					{
						inRawClipTest = false;
					}
				}
			}
			DebugLog(((Object)val).name + ": " + name + (flag2 ? " -> forced valid" : " (kept)"));
			if (flag2)
			{
				FStatus.SetValue(__instance, Enum.Parse(FStatus.FieldType, "Valid"));
				if (MSetGhostValid != null)
				{
					MSetGhostValid.Invoke(__instance, new object[1] { true });
				}
			}
		}

		private static void ExtAwakePostfix(StationExtension __instance)
		{
			ApplyOne(__instance);
		}

		private static void ApplyOne(StationExtension e)
		{
			if ((Object)(object)e == (Object)null)
			{
				return;
			}
			if (!Orig.TryGetValue(e, out var value))
			{
				value = new OrigExt();
				if (FDist != null)
				{
					value.dist = (float)FDist.GetValue(e);
				}
				Orig[e] = value;
			}
			if (FDist != null)
			{
				float effExtRange = EffExtRange;
				FDist.SetValue(e, (effExtRange > 0f) ? effExtRange : value.dist);
			}
		}

		private static void ApplyAllExtensions()
		{
			foreach (StationExtension item in Orig.Keys.Where((StationExtension k) => (Object)(object)k == (Object)null).ToList())
			{
				Orig.Remove(item);
			}
			StationExtension[] array = Object.FindObjectsByType<StationExtension>((FindObjectsSortMode)0);
			foreach (StationExtension e in array)
			{
				ApplyOne(e);
			}
		}

		private static bool IsServer()
		{
			return (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
		}

		private static ZPackage MakePackage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(3);
			val.Write(I.cfgAllowToggle.Value);
			val.Write(I.cfgExtIgnoreCollision.Value);
			val.Write(I.cfgExtRange.Value);
			val.Write(I.cfgForceValid.Value);
			return val;
		}

		private static void NewConnPostfix(ZNetPeer __0)
		{
			if (__0 != null && __0.m_rpc != null)
			{
				__0.m_rpc.Register<ZPackage>("LocalBuildTweaks_Cfg", (Action<ZRpc, ZPackage>)RPC_Receive);
			}
		}

		private static void PeerInfoPostfix(ZRpc __0)
		{
			if (!IsServer() || __0 == null)
			{
				return;
			}
			try
			{
				__0.Invoke("LocalBuildTweaks_Cfg", new object[1] { MakePackage() });
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)I).Logger.LogWarning((object)("Config send failed: " + ex.Message));
			}
		}

		private static void Broadcast()
		{
			if (!IsServer())
			{
				return;
			}
			try
			{
				List<ZNetPeer> value = Traverse.Create((object)ZNet.instance).Field("m_peers").GetValue<List<ZNetPeer>>();
				if (value == null)
				{
					return;
				}
				foreach (ZNetPeer item in value)
				{
					try
					{
						if (item.m_rpc != null)
						{
							item.m_rpc.Invoke("LocalBuildTweaks_Cfg", new object[1] { MakePackage() });
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)I).Logger.LogWarning((object)("Broadcast failed: " + ex.Message));
			}
		}

		private static void RPC_Receive(ZRpc rpc, ZPackage pkg)
		{
			if (IsServer())
			{
				return;
			}
			try
			{
				int num = pkg.ReadInt();
				if (num != 3)
				{
					((BaseUnityPlugin)I).Logger.LogWarning((object)"Server BuildTweaks version differs - using local config");
					return;
				}
				fromServer = new SyncData
				{
					allowToggle = pkg.ReadBool(),
					extIgnoreCollision = pkg.ReadBool(),
					extRange = pkg.ReadSingle(),
					forceValid = pkg.ReadBool()
				};
				((BaseUnityPlugin)I).Logger.LogInfo((object)"Received config from server");
				ApplyAllExtensions();
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)I).Logger.LogWarning((object)("Config receive failed: " + ex.Message));
			}
		}

		private static void NetDestroyPostfix()
		{
			fromServer = null;
		}
	}
}