Decompiled source of Automatics v1.5.1
plugins/Automatics.dll
Decompiled a year ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using Automatics.AutomaticDoor; using Automatics.AutomaticMapping; using Automatics.AutomaticProcessing; using Automatics.ConsoleCommands; using Automatics.Valheim; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using LitJson; using ModUtils; using NDesk.Options; using UnityEngine; [assembly: Guid("1821CB9A-56A9-4013-B268-86F0704773CF")] [assembly: ComVisible(false)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCopyright("Copyright © 2022-2023")] [assembly: AssemblyProduct("Automatics")] [assembly: AssemblyCompany("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyDescription("")] [assembly: AssemblyTitle("Automatics")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyFileVersion("1.5.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.5.1.0")] namespace ModUtils { public class Configuration { private const int DefaultOrder = 4096; private readonly ConfigFile _config; private readonly L10N _localization; private Logger _logger; private string Section { get; set; } = "general"; private int Order { get; set; } = 4096; static Configuration() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown if (!TomlTypeConverter.CanConvert(typeof(StringList))) { TomlTypeConverter.AddConverter(typeof(StringList), new TypeConverter { ConvertToObject = (string str, Type type) => (!string.IsNullOrEmpty(str)) ? new StringList(Csv.ParseLine(str)) : new StringList(), ConvertToString = delegate(object obj, Type type) { StringList source = (StringList)obj; return string.Join(", ", source.Select(Csv.Escape)); } }); } if (!TomlTypeConverter.CanConvert(typeof(KeyboardShortcut))) { TomlTypeConverter.AddConverter(typeof(KeyboardShortcut), new TypeConverter { ConvertToObject = (string str, Type type) => KeyboardShortcut.Deserialize(str), ConvertToString = delegate(object obj, Type type) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut val = (KeyboardShortcut)obj; return ((KeyboardShortcut)(ref val)).Serialize(); } }); } } public Configuration(ConfigFile config, L10N localization) { _config = config; _localization = localization; } private void LogSection(string section) { _logger?.Debug("[CONFIG] === " + GetSection(section) + " / [" + section + "]"); } private void LogConfigEntry<T>(ConfigEntry<T> entry, ConfigurationManagerAttributes attributes) { _logger?.Debug("[CONFIG] ==== " + attributes.DispName + " / [" + ((ConfigEntryBase)entry).Definition.Key + "]"); _logger?.Debug("[CONFIG] " + ((ConfigEntryBase)entry).Description.Description); _logger?.Debug("[CONFIG] "); Type type = typeof(T); object defaultValue = ((ConfigEntryBase)entry).DefaultValue; if (attributes.ObjToStr != null) { _logger?.Debug("[CONFIG] - Default value: " + attributes.ObjToStr(defaultValue)); } else if (TomlTypeConverter.CanConvert(type)) { _logger?.Debug("[CONFIG] - Default value: " + TomlTypeConverter.ConvertToString(defaultValue, type)); } else { _logger?.Debug($"[CONFIG] - Default value: {defaultValue}"); } AcceptableValueBase acceptableValues = ((ConfigEntryBase)entry).Description.AcceptableValues; if (acceptableValues != null) { string[] array = acceptableValues.ToDescriptionString().Split(new char[1] { '\n' }); foreach (string text in array) { _logger?.Debug("[CONFIG] - " + text); } } else if (type.IsEnum) { List<string> list = (from x in Enum.GetValues(type).OfType<T>() select Enum.GetName(type, x)).ToList(); _logger?.Debug("[CONFIG] - Acceptable values: " + string.Join(", ", list)); if (type.GetCustomAttributes(typeof(FlagsAttribute), inherit: false).Any()) { IEnumerable<string> values = list.Where((string x) => !string.Equals(x, "none", StringComparison.OrdinalIgnoreCase) && !string.Equals(x, "all", StringComparison.OrdinalIgnoreCase)).Take(2); _logger?.Debug("[CONFIG] - Multiple values can be set at the same time by separating them with , (e.g. " + string.Join(", ", values) + ")"); } } _logger?.Debug("[CONFIG] "); } public void SetDebugLogger(Logger logger) { _logger = logger; } public void ChangeSection(string section, int initialOrder = 4096) { Section = section; Order = initialOrder; if (_logger != null) { LogSection(Section); } } private ConfigEntry<T> Bind<T>(string section, int order, string key, T defaultValue, AcceptableValueBase acceptableValue = null, Action<ConfigurationManagerAttributes> initializer = null) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes { Category = GetSection(section), Order = order, DispName = GetName(section, key), CustomDrawer = ConfigurationCustomDrawer.Get(typeof(T), acceptableValue) }; initializer?.Invoke(configurationManagerAttributes); string text = (string.IsNullOrEmpty(configurationManagerAttributes.Description) ? GetDescription(section, key) : configurationManagerAttributes.Description); ConfigEntry<T> val = _config.Bind<T>(section, key, defaultValue, new ConfigDescription(text, acceptableValue, new object[1] { configurationManagerAttributes })); if (_logger != null) { LogConfigEntry<T>(val, configurationManagerAttributes); } return val; } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, AcceptableValueBase acceptableValue = null, Action<ConfigurationManagerAttributes> initializer = null) { return Bind(section, Order--, key, defaultValue, acceptableValue, initializer); } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, (T, T) acceptableValue, Action<ConfigurationManagerAttributes> initializer = null) where T : IComparable { var (val, val2) = acceptableValue; return Bind(section, key, defaultValue, (AcceptableValueBase)(object)new AcceptableValueRange<T>(val, val2), initializer); } public ConfigEntry<T> Bind<T>(string key, T defaultValue, AcceptableValueBase acceptableValue = null, Action<ConfigurationManagerAttributes> initializer = null) { return Bind(Section, key, defaultValue, acceptableValue, initializer); } public ConfigEntry<T> Bind<T>(string key, T defaultValue, (T, T) acceptableValue, Action<ConfigurationManagerAttributes> initializer = null) where T : IComparable { return Bind(Section, key, defaultValue, acceptableValue, initializer); } private string GetSection(string section) { return _localization.Translate("@config_" + section + "_section"); } private string GetName(string section, string key) { return _localization.Translate("@config_" + section + "_" + key + "_name"); } private string GetDescription(string section, string key) { return _localization.Translate("@config_" + section + "_" + key + "_description"); } } public class StringList : ICollection<string>, IEnumerable<string>, IEnumerable { private readonly HashSet<string> _values; public int Count => _values.Count; public bool IsReadOnly => false; public StringList() { _values = new HashSet<string>(); } public StringList(IEnumerable<string> collection) { _values = new HashSet<string>(collection); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator<string> GetEnumerator() { return _values.GetEnumerator(); } public void Add(string item) { _values.Add(item); } public void Clear() { _values.Clear(); } public bool Contains(string item) { return _values.Contains(item); } public void CopyTo(string[] array, int arrayIndex) { _values.CopyTo(array, arrayIndex); } public bool Remove(string item) { return _values.Remove(item); } public bool TryAdd(string item) { return _values.Add(item); } } public class LocalizedDescriptionAttribute : DescriptionAttribute { private readonly string _prefix; private readonly string _key; public override string Description => L10N.Translate(_prefix, _key); public LocalizedDescriptionAttribute(string prefix, string key) : base(key) { _prefix = prefix; _key = key; } public LocalizedDescriptionAttribute(string key) : this("", key) { } } public class AcceptableValueEnum<T> : AcceptableValueBase where T : Enum { private readonly bool _isFlags; private readonly IList<T> _values; public AcceptableValueEnum(params T[] values) : base(typeof(T)) { _isFlags = ((AcceptableValueBase)this).ValueType.GetCustomAttributes(typeof(FlagsAttribute), inherit: false).Any(); _values = MakeValues(((AcceptableValueBase)this).ValueType, (IReadOnlyCollection<T>)(object)values, _isFlags); } private static IList<T> MakeValues(Type type, IReadOnlyCollection<T> values, bool isFlags) { object collection; if (values.Count != 0) { collection = values; } else { collection = Enum.GetValues(type).OfType<T>(); } List<T> list = new List<T>((IEnumerable<T>)collection); if (!isFlags) { return list; } HashSet<long> hashSet = new HashSet<long>(); foreach (long item in list.Select((T @enum) => Convert.ToInt64(@enum))) { long[] array = hashSet.ToArray(); foreach (long num in array) { hashSet.Add(num | item); } hashSet.Add(item); } return hashSet.Select((long x) => Enum.ToObject(type, x)).Cast<T>().ToList(); } public override object Clamp(object value) { if (!((AcceptableValueBase)this).IsValid(value)) { return _values[0]; } return value; } public override bool IsValid(object value) { if (value is T item) { return _values.Contains(item); } if (!(value is IConvertible)) { return false; } long @long = Convert.ToInt64(value); return _values.Any((T x) => Convert.ToInt64(x) == @long); } public override string ToDescriptionString() { StringBuilder stringBuilder = new StringBuilder(); Type type = typeof(T); List<string> list = (from x in _values where Enum.IsDefined(type, x) select Enum.GetName(type, x)).ToList(); stringBuilder.Append("# Acceptable values: ").Append(string.Join(", ", list)); if (!_isFlags) { return stringBuilder.ToString(); } List<string> list2 = list.Where((string x) => !string.Equals(x, "none", StringComparison.OrdinalIgnoreCase) && !string.Equals(x, "all", StringComparison.OrdinalIgnoreCase)).Take(2).ToList(); if (list2.Count == 2) { stringBuilder.Append('\n').Append("# Multiple values can be set at the same time by separating them with , (e.g. ").Append(string.Join(", ", list2)) .Append(")"); } return stringBuilder.ToString(); } } public static class ConfigurationCustomDrawer { public delegate bool IsMatchConfig(Type type, AcceptableValueBase acceptableValue); public delegate Action<ConfigEntryBase> CustomDrawerSupplier(); private const string L10NPrefix = "mod_utils"; private const string EnabledKey = "@config_button_enabled"; private const string DisabledKey = "@config_button_disabled"; private const string AddKey = "@config_button_add"; private const string RemoveKey = "@config_button_remove"; private static readonly Dictionary<IsMatchConfig, CustomDrawerSupplier> CustomDrawers; static ConfigurationCustomDrawer() { Dictionary<string, string> field = Reflections.GetField<Dictionary<string, string>>(Localization.instance, "m_translations"); string translationKey = L10N.GetTranslationKey("mod_utils", "@config_button_enabled"); if (!field.ContainsKey(translationKey)) { field.Add(translationKey, "Enabled"); } translationKey = L10N.GetTranslationKey("mod_utils", "@config_button_disabled"); if (!field.ContainsKey(translationKey)) { field.Add(translationKey, "Disabled"); } translationKey = L10N.GetTranslationKey("mod_utils", "@config_button_add"); if (!field.ContainsKey(translationKey)) { field.Add(translationKey, "Add"); } translationKey = L10N.GetTranslationKey("mod_utils", "@config_button_remove"); if (!field.ContainsKey(translationKey)) { field.Add(translationKey, "Remove"); } CustomDrawers = new Dictionary<IsMatchConfig, CustomDrawerSupplier> { { IsBool, () => Bool }, { IsFloatWithRange, () => FloatSlider }, { IsStringList, StringList }, { IsFlagsEnum, () => Flags } }; } private static bool IsBool(Type type, AcceptableValueBase acceptableValue) { return type == typeof(bool); } private static bool IsFloatWithRange(Type type, AcceptableValueBase acceptableValue) { if (type == typeof(float)) { return acceptableValue is AcceptableValueRange<float>; } return false; } private static bool IsStringList(Type type, AcceptableValueBase acceptableValue) { if (type == typeof(float)) { return acceptableValue is AcceptableValueRange<float>; } return false; } private static bool IsFlagsEnum(Type type, AcceptableValueBase acceptableValue) { if (type.IsEnum) { return type.GetCustomAttributes(typeof(FlagsAttribute), inherit: false).Any(); } return false; } public static void Bool(ConfigEntryBase entry) { bool flag = (bool)entry.BoxedValue; string text = L10N.Translate("mod_utils", flag ? "@config_button_enabled" : "@config_button_disabled"); bool flag2 = GUILayout.Toggle(flag, text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (flag2 != flag) { entry.BoxedValue = flag2; } } public static void FloatSlider(ConfigEntryBase entry) { AcceptableValueRange<float> val = (AcceptableValueRange<float>)(object)entry.Description.AcceptableValues; float num = (float)entry.BoxedValue; float minValue = val.MinValue; float maxValue = val.MaxValue; float num2 = GUILayout.HorizontalSlider(num, minValue, maxValue, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); num2 = Mathf.Floor(num2 * 100f) / 100f; if (Math.Abs(num2 - num) > Mathf.Abs(maxValue - minValue) / 1000f) { entry.BoxedValue = num2; } string text = num.ToString("0.00", CultureInfo.InvariantCulture); string text2 = GUILayout.TextField(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); if (text2 == text) { return; } try { num2 = (float)Convert.ToDouble(text2, CultureInfo.InvariantCulture); object boxedValue = Convert.ChangeType(((AcceptableValueBase)val).Clamp((object)num2), entry.SettingType, CultureInfo.InvariantCulture); entry.BoxedValue = boxedValue; } catch (FormatException) { } } private static Action<ConfigEntryBase> StringList() { string inputText = ""; return delegate(ConfigEntryBase entry) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min(Screen.width, 650); int num2 = num - Mathf.RoundToInt((float)num / 2.5f) - 115; string text = L10N.Translate("mod_utils", "@config_button_add"); string text2 = L10N.Translate("mod_utils", "@config_button_remove"); StringList stringList = new StringList((StringList)entry.BoxedValue); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth((float)num2) }); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); inputText = GUILayout.TextField(inputText, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }) && !string.IsNullOrEmpty(inputText)) { if (stringList.TryAdd(inputText)) { entry.BoxedValue = stringList; } inputText = ""; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); double num3 = 0.0; foreach (string item in stringList.ToList()) { int num4 = Mathf.FloorToInt(GUI.skin.label.CalcSize(new GUIContent(item)).x) + Mathf.FloorToInt(GUI.skin.button.CalcSize(new GUIContent(text2)).x); num3 += (double)num4; if (num3 > (double)num2) { GUILayout.EndHorizontal(); num3 = num4; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); } GUILayout.Label(item, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); if (GUILayout.Button(text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }) && stringList.Remove(item)) { entry.BoxedValue = stringList; } } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); }; } public static void Flags(ConfigEntryBase entry) { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min(Screen.width, 650); int num2 = num - Mathf.RoundToInt((float)num / 2.5f) - 115; Type settingType = entry.SettingType; long num3 = Convert.ToInt64(entry.BoxedValue); AcceptableValueBase acceptableValues = entry.Description.AcceptableValues; GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth((float)num2) }); int num4 = 0; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); foreach (object value2 in Enum.GetValues(settingType)) { if (acceptableValues != null && !acceptableValues.IsValid(value2)) { continue; } long num5 = Convert.ToInt64(value2); if (num5 != 0L) { string text = GetFlagsLabel(settingType, value2); int num6 = Mathf.FloorToInt(GUI.skin.toggle.CalcSize(new GUIContent(text + "_")).x); num4 += num6; if (num4 > num2) { GUILayout.EndHorizontal(); num4 = num6; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); } GUI.changed = false; bool flag = GUILayout.Toggle((num3 & num5) == num5, text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); if (GUI.changed) { long value = (flag ? (num3 | num5) : (num3 & ~num5)); entry.BoxedValue = Enum.ToObject(settingType, value); } } } GUILayout.EndHorizontal(); GUI.changed = false; GUILayout.EndVertical(); GUILayout.FlexibleSpace(); static string GetFlagsLabel(Type type, object @object) { return (type.GetMember(Enum.GetName(type, @object) ?? "").FirstOrDefault()?.GetCustomAttributes(typeof(DescriptionAttribute), inherit: false).OfType<DescriptionAttribute>().FirstOrDefault())?.Description ?? @object.ToString(); } } public static Action<ConfigEntryBase> MultiSelect<T>(Func<IEnumerable<T>> allElementSupplier, Func<T, string> labelGenerator) where T : IComparable<T> { return delegate(ConfigEntryBase entry) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Min(Screen.width, 650); int num2 = num - Mathf.RoundToInt((float)num / 2.5f) - 115; GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth((float)num2) }); Type settingType = entry.SettingType; ConstructorInfo constructor = settingType.GetConstructor(new Type[1] { typeof(IEnumerable<T>) }); if ((object)constructor == null) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label($"{settingType} does not define a constructor that takes IEnumerable<{typeof(T)}> as an argument.", Array.Empty<GUILayoutOption>()); GUILayout.EndHorizontal(); GUI.changed = false; GUILayout.EndVertical(); GUILayout.FlexibleSpace(); } else { ICollection<T> collection = (ICollection<T>)constructor.Invoke(new object[1] { entry.BoxedValue }); AcceptableValueBase acceptableValues = entry.Description.AcceptableValues; int num3 = 0; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); foreach (T item in allElementSupplier()) { if (acceptableValues == null || acceptableValues.IsValid((object)item)) { string text = labelGenerator(item); int num4 = Mathf.FloorToInt(GUI.skin.toggle.CalcSize(new GUIContent(text + "_")).x); num3 += num4; if (num3 > num2) { GUILayout.EndHorizontal(); num3 = num4; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); } GUI.changed = false; bool flag = GUILayout.Toggle(collection.Contains(item), text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); if (GUI.changed) { if (flag) { collection.Add(item); } else { collection.Remove(item); } entry.BoxedValue = collection; } } } GUILayout.EndHorizontal(); GUI.changed = false; GUILayout.EndVertical(); GUILayout.FlexibleSpace(); } }; } public static void Register(IsMatchConfig matcher, CustomDrawerSupplier supplier) { CustomDrawers[matcher] = supplier; } public static Action<ConfigEntryBase> Get(Type type, AcceptableValueBase acceptableValue) { return (from x in CustomDrawers where x.Key(type, acceptableValue) select x.Value()).FirstOrDefault(); } } public sealed class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func<object, string> ObjToStr; public Func<string, object> StrToObj; } public static class Csv { public sealed class Parser { private readonly int _end; private readonly StringBuilder _fieldBuffer; private readonly List<string> _recordBuffer; private readonly string _source; private int _offset; private bool _quoted; public Parser(string source, int offset = 0) { _source = source; _end = source.Length - 1; _offset = offset; _quoted = false; _recordBuffer = new List<string>(); _fieldBuffer = new StringBuilder(); } public List<List<string>> Parse() { List<List<string>> list = new List<List<string>>(); int num = -1; while (HasNext()) { List<string> list2 = ParseLine(); int count = list2.Count; if (count != 0) { if (num == -1) { num = count; } else if (num != count) { throw new Exception("Number of fields in a record is not uniform."); } list.Add(list2); } } return list; } public bool HasNext() { return _offset < _source.Length; } public List<string> ParseLine() { _recordBuffer.Clear(); _fieldBuffer.Clear(); List<string> list = new List<string>(); while (_offset < _source.Length) { if (!ParseChar(_source[_offset])) { _offset++; break; } _offset++; } if (_fieldBuffer.Length > 0) { FlushField(); } if (_recordBuffer.Count > 0) { list.AddRange(_recordBuffer); } return list; } private bool ParseChar(char c) { while (true) { char c2; switch (c) { case '"': if (_quoted && _offset < _end) { _offset++; c2 = _source[_offset]; if (c2 != '"') { goto IL_0052; } _fieldBuffer.Append(c2); } else { _quoted = !_quoted && _offset < _end; } break; case ',': if (_quoted) { _fieldBuffer.Append(c); } else { FlushField(); } break; case '\r': { int num = Math.Min(_offset + 1, _end); if (_source[num] == '\n') { _offset = num; } if (!_quoted) { if (_fieldBuffer.Length > 0) { FlushField(); } return false; } _fieldBuffer.Append('\n'); break; } case '\n': if (!_quoted) { if (_fieldBuffer.Length > 0) { FlushField(); } return false; } _fieldBuffer.Append('\n'); break; default: _fieldBuffer.Append(c); break; } break; IL_0052: _quoted = false; c = c2; } return true; } private void FlushField() { _recordBuffer.Add(_fieldBuffer.ToString().Trim()); _fieldBuffer.Clear(); } } private static readonly char[] MustQuoteChars = new char[4] { '"', ',', '\r', '\n' }; public static string Escape(string field) { if (field.IndexOfAny(MustQuoteChars) != -1) { return "\"" + field.Replace("\"", "\"\"") + "\""; } return field; } public static List<List<string>> Parse(string csv) { return new Parser(csv).Parse(); } public static List<string> ParseLine(string line) { return new Parser(line).ParseLine(); } } public class InstanceCache<T> : MonoBehaviour { private static readonly HashSet<T> Cache; private T _instance; public static Action<T> OnCacheAdded { get; set; } public static Action<T> OnCacheRemoved { get; set; } static InstanceCache() { Cache = new HashSet<T>(); } private void Awake() { _instance = GetInstance(); if (_instance != null) { object obj = _instance; Object val = (Object)((obj is Object) ? obj : null); if (val == null || Object.op_Implicit(val)) { Cache.Add(_instance); OnCacheAdded?.Invoke(_instance); return; } } throw new NullReferenceException("Failed to acquire instance."); } private void OnDestroy() { Cache.Remove(_instance); OnCacheRemoved?.Invoke(_instance); _instance = default(T); } protected virtual T GetInstance() { return ((Component)this).GetComponent<T>(); } public static IEnumerable<T> GetAllInstance() { return Cache.ToList(); } } public static class Inventories { public static IEnumerable<ItemData> GetItems(Inventory inventory, string name, int quality = -1, bool isPrefabName = false) { return from data in inventory.GetAllItems() where (isPrefabName ? (((Object)data.m_dropPrefab).name == name) : (data.m_shared.m_name == name)) && (quality < 0 || data.m_quality == quality) select data; } public static int AddItem(Inventory inventory, GameObject prefab, int amount, int quality = -1) { ItemData val = prefab.GetComponent<ItemDrop>().m_itemData.Clone(); val.m_dropPrefab = prefab; val.m_stack = Mathf.Min(amount, val.m_shared.m_maxStackSize); val.m_quality = Mathf.Clamp(quality, 1, val.m_shared.m_maxQuality); int num = inventory.CountItems(val.m_shared.m_name, quality, true); inventory.AddItem(val); return inventory.CountItems(val.m_shared.m_name, quality, true) - num; } public static int FillFreeStackSpace(Inventory from, Inventory to, string name, float worldLevel, int amount, int quality = -1, bool isPrefabName = false) { if (amount <= 0) { return 0; } int num = Mathf.Min(amount, to.FindFreeStackSpace(name, worldLevel)); if (num == 0) { return 0; } int num2 = 0; List<ItemData> list = GetItems(to, name, quality, isPrefabName).ToList(); foreach (ItemData item in GetItems(from, name, quality, isPrefabName)) { foreach (ItemData item2 in list) { if (item2.m_stack >= item2.m_shared.m_maxStackSize) { continue; } int num3 = Mathf.Min(Mathf.Min(num, item.m_stack), item2.m_shared.m_maxStackSize - item2.m_stack); if (num3 != 0) { item2.m_stack += num3; num2 += num3; item.m_stack -= num3; num -= num3; if (item.m_stack == 0) { from.RemoveItem(item); } else { Reflections.InvokeMethod(from, "Changed"); } Reflections.InvokeMethod(to, "Changed"); if (num == 0) { goto end_IL_0127; } if (item.m_stack == 0) { break; } } } continue; end_IL_0127: break; } return num2; } public static int FillFreeStackSpace(Inventory inventory, string name, int amount, int quality = -1, bool isPrefabName = false) { if (amount <= 0) { return 0; } int num = amount; int num2 = 0; foreach (ItemData item in GetItems(inventory, name, quality, isPrefabName)) { if (item.m_stack >= item.m_shared.m_maxStackSize) { continue; } int num3 = Mathf.Min(num, item.m_shared.m_maxStackSize - item.m_stack); if (num3 > 0) { item.m_stack += num3; num2 += num3; num -= num3; Reflections.InvokeMethod(inventory, "Changed"); if (num == 0) { break; } } } return num2; } public static bool HaveItem(Inventory inventory, string name, int amount, int quality = -1, bool isPrefabName = false) { int num = 0; foreach (ItemData item in GetItems(inventory, name, quality, isPrefabName)) { num += item.m_stack; if (num >= amount) { return true; } } return false; } public static int RemoveItem(Inventory inventory, string name, int amount, int quality = -1, bool isPrefabName = false) { if (amount <= 0) { return 0; } int num = 0; foreach (ItemData item in GetItems(inventory, name, quality, isPrefabName)) { int num2 = Mathf.Min(amount, item.m_stack); item.m_stack -= num2; num += num2; amount -= num2; if (item.m_stack == 0) { inventory.RemoveItem(item); } else { Reflections.InvokeMethod(inventory, "Changed"); } if (amount == 0) { break; } } return num; } } public static class Json { public static T Parse<T>(string jsonText, ReaderOption option) { return JsonMapper.ToObject<T>(new JsonReader(jsonText) { AllowComments = option.AllowComments, AllowSingleQuotedStrings = option.AllowSingleQuotedStrings, SkipNonMembers = option.SkipNonMembers }); } public static T Parse<T>(string jsonText) { return Parse<T>(jsonText, new ReaderOption { AllowComments = true, AllowSingleQuotedStrings = false, SkipNonMembers = true }); } public static string ToString(object obj, WriterOption option) { StringBuilder stringBuilder = new StringBuilder(); JsonMapper.ToJson(obj, new JsonWriter(stringBuilder) { IndentValue = option.IndentSize, PrettyPrint = option.Pretty }); return stringBuilder.ToString(); } public static string ToString(object obj) { return ToString(obj, new WriterOption { Pretty = false, IndentSize = 0 }); } } public struct ReaderOption { public bool AllowComments; public bool AllowSingleQuotedStrings; public bool SkipNonMembers; } public struct WriterOption { public bool Pretty; public int IndentSize; } public class L10N { private static readonly Regex InternalNamePattern; private static readonly Regex WordPattern; private static readonly Localization Localization; private readonly string _prefix; static L10N() { InternalNamePattern = new Regex("^(\\$|@)(\\w|\\d|[^\\s(){}[\\]+\\-!?/\\\\&%,.:=<>])+$", RegexOptions.Compiled); WordPattern = new Regex("(\\$|@)((?:\\w|\\d|[^\\s(){}[\\]+\\-!?/\\\\&%,.:=<>])+)", RegexOptions.Compiled); Localization = Localization.instance; } public L10N(string prefix) { _prefix = prefix; } private static string InvokeTranslate(string word) { return Reflections.InvokeMethod<string>(Localization, "Translate", new object[1] { word }); } private static string InvokeInsertWords(string text, string[] words) { return Reflections.InvokeMethod<string>(Localization, "InsertWords", new object[2] { text, words }); } private static void InvokeAddWord(string key, string word) { Reflections.InvokeMethod(Localization, "AddWord", key, word); } internal static string GetTranslationKey(string prefix, string internalName) { if (string.IsNullOrEmpty(internalName)) { return ""; } return internalName[0] switch { '$' => internalName.Substring(1), '@' => prefix + "_" + internalName.Substring(1), _ => internalName, }; } internal static string Translate(string prefix, string word) { return InvokeTranslate(GetTranslationKey(prefix, word)); } public static bool IsInternalName(string text) { if (!string.IsNullOrEmpty(text)) { return InternalNamePattern.IsMatch(text); } return false; } private string GetTranslationKey(string internalName) { return GetTranslationKey(_prefix, internalName); } public void AddWord(string key, string word) { InvokeAddWord(GetTranslationKey(key), word); } public string Translate(string word) { return InvokeTranslate(GetTranslationKey(word)); } public string TranslateInternalName(string internalName) { if (IsInternalName(internalName)) { return InvokeTranslate(GetTranslationKey(internalName)); } return internalName; } public string Localize(string text) { StringBuilder stringBuilder = new StringBuilder(); int num = 0; foreach (Match item in WordPattern.Matches(text)) { GroupCollection groups = item.Groups; string word = ((groups[1].Value == "@") ? (_prefix + "_" + groups[2].Value) : groups[2].Value); stringBuilder.Append(text.Substring(num, groups[0].Index - num)); stringBuilder.Append(InvokeTranslate(word)); num = groups[0].Index + groups[0].Value.Length; } return stringBuilder.ToString(); } public string Localize(string text, params object[] args) { return InvokeInsertWords(Localize(text), Array.ConvertAll(args, (object arg) => (!(arg is string internalName)) ? arg.ToString() : TranslateInternalName(internalName))); } public string LocalizeTextOnly(string text, params object[] args) { return InvokeInsertWords(Localize(text), Array.ConvertAll(args, (object arg) => (arg as string) ?? arg.ToString())); } } public class TranslationsLoader { private static readonly Localization Localization; private static readonly string DefaultLanguage; private static readonly string JsonFilePattern; private readonly L10N _localization; private Dictionary<string, TranslationsFile> _cache; private Logger _logger; static TranslationsLoader() { Localization = Localization.instance; DefaultLanguage = "English"; JsonFilePattern = "*.json"; } public TranslationsLoader(L10N localization) { _localization = localization; } public void SetDebugLogger(Logger logger) { _logger = logger; } private bool LoadAllFile(string directory, string filePattern, string language, Func<string, string, bool> loading) { _logger?.Debug("Load translation files for " + language + " from directory: [directory: " + directory + ", file pattern: " + filePattern + "]"); return Directory.EnumerateFiles(directory, filePattern, SearchOption.AllDirectories).Count((string path) => loading(path, language)) > 0; } public void LoadJson(string languagesDir) { _cache = new Dictionary<string, TranslationsFile>(); if (!Directory.Exists(languagesDir)) { _logger?.Error("Directory does not exist: " + languagesDir); return; } string selectedLanguage = Localization.GetSelectedLanguage(); if (selectedLanguage != DefaultLanguage && !LoadAllFile(languagesDir, JsonFilePattern, DefaultLanguage, ReadJsonFile)) { _logger?.Warning("Directory does not contain a translation file for the default language: " + languagesDir); } if (!LoadAllFile(languagesDir, JsonFilePattern, selectedLanguage, ReadJsonFile)) { _logger?.Warning("Directory does not contain a translation file for the " + selectedLanguage + ": " + languagesDir); } _cache = null; } private bool ReadJsonFile(string path, string language) { if (!_cache.TryGetValue(path, out var value)) { try { value = Json.Parse<TranslationsFile>(File.ReadAllText(path)); _cache.Add(path, value); } catch (Exception arg) { _logger?.Error($"Failed to read Json file\n{arg}"); _cache.Add(path, default(TranslationsFile)); return false; } } if (!string.Equals(value.language, language, StringComparison.OrdinalIgnoreCase)) { return false; } _logger?.Debug("Load translations: " + path); foreach (KeyValuePair<string, string> translation in value.translations) { _localization.AddWord(translation.Key, translation.Value); } return true; } } [Serializable] public struct TranslationsFile { public string language; public Dictionary<string, string> translations; } public class Logger { private readonly Func<LogLevel, bool> _isEnabled; private readonly ManualLogSource _logger; public Logger(ManualLogSource logger, Func<LogLevel, bool> isEnabled) { _logger = logger; _isEnabled = isEnabled; } private void Log(LogLevel level, string message) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (_isEnabled(level)) { _logger.Log(level, (object)message); } } private void Log(LogLevel level, Func<string> message) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (_isEnabled(level)) { _logger.Log(level, (object)message()); } } public void Fatal(Func<string> message) { Log((LogLevel)1, message); } public void Fatal(string message) { Log((LogLevel)1, message); } public void Error(Func<string> message) { Log((LogLevel)2, message); } public void Error(string message) { Log((LogLevel)2, message); } public void Warning(Func<string> message) { Log((LogLevel)4, message); } public void Warning(string message) { Log((LogLevel)4, message); } public void Info(Func<string> message) { Log((LogLevel)16, message); } public void Info(string message) { Log((LogLevel)16, message); } public void Message(Func<string> message) { Log((LogLevel)8, message); } public void Message(string message) { Log((LogLevel)8, message); } public void Debug(Func<string> message) { Log((LogLevel)32, message); } public void Debug(string message) { Log((LogLevel)32, message); } } public abstract class ObjectNode<TNode, TNetwork> : MonoBehaviour where TNode : ObjectNode<TNode, TNetwork> where TNetwork : ObjectNetwork<TNode, TNetwork> { protected static readonly HashSet<TNode> ObjectNodes; private TNode This => (TNode)this; public TNetwork Network { get; set; } static ObjectNode() { ObjectNodes = new HashSet<TNode>(); } protected virtual void Awake() { ObjectNodes.Add(This); ((MonoBehaviour)this).Invoke("NetworkConstruction", Random.Range(1f, 2f)); } protected virtual void OnDestroy() { ObjectNodes.Remove(This); Network?.RemoveNode(This); Network = null; } protected abstract TNetwork CreateNetwork(); protected abstract bool IsConnectable(TNode other); private void NetworkConstruction() { foreach (TNode item in ObjectNodes.Where(IsConnectable)) { if (Network == item.Network) { continue; } if (Network == null) { Network = item.Network; Network.AddNode(This); continue; } if (item.Network == null) { item.Network = Network; item.Network.AddNode(item); continue; } TNetwork obj = ((Network.NodeCount >= item.Network.NodeCount) ? item.Network : Network); TNetwork network = ((Network.NodeCount >= item.Network.NodeCount) ? Network : item.Network); foreach (TNode allNode in obj.GetAllNodes()) { allNode.Network.RemoveNode(allNode); allNode.Network = network; allNode.Network.AddNode(allNode); } } if (Network == null) { Network = CreateNetwork(); Network.AddNode(This); } } } public class ObjectNetwork<TNode, TNetwork> where TNode : ObjectNode<TNode, TNetwork> where TNetwork : ObjectNetwork<TNode, TNetwork> { private readonly HashSet<TNode> _nodes; protected Action<IEnumerable<TNode>> OnNodeChanged { get; set; } public bool IsDirty { get; private set; } public int NodeCount => _nodes.Count; protected ObjectNetwork() { _nodes = new HashSet<TNode>(); } public IEnumerable<TNode> GetAllNodes() { return _nodes.ToList(); } public void AddNode(TNode node) { if (_nodes.Add(node)) { IsDirty = true; } } public void RemoveNode(TNode node) { if (_nodes.Remove(node)) { IsDirty = true; } } public void Update() { if (IsDirty) { OnNodeChanged?.Invoke(GetAllNodes()); IsDirty = false; } } } public static class Objects { private static readonly ConditionalWeakTable<Component, ZNetView> ZNetViewCache; private static readonly ConditionalWeakTable<Component, string> NameCache; static Objects() { ZNetViewCache = new ConditionalWeakTable<Component, ZNetView>(); NameCache = new ConditionalWeakTable<Component, string>(); } public static string GetPrefabName(GameObject gameObject) { return Utils.GetPrefabName(gameObject); } public static string GetName(Component component) { if (!Object.op_Implicit((Object)(object)component)) { return ""; } if (NameCache.TryGetValue(component, out var value)) { return value; } value = (from x in ((object)component).GetType().GetFields(AccessTools.all) where x.Name == "m_name" && x.FieldType == typeof(string) select x.GetValue(component) as string).FirstOrDefault(); if (string.IsNullOrEmpty(value)) { Hoverable component2 = component.GetComponent<Hoverable>(); if (component2 != null) { HoverText val = (HoverText)(object)((component2 is HoverText) ? component2 : null); value = ((val != null) ? val.m_text : component2.GetHoverName()); } if (string.IsNullOrEmpty(value)) { value = Utils.GetPrefabName(component.gameObject); } } NameCache.Add(component, value); return value; } public static bool GetZNetView(Component component, out ZNetView zNetView) { if (!Object.op_Implicit((Object)(object)component)) { zNetView = null; return false; } if (ZNetViewCache.TryGetValue(component, out zNetView)) { return Object.op_Implicit((Object)(object)zNetView); } zNetView = (from x in ((object)component).GetType().GetFields(AccessTools.all) where x.Name == "m_nview" && x.FieldType == typeof(ZNetView) select x).Select(delegate(FieldInfo x) { object? value = x.GetValue(component); return (ZNetView)((value is ZNetView) ? value : null); }).FirstOrDefault() ?? component.GetComponent<ZNetView>(); ZNetViewCache.Add(component, zNetView); return (Object)(object)zNetView != (Object)null; } public static bool GetZdoid(Component component, out ZDOID id) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) id = (GetZNetView(component, out var zNetView) ? zNetView.GetZDO() : null)?.m_uid ?? ZDOID.None; return id != ZDOID.None; } public static float Distance(Component obj1, Component obj2) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) return Vector3.Distance(obj1.transform.position, obj2.transform.position); } public static List<(Collider collider, T obj, float distance)> GetInsideSphere<T>(Vector3 origin, float radius, Func<Collider, T> convertor, Collider[] buffer, int layerMask = -1) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) List<(Collider, T, float)> list = new List<(Collider, T, float)>(); int num = Physics.OverlapSphereNonAlloc(origin, radius, buffer, layerMask); for (int i = 0; i < num; i++) { Collider val = buffer[i]; T val2 = convertor(val); if (val2 != null) { list.Add((val, val2, Vector3.Distance(origin, ((Component)val).transform.position))); } } return list; } public static List<(Collider collider, T obj, float distance)> GetInsideSphere<T>(Vector3 origin, float radius, Func<Collider, T> convertor, int bufferSize = 128, int layerMask = -1) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return GetInsideSphere(origin, radius, convertor, (Collider[])(object)new Collider[bufferSize], layerMask); } } public static class Reflections { public static TR InvokeStaticMethod<T, TR>(string methodName, params object[] args) { return Traverse.Create<T>().Method(methodName, args).GetValue<TR>(args); } public static void InvokeStaticMethod<T>(string methodName, params object[] args) { Traverse.Create<T>().Method(methodName, args).GetValue(args); } public static TR InvokeStaticMethod<T, TR>(string methodName) { return Traverse.Create<T>().Method(methodName, Array.Empty<object>()).GetValue<TR>(); } public static void InvokeStaticMethod<T>(string methodName) { Traverse.Create<T>().Method(methodName, Array.Empty<object>()).GetValue(); } public static T InvokeMethod<T>(object instance, string methodName, params object[] args) { return Traverse.Create(instance).Method(methodName, args).GetValue<T>(args); } public static void InvokeMethod(object instance, string methodName, params object[] args) { Traverse.Create(instance).Method(methodName, args).GetValue(args); } public static T InvokeMethod<T>(object instance, string methodName) { return Traverse.Create(instance).Method(methodName, Array.Empty<object>()).GetValue<T>(); } public static void InvokeMethod(object instance, string methodName) { Traverse.Create(instance).Method(methodName, Array.Empty<object>()).GetValue(); } public static TType GetStaticField<TClass, TType>(string fieldName) { return Traverse.Create<TClass>().Field<TType>(fieldName).Value; } public static TType SetStaticField<TClass, TType>(string fieldName, TType value) { return Traverse.Create<TClass>().Field<TType>(fieldName).Value = value; } public static T GetField<T>(object instance, string fieldName) { return Traverse.Create(instance).Field<T>(fieldName).Value; } public static void SetField<T>(object instance, string fieldName, T value) { Traverse.Create(instance).Field<T>(fieldName).Value = value; } } public class SpriteLoader { private static readonly Dictionary<string, Texture2D> TextureCache; private Logger _logger; static SpriteLoader() { TextureCache = new Dictionary<string, Texture2D>(); } private static Sprite CreateSprite(Texture2D texture, int width, int height) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) return Sprite.Create(texture, new Rect(0f, 0f, (float)width, (float)height), Vector2.zero); } public static string GetTextureFileName(Sprite sprite) { using (IEnumerator<KeyValuePair<string, Texture2D>> enumerator = TextureCache.Where((KeyValuePair<string, Texture2D> pair) => (Object)(object)pair.Value == (Object)(object)sprite.texture).GetEnumerator()) { if (enumerator.MoveNext()) { return Path.GetFileName(enumerator.Current.Key); } } return ""; } public void SetDebugLogger(Logger logger) { _logger = logger; } public Sprite Load(string texturePath, int width, int height) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown if (!File.Exists(texturePath)) { return null; } if (TextureCache.TryGetValue(texturePath, out var value)) { if (!((Object)(object)value != (Object)null)) { return null; } return CreateSprite(value, width, height); } try { _logger?.Info("Try to create sprite: " + texturePath); Texture2D val = new Texture2D(0, 0); ImageConversion.LoadImage(val, File.ReadAllBytes(texturePath)); TextureCache.Add(texturePath, val); return CreateSprite(val, width, height); } catch (Exception arg) { _logger?.Error($"Failed to create sprite: {texturePath}\n{arg}"); TextureCache.Add(texturePath, null); return null; } } } } namespace Automatics { [BepInPlugin("net.eidee.valheim.automatics", "Automatics", "1.5.1")] public class UnityPlugin : BaseUnityPlugin { private const string ModId = "net.eidee.valheim.automatics"; private const string ModName = "Automatics"; private const string ModVersion = "1.5.1"; private void Awake() { Automatics.Initialize((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger); } } internal static class Automatics { public const string L10NPrefix = "automatics"; private static string _modLocation; private static string _guid; private static List<string> _allResourcesDirectory; public static BaseUnityPlugin Plugin { get; private set; } public static Logger Logger { get; private set; } public static L10N L10N { get; private set; } private static IEnumerable<string> GetAllResourcesDirectory() { if (_allResourcesDirectory != null) { return _allResourcesDirectory; } _allResourcesDirectory = new List<string> { _modLocation }; string pluginPath = Paths.PluginPath; if (!Directory.Exists(pluginPath)) { return _allResourcesDirectory; } string[] directories = Directory.GetDirectories(pluginPath); foreach (string text in directories) { if (File.Exists(Path.Combine(text, "automatics-child-mod"))) { _allResourcesDirectory.Add(text); } else if (File.Exists(Path.Combine(text, "automatics-resources-marker"))) { _allResourcesDirectory.Add(text); } } return _allResourcesDirectory; } private static void InitializeModules(Assembly assembly) { foreach (var item2 in from x in ((IEnumerable<Type>)AccessTools.GetTypesFromAssembly(assembly)).SelectMany((Func<Type, IEnumerable<MethodInfo>>)AccessTools.GetDeclaredMethods) select (x, x.GetCustomAttributes(typeof(AutomaticsInitializerAttribute), inherit: false).OfType<AutomaticsInitializerAttribute>().FirstOrDefault()) into x where x.Item2 != null orderby x.Item2.Order select x) { MethodInfo item = item2.Item1; try { item.Invoke(null, new object[0]); } catch (Exception arg) { Logger.Error($"Error while initializing {item.Name}\n{arg}"); } } } public static IEnumerable<string> GetAllResourcePath(string pathname) { return from x in GetAllResourcesDirectory() select Path.Combine(x, pathname); } public static string GetHarmonyId(string moduleName) { return _guid + "." + moduleName; } public static void Initialize(BaseUnityPlugin plugin, ManualLogSource logger) { Plugin = plugin; Logger = new Logger(logger, Config.IsLogEnabled); _modLocation = Path.GetDirectoryName(Plugin.Info.Location) ?? ""; _guid = Plugin.Info.Metadata.GUID; Logger.Debug("Mod location: " + _modLocation); ConfigMigration.Migration(Plugin.Config); ValheimObject.Initialize(GetAllResourcePath("Data")); L10N = new L10N("automatics"); TranslationsLoader translationsLoader = new TranslationsLoader(L10N); foreach (string item in GetAllResourcePath("Languages")) { translationsLoader.LoadJson(item); } Config.Initialize(Plugin.Config); Hooks.OnInitTerminal = (Action)Delegate.Combine(Hooks.OnInitTerminal, new Action(Commands.Register)); Harmony.CreateAndPatchAll(typeof(Patches), _guid); InitializeModules(Assembly.GetExecutingAssembly()); ValheimObject.PostInitialize(); } } [AttributeUsage(AttributeTargets.Method)] [MeansImplicitUse] public class AutomaticsInitializerAttribute : Attribute { public int Order { get; } public AutomaticsInitializerAttribute(int order = 0) { Order = order; } } internal static class Commands { public static void Register() { new ShowCommands().Register(); new PrintNames().Register(); new PrintObjects().Register(); new RemoveMapPins().Register(); } } internal static class Config { private const int NexusID = 1700; private static ConfigEntry<bool> _logEnabled; private static ConfigEntry<LogLevel> _allowedLogLevel; public static bool LogEnabled => _logEnabled.Value; public static LogLevel AllowedLogLevel => _allowedLogLevel.Value; public static bool Initialized { get; private set; } public static Configuration Instance { get; private set; } public static bool IsLogEnabled(LogLevel level) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 if (Initialized) { if (LogEnabled) { return (AllowedLogLevel & level) > 0; } return false; } return true; } public static ConfigEntry<List<ObjectElement>> BindCustomValheimObject(this Configuration config, string key, ValheimObject obj) { ConfigEntry<List<ObjectElement>> entry = config.Bind(key, new List<ObjectElement>()); if (entry.Value.Any()) { obj.RegisterCustom(entry.Value); } entry.SettingChanged += delegate { obj.RegisterCustom(entry.Value); }; return entry; } public static ConfigEntry<StringList> BindValheimObjectList(this Configuration config, string key, ValheimObject obj, IEnumerable<string> defaults = null, IEnumerable<string> includes = null, IEnumerable<string> excludes = null) { List<string> list = new List<string>(); list.AddRange(defaults ?? (from x in obj.GetAllElements() select x.identifier)); if (includes != null) { list.RemoveAll((string x) => !includes.Contains(x)); } if (excludes != null) { list.RemoveAll(excludes.Contains<string>); } return config.Bind(key, new StringList(list), null, delegate(ConfigurationManagerAttributes x) { x.CustomDrawer = ConfigurationCustomDrawer.MultiSelect(() => from y in obj.GetAllElements() select y.identifier, (string identifier) => (!obj.GetName(identifier, out var name)) ? "INTERNAL ERROR" : Automatics.L10N.TranslateInternalName(name)); }); } public static void Initialize(ConfigFile config) { if (!Initialized) { Instance = new Configuration(config, Automatics.L10N); Instance.ChangeSection("hidden"); Instance.Bind("NexusID", 1700, null, delegate(ConfigurationManagerAttributes x) { x.Browsable = false; x.ReadOnly = true; }); Instance.ChangeSection("system"); _logEnabled = Instance.Bind("enable_logging", defaultValue: false); _allowedLogLevel = Instance.Bind<LogLevel>("log_level_to_allow_logging", (LogLevel)15); Instance.ChangeSection("general"); Instance.BindCustomValheimObject("custom_animal", ValheimObject.Animal); Instance.BindCustomValheimObject("custom_dungeon", ValheimObject.Dungeon); Instance.BindCustomValheimObject("custom_flora", ValheimObject.Flora); Instance.BindCustomValheimObject("custom_mineral", ValheimObject.Mineral); Instance.BindCustomValheimObject("custom_monster", ValheimObject.Monster); Instance.BindCustomValheimObject("custom_spawner", ValheimObject.Spawner); Instance.BindCustomValheimObject("custom_spot", ValheimObject.Spot); Initialized = true; } } } public enum AutomaticsModule { [LocalizedDescription("automatics", "@message_module_enabled")] Enabled, [LocalizedDescription("automatics", "@message_module_disabled")] Disabled } public enum Message { [LocalizedDescription("automatics", "@message_none")] None, [LocalizedDescription("automatics", "@message_center")] Center, [LocalizedDescription("automatics", "@message_top_left")] TopLeft } internal static class ConfigMigration { private class Config { public int Offset; public string Category; public string Key; public string Value; } private delegate bool Operation(string category, List<string> lines, int begin, int end); private readonly struct Version : IComparable<Version> { private readonly int _major; private readonly int _minor; private readonly int _patch; public Version(int major, int minor, int patch) { _major = major; _minor = minor; _patch = patch; } public override string ToString() { return $"v{_major}.{_minor}.{_patch}"; } public int CompareTo(Version other) { int major; if (_major != other._major) { major = _major; return major.CompareTo(other._major); } if (_minor == other._minor) { major = _patch; return major.CompareTo(other._patch); } major = _minor; return major.CompareTo(other._minor); } public static bool operator >(Version a, Version b) { return a.CompareTo(b) > 0; } public static bool operator <(Version a, Version b) { return a.CompareTo(b) < 0; } } private static readonly Regex VersionPattern; private static readonly char[] KeyValueSeparator; private static readonly Dictionary<string, List<Config>> ConfigCache; static ConfigMigration() { VersionPattern = new Regex("v(\\d+)\\.(\\d+)\\.(\\d+)$", RegexOptions.Compiled); KeyValueSeparator = new char[1] { '=' }; ConfigCache = new Dictionary<string, List<Config>>(); } private static Operation RenameCategory(string newCategory) { return delegate(string category, List<string> lines, int begin, int end) { string text = lines[begin]; lines[begin] = newCategory; Automatics.Logger.Message("Rename category: " + text + " => " + newCategory); return true; }; } private static Operation RemoveConfig(string key) { return delegate(string category, List<string> lines, int begin, int end) { foreach (Config item in FindConfig(category, key, lines, begin, end)) { string key2 = item.Key; item.Key = ""; UpdateConfig(item, lines); Automatics.Logger.Message("Remove config: " + category + " " + key2); } return true; }; } private static Operation RenameConfig(string key, string newKey) { return delegate(string category, List<string> lines, int begin, int end) { foreach (Config item in FindConfig(category, key, lines, begin, end)) { string key2 = item.Key; item.Key = (GetMatcher(key).IsRegex ? Regex.Replace(key, key, newKey) : newKey); UpdateConfig(item, lines); Automatics.Logger.Message("Rename config: " + category + " " + key2 + " => " + item.Key); } return true; }; } private static Operation ReplaceValue(string key, string value, string newValue) { return delegate(string category, List<string> lines, int begin, int end) { foreach (Config item in from x in FindConfig(category, key, lines, begin, end) where string.Equals(x.Value, value, StringComparison.OrdinalIgnoreCase) select x) { string value2 = item.Value; item.Value = newValue; UpdateConfig(item, lines); Automatics.Logger.Message("Replace value: " + category + " " + key + " " + value2 + " => " + newValue); } return true; }; } private static IEnumerable<Config> FindConfig(string category, string key, List<string> lines, int begin = 0, int end = int.MaxValue) { (bool IsRegex, string Pattern) matcher = GetMatcher(key); if (ConfigCache.TryGetValue(category, out var value)) { return value.Where((Config x) => x.Category == category && IsMatch(x.Key, matcher)); } end = Mathf.Min(end, lines.Count); begin = Mathf.Clamp(begin, 0, end); value = new List<Config>(0); string text = ""; int i; for (i = begin; i < end; i++) { string text2 = lines[i]; if (text2.StartsWith("#")) { continue; } if (Regex.IsMatch(text2, "^\\[[\\w\\d_]+\\]$")) { text = text2; if (!ConfigCache.TryGetValue(text, out value)) { value = new List<Config>(); ConfigCache[text] = value; } } else if (!value.Any((Config x) => x.Offset == i)) { string[] array = text2.Split(KeyValueSeparator, 2, StringSplitOptions.None); if (array.Length == 2) { Config item = new Config { Offset = i, Category = text, Key = array[0].Trim(), Value = array[1].Trim() }; value.Add(item); } } } if (ConfigCache.TryGetValue(category, out value)) { return value.Where((Config x) => x.Category == category && IsMatch(x.Key, matcher)); } Automatics.Logger.Warning("No config matching condition exists: " + category + " / " + key); return Array.Empty<Config>(); } private static void UpdateConfig(Config config, List<string> lines) { if (config.Offset >= 0 || config.Offset < lines.Count) { if (string.IsNullOrEmpty(config.Key)) { lines[config.Offset] = ""; } else { lines[config.Offset] = config.Key + " = " + config.Value; } } } private static (bool IsRegex, string Pattern) GetMatcher(string pattern) { if (string.IsNullOrEmpty(pattern)) { return (false, ""); } if (!pattern.StartsWith("r/")) { return (false, pattern); } return (true, pattern.Substring(2)); } private static bool IsMatch(string value, (bool IsRegex, string Pattern) matcher) { if (!matcher.IsRegex) { if (!string.IsNullOrEmpty(matcher.Pattern)) { return string.Equals(value, matcher.Pattern, StringComparison.OrdinalIgnoreCase); } return false; } return Regex.IsMatch(value, matcher.Pattern, RegexOptions.IgnoreCase); } public static void Migration(ConfigFile config) { string configFilePath = config.ConfigFilePath; if (!File.Exists(configFilePath)) { Automatics.Logger.Error("Config file not found: " + configFilePath); return; } List<string> list = File.ReadAllLines(configFilePath).ToList(); bool flag = false; Version version = ParseVersion(list[0]); Version version2 = new Version(1, 3, 0); if (version < version2) { Automatics.Logger.Message($"Migrating config from {version} to {version2}"); flag = true; MigrationFor130(list); } version2 = new Version(1, 4, 0); if (version < version2) { Automatics.Logger.Message($"Migrating config from {version} to {version2}"); flag = true; MigrationFor140(list); } version2 = new Version(1, 4, 5); if (version < version2) { Automatics.Logger.Message($"Migrating config from {version} to {version2}"); flag = true; MigrationFor145(list); } if (flag) { File.WriteAllText(configFilePath, string.Join(Environment.NewLine, list), Encoding.UTF8); config.Reload(); } } private static void MigrationFor130(List<string> lines) { Migration(lines, new Dictionary<string, List<Operation>> { { "[logging]", new List<Operation> { RenameCategory("[system]"), RenameConfig("logging_enabled", "enable_logging"), RenameConfig("allowed_log_level", "log_level_to_allow_logging") } }, { "[automatic_door]", new List<Operation> { RenameConfig("automatic_door_enabled", "enable_automatic_door"), RenameConfig("player_search_radius_to_open", "distance_for_automatic_opening"), RenameConfig("player_search_radius_to_close", "distance_for_automatic_closing"), RenameConfig("toggle_automatic_door_enabled_key", "automatic_door_enable_disable_toggle") } }, { "[automatic_map_pinning]", new List<Operation> { RenameCategory("[automatic_mapping]"), RenameConfig("automatic_map_pinning_enabled", "enable_automatic_mapping"), RenameConfig("allow_pinning_vein", "allow_pinning_deposit"), RenameConfig("allow_pinning_vein_custom", "allow_pinning_deposit_custom"), RenameConfig("ignore_tamed_animals", "not_pinning_tamed_animals"), RenameConfig("in_ground_veins_need_wishbone", "need_to_equip_wishbone_for_underground_deposits") } }, { "[automatic_processing]", new List<Operation> { RenameConfig("automatic_processing_enabled", "enable_automatic_processing"), RenameConfig("r/^([\\w_]+)_allow_automatic_processing", "allow_processing_by_$1"), RenameConfig("r/^([\\w_]+)_container_search_range", "container_search_range_by_$1"), RenameConfig("r/^([\\w_]+)_material_count_that_suppress_automatic_process", "$1_material_count_of_suppress_processing"), RenameConfig("r/^([\\w_]+)_fuel_count_that_suppress_automatic_process", "$1_fuel_count_of_suppress_processing"), RenameConfig("r/^([\\w_]+)_product_count_that_suppress_automatic_store", "$1_product_count_of_suppress_processing") } }, { "[automatic_feeding]", new List<Operation> { RenameConfig("automatic_feeding_enabled", "enable_automatic_feeding"), RenameConfig("need_close_to_eat_the_feed", "need_get_close_to_eat_the_feed"), RenameConfig("automatic_repair_enabled", "enable_automatic_repair") } } }); } private static void MigrationFor140(List<string> lines) { Migration(lines, new Dictionary<string, List<Operation>> { { "[automatic_door]", new List<Operation> { RemoveConfig("allow_automatic_door_custom"), ReplaceValue("allow_automatic_door", "All", "WoodDoor, WoodGate, IronGate, DarkwoodGate, WoodShutter") } }, { "[automatic_mapping]", new List<Operation> { RenameConfig("dynamic_object_search_range", "dynamic_object_mapping_range"), RenameConfig("static_object_search_range", "static_object_mapping_range"), RenameConfig("location_search_range", "location_mapping_range"), RenameConfig("allow_pinning_deposit", "allow_pinning_mineral"), RemoveConfig("allow_pinning_ship"), RemoveConfig("r/^allow_pinning_[\\w]+_custom$"), RenameConfig("static_object_search_interval", "static_object_mapping_interval"), RenameConfig("need_to_equip_wishbone_for_underground_deposits", "need_to_equip_wishbone_for_underground_minerals"), RenameConfig("static_object_search_key", "static_object_mapping_key"), ReplaceValue("allow_pinning_animal", "All", "Boar, Piggy, Deer, Wolf, WolfCub, Lox, LoxCalf, Hen, Chicken, Hare, Bird, Fish"), ReplaceValue("allow_pinning_monster", "All", "Greyling, Neck, Ghost, Greydwarf, GreydwarfBrute, GreydwarfShaman, RancidRemains, Skeleton, Troll, Abomination, Blob, Draugr, DraugrElite, Leech, Oozer, Surtling, Wraith, Drake, Fenring, StoneGolem, Deathsquito, Fuling, FulingBerserker, FulingShaman, Growth, Serpent, Bat, FenringCultist, Ulv, DvergrRogue, DvergrMage, Tick, Seeker, SeekerBrood, Gjall, SeekerSoldier"), ReplaceValue("allow_pinning_flora", "All", "Dandelion, Mushroom, Raspberries, Blueberries, Carrot, CarrotSeeds, YellowMushroom, Thistle, Turnip, TurnipSeeds, Onion, OnionSeeds, Barley, Cloudberries, Flex, JotunPuffs, Magecap"), ReplaceValue("allow_pinning_mineral", "All", "CopperDeposit, TinDeposit, MudPile, ObsidianDeposit, SilverVein, PetrifiedBone, SoftTissue"), ReplaceValue("allow_pinning_spawner", "All", "GreydwarfNest, EvilBonePile, BodyPile"), ReplaceValue("allow_pinning_other", "All", "Vegvisir, Runestone, WildBeehive"), ReplaceValue("allow_pinning_dungeon", "All", "BurialChambers, TrollCave, SunkenCrypts, MountainCave, InfestedMine"), ReplaceValue("allow_pinning_spot", "All", "InfestedTree, FireHole, DrakeNest, GoblinCamp, TarPit, DvergrExcavation, DvergrGuardTower, DvergrHarbour, DvergrLighthouse, PetrifiedBone") } }, { "[automatic_processing]", new List<Operation> { RemoveConfig("r/^([\\w_]+)_product_count_of_suppress_processing") } } }); } private static void MigrationFor145(List<string> lines) { Migration(lines, new Dictionary<string, List<Operation>> { { "[automatic_processing]", new List<Operation> { ReplaceValue("r/^allow_processing_by", "All", "Craft, Refuel, Store") } } }); } private static void Migration(List<string> lines, Dictionary<string, List<Operation>> operations) { string category = ""; int begin = -1; int i; List<Operation> value; for (i = 0; i < lines.Count; i++) { string text = lines[i]; if (text.StartsWith("#") || string.IsNullOrWhiteSpace(text) || !Regex.IsMatch(text, "^\\[[\\w\\d_]+\\]$")) { continue; } if (begin > 0 && operations.TryGetValue(category, out value)) { value.RemoveAll((Operation x) => x(category, lines, begin, i)); } category = text; begin = i; } if (operations.TryGetValue(category, out value)) { value.RemoveAll((Operation x) => x(category, lines, begin, i)); } foreach (KeyValuePair<string, List<Operation>> item in operations.Where((KeyValuePair<string, List<Operation>> pair) => pair.Value.Any())) { Automatics.Logger.Warning($"{item.Value.Count} migrations for {item.Key} were not performed."); } ConfigCache.Clear(); } private static Version ParseVersion(string line) { Match match = VersionPattern.Match(line); if (!match.Success) { Automatics.Logger.Error("Invalid version string: " + line); return new Version(0, 0, 0); } return new Version(int.Parse(match.Groups[1].Value), int.Parse(match.Groups[2].Value), int.Parse(match.Groups[3].Value)); } } internal static class Hooks { public static Action<Player, ZNetView> OnPlayerAwake { get; set; } public static Action<Player, bool> OnPlayerUpdate { get; set; } public static Action<Player, float> OnPlayerFixedUpdate { get; set; } public static Action OnInitTerminal { get; set; } } [DisallowMultipleComponent] internal sealed class ContainerCache : InstanceCache<Container> { } [DisallowMultipleComponent] internal sealed class PickableCache : InstanceCache<Pickable> { } [HarmonyPatch] internal static class Patches { [HarmonyPatch(typeof(Player), "Awake")] [HarmonyPostfix] private static void Player_Awake_Postfix(Player __instance) { if (Objects.GetZNetView((Component)(object)__instance, out var zNetView)) { Hooks.OnPlayerAwake?.Invoke(__instance, zNetView); } } [HarmonyPatch(typeof(Player), "Update")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Player_Update_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown Label label = default(Label); Label label2 = default(Label); return new CodeMatcher(instructions, generator).MatchEndForward((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Player), "UpdatePlacement", (Type[])null, (Type[])null), (string)null) }).Advance(1).CreateLabel(ref label) .Insert((CodeInstruction[])(object)new CodeInstruction[3] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldloc_1, (object)null), new CodeInstruction(OpCodes.Callvirt, (object)AccessTools.Method(typeof(Action<Player, bool>), "Invoke", (Type[])null, (Type[])null)) }) .CreateLabel(ref label2) .MatchStartBackwards((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null) }) .Insert((CodeInstruction[])(object)new CodeInstruction[5] { new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Hooks), "get_OnPlayerUpdate", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Dup, (object)null), new CodeInstruction(OpCodes.Brtrue_S, (object)label2), new CodeInstruction(OpCodes.Pop, (object)null), new CodeInstruction(OpCodes.Br_S, (object)label) }) .InstructionEnumeration(); } [HarmonyPatch(typeof(Player), "FixedUpdate")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> Player_FixedUpdate_Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown Label label = default(Label); Label label2 = default(Label); return new CodeMatcher(instructions, generator).MatchStartForward((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Player), "UpdateStealth", (Type[])null, (Type[])null), (string)null) }).Advance(1).CreateLabel(ref label) .Insert((CodeInstruction[])(object)new CodeInstruction[3] { new CodeInstruction(OpCodes.Ldarg_0, (object)null), new CodeInstruction(OpCodes.Ldloc_0, (object)null), new CodeInstruction(OpCodes.Callvirt, (object)AccessTools.Method(typeof(Action<Player, float>), "Invoke", (Type[])null, (Type[])null)) }) .CreateLabel(ref label2) .MatchStartBackwards((CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null) }) .Insert((CodeInstruction[])(object)new CodeInstruction[5] { new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Hooks), "get_OnPlayerFixedUpdate", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Dup, (object)null), new CodeInstruction(OpCodes.Brtrue_S, (object)label2), new CodeInstruction(OpCodes.Pop, (object)null), new CodeInstruction(OpCodes.Br_S, (object)label) }) .InstructionEnumeration(); } [HarmonyPatch(typeof(Terminal), "InitTerminal")] [HarmonyPrefix] private static void Terminal_InitTerminal_Prefix(out bool __state, bool ___m_terminalInitialized) { __state = ___m_terminalInitialized; } [HarmonyPatch(typeof(Terminal), "InitTerminal")] [HarmonyPostfix] private static void Terminal_InitTerminal_Postfix(bool __state) { if (!__state) { Hooks.OnInitTerminal?.Invoke(); } } [HarmonyPatch(typeof(Container), "Awake")] [HarmonyPostfix] private static void Container_Awake_Postfix(Container __instance, ZNetView ___m_nview) { if (Object.op_Implicit((Object)(object)___m_nview) && ___m_nview.GetZDO() != null) { ((Component)__instance).gameObject.AddComponent<ContainerCache>(); } } [HarmonyPostfix] [HarmonyPatch(typeof(Pickable), "Awake")] private static void Pickable_Awake_Postfix(Pickable __instance, ZNetView ___m_nview) { if (Object.op_Implicit((Object)(object)___m_nview) && ___m_nview.GetZDO() != null) { ((Component)__instance).gameObject.AddComponent<PickableCache>(); } } } } namespace Automatics.Valheim { [Serializable] internal class ObjectMatcher { [NonSerialized] private bool _regex; [NonSerialized] private string _value; [NonSerialized] private Regex _pattern; public bool regex { get { return _regex; } set { _regex = value; OnUpdate(); } } public string value { get { return _value; } set { _value = value; OnUpdate(); } } public bool Matches(string name) { if (!regex || _pattern == null) { return string.Equals(name, _value, StringComparison.OrdinalIgnoreCase); } return _pattern.IsMatch(name); } private void OnUpdate() { _pattern = null; if (_regex && !string.IsNullOrEmpty(_value)) { _pattern = new Regex(_value); } } } [Serializable] internal class ObjectElement { public string identifier { get; set; } public string label { get; set; } public List<ObjectMatcher> matches { get; set; } public bool IsValid() { if (!string.IsNullOrEmpty(identifier) && !string.IsNullOrEmpty(label)) { return matches != null; } return false; } } [Serializable] internal class ObjectDataJson { public string type { get; set; } public int order { get; set; } public List<ObjectElement> values { get; set; } public bool IsValid() { if (!string.IsNullOrEmpty(type) && values != null) { return values.All((ObjectElement x) => x.IsValid()); } return false; } } internal static class ObjectElementList { private static bool _initialized; private static string EscapeForToml(string json) { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; char[] array = json.ToCharArray(); foreach (char c in array) { switch (c) { case '\\': flag = true; stringBuilder.Append("$$"); continue; case '"': if (!flag) { stringBuilder.Append('\\'); } flag = false; break; default: flag = false; break; } stringBuilder.Append(c); } return stringBuilder.ToString(); } private static string UnescapeToml(string toml) { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; char[] array = toml.ToCharArray(); foreach (char c in array) { switch (c) { case '\\': if (flag) { stringBuilder.Append('$'); } flag = false; continue; case '$': if (flag) { stringBuilder.Append('\\'); } flag = !flag; continue; } if (flag) { stringBuilder.Append('$'); } flag = false; stringBuilder.Append(c); } return stringBuilder.ToString(); } private static Action<ConfigEntryBase> GetCustomDrawer() { string identifierInput = ""; string labelInput = ""; string valueInput = ""; return delegate(ConfigEntryBase entry) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Expected O, but got Unknown //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Expected O, but got Unknown //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Expected O, but got Unknown //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Expected O, but got Unknown int num = Mathf.Min(Screen.width, 650); int num2 = num - Mathf.RoundToInt((float)num / 2.5f) - 115; string text = Automatics.L10N.Translate("mod_utils_config_button_add"); string text2 = Automatics.L10N.Translate("mod_utils_config_button_remove"); string text3 = Automatics.L10N.Translate("@config_object_element_identifier"); string text4 = Automatics.L10N.Translate("@config_object_element_label"); string text5 = Automatics.L10N.Translate("@config_object_element_value"); float num3 = Mathf.Max(new float[3] { GUI.skin.label.CalcSize(new GUIContent(text3)).x, GUI.skin.label.CalcSize(new GUIContent(text4)).x, GUI.skin.label.CalcSize(new GUIContent(text5)).x }); List<ObjectElement> list = new List<ObjectElement>((List<ObjectElement>)entry.BoxedValue); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth((float)num2) }); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(new GUIContent(text3, ""), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num3) }); identifierInput = GUILayout.TextField(string.Concat(identifierInput.Where((char x) => !char.IsWhiteSpace(x))), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(text4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num3) }); labelInput = GUILayout.TextField(labelInput, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(text5, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num3) }); valueInput = GUILayout.TextField(valueInput, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button(text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }) && !string.IsNullOrEmpty(identifierInput) && !string.IsNullOrEmpty(labelInput) && !string.IsNullOrEmpty(valueInput)) { bool flag = valueInput.StartsWith("r/"); valueInput = (flag ? valueInput.Substring(2) : valueInput); list.Add(new ObjectElement { identifier = identifierInput, label = labelInput, matches = new List<ObjectMatcher> { new ObjectMatcher { regex = flag, value = valueInput } } }); entry.BoxedValue = list; identifierInput = ""; labelInput = ""; valueInput = ""; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); double num4 = 0.0; foreach (ObjectElement item in list.ToList()) { string identifier = item.identifier; string label = item.label; string text6 = Automatics.L10N.TranslateInternalName(label); string value = item.matches[0].value; int num5 = Mathf.FloorToInt(GUI.skin.label.CalcSize(new GUIContent(text6)).x) + Mathf.FloorToInt(GUI.skin.button.CalcSize(new GUIContent(text2)).x); num4 += (double)num5; if (num4 > (double)num2) { GUILayout.EndHorizontal(); num4 = num5; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); } string text7 = Automatics.L10N.LocalizeTextOnly("@config_object_element_preview", text6, label, identifier, value); GUILayout.Label(new GUIContent(text6, text7), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); if (GUILayout.Button(text2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }) && list.Remove(item)) { entry.BoxedValue = list; } } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); }; } public static void Initialize() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown if (!_initialized) { _initialized = true; TomlTypeConverter.AddConverter(typeof(List<ObjectElement>), new TypeConverter { ConvertToObject = (string str, Type type) => (!string.IsNullOrEmpty(str)) ? Json.Parse<List<ObjectElement>>(UnescapeToml(str.Trim(new char[1] { '"' }))) : new List<ObjectElement>(), ConvertToString = delegate(object obj, Type type) { List<ObjectElement> list = (List<ObjectElement>)obj; return (!list.Any()) ? "" : ("\"" + EscapeForToml(Json.ToString(list)) + "\""); } }); ConfigurationCustomDrawer.Register((Type type, AcceptableValueBase value) => type == typeof(List<ObjectElement>), GetCustomDrawer); } } } internal class ValheimObject { private static readonly List<ObjectDataJson> JsonCache; public static readonly ValheimObject Animal; public static readonly ValheimObject Dungeon; public static readonly ValheimObject Flora; public static readonly ValheimObject Mineral; public static readonly ValheimObject Monster; public static readonly ValheimObject Spawner; public static readonly ValheimObject Spot; private readonly string _type; private readonly Dictionary<string, ObjectElement> _elements; private readonly Dictionary<string, ObjectElement> _customElements; private readonly List<ObjectElement> _allElements; static ValheimObject() { JsonCache = new List<ObjectDataJson>(); Animal = new ValheimObject("animal"); Dungeon = new ValheimObject("dungeon"); Flora = new ValheimObject("flora"); Mineral = new ValheimObject("mineral"); Monster = new ValheimObject("monster"); Spawner = new ValheimObject("spawner"); Spot = new ValheimObject("spot"); ObjectElementList.Initialize(); } public ValheimObject(string type) { _type = type; _elements = new Dictionary<string, ObjectElement>(); _customElements = new Dictionary<string, ObjectElement>(); _allElements = new List<ObjectElement>(); if (JsonCache.Any()) { Register(JsonCache); } } private static void Initialize(string directory) { if (!Directory.Exists(directory)) { Automatics.Logger.Debug("Directory does not exist: " + directory); return; } JsonCache.AddRange(from x in Directory.EnumerateFiles(directory, "*.json", SearchOption.AllDirectories).Select(delegate(string x) { try { return Json.Parse<ObjectDataJson>(File.ReadAllText(x)); } catch (Exception arg) { Automatics.Logger.Error($"Failed to read Json file\n{arg}"); return new ObjectDataJson(); } }) where x.IsValid() select x); } public static void Initialize(IEnumerable<string> directories) { foreach (string directory in directories) { Initialize(directory); } Animal.Register(JsonCache); Dungeon.Register(JsonCache); Flora.Register(JsonCache); Mineral.Register(JsonCache); Monster.Register(JsonCache); Spawner.Register(JsonCache); Spot.Register(JsonCache); } public static void PostInitialize() { JsonCache.Clear(); } private void UpdateElements() { _allElements.Clear(); _allElements.AddRange(_elements.Values); _allElements.AddRange(_customElements.Values); } private void Register(IEnumerable<ObjectDataJson> jsons) { foreach (ObjectElement item in (from x in jsons where x.type.ToLower() == _type orderby x.order select x).SelectMany((ObjectDataJson x) => x.values)) { _elements[item.identifier.ToLower()] = new ObjectElement { identifier = item.identifier, label = item.label, matches = new List<ObjectMatcher>(item.matches) }; } UpdateElements(); } public void RegisterCustom(IEnumerable<ObjectElement> elements) { _customElements.Clear(); foreach (ObjectElement element in elements) { _customElements[element.identifier.ToLower()] = new ObjectElement { identifier = element.identifier, label = element.label, matches = new List<ObjectMatcher>(element.matches) }; } UpdateElements(); } public IEnumerable<ObjectElement> GetElements() { return new List<ObjectElement>(_elements.Values); } public IEnumerable<ObjectElement> GetCustomElements() { return new List<ObjectElement>(_customElements.Values); } public IEnumerable<ObjectElement> GetAllElements() { return _allElements.ToList(); } public bool GetIdentify(string name, out string identifier) { ObjectElement objectElement = _allElements.FirstOrDefault((ObjectElement x) => x.matches.All((ObjectMatcher y) => y.Matches(name))); if (objectElement == null || !objectElement.IsValid()) { identifier = ""; return false; } identifier = objectElement.identifier; return true; } public bool GetName(string identifier, out string name) { string key = identifier.ToLower(); if (!_elements.TryGetValue(key, out var value) && !_customElements.TryGetValue(key, out value)) { name = ""; return false; } name = value.label; return true; } public bool IsDefined(string nameOrIdentify) { if (!GetIdentify(nameOrIdentify, out var identifier)) { return GetName(nameOrIdentify, out identifier); } return true; } } } namespace Automatics.ConsoleCommands { internal abstract class Command { private static readonly Dictionary<string, Command> Commands; private static readonly List<string> EmptyList; private readonly Lazy<OptionSet> _optionsLazy; protected readonly string command; protected List<string> extraOptions; private bool _showHelp; protected OptionSet options => _optionsLazy.Value; protected bool HaveExtraOption { get; set; } protected bool HaveExtraDescription { get; set; } static Command() { Commands = new Dictionary<string, Command>(); EmptyList = new List<string>(); } protected Command(string command) { this.command = command.ToLower(); _optionsLazy = new Lazy<OptionSet>(delegate { OptionSet optionSet = CreateOptionSet(); optionSet.Add("h|help", Automatics.L10N.Translate("@command_common_help_description"), delegate(string v) { _showHelp = v != null; }); return optionSet; }); extraOptions = new List<string>(); } private static IEnumerable<string> ParseArgs(string line) { List<string> list = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; bool flag2 = false; foreach (char c in line) { switch (c) { case '\\': if (!flag2) { flag2 = true; continue; } break; case '"': if (!flag2) { flag = !flag; continue; } break; case ' ': if (!flag) { list.Add(stringBuilder.ToString()); stringBuilder.Clear(); flag2 = false; continue; } break; } stringBuilder.Append(c); flag2 = false; } if (stringBuilder.Length > 0) { list.Add(stringBuilder.ToString()); } return list.Skip(1).ToList(); } protected static IEnumerable<(string Command, Command Instance)> GetAllCommands() { return Commands.Select((KeyValuePair<string, Command> x) => (x.Key, x.Value)); } [Conditional("DEBUG")] private void DebugLog() { Automatics.Logger.Debug("[COMMAND]: ### " + command); string[] array = Help().Split(new char[1] { '\n' }, StringSplitOptions.None); foreach (string text in array) { Automatics.Logger.Debug("[COMMAND]: " + text.Trim()); } } protected abstract void CommandAction(ConsoleEventArgs args); protected virtual OptionSet CreateOptionSet() { return new OptionSet(); } protected virtual List<string> GetSuggestions() { return EmptyList; } protected virtual void ResetOptions() { _showHelp = false; } protected bool ParseArgs(ConsoleEventArgs args) { ResetOptions(); try { extraOptions = options.Parse(ParseArgs(args.FullLine)); } catch (OptionException ex) { args.Context.AddString(command + ":"); args.Context.AddString(ex.Message); args.Context.AddString(Automatics.L10N.LocalizeTextOnly("@command_common_option_parse_error", command)); return false; } if (!_showHelp) { return true; } args.Context.AddString(Help()); return false; } protected string Usage() { return Automatics.L10N.Translate("@command_" + command + "_usage"); } protected string Description() { return Automatics.L10N.Translate("@command_" + command + "_description"); } protected string ExtraOption() { return Automatics.L10N.Translate("@command_" + command + "_extra_option"); } protected string ExtraDescription() { return Automatics.L10N.Translate("@command_" + command + "_extra_description"); } protected string Help() { StringWriter stringWriter = new StringWriter(); stringWriter.WriteLine(Usage()); stringWriter.WriteLine(Description()); if (HaveExtraOption) { stringWriter.WriteLine(); stringWriter.WriteLine(ExtraOption()); } stringWriter.WriteLine(); stringWriter.WriteLine(Automatics.L10N.Translate("@command_common_help_options_label")); options.WriteOptionDescriptions(stringWriter); if (HaveExtraDescription) { stringWriter.WriteLine(); stringWriter.WriteLine(ExtraDescription()); } return stringWriter.ToString(); } public string Print(bool verbose) { if (verbose) { return Help(); } return "\"" + command + "\" " + Description(); } public void Register(bool isCheat = false, bool isNetwork = false, bool onlyServer = false, bool isSecret = false, bool allowInDevBuild = false) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0046: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) Commands[command] = this; new ConsoleCommand(command, Description(), new ConsoleEvent(CommandAction), isCheat, isNetwork, onlyServer, isSecret, allowInDevBuild, new ConsoleOptionsFetcher(GetSuggestions), false, false, false); } } internal class PrintNames : Command { public PrintNames() : base("printnames") { base.HaveExtraOption = true; base.HaveExtraDescription = true; } protected override void CommandAction(ConsoleEventArgs args) { if (!ParseArgs(args)) { return; } Automatics.Logger.Message(() => "Command exec: " + args.FullLine); List<(bool Regex, string Value)> filters = extraOptions.Select((string arg) => (!arg.StartsWith("r/", StringComparison.OrdinalIgnoreCase)) ? (false, arg) : (true, arg.Substring(2))).ToList(); foreach (var item3 in from <>h__TransparentIdentifier0 in GetAllTranslations().Select(delegate(KeyValuePair<string, string> translation) { KeyValuePair<string, string> keyValuePair = translation; string key; if (!keyValuePair.Key.StartsWith("automatics_")) { keyValuePair = translation; key = "$" + keyValuePair.Key; } else { keyValuePair = translation; key = "@" + keyValuePair.Key.Substring(11); } return new { translation, key }; }) let value = translation.Value where filters.All(delegate((bool Regex, string Value) filter) { if (!filter.Regex) { if (key.IndexOf(filter.Value, StringComparison.OrdinalIgnoreCase) < 0) { return value.IndexOf(filter.Value, StringComparison.OrdinalIgnoreCase) >= 0; } return true; } return Regex.IsMatch(key, filter.Value) || Regex.IsMatch(value, filter.Value); }) select (key, value)) { string item = item3.Item1; string item2 = item3.Item2; string text = Automatics.L10N.LocalizeTextOnly("@command_printnames_result_format", item, item2); args.Context.AddString(text); Automatics.Logger.Message(() => " " + text); } args.Context.AddString(""); static Dictionary<string, string> GetAllTranslations() { return Reflections.GetField<Dictionary<string, string>>(Localization.instance, "m_translations"); } } } internal class PrintObjects : Command { private static readonly Collider[] ColliderBuffer; private int _radius; private int _number; private string _include; private string _exclude; static PrintObjects() { ColliderBuffer = (Collider[])(object)new Collider[4096]; } public PrintObjects() : base("printobjects") { base.HaveExtraOption = true; base.HaveExtraDescription = true; } private static (bool IsValid, bool IsRegex, string Value) CreateFilter(string arg) { if (string.IsNullOrEmpty(arg)) { return (false, false, ""); } if (!arg.StartsWith("r/", StringComparison.OrdinalIgnoreCase)) { return (true, false, arg); } return (true, true, arg.Substring(2)); } private static boo