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 LockConfiguration; internal static ConfigEntry ModEnabled; internal static ConfigEntry Mode; internal static ConfigEntry 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("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("1 - General", "Mod Enabled", true, "Master switch for this add-on. When off, AzuCraftyBoxes behaves normally. [Synced with Server]"); Mode = ((BaseUnityPlugin)this).Config.Bind("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("2 - AzuCraftyBoxes", "Toggle Full and Building Only", new KeyboardShortcut((KeyCode)289, Array.Empty()), "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 lockingConfig, ConfigEntry enabledConfig, ConfigEntry 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 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 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; } } }