Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of World and Performance v1.0.3
DigiWorld.dll
Decompiled 3 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DigiCore; using FogReducer; using HarmonyLib; using LagMeter; using Microsoft.CodeAnalysis; using PickableLights; using SailingTweaks; using SmartSave; using SmokeReducer; using UnityEngine; using VeinMining; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("DigiWorld")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("DigiWorld")] [assembly: AssemblyTitle("DigiWorld")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal static class SettingOrder { internal sealed class ConfigurationManagerAttributes { public int? Order; } internal static void Apply(ConfigFile config) { //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown FieldInfo fieldInfo = AccessTools.Field(typeof(ConfigEntryBase), "<Description>k__BackingField"); if (fieldInfo == null) { return; } int num = 1000; foreach (ConfigDefinition item in config.Keys.ToList()) { ConfigEntryBase val = config[item]; ConfigDescription description = val.Description; object[] array = ((description != null) ? description.Tags : null) ?? new object[0]; object obj = array.FirstOrDefault((object t) => t != null && t.GetType().Name == "ConfigurationManagerAttributes"); if (obj != null) { FieldInfo fieldInfo2 = AccessTools.Field(obj.GetType(), "Order"); if (fieldInfo2 != null && fieldInfo2.GetValue(obj) == null) { fieldInfo2.SetValue(obj, num); } } else { fieldInfo.SetValue(val, (object?)new ConfigDescription(((description != null) ? description.Description : null) ?? "", (description != null) ? description.AcceptableValues : null, array.Concat(new object[1] { new ConfigurationManagerAttributes { Order = num } }).ToArray())); } num--; } } } namespace DigiCore { public static class ChestFlags { public const string NoStore = "digi_nostore"; public static bool StoreBlocked(Container chest) { if ((Object)(object)chest == (Object)null) { return false; } ZNetView component = ((Component)chest).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid()) { return component.GetZDO().GetBool("digi_nostore", false); } return false; } } public sealed class ModuleInfo { public readonly Type Type; public readonly string Title; public readonly string OldGuid; public readonly string Description; public readonly string[] Requires; public readonly bool DefaultOn; public ModuleInfo(Type type, string title, string oldGuid, string description, string[] requires = null, bool defaultOn = true) { Type = type; Title = title; OldGuid = oldGuid; Description = description; Requires = requires ?? new string[0]; DefaultOn = defaultOn; } } public abstract class DigiPlugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<string, string> <>9__11_0; public static Func<string, bool> <>9__11_1; public static Func<string, bool> <>9__11_2; public static Func<string, string> <>9__11_3; public static Func<string, string[]> <>9__19_0; public static Func<string[], string> <>9__19_1; public static Func<string[], string> <>9__19_4; public static Func<IGrouping<string, string[]>, string> <>9__19_2; public static PopupButtonCallback <>9__19_3; public static Func<string, bool> <>9__23_0; internal string <Awake>b__11_0(string l) { return l.Trim(); } internal bool <Awake>b__11_1(string l) { return l.Length > 0; } internal bool <Awake>b__11_2(string r) { return !Chainloader.PluginInfos.ContainsKey(r); } internal string <Awake>b__11_3(string r) { if (!RequirementNames.TryGetValue(r, out var value)) { return r; } return value; } internal string[] <ShowDuplicates>b__19_0(string d) { return d.Split(new char[1] { '|' }); } internal string <ShowDuplicates>b__19_1(string[] d) { return d[0]; } internal string <ShowDuplicates>b__19_2(IGrouping<string, string[]> g) { return "<b>" + g.Key + ":</b> " + string.Join(", ", g.Select((string[] d) => d[1])); } internal string <ShowDuplicates>b__19_4(string[] d) { return d[1]; } internal void <ShowDuplicates>b__19_3() { UnifiedPopup.Pop(); } internal bool <Apply>b__23_0(string r) { return !Chainloader.PluginInfos.ContainsKey(r); } } public const string ModulesSection = "0 - Sub-mods"; internal static DigiPlugin StartingHost; internal static ModuleInfo StartingModule; private static readonly Dictionary<string, string> RequirementNames = new Dictionary<string, string> { { "randyknapp.mods.epicloot", "Epic Loot" }, { "com.jotunn.jotunn", "Jotunn" }, { "ValheimLegends", "Valheim Legends" }, { "vapok.mods.adventurebackpacks", "Adventure Backpacks" }, { "MidnightsFX.StarLevelSystem", "Star Level System" }, { "sosal.nordicrugs", "Nordic Rugs" }, { "LuminairPrime.valheim.localcraftfromstorageprime", "Local Craft From Storage Prime" } }; private readonly Dictionary<ModuleInfo, DigiModule> _running = new Dictionary<ModuleInfo, DigiModule>(); private readonly Dictionary<ModuleInfo, ConfigEntry<bool>> _switches = new Dictionary<ModuleInfo, ConfigEntry<bool>>(); private HashSet<string> _migrated; private const string DuplicatesKey = "digi.valheim.duplicates"; private const string DuplicatesShownKey = "digi.valheim.duplicates.shown"; protected abstract ModuleInfo[] Modules { get; } private string MigratedFile => Path.Combine(Paths.ConfigPath, ((BaseUnityPlugin)this).Info.Metadata.GUID + ".migrated.txt"); private static List<string> Duplicates { get { List<string> list = AppDomain.CurrentDomain.GetData("digi.valheim.duplicates") as List<string>; if (list == null) { list = new List<string>(); AppDomain.CurrentDomain.SetData("digi.valheim.duplicates", list); } return list; } } protected virtual void Awake() { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; ((BaseUnityPlugin)this).Config.Save(); _migrated = (File.Exists(MigratedFile) ? new HashSet<string>(from l in File.ReadAllLines(MigratedFile) select l.Trim() into l where l.Length > 0 select l) : new HashSet<string>()); bool flag = !_migrated.Contains("#switches"); ModuleInfo[] modules = Modules; foreach (ModuleInfo moduleInfo in modules) { List<string> list = moduleInfo.Requires.Where((string r) => !Chainloader.PluginInfos.ContainsKey(r)).ToList(); string value; string text = ((moduleInfo.Requires.Length == 0) ? "" : (" Needs " + string.Join(" and ", moduleInfo.Requires.Select((string r) => (!RequirementNames.TryGetValue(r, out value)) ? r : value)) + ".")); ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("0 - Sub-mods", moduleInfo.Title, moduleInfo.DefaultOn, moduleInfo.Description + text); if (flag && !moduleInfo.DefaultOn && File.Exists(OldConfigPath(moduleInfo))) { val.Value = true; } _switches[moduleInfo] = val; ModuleInfo captured = moduleInfo; val.SettingChanged += delegate { Apply(captured); }; if (list.Count > 0) { ((BaseUnityPlugin)this).Logger.LogInfo((object)(moduleInfo.Title + ": off, needs " + string.Join(", ", list) + ".")); } } if (flag) { Remember("#switches"); } modules = Modules; foreach (ModuleInfo module in modules) { Apply(module); } } private void Start() { foreach (KeyValuePair<ModuleInfo, DigiModule> item in _running.ToList()) { if (!((Object)(object)item.Value == (Object)null) && Chainloader.PluginInfos.ContainsKey(item.Key.OldGuid)) { item.Value.Teardown(); _running.Remove(item.Key); Forget(item.Key.OldGuid); ReportDuplicate(item.Key); } } SettingOrder.Apply(((BaseUnityPlugin)this).Config); if (!Application.isBatchMode) { ((MonoBehaviour)this).StartCoroutine(ShowDuplicates()); } } private void ReportDuplicate(ModuleInfo module) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(module.Title + ": not started, because the separate mod (" + module.OldGuid + ") is installed too. Remove that one.")); string item = ((BaseUnityPlugin)this).Info.Metadata.Name.Replace("Digi's ", "") + "|" + module.Title; if (!Duplicates.Contains(item)) { Duplicates.Add(item); } } internal void ReportReplaced(string what) { string item = ((BaseUnityPlugin)this).Info.Metadata.Name.Replace("Digi's ", "") + "|" + what; if (!Duplicates.Contains(item)) { Duplicates.Add(item); } } private IEnumerator ShowDuplicates() { while ((Object)(object)FejdStartup.instance == (Object)null || !UnifiedPopup.IsAvailable()) { yield return (object)new WaitForSeconds(0.5f); } yield return (object)new WaitForSeconds(1f); if (Duplicates.Count == 0 || AppDomain.CurrentDomain.GetData("digi.valheim.duplicates.shown") != null) { yield break; } AppDomain.CurrentDomain.SetData("digi.valheim.duplicates.shown", true); IEnumerable<string> values = from d in Duplicates select d.Split(new char[1] { '|' }) into d group d by d[0] into g select "<b>" + g.Key + ":</b> " + string.Join(", ", g.Select((string[] d) => d[1])); string text = "These mods are installed twice: on their own and inside one of our combined mods.\n\n" + string.Join("\n", values) + "\n\nThe copy in the combined mod stays off until you remove the separate ones in your mod manager (or make a new profile with Digi Modpack). Your settings are copied over."; object obj = <>c.<>9__19_3; if (obj == null) { PopupButtonCallback val = delegate { UnifiedPopup.Pop(); }; <>c.<>9__19_3 = val; obj = (object)val; } UnifiedPopup.Push((PopupBase)new WarningPopup("Mods installed twice", text, (PopupButtonCallback)obj, false)); } private static string OldConfigPath(ModuleInfo module) { return Path.Combine(Paths.ConfigPath, module.OldGuid + ".cfg"); } private void Remember(string key) { if (!_migrated.Add(key)) { return; } try { File.AppendAllText(MigratedFile, key + Environment.NewLine); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write " + MigratedFile + ": " + ex.Message)); } } private void Forget(string key) { if (!_migrated.Remove(key)) { return; } try { File.WriteAllLines(MigratedFile, _migrated.ToArray()); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write " + MigratedFile + ": " + ex.Message)); } } private void Apply(ModuleInfo module) { bool value = _switches[module].Value; _running.TryGetValue(module, out var value2); if (value && (Object)(object)value2 == (Object)null) { if (!module.Requires.Any((string r) => !Chainloader.PluginInfos.ContainsKey(r))) { if (Chainloader.PluginInfos.ContainsKey(module.OldGuid)) { ReportDuplicate(module); } else { StartModule(module); } } } else if (!value && (Object)(object)value2 != (Object)null) { value2.Teardown(); _running.Remove(module); ((BaseUnityPlugin)this).Logger.LogInfo((object)(module.Title + ": switched off.")); } } private void StartModule(ModuleInfo module) { StartingHost = this; StartingModule = module; try { DigiModule digiModule = (DigiModule)(object)((Component)this).gameObject.AddComponent(module.Type); digiModule.Host = this; digiModule.Info2 = module; _running[module] = digiModule; MigrateOldSettings(module, digiModule); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"{module.Title} could not start: {arg}"); } finally { StartingHost = null; StartingModule = null; } SettingOrder.Apply(((BaseUnityPlugin)this).Config); } private void MigrateOldSettings(ModuleInfo module, DigiModule component) { if (_migrated.Contains(module.OldGuid)) { return; } string path = OldConfigPath(module); if (File.Exists(path) && component.ConfigOrNull != null) { Dictionary<string, string> dictionary = ReadCfg(path); int num = 0; foreach (ModuleConfig.Bound entry in component.ConfigOrNull.Entries) { if (dictionary.TryGetValue(entry.Section + "\n" + entry.Key, out var value)) { try { entry.Entry.SetSerializedValue(value); num++; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)(module.Title + ": could not copy '" + entry.Key + "': " + ex.Message)); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"{module.Title}: copied {num} setting(s) from {Path.GetFileName(path)}."); } Remember(module.OldGuid); } private static Dictionary<string, string> ReadCfg(string path) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); string text = ""; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { text = text2.Substring(1, text2.Length - 2); continue; } int num = text2.IndexOf(" = ", StringComparison.Ordinal); if (num > 0) { dictionary[text + "\n" + text2.Substring(0, num).Trim()] = text2.Substring(num + 3); } } return dictionary; } protected virtual void OnDestroy() { foreach (DigiModule item in _running.Values.ToList()) { item?.Teardown(); } _running.Clear(); } public bool IsRunning(Type moduleType) { return _running.Any((KeyValuePair<ModuleInfo, DigiModule> p) => p.Key.Type == moduleType && (Object)(object)p.Value != (Object)null); } } public abstract class DigiModule : MonoBehaviour { internal DigiPlugin Host; internal ModuleInfo Info2; private ModuleConfig _config; private ManualLogSource _logger; private Harmony _harmony; internal ModuleConfig ConfigOrNull => _config; protected ModuleConfig Config => _config ?? (_config = new ModuleConfig(((BaseUnityPlugin)(Host ?? DigiPlugin.StartingHost)).Config, (Info2 ?? DigiPlugin.StartingModule).Title)); protected ManualLogSource Logger => _logger ?? (_logger = Logger.CreateLogSource("Digi's " + (Info2 ?? DigiPlugin.StartingModule)?.Title)); protected Harmony ModuleHarmony { get { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_004a: Expected O, but got Unknown Harmony obj = _harmony; if (obj == null) { Harmony val = new Harmony(((object)this).GetType().Namespace?.ToLowerInvariant() + "." + Guid.NewGuid().ToString("N")); Harmony val2 = val; _harmony = val; obj = val2; } return obj; } } protected void ReportReplacedMod(string what) { (Host ?? DigiPlugin.StartingHost)?.ReportReplaced(what); } protected Harmony PatchModule(Type type) { ModuleHarmony.PatchAll(type); return ModuleHarmony; } protected Harmony PatchModuleNamespace() { string text = ((object)this).GetType().Namespace; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(((object)this).GetType().Assembly); foreach (Type type in typesFromAssembly) { if (type.Namespace == text || (type.Namespace != null && text != null && type.Namespace.StartsWith(text + "."))) { ModuleHarmony.CreateClassProcessor(type).Patch(); } } return ModuleHarmony; } internal void Teardown() { try { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { Logger.LogWarning((object)("Unpatch: " + ex.Message)); } Object.Destroy((Object)(object)this); } } public sealed class ModuleConfig { public sealed class Bound { public string Section; public string Key; public ConfigEntryBase Entry; } private readonly ConfigFile _file; private readonly string _prefix; internal readonly List<Bound> Entries = new List<Bound>(); public ConfigFile File => _file; public string ConfigFilePath => _file.ConfigFilePath; internal ModuleConfig(ConfigFile file, string title) { _file = file; _prefix = title + " - "; } public void Reload() { } public void Save() { _file.Save(); } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null) { ConfigEntry<T> val = _file.Bind<T>(_prefix + section, key, defaultValue, configDescription); if (!Entries.Any((Bound b) => b.Section == section && b.Key == key)) { Entries.Add(new Bound { Section = section, Key = key, Entry = (ConfigEntryBase)(object)val }); } return val; } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown return Bind(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } public ConfigEntry<T> Bind<T>(ConfigDefinition definition, T defaultValue, ConfigDescription configDescription = null) { return Bind(definition.Section, definition.Key, defaultValue, configDescription); } public bool TryGetEntry<T>(ConfigDefinition definition, out ConfigEntry<T> entry) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown return _file.TryGetEntry<T>(new ConfigDefinition(_prefix + definition.Section, definition.Key), ref entry); } } } namespace DigiWorld { [BepInPlugin("digi.valheim.world", "Digi's World and Performance", "1.0.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : DigiPlugin { public const string Guid = "digi.valheim.world"; public const string Name = "Digi's World and Performance"; public const string Version = "1.0.3"; protected override ModuleInfo[] Modules { get; } = new ModuleInfo[7] { new ModuleInfo(typeof(SailingTweaks.Plugin), "Sailing Tweaks", "digi.valheim.sailingtweaks", "Tailwind while steering, faster ships, no wave damage and more."), new ModuleInfo(typeof(VeinMining.Plugin), "Vein Mining", "digi.valheim.veinmining", "Hold Left Alt while mining: when one piece breaks, the whole ore deposit breaks, with full XP."), new ModuleInfo(typeof(FogReducer.Plugin), "Fog Reducer", "digi.valheim.fogreducer", "A fog slider per biome, plus one for the Mistlands mist."), new ModuleInfo(typeof(SmokeReducer.Plugin), "Smoke Reducer", "digi.valheim.smokereducer", "Less smoke from fires and less steam from ovens, for better FPS at big bases."), new ModuleInfo(typeof(PickableLights.Plugin), "Pickable Lights", "digi.valheim.pickablelights", "Glowing thistle, mushrooms, Jotun Puffs and Magecap stop adding real lights, for better FPS."), new ModuleInfo(typeof(SmartSave.Plugin), "Smart Save", "digi.valheim.smartsave", "The autosave waits for a quiet moment instead of freezing the game mid-fight."), new ModuleInfo(typeof(LagMeter.Plugin), "Lag Meter", "digi.valheim.lagmeter", "Writes a short performance report to the log every 10 seconds, to find what causes lag (off by default).", null, defaultOn: false) }; } } namespace VeinMining { public class Plugin : DigiModule { public enum Trigger { WhenFirstPieceBreaks, OnFirstHit } public enum Rocks { OreDepositsOnly, AllRocks } private sealed class Area { public int Index; public float Health; public Vector3 Center; } private sealed class VeinStart { internal List<Area> Areas; internal int HitIndex; internal float PerSwing; internal float HitHealth; } [HarmonyPatch(typeof(ItemDrop), "Awake")] private static class GatherDrops { private static void Postfix(ItemDrop __instance) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) if (_collectUntil <= 0f || Time.time > _collectUntil || !_dropsNearYou.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Vector3 val = ((Component)__instance).transform.position - _collectOrigin; if (((Vector3)(ref val)).sqrMagnitude > _collectRadius * _collectRadius) { return; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid() && component.IsOwner()) { Vector2 val2 = Random.insideUnitCircle * 0.5f; Vector3 position = ((Component)localPlayer).transform.position + new Vector3(val2.x, 0.7f, val2.y); ((Component)__instance).transform.position = position; Rigidbody component2 = ((Component)__instance).GetComponent<Rigidbody>(); if (!((Object)(object)component2 == (Object)null)) { component2.position = position; component2.linearVelocity = Vector3.zero; } } } } [HarmonyPatch(typeof(MineRock5), "Damage")] private static class MineRock5Damage { private static void Prefix(MineRock5 __instance, HitData hit, out VeinStart __state) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) __state = null; ZNetView view = Mr5View.Invoke(__instance); if (Eligible(hit, view) && WorksOn(((Component)__instance).gameObject, __instance.m_dropItems)) { int hitIndex; List<Area> areas = ReadAreas(__instance, hit.m_hitCollider, out hitIndex); __state = Begin(hit, __instance.m_damageModifiers, __instance.m_minToolTier, areas, hitIndex); } } private static void Postfix(MineRock5 __instance, HitData hit, VeinStart __state) { if (__state != null) { Finish((MonoBehaviour)(object)__instance, Mr5View.Invoke(__instance), "RPC_Damage", hit, __state); } } } [HarmonyPatch(typeof(MineRock), "Damage")] private static class MineRockDamage { private static void Prefix(MineRock __instance, HitData hit, out VeinStart __state) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) __state = null; ZNetView view = MrView.Invoke(__instance); if (Eligible(hit, view) && WorksOn(((Component)__instance).gameObject, __instance.m_dropItems)) { int hitIndex; List<Area> areas = ReadAreas(__instance, hit.m_hitCollider, out hitIndex); __state = Begin(hit, __instance.m_damageModifiers, __instance.m_minToolTier, areas, hitIndex); } } private static void Postfix(MineRock __instance, HitData hit, VeinStart __state) { if (__state != null) { Finish((MonoBehaviour)(object)__instance, MrView.Invoke(__instance), "Hit", hit, __state); } } } [HarmonyPatch(typeof(MineRock5), "DamageArea")] private static class QuietMineRock5 { private static void Prefix(MineRock5 __instance, out EffectList __state) { __state = null; if (_veinHits && !_effectsEveryPiece.Value) { __state = __instance.m_hitEffect; __instance.m_hitEffect = NoEffect; } } private static void Postfix(MineRock5 __instance, EffectList __state) { if (__state != null) { __instance.m_hitEffect = __state; } } } [HarmonyPatch(typeof(MineRock), "RPC_Hit")] private static class QuietMineRock { private static void Prefix(MineRock __instance, out EffectList __state) { __state = null; if (_veinHits && !_effectsEveryPiece.Value) { __state = __instance.m_hitEffect; __instance.m_hitEffect = NoEffect; } } private static void Postfix(MineRock __instance, EffectList __state) { if (__state != null) { __instance.m_hitEffect = __state; } } } [HarmonyPatch] private static class QuietDamageText { private static IEnumerable<MethodBase> TargetMethods() { MethodInfo[] methods = typeof(DamageText).GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "ShowText") { yield return methodInfo; } } } private static bool Prefix() { if (_veinHits) { return _effectsEveryPiece.Value; } return true; } } [HarmonyPatch(typeof(MineRock5), "CheckSupport")] private static class OneSupportCheck { private static bool Prefix() { return !_veinHits; } } public const string Guid = "digi.valheim.veinmining"; public const string Name = "Digi's Vein Mining"; public const string Version = "1.0.1"; private static readonly string[] ControllerButtons = new string[13] { "None", "JoyLStick", "JoyRStick", "JoyLBumper", "JoyRBumper", "JoyLTrigger", "JoyRTrigger", "JoyDPadUp", "JoyDPadDown", "JoyDPadLeft", "JoyDPadRight", "JoyButtonX", "JoyButtonY" }; private static ConfigEntry<bool> _enabled; private static ConfigEntry<bool> _alwaysOn; private static ConfigEntry<bool> _xpWholeVein; private static ConfigEntry<bool> _durabilityWholeVein; private static ConfigEntry<bool> _dropsNearYou; private static ConfigEntry<bool> _effectsEveryPiece; private static ConfigEntry<bool> _showMessage; private static ConfigEntry<KeyCode> _key; private static ConfigEntry<string> _controllerButton; private static ConfigEntry<string> _extraOres; private static ConfigEntry<Trigger> _trigger; private static ConfigEntry<Rocks> _rocks; private static ConfigEntry<int> _maxPieces; private static ConfigEntry<float> _maxDistance; private static ConfigEntry<float> _xpMultiplier; private static readonly FieldRef<MineRock5, ZNetView> Mr5View = AccessTools.FieldRefAccess<MineRock5, ZNetView>("m_nview"); private static readonly FieldRef<MineRock, ZNetView> MrView = AccessTools.FieldRefAccess<MineRock, ZNetView>("m_nview"); private static readonly FieldInfo Mr5Areas = AccessTools.Field(typeof(MineRock5), "m_hitAreas"); private static readonly Type Mr5AreaType = AccessTools.Inner(typeof(MineRock5), "HitArea"); private static readonly FieldInfo Mr5AreaHealth = AccessTools.Field(Mr5AreaType, "m_health"); private static readonly FieldInfo Mr5AreaCollider = AccessTools.Field(Mr5AreaType, "m_collider"); private static readonly MethodInfo Mr5LoadHealth = AccessTools.Method(typeof(MineRock5), "LoadHealth", (Type[])null, (Type[])null); private static readonly MethodInfo Mr5CheckSupport = AccessTools.Method(typeof(MineRock5), "CheckSupport", (Type[])null, (Type[])null); private static readonly FieldRef<MineRock, Collider[]> MrAreas = AccessTools.FieldRefAccess<MineRock, Collider[]>("m_hitAreas"); private static readonly Dictionary<string, bool> OreCache = new Dictionary<string, bool>(); private static bool _veinHits; private static float _collectUntil; private static Vector3 _collectOrigin; private static float _collectRadius; private static readonly EffectList NoEffect = new EffectList(); private void Awake() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Expected O, but got Unknown _enabled = base.Config.Bind("1 - Vein mining", "Enabled", defaultValue: true, "Turn vein mining on or off."); _key = base.Config.Bind<KeyCode>("1 - Vein mining", "Key", (KeyCode)308, "Hold this key while mining with a pickaxe to mine the whole deposit."); _controllerButton = base.Config.Bind("1 - Vein mining", "Controller button", "JoyLStick", new ConfigDescription("Hold this controller button while mining to mine the whole deposit (JoyLStick = press the left stick). None = keyboard only.", (AcceptableValueBase)(object)new AcceptableValueList<string>(ControllerButtons), Array.Empty<object>())); _alwaysOn = base.Config.Bind("1 - Vein mining", "Always on", defaultValue: false, "Mine whole deposits without holding the key."); _trigger = base.Config.Bind("1 - Vein mining", "When the deposit breaks", Trigger.WhenFirstPieceBreaks, "WhenFirstPieceBreaks: mine one piece as usual, and when it breaks the rest of the deposit breaks with it. OnFirstHit: the whole deposit breaks on your first hit."); _rocks = base.Config.Bind("1 - Vein mining", "Works on", Rocks.OreDepositsOnly, "OreDepositsOnly: copper, tin, iron, silver, obsidian, black marble and other ore deposits. AllRocks: every rock you can mine with a pickaxe, big stones too."); _extraOres = base.Config.Bind("1 - Vein mining", "Also counts as ore", "Obsidian, SoftTissue, BlackMarble", "Item names (comma separated) that make a rock an ore deposit. Items with \"Ore\" or \"Scrap\" in their name always count."); _maxPieces = base.Config.Bind("1 - Vein mining", "Max pieces", 200, new ConfigDescription("Most extra pieces that break at once.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>())); _maxDistance = base.Config.Bind("1 - Vein mining", "Max distance (m)", 30f, new ConfigDescription("Only pieces up to this many meters from the piece you hit break with it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>())); _xpWholeVein = base.Config.Bind("2 - Skill and durability", "XP for the whole deposit", defaultValue: true, "Get Pickaxe XP for every piece, as if you had mined each one yourself. Off = XP only for the hits you made."); _xpMultiplier = base.Config.Bind("2 - Skill and durability", "XP multiplier", 1f, new ConfigDescription("Multiplies the XP for the extra pieces: 1 = the same as mining them by hand.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>())); _durabilityWholeVein = base.Config.Bind("2 - Skill and durability", "Durability for the whole deposit", defaultValue: false, "Your pickaxe wears down as if you had mined every piece yourself. Off = only the hits you made wear it down."); _dropsNearYou = base.Config.Bind("3 - Drops and effects", "Drops near you", defaultValue: true, "Everything the deposit drops lands at your feet instead of all over the deposit. Works on deposits your game looks after: when you play alone or host, or when you were the first player near the deposit."); _effectsEveryPiece = base.Config.Bind("3 - Drops and effects", "Hit effects for every piece", defaultValue: false, "Hit sounds and damage numbers for every extra piece. Off = only the break effects, quieter and smoother (on deposits your game looks after, as above)."); _showMessage = base.Config.Bind("3 - Drops and effects", "Show message", defaultValue: true, "Show \"Vein mined: 23 pieces\" in the top left corner."); _extraOres.SettingChanged += delegate { OreCache.Clear(); }; _rocks.SettingChanged += delegate { OreCache.Clear(); }; PatchModuleNamespace(); } private static List<Area> ReadAreas(MineRock5 rock, Collider hitCollider, out int hitIndex) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) hitIndex = -1; Mr5LoadHealth.Invoke(rock, null); if (!(Mr5Areas.GetValue(rock) is IList list)) { return null; } List<Area> list2 = new List<Area>(list.Count); for (int i = 0; i < list.Count; i++) { Collider val = (Collider)Mr5AreaCollider.GetValue(list[i]); if ((Object)(object)val == (Object)(object)hitCollider) { hitIndex = i; } Area obj = new Area { Index = i, Health = (float)Mr5AreaHealth.GetValue(list[i]) }; Vector3 center; if (!((Object)(object)val != (Object)null)) { center = ((Component)rock).transform.position; } else { Bounds bounds = val.bounds; center = ((Bounds)(ref bounds)).center; } obj.Center = center; list2.Add(obj); } return list2; } private static List<Area> ReadAreas(MineRock rock, Collider hitCollider, out int hitIndex) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) hitIndex = -1; Collider[] array = MrAreas.Invoke(rock); ZNetView obj = MrView.Invoke(rock); ZDO val = ((obj != null) ? obj.GetZDO() : null); if (array == null || val == null) { return null; } float health = rock.GetHealth(); List<Area> list = new List<Area>(array.Length); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] == (Object)(object)hitCollider) { hitIndex = i; } Area obj2 = new Area { Index = i, Health = val.GetFloat("Health" + i, health) }; Vector3 center; if (!((Object)(object)array[i] != (Object)null)) { center = ((Component)rock).transform.position; } else { Bounds bounds = array[i].bounds; center = ((Bounds)(ref bounds)).center; } obj2.Center = center; list.Add(obj2); } return list; } private static bool KeyHeld() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (_alwaysOn.Value) { return true; } if (UnityInput.Current.GetKey(_key.Value)) { return true; } string value = _controllerButton.Value; if (!string.IsNullOrEmpty(value) && value != "None") { return ZInput.GetButton(value); } return false; } private static bool WorksOn(GameObject rock, DropTable drops) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) if (_rocks.Value == Rocks.AllRocks) { return true; } string prefabName = Utils.GetPrefabName(rock); if (OreCache.TryGetValue(prefabName, out var value)) { return value; } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = _extraOres.Value.Split(new char[1] { ',' }); foreach (string text in array) { if (text.Trim().Length > 0) { hashSet.Add(text.Trim()); } } if (drops?.m_drops != null) { foreach (DropData drop in drops.m_drops) { if (!((Object)(object)drop.m_item == (Object)null)) { string name = ((Object)drop.m_item).name; if (name.Contains("Ore") || name.Contains("Scrap") || hashSet.Contains(name)) { value = true; } } } } OreCache[prefabName] = value; return value; } private static VeinStart Begin(HitData hit, DamageModifiers modifiers, int minToolTier, List<Area> areas, int hitIndex) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) if (areas == null || hitIndex < 0) { return null; } float health = areas[hitIndex].Health; if (health <= 0f) { return null; } HitData val = hit.Clone(); DamageModifier val2 = default(DamageModifier); val.ApplyResistance(modifiers, ref val2); float totalDamage = val.GetTotalDamage(); if (totalDamage <= 0f || !val.CheckToolTier(minToolTier, false)) { return null; } if (_trigger.Value == Trigger.WhenFirstPieceBreaks && health - totalDamage > 0f) { return null; } VeinStart result = new VeinStart { Areas = areas, HitIndex = hitIndex, PerSwing = totalDamage, HitHealth = health }; _collectOrigin = areas[hitIndex].Center; _collectRadius = _maxDistance.Value + 3f; _collectUntil = Time.time + 3f; return result; } private static bool Eligible(HitData hit, ZNetView view) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Invalid comparison between Unknown and I4 if (!_enabled.Value || hit == null || (Object)(object)view == (Object)null || !view.IsValid()) { return false; } if ((Object)(object)hit.m_hitCollider == (Object)null || hit.m_radius > 0f || (int)hit.m_skill != 12) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)hit.GetAttacker() == (Object)(object)localPlayer) { return KeyHeld(); } return false; } private static void Finish(MonoBehaviour rock, ZNetView view, string rpc, HitData hit, VeinStart start) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) if (!view.IsValid()) { return; } Vector3 origin = start.Areas[start.HitIndex].Center; float value = _maxDistance.Value; List<Area> list = new List<Area>(); foreach (Area area in start.Areas) { if (!(area.Health <= 0f) && (area.Index != start.HitIndex || _trigger.Value != Trigger.WhenFirstPieceBreaks) && Vector3.Distance(area.Center, origin) <= value) { list.Add(area); } } if (list.Count == 0) { _collectUntil = 0f; return; } list.Sort((Area a, Area b) => Vector3.Distance(a.Center, origin).CompareTo(Vector3.Distance(b.Center, origin))); int num = _maxPieces.Value + ((_trigger.Value == Trigger.OnFirstHit) ? 1 : 0); if (list.Count > num) { list.RemoveRange(num, list.Count - num); } int num2 = 0; int num3 = 0; _veinHits = view.IsOwner(); try { foreach (Area item in list) { if (!view.IsValid()) { break; } HitData val = hit.Clone(); val.m_point = item.Center; val.m_radius = 0f; val.m_pushForce = 0f; ((DamageTypes)(ref val.m_damage)).Modify(item.Health / start.PerSwing * 1.05f + 0.01f); view.InvokeRPC(rpc, new object[2] { val, item.Index }); int num4 = Mathf.Max(1, Mathf.CeilToInt(item.Health / start.PerSwing - 0.001f)); if (item.Index == start.HitIndex) { num4--; } num2 += num4; if (item.Index != start.HitIndex) { num3++; } } } finally { _veinHits = false; } if (rock is MineRock5 && view.IsValid() && view.IsOwner()) { Mr5CheckSupport.Invoke(rock, null); } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (_xpWholeVein.Value && _xpMultiplier.Value > 0f && num2 > 0) { float num5 = hit.m_skillRaiseAmount * _xpMultiplier.Value; for (int num6 = 0; num6 < Mathf.Min(num2, 20000); num6++) { ((Character)localPlayer).RaiseSkill(hit.m_skill, num5); } } if (_durabilityWholeVein.Value && num2 > 0) { ItemData currentWeapon = ((Humanoid)localPlayer).GetCurrentWeapon(); if (currentWeapon != null && currentWeapon.m_shared.m_useDurability) { currentWeapon.m_durability = Mathf.Max(0f, currentWeapon.m_durability - currentWeapon.m_shared.m_useDurabilityDrain * Game.m_durabilityRate * (float)num2); } } if (_showMessage.Value && num3 > 0) { ((Character)localPlayer).Message((MessageType)1, $"Vein mined: {num3 + 1} pieces", 0, (Sprite)null, false); } } } } namespace SmokeReducer { public class Plugin : DigiModule { private sealed class Original { public float Interval; } private sealed class OriginalRate { public float Rate; } public const string Guid = "digi.valheim.smokereducer"; public const string Name = "Digi's Smoke Reducer"; public const string Version = "1.1.1"; private static ConfigEntry<float> _intervalMultiplier; private static ConfigEntry<int> _maxSmoke; private static ConfigEntry<int> _cookingSmoke; private static readonly ConditionalWeakTable<SmokeSpawner, Original> Originals = new ConditionalWeakTable<SmokeSpawner, Original>(); private static readonly ConditionalWeakTable<ParticleSystem, OriginalRate> OriginalRates = new ConditionalWeakTable<ParticleSystem, OriginalRate>(); private static readonly List<CookingStation> Stations = new List<CookingStation>(); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.smokereducer".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown MigrateConfig(); _intervalMultiplier = base.Config.Bind("Smoke", "Spawn interval multiplier", 2f, new ConfigDescription("Fires make smoke this many times less often: 2 = half the smoke, 1 = normal. Less smoke = better FPS.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); _maxSmoke = base.Config.Bind("Smoke", "Max smoke puffs", 50, new ConfigDescription("Most smoke puffs around you at once (normal 100); the oldest fade first. Lower = better FPS.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), Array.Empty<object>())); _cookingSmoke = base.Config.Bind("Smoke", "Oven and cooking steam (%)", 50, new ConfigDescription("Steam and smoke from food in ovens and cooking stations (cooking, done and burnt food): 100 = normal, 0 = none.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); _intervalMultiplier.SettingChanged += delegate { ApplyAll(); }; _cookingSmoke.SettingChanged += delegate { ApplyAllStations(); }; PatchModule(typeof(Plugin)); base.Logger.LogInfo((object)$"Smoke puffs x1/{_intervalMultiplier.Value:0.#}, max {_maxSmoke.Value}, cooking steam {_cookingSmoke.Value}%."); } [HarmonyPatch(typeof(SmokeSpawner), "Awake")] [HarmonyPostfix] private static void SpawnerAwake(SmokeSpawner __instance) { Apply(__instance); } private static void Apply(SmokeSpawner spawner) { if (!((Object)(object)spawner == (Object)null)) { Original value = Originals.GetValue(spawner, (SmokeSpawner s) => new Original { Interval = s.m_interval }); spawner.m_interval = value.Interval * _intervalMultiplier.Value; } } private static void ApplyAll() { foreach (IMonoUpdater instance in SmokeSpawner.Instances) { Apply((SmokeSpawner)(object)((instance is SmokeSpawner) ? instance : null)); } } [HarmonyPatch(typeof(SmokeSpawner), "Spawn")] [HarmonyPrefix] private static void BeforeSpawn() { int num = 0; while (Smoke.GetTotalSmoke() >= _maxSmoke.Value && num++ < 100) { Smoke.FadeOldest(); } } [HarmonyPatch(typeof(CookingStation), "Awake")] [HarmonyPostfix] private static void StationAwake(CookingStation __instance) { Stations.RemoveAll((CookingStation s) => (Object)(object)s == (Object)null); Stations.Add(__instance); ApplyStation(__instance); } private static void ApplyAllStations() { Stations.RemoveAll((CookingStation s) => (Object)(object)s == (Object)null); foreach (CookingStation station in Stations) { ApplyStation(station); } } private static void ApplyStation(CookingStation station) { if ((Object)(object)station == (Object)null || station.m_slots == null) { return; } Transform[] slots = station.m_slots; foreach (Transform val in slots) { if ((Object)(object)val != (Object)null) { Scale(((Component)val).GetComponent<ParticleSystem>()); } } if (station.m_donePS != null) { ParticleSystem[] donePS = station.m_donePS; for (int i = 0; i < donePS.Length; i++) { Scale(donePS[i]); } } if (station.m_burntPS != null) { ParticleSystem[] donePS = station.m_burntPS; for (int i = 0; i < donePS.Length; i++) { Scale(donePS[i]); } } } private static void Scale(ParticleSystem ps) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ps == (Object)null)) { EmissionModule emission = ps.emission; OriginalRate value = OriginalRates.GetValue(ps, delegate(ParticleSystem p) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) OriginalRate originalRate = new OriginalRate(); EmissionModule emission2 = p.emission; originalRate.Rate = ((EmissionModule)(ref emission2)).rateOverTimeMultiplier; return originalRate; }); ((EmissionModule)(ref emission)).rateOverTimeMultiplier = value.Rate * (float)_cookingSmoke.Value / 100f; } } } } namespace SmartSave { public class Plugin : DigiModule { public const string Guid = "digi.valheim.smartsave"; public const string Name = "Digi's Smart Save"; public const string Version = "1.0.1"; private static ConfigEntry<bool> _enabled; private static ConfigEntry<bool> _message; private static ConfigEntry<int> _interval; private static ConfigEntry<int> _maxDelay; private static ConfigEntry<float> _stillSeconds; private static float _stillTime; private void Awake() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown _enabled = base.Config.Bind("General", "Wait for a quiet moment", defaultValue: true, "When the autosave is due, wait until you are not fighting, running or sailing. Off = save on the dot, like normal."); _interval = base.Config.Bind("General", "Save every (minutes)", 30, new ConfigDescription("How often the world is saved. Normal is 30.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 120), Array.Empty<object>())); _maxDelay = base.Config.Bind("General", "Wait at most (minutes)", 5, new ConfigDescription("If no quiet moment comes, save anyway after this long, so no progress is at risk.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), Array.Empty<object>())); _stillSeconds = base.Config.Bind("General", "Standing still counts after (s)", 3f, new ConfigDescription("Standing still this long (with no creature chasing you) counts as a quiet moment.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); _message = base.Config.Bind("General", "Show save message", defaultValue: true, "Show \"Autosaving...\" in the top left corner when the autosave starts."); PatchModule(typeof(Plugin)); } private void Update() { //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) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { _stillTime = 0f; return; } Vector3 velocity = ((Character)localPlayer).GetVelocity(); _stillTime = ((((Vector3)(ref velocity)).sqrMagnitude < 0.04f && !((Character)localPlayer).InAttack() && (Object)(object)localPlayer.GetControlledShip() == (Object)null) ? (_stillTime + Time.unscaledDeltaTime) : 0f); } [HarmonyPatch(typeof(Game), "UpdateSaving")] [HarmonyPrefix] private static bool UpdateSaving(Game __instance, float dt) { Game.m_saveInterval = (float)_interval.Value * 60f; if (!_enabled.Value) { return true; } if (SaveSystem.HasSessionFlag((SaveSystemSessionFlags)3)) { return false; } __instance.m_saveTimer += dt; float num = __instance.m_saveTimer - Game.m_saveInterval; if (num <= 0f) { return false; } if (!QuietMoment() && num < (float)_maxDelay.Value * 60f) { return false; } if ((Object)(object)ZNet.instance == (Object)null) { return false; } bool flag = default(bool); if (!ZNet.instance.EnoughDiskSpaceAvailable(ref flag, false, (Action<bool>)null)) { __instance.m_saveTimer -= 300f; return false; } if (_message.Value) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, "Autosaving...", 0, (Sprite)null, false); } } __instance.SavePlayerProfile(true, false); ZNet.instance.Save(false, true, true); return false; } private static bool QuietMoment() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return true; } if (BeingChased(localPlayer)) { return false; } if (InventoryGui.IsVisible() || Menu.IsVisible() || StoreGui.IsVisible() || ((Object)(object)Minimap.instance != (Object)null && (int)Minimap.instance.m_mode == 2)) { return true; } if (((Character)localPlayer).IsSitting() || ((Character)localPlayer).InBed() || (((Character)localPlayer).IsAttached() && (Object)(object)localPlayer.GetControlledShip() == (Object)null)) { return true; } return _stillTime >= _stillSeconds.Value; } private static bool BeingChased(Player player) { foreach (BaseAI baseAIInstance in BaseAI.BaseAIInstances) { if ((Object)(object)baseAIInstance != (Object)null && (Object)(object)baseAIInstance.GetTargetCreature() == (Object)(object)player) { return true; } } return false; } } } namespace SailingTweaks { public class Plugin : DigiModule { public const string Guid = "digi.valheim.sailingtweaks"; public const string Name = "Digi's Sailing Tweaks"; public const string Version = "1.0.1"; private static ConfigEntry<bool> _tailwind; private static ConfigEntry<bool> _tailwindSailUpOnly; private static ConfigEntry<bool> _noWaveDamage; private static ConfigEntry<bool> _push; private static ConfigEntry<bool> _hideSail; private static ConfigEntry<float> _minWind; private static ConfigEntry<float> _sailSpeed; private static ConfigEntry<float> _rowSpeed; private static ConfigEntry<float> _explore; private static ConfigEntry<float> _pushStrength; private static readonly FieldRef<Ship, float> LastDepth = AccessTools.FieldRefAccess<Ship, float>("m_lastDepth"); private static readonly FieldRef<Ship, float> LastWaterForceTime = AccessTools.FieldRefAccess<Ship, float>("m_lastUpdateWaterForceTime"); private static readonly FieldRef<Ship, float> LastImpactTime = AccessTools.FieldRefAccess<Ship, float>("m_lastWaterImpactTime"); private static readonly FieldInfo SailCloth = AccessTools.Field(typeof(Ship), "m_sailCloth"); private static readonly ConditionalWeakTable<Ship, Renderer> SailRenderers = new ConditionalWeakTable<Ship, Renderer>(); private static bool HiddenAny; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.sailingtweaks".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown MigrateConfig(); _tailwind = base.Config.Bind("1 - Wind", "Always tailwind", defaultValue: true, "While you steer a ship, the wind always blows from behind."); _tailwindSailUpOnly = base.Config.Bind("1 - Wind", "Only with sail up", defaultValue: false, "Tailwind only while the sail is up; while rowing you get the real wind."); _minWind = base.Config.Bind("1 - Wind", "Minimum wind strength (%)", 0f, new ConfigDescription("While you steer, the wind is always at least this strong. 0 = normal (no wind can leave you stuck).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>())); _sailSpeed = base.Config.Bind("2 - Speed", "Sailing speed multiplier", 10f, new ConfigDescription("Push from the sail, times this. The ship gets faster, but less than this number, because water slows a fast ship down more.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); _rowSpeed = base.Config.Bind("2 - Speed", "Rowing speed multiplier", 2f, new ConfigDescription("Rowing power, times this (forward and backward).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); _noWaveDamage = base.Config.Bind("3 - Ship", "No wave damage", defaultValue: true, "Ships take no damage from slamming into waves. Capsizing and the Ashlands' burning sea still hurt."); _push = base.Config.Bind("3 - Ship", "Stronger push", defaultValue: true, "Walking into a ship from outside pushes it much harder, so you can turn or launch big ships by hand."); _pushStrength = base.Config.Bind("3 - Ship", "Push strength", 4f, new ConfigDescription("How hard you push a ship by walking into it (higher = moves faster).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); _hideSail = base.Config.Bind("4 - View", "Hide sail", defaultValue: false, "Hide the sail of the ship you are on while it is up, so it does not block your view. Only on your screen."); _explore = base.Config.Bind("4 - View", "Map reveal radius at sea", 2f, new ConfigDescription("How far around you the map gets uncovered while you are on a ship, times this.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); PatchModule(typeof(Plugin)); } private static Ship SteeredShip() { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return null; } return localPlayer.GetControlledShip(); } private static bool OnShip(Player player, Ship ship) { if ((Object)(object)player != (Object)null) { if (!((Object)(object)((Character)player).GetStandingOnShip() == (Object)(object)ship)) { return (Object)(object)player.GetControlledShip() == (Object)(object)ship; } return true; } return false; } [HarmonyPatch(typeof(EnvMan), "GetWindDir")] [HarmonyPostfix] private static void WindDir(ref Vector3 __result) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!_tailwind.Value) { return; } Ship val = SteeredShip(); if (!((Object)(object)val == (Object)null) && (!_tailwindSailUpOnly.Value || val.IsSailUp())) { Vector3 forward = ((Component)val).transform.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude > 0.0001f) { __result = ((Vector3)(ref forward)).normalized; } } } [HarmonyPatch(typeof(EnvMan), "GetWindIntensity")] [HarmonyPostfix] private static void WindIntensity(ref float __result) { if (!(_minWind.Value <= 0f) && !((Object)(object)SteeredShip() == (Object)null)) { __result = Mathf.Max(__result, _minWind.Value / 100f); } } [HarmonyPatch(typeof(Ship), "GetSailForce")] [HarmonyPostfix] private static void SailForce(ref Vector3 __result) { //IL_0002: 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) //IL_0016: Unknown result type (might be due to invalid IL or missing references) __result *= _sailSpeed.Value; } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] [HarmonyPrefix] private static void ShipUpdatePrefix(Ship __instance, out Vector2 __state) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) __state = new Vector2(__instance.m_backwardForce, __instance.m_sailForceOffset); __instance.m_backwardForce *= _rowSpeed.Value; __instance.m_sailForceOffset /= _sailSpeed.Value; } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] [HarmonyFinalizer] private static void ShipUpdateFinalizer(Ship __instance, Vector2 __state) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) __instance.m_backwardForce = __state.x; __instance.m_sailForceOffset = __state.y; if (_hideSail.Value || HiddenAny) { UpdateSail(__instance); } } private static void UpdateSail(Ship ship) { Renderer value = SailRenderers.GetValue(ship, delegate(Ship s) { object? obj = SailCloth?.GetValue(s); Component val = (Component)((obj is Component) ? obj : null); return (Renderer)(object)((!((Object)(object)val != (Object)null)) ? null : val.GetComponentInChildren<SkinnedMeshRenderer>(true)); }); if (!((Object)(object)value == (Object)null)) { bool flag = _hideSail.Value && ship.IsSailUp() && OnShip(Player.m_localPlayer, ship); if (value.enabled == flag) { value.enabled = !flag; } if (flag) { HiddenAny = true; } } } [HarmonyPatch(typeof(Ship), "UpdateWaterForce")] [HarmonyPrefix] private static bool WaterForce(Ship __instance, float depth, float time) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (!_noWaveDamage.Value) { return true; } float num = depth - LastDepth.Invoke(__instance); float num2 = time - LastWaterForceTime.Invoke(__instance); LastDepth.Invoke(__instance) = depth; LastWaterForceTime.Invoke(__instance) = time; float num3 = num / num2; if (num3 <= 0f && Mathf.Abs(num3) > __instance.m_minWaterImpactForce && time - LastImpactTime.Invoke(__instance) > __instance.m_minWaterImpactInterval) { LastImpactTime.Invoke(__instance) = time; __instance.m_waterImpactEffect.Create(((Component)__instance).transform.position, ((Component)__instance).transform.rotation, (Transform)null, 1f, -1, default(ZDOID)); } return false; } [HarmonyPatch(typeof(Character), "OnCollisionStay")] [HarmonyPostfix] private static void Push(Character __instance, Collision collision) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) if (!_push.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } Rigidbody rigidbody = collision.rigidbody; Ship val = (((Object)(object)rigidbody != (Object)null) ? ((Component)rigidbody).GetComponent<Ship>() : null); if ((Object)(object)val == (Object)null || !val.IsOwner()) { return; } Player localPlayer = Player.m_localPlayer; if (!OnShip(localPlayer, val)) { Vector3 moveDir = ((Character)localPlayer).GetMoveDir(); moveDir.y = 0f; Vector3 val2 = ((Component)val).transform.position - ((Component)localPlayer).transform.position; val2.y = 0f; if (!(((Vector3)(ref moveDir)).sqrMagnitude < 0.01f) && !(Vector3.Dot(moveDir, val2) <= 0f) && collision.contactCount != 0) { Vector3 val3 = ((Vector3)(ref moveDir)).normalized * (_pushStrength.Value * rigidbody.mass); ContactPoint contact = collision.GetContact(0); rigidbody.AddForceAtPosition(val3, ((ContactPoint)(ref contact)).point, (ForceMode)0); } } } [HarmonyPatch(typeof(Minimap), "UpdateExplore")] [HarmonyPrefix] private static void Explore(Minimap __instance, Player player, out float __state) { __state = __instance.m_exploreRadius; if ((Object)(object)player != (Object)null && ((Object)(object)((Character)player).GetStandingOnShip() != (Object)null || (Object)(object)player.GetControlledShip() != (Object)null)) { __instance.m_exploreRadius *= _explore.Value; } } [HarmonyPatch(typeof(Minimap), "UpdateExplore")] [HarmonyFinalizer] private static void ExploreDone(Minimap __instance, float __state) { __instance.m_exploreRadius = __state; } } } namespace PickableLights { public class Plugin : DigiModule { public const string Guid = "digi.valheim.pickablelights"; public const string Name = "Digi's Pickable Lights"; public const string Version = "1.1.0"; private const string Defaults = "Pickable_Thistle, Pickable_Mushroom_yellow, Pickable_Mushroom_JotunPuffs, Pickable_Mushroom_Magecap"; private const string OldDefaults = "Pickable_Thistle, Pickable_Mushroom_yellow"; private static ConfigEntry<bool> _enabled; private static ConfigEntry<string> _pickables; private static HashSet<string> _names = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<Pickable> Loaded = new HashSet<Pickable>(); private void Awake() { _enabled = base.Config.Bind("General", "Lights off", defaultValue: true, "Glowing pickables (thistle, yellow mushrooms, Jotun Puffs, Magecap) still glow, but no longer light up their surroundings. Better FPS where many of them grow."); _pickables = base.Config.Bind("General", "Pickables", "Pickable_Thistle, Pickable_Mushroom_yellow, Pickable_Mushroom_JotunPuffs, Pickable_Mushroom_Magecap", "Which pickables lose their light, by internal name (the one the spawn console command uses), comma-separated. Others that glow: Pickable_Mushroom_blue (frost caves), Pickable_RoyalJelly, Pickable_GlowWorm."); if (_pickables.Value.Replace(" ", "") == "Pickable_Thistle, Pickable_Mushroom_yellow".Replace(" ", "")) { _pickables.Value = "Pickable_Thistle, Pickable_Mushroom_yellow, Pickable_Mushroom_JotunPuffs, Pickable_Mushroom_Magecap"; } _pickables.SettingChanged += delegate { ParseNames(); ApplyAll(); }; _enabled.SettingChanged += delegate { ApplyAll(); }; ParseNames(); PatchModule(typeof(Plugin)); } private static void ParseNames() { _names = new HashSet<string>(from s in (_pickables.Value ?? "").Split(new char[1] { ',' }) select s.Trim() into s where s.Length > 0 select s, StringComparer.OrdinalIgnoreCase); } [HarmonyPatch(typeof(Pickable), "Awake")] [HarmonyPostfix] private static void PickableAwake(Pickable __instance) { Loaded.Add(__instance); Apply(__instance); } private static void ApplyAll() { Loaded.RemoveWhere((Pickable p) => (Object)(object)p == (Object)null); foreach (Pickable item in Loaded) { Apply(item); } } private static void Apply(Pickable pickable) { if ((Object)(object)pickable == (Object)null) { return; } bool flag = _enabled.Value && _names.Contains(Utils.GetPrefabName(((Component)pickable).gameObject)); Light[] componentsInChildren = ((Component)pickable).GetComponentsInChildren<Light>(true); foreach (Light val in componentsInChildren) { LightLod component = ((Component)val).GetComponent<LightLod>(); if (flag) { if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = false; } ((Behaviour)val).enabled = false; } else if (!((Behaviour)val).enabled || ((Object)(object)component != (Object)null && !((Behaviour)component).enabled)) { ((Behaviour)val).enabled = true; if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = true; } } } } } } namespace LagMeter { public class Plugin : DigiModule { public const string Guid = "digi.valheim.lagmeter"; public const string Name = "Digi's Lag Meter"; public const string Version = "1.3.1"; private static ConfigEntry<bool> _enabled; private static ConfigEntry<int> _interval; private static readonly Dictionary<MethodBase, string> Labels = new Dictionary<MethodBase, string>(); private static readonly Dictionary<string, long> Ticks = new Dictionary<string, long>(); private static readonly Dictionary<string, int> Calls = new Dictionary<string, int>(); private static readonly List<string> Order = new List<string>(); private static FieldInfo _sceneInstances; private float _start; private int _frames; private float _worst; private int _gc0; private int _reports; private bool _censusDone; private static readonly FieldInfo ObjectsById = AccessTools.Field(typeof(ZDOMan), "m_objectsByID"); private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.lagmeter".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown MigrateConfig(); _enabled = base.Config.Bind("General", "Enabled", defaultValue: true, "Measure and log. Turn off (or remove the mod) when done."); _interval = base.Config.Bind("General", "Interval (seconds)", 10, new ConfigDescription("Seconds per log line.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 120), Array.Empty<object>())); _sceneInstances = AccessTools.Field(typeof(ZNetScene), "m_instances"); Harmony moduleHarmony = base.ModuleHarmony; Measure(moduleHarmony, "Scripts (Update)", typeof(MonoUpdaters), "Update"); Measure(moduleHarmony, "Scripts (FixedUpdate)", typeof(MonoUpdaters), "FixedUpdate"); Measure(moduleHarmony, "Scripts (LateUpdate)", typeof(MonoUpdaters), "LateUpdate"); Measure(moduleHarmony, "Object streaming", typeof(ZNetScene), "Update"); Measure(moduleHarmony, "Network", typeof(ZNet), "Update"); Measure(moduleHarmony, "Network (late)", typeof(ZNet), "LateUpdate"); Measure(moduleHarmony, "Building wear/support", typeof(WearNTear), "UpdateWear"); Measure(moduleHarmony, "Plants", typeof(Plant), "SUpdate"); Measure(moduleHarmony, "Fires", typeof(Fireplace), "UpdateFireplace"); Measure(moduleHarmony, "Smoke render", typeof(SmokeRenderer), "LateUpdate"); Measure(moduleHarmony, "Grass", typeof(ClutterSystem), "LateUpdate"); Measure(moduleHarmony, "Light LOD", typeof(LightLod), "UpdateLights"); Measure(moduleHarmony, "Cooking stations", typeof(CookingStation), "Update"); Measure(moduleHarmony, "Terrain", typeof(TerrainComp), "Update"); SaveProbe.Init(moduleHarmony, base.Logger); base.Logger.LogInfo((object)$"Timing {Order.Count} game systems; a [Lag] line is written every {_interval.Value}s in a world."); } private void Measure(Harmony harmony, string label, Type type, string method) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_00d4: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.DeclaredMethod(type, method, (Type[])null, (Type[])null); if (methodInfo == null) { base.Logger.LogWarning((object)("Not timed, " + type.Name + "." + method + " not found.")); return; } Labels[methodInfo] = label; if (!Ticks.ContainsKey(label)) { Ticks[label] = 0L; Calls[label] = 0; Order.Add(label); } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "StartTimer", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "StopTimer", (Type[])null) { priority = 0 }, (HarmonyMethod)null); } private static void StartTimer(out long __state) { __state = Stopwatch.GetTimestamp(); } private static Exception StopTimer(Exception __exception, MethodBase __originalMethod, long __state) { if (_enabled.Value && Labels.TryGetValue(__originalMethod, out var value)) { Ticks[value] += Stopwatch.GetTimestamp() - __state; Calls[value]++; } return __exception; } private void Update() { SaveProbe.Tick(); if (!_enabled.Value || (Object)(object)Player.m_localPlayer == (Object)null) { _start = Time.realtimeSinceStartup; _frames = 0; _worst = 0f; _gc0 = GC.CollectionCount(0); Reset(); return; } _frames++; _worst = Mathf.Max(_worst, Time.unscaledDeltaTime); float num = Time.realtimeSinceStartup - _start; if (num < (float)_interval.Value) { return; } try { base.Logger.LogInfo((object)Report(num)); if (!_censusDone && Time.realtimeSinceStartup > 30f && ZDOMan.instance != null) { _censusDone = true; base.Logger.LogInfo((object)Census()); } } catch (Exception ex) { base.Logger.LogWarning((object)("Report failed: " + ex.Message)); } _start = Time.realtimeSinceStartup; _frames = 0; _worst = 0f; _gc0 = GC.CollectionCount(0); Reset(); } private static string Census() { if (!(ObjectsById?.GetValue(ZDOMan.instance) is IDictionary dictionary)) { return "[Census] ZDO list not found"; } Dictionary<int, int> dictionary2 = new Dictionary<int, int>(); foreach (object value3 in dictionary.Values) { ZDO val = (ZDO)((value3 is ZDO) ? value3 : null); if (val != null) { int prefab = val.GetPrefab(); dictionary2.TryGetValue(prefab, out var value); dictionary2[prefab] = value + 1; } } Dictionary<string, int> dictionary3 = new Dictionary<string, int>(); List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(); foreach (KeyValuePair<int, int> item in dictionary2) { GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(item.Key) : null); string key = (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : $"unknown#{item.Key}"); list.Add(new KeyValuePair<string, int>(key, item.Value)); string key2 = (((Object)(object)val2 == (Object)null) ? "unknown (removed mod?)" : (((Object)(object)val2.GetComponent<ItemDrop>() != (Object)null) ? "dropped items" : (((Object)(object)val2.GetComponent<Piece>() != (Object)null) ? "building pieces" : (((Object)(object)val2.GetComponent<Character>() != (Object)null) ? "creatures" : (((Object)(object)val2.GetComponent<TreeBase>() != (Object)null || (Object)(object)val2.GetComponent<TreeLog>() != (Object)null) ? "trees/logs" : (((Object)(object)val2.GetComponent<Destructible>() != (Object)null || (Object)(object)val2.GetComponent<MineRock>() != (Object)null || (Object)(object)val2.GetComponent<MineRock5>() != (Object)null) ? "rocks/bushes/destructibles" : (((Object)(object)val2.GetComponent<Pickable>() != (Object)null) ? "pickables" : (((Object)(object)val2.GetComponent<TerrainComp>() != (Object)null || (Object)(object)val2.GetComponent<TerrainModifier>() != (Object)null) ? "terrain edits" : "other")))))))); dictionary3.TryGetValue(key2, out var value2); dictionary3[key2] = value2 + item.Value; } int count = dictionary.Count; StringBuilder stringBuilder = new StringBuilder($"[Census] {count:N0} saved objects in the world, {dictionary2.Count} types"); stringBuilder.Append("\n by kind: " + string.Join(", ", from k in dictionary3 orderby k.Value descending select $"{k.Key} {k.Value:N0}")); stringBuilder.Append("\n top types: " + string.Join(", ", from n in list.OrderByDescending((KeyValuePair<string, int> n) => n.Value).Take(30) select $"{n.Key} {n.Value:N0}")); return stringBuilder.ToString(); } private static void Reset() { foreach (string item in Order) { Ticks[item] = 0L; Calls[item] = 0; } } private string Report(float elapsed) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) int frames = Mathf.Max(1, _frames); StringBuilder stringBuilder = new StringBuilder(); Vector3 position = ((Component)Player.m_localPlayer).transform.position; stringBuilder.Append($"[Lag] {elapsed:0}s at ({position.x:0}, {position.z:0}): {(float)_frames / elapsed:0.0} fps avg ({1000f * elapsed / (float)frames:0.0} ms/frame), " + $"worst frame {_worst * 1000f:0} ms, GCs {GC.CollectionCount(0) - _gc0}"); Light[] source = Object.FindObjectsOfType<Light>(); int num = source.Count((Light l) => ((Behaviour)l).enabled && (int)l.type != 1); int num2 = source.Count((Light l) => ((Behaviour)l).enabled && (int)l.type != 1 && (int)l.shadows > 0); Fireplace[] array = Object.FindObjectsOfType<Fireplace>(); int num3 = array.Count((Fireplace f) => f.IsBurning()); Rigidbody[] array2 = Object.FindObjectsOfType<Rigidbody>(); int num4 = array2.Count((Rigidbody b) => !b.IsSleeping() && !b.isKinematic); int num5 = SlowUpdate.GetAllInstaces().Count((SlowUpdate s) => s is Plant); int num6 = (_sceneInstances?.GetValue(ZNetScene.instance) as ICollection)?.Count ?? (-1); stringBuilder.Append($"\n loaded: point lights {num} on ({num2} with shadows), smoke puffs {Smoke.Instances.Count}, " + $"fires {num3}/{array.Length} burning, building pieces {WearNTear.GetAllInstances().Count}, plants {num5}, " + $"objects {num6}, physics bodies {array2.Length} ({num4} moving)"); double freq = Stopwatch.Frequency; IEnumerable<string> values = from l in Order where Calls[l] > 0 orderby Ticks[l] descending select $"{l} {(double)Ticks[l] * 1000.0 / freq / (double)frames:0.00}"; stringBuilder.Append("\n ms per frame: " + string.Join(", ", values)); if (++_reports % Math.Max(1, 60 / _interval.Value) == 1 && _sceneInstances?.GetValue(ZNetScene.instance) is IDictionary dictionary) { Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); foreach (object value2 in dictionary.Values) { ZNetView val = (ZNetView)((value2 is ZNetView) ? value2 : null); if (!((Object)(object)val == (Object)null)) { string prefabName = Utils.GetPrefabName(((Component)val).gameObject); dictionary2.TryGetValue(prefabName, out var value); dictionary2[prefabName] = value + 1; } } IEnumerable<string> values2 = from p in dictionary2.OrderByDescending((KeyValuePair<string, int> p) => p.Value).Take(25) select $"{p.Key} {p.Value}"; stringBuilder.Append($"\n top objects ({dictionary2.Count} types): " + string.Join(", ", values2)); IEnumerable<string> values3 = from g in (from l in source where ((Behaviour)l).enabled && (int)l.type != 1 group l by Utils.GetPrefabName(((Component)((Component)l).transform.root).gameObject) into g orderby g.Count() descending select g).Take(15) select $"{g.Key} {g.Count()}" + (g.Any((Light l) => (int)l.shadows > 0) ? " (shadows)" : ""); stringBuilder.Append("\n lights by object: " + string.Join(", ", values3)); } return stringBuilder.ToString(); } } internal static class SaveProbe { private sealed class Probe { public long Ticks; public long Memory; public int Collections; } private static readonly string[] Steps = new string[8] { "Character save", "Other players' characters", "World save, main thread (the freeze)", " copy object data", " clone changed areas", " copy extra data of ALL objects", " zone data", "World file write (background thread)" }; private static readonly Dictionary<MethodBase, string> Labels = new Dictionary<MethodBase, string>(); private static readonly Dictionary<string, string> Results = new Dictionary<string, string>(); private static readonly object Lock = new object(); private static ManualLogSource _log; private static bool _watching; private static bool _writeDone; private static float _watchStart; private static float _worst; private static int _frames; private static int _collectionsAtStart; internal static void Init(Harmony harmony, ManualLogSource log) { _log = log; Patch(harmony, Steps[0], AccessTools.Method(typeof(Game), "SavePlayerProfile", (Type[])null, (Type[])null)); Patch(harmony, Steps[1], AccessTools.Method(typeof(ZNet), "SaveOtherPlayerProfiles", (Type[])null, (Type[])null)); Patch(harmony, Steps[2], AccessTools.Method(typeof(ZNet), "SaveWorld", (Type[])null, (Type[])null)); Patch(harmony, Steps[3], AccessTools.Method(typeof(ZDOMan), "PrepareSave", (Type[])null, (Type[])null)); Patch(harmony, Steps[4], AccessTools.Method(typeof(ZDOMan), "GetSaveClonePerChunk", (Type[])null, (Type[])null)); Patch(harmony, Steps[5], AccessTools.Method(typeof(ZDOExtraData), "PrepareSave", (Type[])null, (Type[])null)); Patch(harmony, Steps[6], AccessTools.Method(typeof(ZoneSystem), "PrepareSave", (Type[])null, (Type[])null)); Patch(harmony, Steps[7], AccessTools.Method(typeof(ZNet), "SaveWorldThread", (Type[])null, (Type[])null)); } private static void Patch(Harmony harmony, string label, MethodInfo target) { //IL_003d: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0071: Expected O, but got Unknown if (target == null) { _log.LogWarning((object)("Save step not measured, method not found: " + label)); return; } Labels[target] = label; harmony.Patch((MethodBase)target, new HarmonyMethod(typeof(SaveProbe), "Start", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SaveProbe), "Stop", (Type[])null) { priority = 0 }, (HarmonyMethod)null); } private static void Start(out Probe __state) { __state = new Probe { Ticks = Stopwatch.GetTimestamp(), Memory = GC.GetTotalMemory(forceFullCollection: false), Collections = GC.CollectionCount(0) }; } private static Exception Stop(Exception __exception, MethodBase __originalMethod, Probe __state) { if (__state == null || !Labels.TryGetValue(__originalMethod, out var value)) { return __exception; } double num = (double)(Stopwatch.GetTimestamp() - __state.Ticks) * 1000.0 / (double)Stopwatch.Frequency; double num2 = (double)(GC.GetTotalMemory(forceFullCollection: false) - __state.Memory) / 1048576.0; int num3 = GC.CollectionCount(0) - __state.Collections; string value2 = $"{num:0} ms, memory {num2:+0;-0;0} MB, {num3} GCs"; lock (Lock) { Results[value] = value2; if (value == Steps[7]) { _writeDone = true; } } if (value == Steps[2]) { _watching = true; _watchStart = Time.realtimeSinceStartup; _worst = 0f; _frames = 0; _collectionsAtStart = GC.CollectionCount(0); } return __exception; } internal static void Tick() { if (!_watching) { return; } _frames++; _worst = Mathf.Max(_worst, Time.unscaledDeltaTime); float num = Time.realtimeSinceStartup - _watchStart; if (num < 15f) { return; } _watching = false; StringBuilder stringBuilder = new StringBuilder($"[Save] world save at {DateTime.Now:HH:mm:ss}, {Objects()} objects in the world"); lock (Lock) { string[] steps = Steps; foreach (string text in steps) { if (Results.TryGetValue(text, out var value)) { stringBuilder.Append("\n " + text + ": " + value); } } if (!_writeDone) { stringBuilder.Append("\n World file write (background thread): still running after 15 s"); } Results.Clear(); _writeDone = false; } stringBuilder.Append($"\n 15 s after the save: {(float)_frames / num:0.0} fps avg, worst frame {_worst * 1000f:0} ms, " + $"{GC.CollectionCount(0) - _collectionsAtStart} GCs, memory now {(double)GC.GetTotalMemory(forceFullCollection: false) / 1048576.0:0} MB"); _log.LogInfo((object)stringBuilder.ToString()); } private static string Objects() { FieldInfo fieldInfo = AccessTools.Field(typeof(ZDOMan), "m_objectsByID"); ICollection collection = ((ZDOMan.instance != null) ? (fieldInfo?.GetValue(ZDOMan.instance) as ICollection) : null); if (collection == null) { return "?"; } return collection.Count.ToString("N0"); } } } namespace FogReducer { public class Plugin : DigiModule { public const string Guid = "digi.valheim.fogreducer"; public const string Name = "Digi's Fog Reducer"; public const string Version = "1.0.2"; private static readonly Biome[] Biomes; private static readonly Dictionary<Biome, ConfigEntry<int>> Fog; private static ConfigEntry<int> _otherFog; private static ConfigEntry<int> _mistParticles; private static float _current; private static ParticleMist _mist; private static int _localEmission; private static int _localEmissionPerUnit; private static float _emissionMax; private static float _emissionPerUnit; private static float _distantEmissionMax; private void MigrateConfig() { string text = Path.Combine(Paths.ConfigPath, "digi.valheim.fogreducer".Replace("digi.valheim.", "pieter.valheim.") + ".cfg"); if (!File.Exists(base.Config.ConfigFilePath) && File.Exists(text)) { File.Copy(text, base.Config.ConfigFilePath); base.Config.Reload(); } } private void Awake() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown MigrateConfig(); int num = 100; Biome[] biomes = Biomes; foreach (Biome val in biomes) { Fog[val] = base.Config.Bind("Fog per biome", Display(val), 50, new ConfigDescription("Fog in the " + Display(val) + ": 100 = normal, 50 = half, 0 = no fog.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[1] { new ConfigurationManagerAttributes { Order = num-- } })); } _otherFog = base.Config.Bind("Fog per biome", "Other / unknown", 50, new ConfigDescription("Fog in biomes added by other mods: 100 = normal, 50 = half, 0 = no fog.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[1] { new ConfigurationManagerAttributes { Order = num-- } })); _mistParticles = base.Config.Bind("Mistlands", "Mist particles", 50, new ConfigDescription("The thick Mistlands mist you walk through (not the same as fog): 100 = normal, 0 = none.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); PatchModule(typeof(Plugin)); } private unsafe static string Display(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 if ((int)biome != 8) { if ((int)biome != 32) { if ((int)biome != 64) { return ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString(); } return "Deep North"; } return "Ashlands"; } return "Black Forest"; } private static float Target() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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) Biome key = (Biome)(((Object)(object)EnvMan.instance != (Object)null) ? ((int)EnvMan.instance.GetCurrentBiome()) : 0); ConfigEntry<int> value; return (float)(Fog.TryGetValue(key, out value) ? value.Value : _otherFog.Value) / 100f; } [HarmonyPatch(typeof(EnvMan), "SetEnv")] [HarmonyPostfix] private static void SetEnv(float dt) { float num = Target(); _current = ((_current < 0f) ? num : Mathf.MoveTowards(_current, num, Mathf.Max(dt, 0f) * 0.25f)); RenderSettings.fogDensity *= _current; } [HarmonyPatch(typeof(ParticleMist), "Update")] [HarmonyPrefix] private static void MistUpdate(ParticleMist __instance) { if ((Object)(object)_mist != (Object)(object)__instance) { _mist = __instance; _localEmission = __instance.m_localEmission; _localEmissionPerUnit = __instance.m_localEmissionPerUnit; _emissionMax = __instance.m_emissionMax; _emissionPerUnit = __instance.m_emissionPerUnit; _distantEmissionMax = __instance.m_distantEmissionMax; } float num = (float)_mistParticles.Value / 100f; __instance.m_localEmission = Mathf.RoundToInt((float)_localEmission * num); __instance.m_localEmissionPerUnit = Mathf.Max(1, Mathf.RoundToInt((float)_localEmissionPerUnit * num)); __instance.m_emissionMax = _emissionMax * num; __instance.m_emissionPerUnit = _emissionPerUnit * num; __instance.m_distantEmissionMax = _distantEmissionMax * num; } static Plugin() { Biome[] array = new Biome[9]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); Biomes = (Biome[])(object)array; Fog = new Dictionary<Biome, ConfigEntry<int>>(); _current = -1f; } } internal sealed class ConfigurationManagerAttributes { public int? Order; } }