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 _registrations = new List(); 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)delegate { ResetRuntime(); }, 0)); _registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)3, (Action)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> _entries = new List>(); internal ConfigEntry Enabled { get; } internal ConfigEntry ConstructionRadius { get; } internal ConfigEntry RangeMode { get; } internal ConfigEntry NeverReduce { get; } internal ConfigEntry StationOverrides { get; } internal ConfigEntry 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(file.Bind("Workbench", "Enabled", true, "Apply this client's configured construction radius to vanilla crafting stations."), (ConfigScope)1); ConstructionRadius = Add(file.Bind("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(5f, 100f), Array.Empty())), (ConfigScope)1); RangeMode = Add(file.Bind("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(file.Bind("Workbench", "NeverReduce", false, "Never reduce a station below its original range, including current extension bonuses."), (ConfigScope)1); StationOverrides = Add(file.Bind("Workbench", "StationOverrides", "", "Per-prefab settings: piece_workbench=40;forge=35;custom_station=off. Radius 5-100m; off preserves that station."), (ConfigScope)1); DebugLogging = Add(file.Bind("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 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 pair in _entries) { yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, pair.Item1.Definition.Section, pair.Item1.Definition.Key, pair.Item2, (Func)(() => 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("Workbench", "Enabled", Enabled.Value, (ConfigDescription)null).Value; ConstructionRadius.Value = val.Bind("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 Add(ConfigEntry entry, ConfigScope scope) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) _entries.Add(Tuple.Create((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 Records = new Dictionary(); private static readonly List Dead = new List(); private static Dictionary _overrides = WorkbenchRules.ParseOverrides(""); private static string _raw; private static readonly FieldRef UpdateTimer = AccessTools.FieldRefAccess("m_updateExtensionTimer"); private static readonly FieldRef MarkerCircle = AccessTools.FieldRefAccess("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 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 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 ParseOverrides(string raw) { Dictionary dictionary = new Dictionary(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 candidatePaths) { string[] paths = ((candidatePaths == null) ? Array.Empty() : 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 paths, string fileName) { return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal)); } } }