Decompiled source of AzuCraftyBoxesBuildingOnly v1.1.0

AzuCraftyBoxesBuildingOnly.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.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 AzuCraftyBoxesBuildingOnly
{
	public enum CraftyBoxesMode
	{
		Full,
		BuildingOnly,
		Disabled
	}
	[BepInPlugin("local.valheim.azucraftyboxesbuildingonly", "AzuCraftyBoxes Building Only", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("local.valheim.compatibilityfixes")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private static class AzuShouldPreventPatch
		{
			public static void Postfix(ref bool __result)
			{
				if (ModEnabled.Value && Mode.Value != CraftyBoxesMode.Full && !AzuCallContext.InInfrastructure && (Mode.Value != CraftyBoxesMode.BuildingOnly || !AzuCallContext.InBuilding))
				{
					__result = true;
				}
			}
		}

		public const string PluginGuid = "local.valheim.azucraftyboxesbuildingonly";

		public const string PluginName = "AzuCraftyBoxes Building Only";

		public const string PluginVersion = "1.1.0";

		public const string AzuCraftyBoxesGuid = "Azumatt.AzuCraftyBoxes";

		public const string CombinedPluginGuid = "local.valheim.compatibilityfixes";

		internal static ConfigEntry<bool> LockConfiguration;

		internal static ConfigEntry<bool> ModEnabled;

		internal static ConfigEntry<CraftyBoxesMode> Mode;

		internal static ConfigEntry<KeyboardShortcut> ToggleMode;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private FileSystemWatcher _configWatcher;

		private readonly object _configReloadLock = new object();

		private DateTime _lastConfigReloadTime;

		private const long ConfigReloadDelayTicks = 10000000L;

		private void Awake()
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			LockConfiguration = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Lock Configuration", true, "If true, synchronized gameplay settings are controlled by the server and can be changed only by server admins. [Synced with Server]");
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Mod Enabled", true, "Master switch for this add-on. When off, AzuCraftyBoxes behaves normally. [Synced with Server]");
			Mode = ((BaseUnityPlugin)this).Config.Bind<CraftyBoxesMode>("2 - AzuCraftyBoxes", "Mode", CraftyBoxesMode.BuildingOnly, "Full: normal AzuCraftyBoxes. BuildingOnly: nearby containers work only for placing structures; crafting, cooking, smelting, fermenting, fueling, turrets, and similar actions require carried items. Disabled: block all AzuCraftyBoxes pulling while keeping its container registry ready for a runtime mode change. [Synced with Server]");
			ToggleMode = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("2 - AzuCraftyBoxes", "Toggle Full and Building Only", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Local input hotkey that requests a synchronized switch between Full and BuildingOnly. It works for the server, server admins, or anyone when Lock Configuration is false. It does nothing while Mode is Disabled. [Not Synced with Server]");
			if (!AzuServerSyncBridge.Initialize(LockConfiguration, ModEnabled, Mode, ((BaseUnityPlugin)this).Logger))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"AzuCraftyBoxes' bundled ServerSync could not be initialized. This add-on is disabled to avoid unsynchronized multiplayer behavior.");
				return;
			}
			_harmony = new Harmony("local.valheim.azucraftyboxesbuildingonly");
			_harmony.PatchAll(typeof(BuildingOperationContextPatch));
			_harmony.PatchAll(typeof(AzuInfrastructureContextPatch));
			InstallAzuCraftyBoxesGate();
			SetupConfigWatcher();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Loaded AzuCraftyBoxes Building Only 1.1.0. Mode: " + Mode.Value.ToString() + ". Server and all clients are required."));
		}

		private void Update()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!ModEnabled.Value)
			{
				return;
			}
			KeyboardShortcut value = ToggleMode.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				if (!AzuServerSyncBridge.CanEditSynchronizedSettings())
				{
					ShowModeMessage("AzuCraftyBoxes mode is controlled by the server.");
					return;
				}
				if (Mode.Value == CraftyBoxesMode.Disabled)
				{
					ShowModeMessage("AzuCraftyBoxes mode is Disabled; change it in config before using the toggle hotkey.");
					return;
				}
				Mode.Value = ((Mode.Value == CraftyBoxesMode.Full) ? CraftyBoxesMode.BuildingOnly : CraftyBoxesMode.Full);
				ShowModeMessage("AzuCraftyBoxes mode: " + Mode.Value);
			}
		}

		private void SetupConfigWatcher()
		{
			_configWatcher = new FileSystemWatcher(Paths.ConfigPath, "local.valheim.azucraftyboxesbuildingonly.cfg");
			_configWatcher.Changed += ReloadConfig;
			_configWatcher.Created += ReloadConfig;
			_configWatcher.Renamed += ReloadConfig;
			_configWatcher.IncludeSubdirectories = false;
			_configWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_configWatcher.EnableRaisingEvents = true;
		}

		private void ReloadConfig(object sender, FileSystemEventArgs args)
		{
			DateTime now = DateTime.Now;
			if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000)
			{
				return;
			}
			_lastConfigReloadTime = now;
			if (!AzuServerSyncBridge.CanEditSynchronizedSettings())
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Ignored a local config-file change because synchronized settings are locked by the server.");
				return;
			}
			lock (_configReloadLock)
			{
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
					((BaseUnityPlugin)this).Config.Save();
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Reloaded AzuCraftyBoxes Building Only configuration.");
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Failed to reload configuration: " + ex.Message));
				}
			}
		}

		private void OnDestroy()
		{
			if (_configWatcher != null)
			{
				_configWatcher.Dispose();
			}
		}

		private static void ShowModeMessage(string message)
		{
			Log.LogInfo((object)message);
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, message, 0, (Sprite)null);
			}
		}

		private void InstallAzuCraftyBoxesGate()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method("AzuCraftyBoxes.Util.Functions.MiscFunctions:ShouldPrevent", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"AzuCraftyBoxes ShouldPrevent was not found. Building-only mode cannot be enforced with this AzuCraftyBoxes version.");
				return;
			}
			HarmonyMethod val = new HarmonyMethod(typeof(AzuShouldPreventPatch), "Postfix", (Type[])null);
			val.priority = 0;
			val.after = new string[1] { "Azumatt.AzuCraftyBoxes" };
			HarmonyMethod val2 = val;
			_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Installed the AzuCraftyBoxes Full/BuildingOnly/Disabled runtime gate.");
		}
	}
	internal static class AzuServerSyncBridge
	{
		private static object _configSync;

		private static PropertyInfo _isSourceOfTruth;

		private static PropertyInfo _isLocked;

		private static PropertyInfo _isAdmin;

		internal static bool Initialize(ConfigEntry<bool> lockingConfig, ConfigEntry<bool> enabledConfig, ConfigEntry<CraftyBoxesMode> modeConfig, ManualLogSource log)
		{
			try
			{
				Type type = AccessTools.TypeByName("AzuCraftyBoxes.AzuCraftyBoxesPlugin");
				if (type == null)
				{
					throw new TypeLoadException("AzuCraftyBoxes.AzuCraftyBoxesPlugin was not found.");
				}
				Type type2 = type.Assembly.GetType("ServerSync.ConfigSync", throwOnError: false);
				if (type2 == null)
				{
					throw new TypeLoadException("AzuCraftyBoxes' bundled ServerSync.ConfigSync was not found.");
				}
				_configSync = Activator.CreateInstance(type2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[1] { "local.valheim.azucraftyboxesbuildingonly" }, null);
				if (_configSync == null)
				{
					throw new InvalidOperationException("ServerSync.ConfigSync could not be created.");
				}
				SetMember(type2, "DisplayName", "AzuCraftyBoxes Building Only");
				SetMember(type2, "CurrentVersion", "1.1.0");
				SetMember(type2, "MinimumRequiredVersion", "1.1.0");
				SetMember(type2, "ModRequired", true);
				RegisterEntry(type2, "AddLockingConfigEntry", (ConfigEntryBase)(object)lockingConfig);
				RegisterEntry(type2, "AddConfigEntry", (ConfigEntryBase)(object)enabledConfig);
				RegisterEntry(type2, "AddConfigEntry", (ConfigEntryBase)(object)modeConfig);
				_isSourceOfTruth = type2.GetProperty("IsSourceOfTruth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_isLocked = type2.GetProperty("IsLocked", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_isAdmin = type2.GetProperty("IsAdmin", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (_isSourceOfTruth == null || _isLocked == null || _isAdmin == null)
				{
					throw new MissingMemberException("Required ServerSync ownership properties were not found.");
				}
				log.LogInfo((object)"Registered required-mod version checking and server-synchronized gameplay settings through AzuCraftyBoxes' bundled ServerSync.");
				return true;
			}
			catch (Exception ex)
			{
				_configSync = null;
				log.LogError((object)("ServerSync initialization failed: " + ex));
				return false;
			}
		}

		internal static bool CanEditSynchronizedSettings()
		{
			if (_configSync == null)
			{
				return false;
			}
			bool boolean = GetBoolean(_isSourceOfTruth, fallback: true);
			bool boolean2 = GetBoolean(_isLocked, fallback: true);
			bool boolean3 = GetBoolean(_isAdmin, fallback: false);
			if (!(boolean || boolean3))
			{
				return !boolean2;
			}
			return true;
		}

		private static void SetMember(Type configSyncType, string name, object value)
		{
			FieldInfo field = configSyncType.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(_configSync, value);
				return;
			}
			PropertyInfo? property = configSyncType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null)
			{
				throw new MissingMemberException(configSyncType.FullName, name);
			}
			property.SetValue(_configSync, value, null);
		}

		private static void RegisterEntry(Type configSyncType, string methodName, ConfigEntryBase entry)
		{
			MethodInfo methodInfo = null;
			MethodInfo[] methods = configSyncType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo2 in methods)
			{
				if (methodInfo2.Name == methodName && methodInfo2.IsGenericMethodDefinition && methodInfo2.GetParameters().Length == 1)
				{
					methodInfo = methodInfo2;
					break;
				}
			}
			if (methodInfo == null)
			{
				throw new MissingMethodException(configSyncType.FullName, methodName);
			}
			Type[] genericArguments = ((object)entry).GetType().GetGenericArguments();
			if (genericArguments.Length != 1)
			{
				throw new InvalidOperationException("Could not determine the config entry's value type.");
			}
			object obj = methodInfo.MakeGenericMethod(genericArguments[0]).Invoke(_configSync, new object[1] { entry });
			if (obj == null)
			{
				throw new InvalidOperationException(methodName + " returned no synchronized entry.");
			}
			FieldInfo field = obj.GetType().GetField("SynchronizedConfig", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(obj, true);
			}
		}

		private static bool GetBoolean(PropertyInfo property, bool fallback)
		{
			if (property == null || _configSync == null)
			{
				return fallback;
			}
			object value = property.GetValue(_configSync, null);
			if (value is bool)
			{
				return (bool)value;
			}
			return false;
		}
	}
	internal static class AzuCallContext
	{
		[ThreadStatic]
		private static int _buildingDepth;

		[ThreadStatic]
		private static int _infrastructureDepth;

		internal static bool InBuilding => _buildingDepth > 0;

		internal static bool InInfrastructure => _infrastructureDepth > 0;

		internal static void EnterBuilding()
		{
			_buildingDepth++;
		}

		internal static void ExitBuilding()
		{
			if (_buildingDepth > 0)
			{
				_buildingDepth--;
			}
		}

		internal static void EnterInfrastructure()
		{
			_infrastructureDepth++;
		}

		internal static void ExitInfrastructure()
		{
			if (_infrastructureDepth > 0)
			{
				_infrastructureDepth--;
			}
		}
	}
	[HarmonyPatch]
	internal static class BuildingOperationContextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodBase methodBase = AccessTools.Method(typeof(Player), "HaveRequirements", new Type[2]
			{
				typeof(Piece),
				typeof(RequirementMode)
			}, (Type[])null);
			if (methodBase != null)
			{
				yield return methodBase;
			}
			methodBase = AccessTools.Method(typeof(Player), "PlacePiece", new Type[4]
			{
				typeof(Piece),
				typeof(Vector3),
				typeof(Quaternion),
				typeof(bool)
			}, (Type[])null);
			if (methodBase != null)
			{
				yield return methodBase;
			}
			methodBase = AccessTools.Method(typeof(Hud), "SetupPieceInfo", new Type[1] { typeof(Piece) }, (Type[])null);
			if (methodBase != null)
			{
				yield return methodBase;
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			AzuCallContext.EnterBuilding();
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			AzuCallContext.ExitBuilding();
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class AzuInfrastructureContextPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodBase[] array = new MethodBase[9]
			{
				AccessTools.Method(typeof(Container), "Awake", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Container), "OnDestroyed", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(WearNTear), "OnDestroy", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "UpdateTeleport", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(ObjectDB), "Awake", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "SetLocalPlayer", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "OnSpawned", (Type[])null, (Type[])null),
				AccessTools.Method(typeof(Player), "OnRespawn", (Type[])null, (Type[])null)
			};
			MethodBase[] array2 = array;
			foreach (MethodBase methodBase in array2)
			{
				if (methodBase != null)
				{
					yield return methodBase;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix()
		{
			AzuCallContext.EnterInfrastructure();
		}

		[HarmonyFinalizer]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			AzuCallContext.ExitInfrastructure();
			return __exception;
		}
	}
}