Decompiled source of WorkbenchRange v0.5.0

WorkbenchRange.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.ModCore.API;
using JG224.WorkbenchRange.Core;
using JG224.WorkbenchRange.Patches;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("WorkbenchRange")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("WorkbenchRange")]
[assembly: AssemblyTitle("WorkbenchRange")]
[assembly: AssemblyVersion("0.5.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 JG224.WorkbenchRange
{
	[BepInPlugin("jg224.WorkbenchRange", "WorkbenchRange", "0.5.0")]
	[BepInProcess("valheim.exe")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.WorkbenchRange";

		public const string PluginName = "WorkbenchRange";

		public const string PluginVersion = "0.5.0";

		public const string ModCoreGuid = "com.jg224.modcore";

		public const string GeneralTweaksGuid = "JG224.GeneralTweaks";

		public const int ProtocolVersion = 1;

		public static readonly ModuleId ModuleId = new ModuleId("workbenchrange");

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private Harmony _harmony;

		private bool _active;

		private bool _shutDown;

		internal static ManualLogSource Log { get; private set; }

		internal static WorkbenchRangeConfig Settings { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = WorkbenchRangeConfig.Load((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger);
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before WorkbenchRange.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				if (TryGetLegacyOwner(out var version))
				{
					string text = "GeneralTweaks " + version?.ToString() + " still contains Workbench Range; WorkbenchRange is paused to prevent duplicate mutations.";
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)3, text);
					((BaseUnityPlugin)this).Logger.LogWarning((object)(text + " Update GeneralTweaks to 3.0.0 or newer."));
					return;
				}
				_harmony = new Harmony("jg224.WorkbenchRange");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				_active = true;
				Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Client-local construction range ready.");
				Game.isModded = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("WorkbenchRange 0.5.0 loaded; client-local settings, protocol " + 1 + "."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("WorkbenchRange failed to initialize safely: " + ex));
				if (Core != null)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void Update()
		{
			if (_active)
			{
				WorkbenchRuntime.Tick(Time.unscaledTimeAsDouble);
			}
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void RegisterWithCore()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			SemanticVersion val = default(SemanticVersion);
			if (!SemanticVersion.TryParse("0.5.0", ref val))
			{
				throw new InvalidOperationException("Invalid plugin version.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.WorkbenchRange", "WorkbenchRange", val, 1, (ModuleSide)1, (ModuleRequirement)1, 0uL, 1, 1)));
			foreach (IDisposable item in Settings.RegisterMetadata(Core, ModuleId))
			{
				_registrations.Add(item);
			}
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)9, (Action<LifecycleEvent>)delegate
			{
				ResetRuntime();
			}, 0));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)3, (Action<LifecycleEvent>)delegate
			{
				ResetRuntime();
			}, 0));
		}

		private static bool TryGetLegacyOwner(out Version version)
		{
			version = null;
			if (!Chainloader.PluginInfos.TryGetValue("JG224.GeneralTweaks", out var value))
			{
				return false;
			}
			version = value.Metadata.Version;
			if (version != null)
			{
				return version.CompareTo(new Version(3, 0, 0)) < 0;
			}
			return false;
		}

		private void ResetRuntime()
		{
			WorkbenchRuntime.RestoreAll();
		}

		private void Shutdown()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			if (_shutDown)
			{
				return;
			}
			_shutDown = true;
			_active = false;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			ResetRuntime();
			for (int num = _registrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_registrations[num].Dispose();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Registration cleanup failed: " + ex.Message));
					}
				}
			}
			_registrations.Clear();
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			Settings = null;
			Log = null;
		}

		internal static void Debug(string message)
		{
			WorkbenchRangeConfig settings = Settings;
			if (settings != null && settings.DebugLogging.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)message);
				}
			}
		}
	}
	internal sealed class WorkbenchRangeConfig
	{
		private readonly ConfigFile _file;

		private readonly List<Tuple<ConfigEntryBase, ConfigScope>> _entries = new List<Tuple<ConfigEntryBase, ConfigScope>>();

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<float> ConstructionRadius { get; }

		internal ConfigEntry<ConstructionRangeMode> RangeMode { get; }

		internal ConfigEntry<bool> NeverReduce { get; }

		internal ConfigEntry<string> StationOverrides { get; }

		internal ConfigEntry<bool> DebugLogging { get; }

		private WorkbenchRangeConfig(ConfigFile file)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			_file = file;
			Enabled = Add<bool>(file.Bind<bool>("Workbench", "Enabled", true, "Apply this client's configured construction radius to vanilla crafting stations."), (ConfigScope)1);
			ConstructionRadius = Add<float>(file.Bind<float>("Workbench", "ConstructionRadius", 30f, new ConfigDescription("Construction radius in metres; RangeMode chooses base or final radius. Vanilla's usual base is 20.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>())), (ConfigScope)1);
			RangeMode = Add<ConstructionRangeMode>(file.Bind<ConstructionRangeMode>("Workbench", "RangeMode", ConstructionRangeMode.Base, "Base preserves vanilla extension bonuses; Final subtracts those bonuses to target the final radius (bonuses alone remain a lower bound)."), (ConfigScope)1);
			NeverReduce = Add<bool>(file.Bind<bool>("Workbench", "NeverReduce", false, "Never reduce a station below its original range, including current extension bonuses."), (ConfigScope)1);
			StationOverrides = Add<string>(file.Bind<string>("Workbench", "StationOverrides", "", "Per-prefab settings: piece_workbench=40;forge=35;custom_station=off. Radius 5-100m; off preserves that station."), (ConfigScope)1);
			DebugLogging = Add<bool>(file.Bind<bool>("Diagnostics", "DebugLogging", false, "Enable verbose WorkbenchRange diagnostics."), (ConfigScope)5);
		}

		internal static WorkbenchRangeConfig Load(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			string text = LegacyWorkbenchConfigRules.FindImportSource(Directory.GetFiles(configDirectory, "*.cfg", SearchOption.TopDirectoryOnly));
			WorkbenchRangeConfig workbenchRangeConfig = new WorkbenchRangeConfig(PluginConfigFiles.Attach(plugin, new ConfigFile(Path.Combine(configDirectory, "jg224.WorkbenchRange.cfg"), true, plugin.Info.Metadata)));
			if (text != null)
			{
				workbenchRangeConfig.ImportLegacy(text, log);
			}
			return workbenchRangeConfig;
		}

		internal IEnumerable<IDisposable> RegisterMetadata(ICoreServices services, ModuleId owner)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			foreach (Tuple<ConfigEntryBase, ConfigScope> pair in _entries)
			{
				yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, pair.Item1.Definition.Section, pair.Item1.Definition.Key, pair.Item2, (Func<string>)(() => Convert.ToString(pair.Item1.BoxedValue, CultureInfo.InvariantCulture)), 1));
			}
		}

		private void ImportLegacy(string path, ManualLogSource log)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			try
			{
				ConfigFile val = new ConfigFile(path, false);
				Enabled.Value = val.Bind<bool>("Workbench", "Enabled", Enabled.Value, (ConfigDescription)null).Value;
				ConstructionRadius.Value = val.Bind<float>("Workbench", "ConstructionRadius", ConstructionRadius.Value, (ConfigDescription)null).Value;
				_file.Save();
				if (log != null)
				{
					log.LogInfo((object)("Imported Workbench Range settings from " + Path.GetFileName(path) + "."));
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.LogWarning((object)("Could not import GeneralTweaks Workbench settings; using defaults. " + ex.Message));
				}
			}
		}

		private ConfigEntry<T> Add<T>(ConfigEntry<T> entry, ConfigScope scope)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_entries.Add(Tuple.Create<ConfigEntryBase, ConfigScope>((ConfigEntryBase)(object)entry, scope));
			return entry;
		}
	}
}
namespace JG224.WorkbenchRange.Patches
{
	internal static class WorkbenchRuntime
	{
		private sealed class Record
		{
			internal CraftingStation Station;

			internal float OriginalRange;

			internal float Applied = float.NaN;
		}

		private static readonly Dictionary<int, Record> Records = new Dictionary<int, Record>();

		private static readonly List<int> Dead = new List<int>();

		private static Dictionary<string, float> _overrides = WorkbenchRules.ParseOverrides("");

		private static string _raw;

		private static readonly FieldRef<CraftingStation, float> UpdateTimer = AccessTools.FieldRefAccess<CraftingStation, float>("m_updateExtensionTimer");

		private static readonly FieldRef<CraftingStation, CircleProjector> MarkerCircle = AccessTools.FieldRefAccess<CraftingStation, CircleProjector>("m_areaMarkerCircle");

		private static double _nextScan;

		internal static void Invalidate()
		{
			_nextScan = 0.0;
		}

		internal static void TrackAndApply(CraftingStation station)
		{
			if (Object.op_Implicit((Object)(object)station))
			{
				int instanceID = ((Object)station).GetInstanceID();
				if (!Records.TryGetValue(instanceID, out var value) || (Object)(object)value.Station != (Object)(object)station)
				{
					value = new Record
					{
						Station = station,
						OriginalRange = station.m_rangeBuild
					};
					Records[instanceID] = value;
				}
				RefreshOverrides();
				Apply(value);
			}
		}

		internal static void Tick(double now)
		{
			if (now < _nextScan)
			{
				return;
			}
			_nextScan = now + 1.0;
			RefreshOverrides();
			List<IMonoUpdater> instances = CraftingStation.Instances;
			for (int i = 0; i < instances.Count; i++)
			{
				IMonoUpdater obj = instances[i];
				CraftingStation val = (CraftingStation)(object)((obj is CraftingStation) ? obj : null);
				if (Object.op_Implicit((Object)(object)val))
				{
					TrackAndApply(val);
				}
			}
			Dead.Clear();
			foreach (KeyValuePair<int, Record> record in Records)
			{
				if (!Object.op_Implicit((Object)(object)record.Value.Station))
				{
					Dead.Add(record.Key);
				}
			}
			for (int j = 0; j < Dead.Count; j++)
			{
				Records.Remove(Dead[j]);
			}
		}

		internal static void RestoreAll()
		{
			foreach (Record value in Records.Values)
			{
				Restore(value);
			}
			Records.Clear();
			Dead.Clear();
			_raw = null;
			_nextScan = 0.0;
		}

		private static void RefreshOverrides()
		{
			string text = Plugin.Settings?.StationOverrides.Value ?? "";
			if (!(_raw == text))
			{
				_raw = text;
				_overrides = WorkbenchRules.ParseOverrides(text);
			}
		}

		private static void Apply(Record record)
		{
			WorkbenchRangeConfig settings = Plugin.Settings;
			float num = settings?.ConstructionRadius.Value ?? 30f;
			string key = ((Object)((Component)record.Station).gameObject).name.Replace("(Clone)", "").Trim();
			if (_overrides.TryGetValue(key, out var value))
			{
				num = value;
			}
			if (settings == null || !settings.Enabled.Value || num < 0f)
			{
				Restore(record);
			}
			else if (CanRefresh(record.Station))
			{
				if (float.IsNaN(record.Applied) || !WorkbenchRules.OwnsValue(record.Station.m_rangeBuild, record.Applied))
				{
					record.OriginalRange = record.Station.m_rangeBuild;
				}
				float extensionBonus = (float)record.Station.GetExtentionCount(false) * record.Station.m_extraRangePerLevel;
				float num2 = WorkbenchRules.ResolveBaseRange(num, record.OriginalRange, extensionBonus, settings.RangeMode.Value, settings.NeverReduce.Value);
				if (!WorkbenchRules.OwnsValue(record.Station.m_rangeBuild, num2))
				{
					record.Station.m_rangeBuild = num2;
					record.Applied = num2;
					Refresh(record.Station);
				}
			}
		}

		private static void Restore(Record record)
		{
			if (Object.op_Implicit((Object)(object)record.Station) && WorkbenchRules.OwnsValue(record.Station.m_rangeBuild, record.Applied))
			{
				record.Station.m_rangeBuild = record.OriginalRange;
				Refresh(record.Station);
			}
			record.Applied = float.NaN;
		}

		private static void Refresh(CraftingStation station)
		{
			try
			{
				UpdateTimer.Invoke(station) = 2f;
				if (CanRefresh(station))
				{
					station.GetStationBuildRange();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Could not refresh crafting-station range marker: " + ex));
				}
			}
		}

		private static bool CanRefresh(CraftingStation station)
		{
			if (Object.op_Implicit((Object)(object)station.m_areaMarker))
			{
				return Object.op_Implicit((Object)(object)MarkerCircle.Invoke(station));
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(CraftingStation), "Start")]
	internal static class WorkbenchStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix(CraftingStation __instance)
		{
			WorkbenchRuntime.TrackAndApply(__instance);
		}
	}
}
namespace JG224.WorkbenchRange.Core
{
	internal enum ConstructionRangeMode
	{
		Base,
		Final
	}
	internal static class WorkbenchRules
	{
		internal static bool IsValidRange(float range)
		{
			if (!float.IsNaN(range) && !float.IsInfinity(range) && range >= 5f)
			{
				return range <= 100f;
			}
			return false;
		}

		internal static float ResolveBaseRange(float requested, float original, float extensionBonus, ConstructionRangeMode mode, bool neverReduce)
		{
			if (!IsValidRange(requested) || !Finite(original) || !Finite(extensionBonus))
			{
				return original;
			}
			extensionBonus = Math.Max(0f, extensionBonus);
			float num = ((mode == ConstructionRangeMode.Final) ? Math.Max(0f, requested - extensionBonus) : requested);
			if (!neverReduce)
			{
				return num;
			}
			return Math.Max(original, num);
		}

		internal static Dictionary<string, float> ParseOverrides(string raw)
		{
			Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
			string[] array = (raw ?? "").Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '=' });
				if (array2.Length == 2 && array2[0].Trim().Length != 0)
				{
					float result;
					if (string.Equals(array2[1].Trim(), "off", StringComparison.OrdinalIgnoreCase))
					{
						dictionary[array2[0].Trim()] = -1f;
					}
					else if (float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out result) && IsValidRange(result))
					{
						dictionary[array2[0].Trim()] = result;
					}
				}
			}
			return dictionary;
		}

		internal static bool OwnsValue(float current, float applied)
		{
			if (Finite(current) && Finite(applied))
			{
				return Math.Abs(current - applied) < 0.001f;
			}
			return false;
		}

		private static bool Finite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}
	}
	internal static class LegacyWorkbenchConfigRules
	{
		internal const string CurrentFileName = "jg224.WorkbenchRange.cfg";

		internal const string GeneralTweaksFileName = "jg224.GeneralTweaks.cfg";

		internal const string LegacyGeneralTweaksFileName = "JG224.GeneralTweaks.cfg";

		internal static string FindImportSource(IEnumerable<string> candidatePaths)
		{
			string[] paths = ((candidatePaths == null) ? Array.Empty<string>() : candidatePaths.ToArray());
			if (FindExact(paths, "jg224.WorkbenchRange.cfg") != null)
			{
				return null;
			}
			return FindExact(paths, "jg224.GeneralTweaks.cfg") ?? FindExact(paths, "JG224.GeneralTweaks.cfg");
		}

		private static string FindExact(IEnumerable<string> paths, string fileName)
		{
			return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal));
		}
	}
}