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 ValheimTune v0.7.10
plugins/ValheimTune.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using UnityEngine; using ValheimTune.Patches; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ValheimTune")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.7.10.0")] [assembly: AssemblyInformationalVersion("0.7.10+b2c467d0fa51eb40ac2ecf06e8c3e399c9db9441")] [assembly: AssemblyProduct("ValheimTune")] [assembly: AssemblyTitle("ValheimTune")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.7.10.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; } } } namespace ValheimTune { public static class AssetUnload { public enum Decision { Run, Defer } public static Decision Decide(int peers, double deferredFor, double maxDeferSecs) { if (peers <= 0) { return Decision.Run; } if (deferredFor >= maxDeferSecs) { return Decision.Run; } return Decision.Defer; } } public static class Cfg { public static ConfigEntry<int> LogIntervalSeconds; public static ConfigEntry<int> SendWindowBytes; public static ConfigEntry<int> MinHeadroomBytes; public static ConfigEntry<bool> AllPeersPerRound; public static ConfigEntry<float> RoundSeconds; public static ConfigEntry<int> SendRateMax; public static ConfigEntry<int> SendRateMin; public static ConfigEntry<bool> OverrideSendRate; public static ConfigEntry<bool> OverrideSendWindow; public static ConfigEntry<int> MaxPacketsPerPeerPerFrame; public static ConfigEntry<int> TargetFrameRate; public static ConfigEntry<int> ConfigReloadSeconds; public static ConfigEntry<bool> FloatingDropsRun; public static ConfigEntry<bool> FloatingDropsDelete; public static ConfigEntry<string> HotObjectsIgnore; public static ConfigEntry<bool> DirtySets; public static ConfigEntry<float> ReconcileSeconds; public static ConfigEntry<int> RelayMinIntervalMs; public static ConfigEntry<string> KnownGoodBuilds; public static ConfigEntry<bool> DisableOnUnknownBuild; public static ConfigEntry<bool> TopKSort; public static ConfigEntry<int> TopK; public static ConfigEntry<bool> ServerSkipRenderMesh; public static ConfigEntry<bool> DeferAssetUnload; public static ConfigEntry<int> AssetUnloadMaxDeferMinutes; public static ConfigEntry<bool> SaveDirtyFix; public static ConfigEntry<bool> SpawnerLinkFix; public static ConfigEntry<bool> DeadZdoPrune; public static ConfigEntry<bool> DisconnectNoSleep; public static ConfigEntry<bool> GlobalKeyDedupe; public static void Bind(ConfigFile c) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Expected O, but got Unknown //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Expected O, but got Unknown LogIntervalSeconds = c.Bind<int>("Measure", "LogIntervalSeconds", 10, "How often to print the stats line. 0 disables."); ConfigReloadSeconds = c.Bind<int>("Measure", "ConfigReloadSeconds", 5, "Re-read the cfg file this often so knobs that are read at runtime change without a restart. 0 disables. Patch-time settings (SendZDOs window, Steam send rate) still need a restart."); SendWindowBytes = c.Bind<int>("Sync", "SendWindowBytes", 10240, new ConfigDescription("Per-peer bytes in flight before the server stops queueing ZDO data. Vanilla 10240. Try 32768. Also sizes each iteration of the disconnect flush. Max 262144: one ZDO package can grow to the window size, and Steam rejects messages over 512 KB, which stalls the player's connection until it times out.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(4096, 262144), Array.Empty<object>())); MinHeadroomBytes = c.Bind<int>("Sync", "MinHeadroomBytes", 2048, new ConfigDescription("Below this much free window the peer is skipped this round. Vanilla 2048. Scale with the window; must stay below SendWindowBytes.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(256, 262144), Array.Empty<object>())); AllPeersPerRound = c.Bind<bool>("Sync", "AllPeersPerRound", false, "Serve every peer each round instead of one peer per frame. Turn on after B1 is verified with several players; otherwise it concentrates the scan cost into one frame (6 peers x 4.1 ms full scans per 0.05 s if the DirtySets watchdog ever disables dirty sets)."); RoundSeconds = c.Bind<float>("Sync", "RoundSeconds", 0.05f, new ConfigDescription("Round period when AllPeersPerRound is on. Vanilla 0.05.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>())); SendRateMax = c.Bind<int>("Steam", "SendRateMaxBytesPerSec", 153600, "Steam per-connection send rate cap. Vanilla 153600 (150 KB/s). Try 1048576. Bounded by your upload / player count."); SendRateMin = c.Bind<int>("Steam", "SendRateMinBytesPerSec", 153600, "Leave at vanilla so the estimator can back off on loss."); OverrideSendRate = c.Bind<bool>("Steam", "OverrideSendRate", true, "Set false if another networking mod manages Steam send rates: ValheimTune then never writes them. Even when true, a rate left at vanilla (153600) is not written. Restart to apply."); OverrideSendWindow = c.Bind<bool>("Sync", "OverrideSendWindow", true, "Set false if another networking mod changes the ZDO send queue size: ValheimTune then leaves ZDOMan.SendZDOs untouched. Even when true, nothing is changed while SendWindowBytes and MinHeadroomBytes are both vanilla. Restart to apply."); MaxPacketsPerPeerPerFrame = c.Bind<int>("Receive", "MaxPacketsPerPeerPerFrame", 0, "Stop draining one peer's socket after this many packets in a frame; the rest wait in Steam's queue. 0 = vanilla (unlimited). Try 64."); TargetFrameRate = c.Bind<int>("Server", "TargetFrameRate", 0, "Application.targetFrameRate for the headless server. 0 = do not touch. Round period is 0.05 + players/fps, so 30 -> 60 doubles the sync rate at 6 players. Costs CPU."); FloatingDropsRun = c.Bind<bool>("Cleanup", "FloatingDropsRun", false, "One-shot trigger: set true to scan for item drops floating in water. The plugin runs it on the next config reload and sets this back to false. Measured ~50 ms of main-thread stall on a 698k-ZDO world (one 67 ms frame, 2026-09-07)."); FloatingDropsDelete = c.Bind<bool>("Cleanup", "FloatingDropsDelete", false, "When a scan runs with this true, the found items are DELETED (server takes ownership and destroys them; clients see them vanish). Leave false for a dry run that only logs counts."); HotObjectsIgnore = c.Bind<string>("Measure", "HotObjectsIgnore", "Player,Fish1,Fish2,Fish3", "Comma-separated prefab names left out of the hot-objects line (things that are expected to move)."); DirtySets = c.Bind<bool>("Sync", "DirtySets", true, "B1: only consider changed ZDOs each round instead of rescanning the whole active area. Full scan on join, zone change, and every ReconcileSeconds."); ReconcileSeconds = c.Bind<float>("Sync", "ReconcileSeconds", 30f, "Safety-net full scan interval per peer when DirtySets is on."); RelayMinIntervalMs = c.Bind<int>("Sync", "RelayMinIntervalMs", 0, "Do not re-send a non-prioritized object (fish, items, pieces) to the same peer more often than this, in ms. 0 = vanilla. 200 = 5 Hz; fish and drifting items are the bulk of idle traffic at a big base."); KnownGoodBuilds = c.Bind<string>("Compat", "KnownGoodBuilds", "1.0.7, 1.0.12, 1.0.14, 1.0.15, 1.0.16, 1.0.17, 1.0.20", "Game versions (Version.CurrentVersion) this build of the plugin was verified against. Comma-separated."); DisableOnUnknownBuild = c.Bind<bool>("Compat", "DisableOnUnknownBuild", true, "On a version not in KnownGoodBuilds, keep only measurement, the send-rate postfix and the constant swap; every replacement patch runs vanilla."); TopKSort = c.Bind<bool>("Sync", "TopKSort", true, "B2: bounded-heap selection of the objects that fit the send window instead of a full sort of every candidate. Only matters during joins, zone changes and the reconcile scan."); ServerSkipRenderMesh = c.Bind<bool>("Server", "SkipRenderMesh", false, "B4a: skip Heightmap.RebuildRenderMesh on a dedicated server. The render mesh is rebuilt for every ghost zone a player explores and every terrain edit that loads with one, and never drawn on a -nographics process. Collision mesh untouched. Watch meshSkips on the stats line."); DeferAssetUnload = c.Bind<bool>("Server", "DeferAssetUnload", false, "G1: hold the hourly Resources.UnloadUnusedAssets() until no players are connected. Vanilla runs it every hour regardless (Game.cs:239); measured 443-607 ms of main-thread stall on a 698k-ZDO world, landing with players online. Deferred, not skipped: it still runs the moment the server empties, or after AssetUnloadMaxDeferMinutes whichever comes first."); AssetUnloadMaxDeferMinutes = c.Bind<int>("Server", "AssetUnloadMaxDeferMinutes", 240, new ConfigDescription("Backstop for DeferAssetUnload: run the collection even with players online once it has been held this long. Stops a server that never empties from never collecting.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 1440), Array.Empty<object>())); SaveDirtyFix = c.Bind<bool>("Fixes", "SaveDirtyFix", true, "S1: mark a save chunk dirty when a client update arrives. Vanilla only marks chunks for changes the server makes itself, so an area only players touched since the last save can be skipped by the incremental save and revert on restart."); SpawnerLinkFix = c.Bind<bool>("Fixes", "SpawnerLinkFix", true, "S2: before a save, also save the other side of any spawner/creature link that is about to be written. Vanilla gives links new hashes every save, so a link split across a saved and an unsaved chunk breaks on restart and the spawner spawns a duplicate."); DeadZdoPrune = c.Bind<bool>("Fixes", "DeadZdoPrune", true, "G1: at each save, forget destroyed-object records older than one hour. Vanilla keeps one per destroyed object until restart; they are only needed for seconds."); DisconnectNoSleep = c.Bind<bool>("Fixes", "DisconnectNoSleep", true, "N1: remove vanilla's 100 ms main-thread sleep in ZSteamSocket.Close (every disconnect and every rejected join freezes the server for 100 ms) and close with Steam's linger instead, so queued messages still go out. Restart to apply."); GlobalKeyDedupe = c.Bind<bool>("Fixes", "GlobalKeyDedupe", true, "G2: ignore a SetGlobalKey for a key that is already set to the same value. Vanilla compares case-sensitively against lowercased keys, so e.g. every unshielded ship in the Ashlands ocean re-broadcasts all global keys to every player every 10 s."); TopK = c.Bind<int>("Sync", "TopK", 0, new ConfigDescription("How many candidates to order per round. 0 = SendWindowBytes / 64 (512 at 32 KB), never below 64.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 8192), Array.Empty<object>())); } } public sealed class ChurnTally { private readonly Dictionary<int, int> _counts = new Dictionary<int, int>(); public int Total { get; private set; } public void Add(int key) { _counts.TryGetValue(key, out var value); _counts[key] = value + 1; Total++; } public List<KeyValuePair<int, int>> Top(int n) { return _counts.OrderByDescending((KeyValuePair<int, int> kv) => kv.Value).Take(n).ToList(); } public void Reset() { _counts.Clear(); Total = 0; } } public static class CloseSwap { public static bool TryFind(IReadOnlyList<(OpCode op, string name)> ins, out int sleepCall, out int lingerArg) { sleepCall = -1; lingerArg = -1; int num = 0; int num2 = 0; for (int i = 0; i < ins.Count; i++) { if (IsCall(ins[i].op)) { string text = ins[i].name ?? ""; if (text == "Thread.Sleep") { num++; sleepCall = i; } else if (text.EndsWith(".CloseConnection") || text == "CloseConnection") { num2++; lingerArg = i - 1; } } } if (num != 1 || num2 != 1) { return false; } if (sleepCall < 1 || (ins[sleepCall - 1].op != OpCodes.Ldc_I4_S && ins[sleepCall - 1].op != OpCodes.Ldc_I4)) { return false; } if (lingerArg >= 0) { return ins[lingerArg].op == OpCodes.Ldc_I4_0; } return false; } private static bool IsCall(OpCode op) { if (!(op == OpCodes.Call)) { return op == OpCodes.Callvirt; } return true; } public static bool TryApply(List<CodeInstruction> code, out List<CodeInstruction> result) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown result = code; List<(OpCode, string)> list = new List<(OpCode, string)>(code.Count); foreach (CodeInstruction item in code) { list.Add((item.opcode, (item.operand is MethodBase methodBase) ? (methodBase.DeclaringType?.Name + "." + methodBase.Name) : "")); } if (!TryFind(list, out var sleepCall, out var lingerArg)) { return false; } result = new List<CodeInstruction>(code); int[] array = new int[2] { sleepCall - 1, sleepCall }; foreach (int index in array) { result[index] = new CodeInstruction(OpCodes.Nop, (object)null) { labels = code[index].labels, blocks = code[index].blocks }; } result[lingerArg] = new CodeInstruction(OpCodes.Ldc_I4_1, (object)null) { labels = code[lingerArg].labels, blocks = code[lingerArg].blocks }; return true; } } public static class Compat { public static bool ReplacementsAllowed = true; public static string GameVersion = "?"; public const string DefaultKnownGoodBuilds = "1.0.7, 1.0.12, 1.0.14, 1.0.15, 1.0.16, 1.0.17, 1.0.20"; public static bool IsKnownOrShipped(string version, string knownList) { if (!IsKnown(version, knownList)) { return IsKnown(version, "1.0.7, 1.0.12, 1.0.14, 1.0.15, 1.0.16, 1.0.17, 1.0.20"); } return true; } public static bool IsKnown(string version, string knownList) { if (string.IsNullOrEmpty(knownList)) { return false; } return knownList.Split(',').Any((string s) => s.Trim() == version); } } public static class ConstSwap { public static int LastReplaced; public static bool ShouldOverride(bool enabled, int value, int vanilla) { if (enabled) { return value != vanilla; } return false; } public static IEnumerable<CodeInstruction> Replace(IEnumerable<CodeInstruction> code, IReadOnlyDictionary<int, int> map) { LastReplaced = 0; foreach (CodeInstruction item in code) { if (item.opcode == OpCodes.Ldc_I4 && item.operand is int key && map.TryGetValue(key, out var value)) { LastReplaced++; yield return new CodeInstruction(OpCodes.Ldc_I4, (object)value) { labels = item.labels, blocks = item.blocks }; } else { yield return item; } } } } public sealed class DirtyPeerState<TId> { private (int x, int y) _lastZone = (x: int.MinValue, y: int.MinValue); private int _lastSimKey; private float _lastFullScan = float.NegativeInfinity; private bool _wasActive; private readonly List<TId> _prune = new List<TId>(); public HashSet<TId> Pending { get; } = new HashSet<TId>(); public int FullScans { get; private set; } public bool NeedsFullScan((int x, int y) zone, float now, float reconcileSeconds, bool active, int simKey) { int num; if (_wasActive) { (int, int) tuple = zone; (int, int) lastZone = _lastZone; if (tuple.Item1 == lastZone.Item1 && tuple.Item2 == lastZone.Item2 && simKey == _lastSimKey) { num = ((now - _lastFullScan > reconcileSeconds) ? 1 : 0); goto IL_0045; } } num = 1; goto IL_0045; IL_0045: _wasActive = active; if (num != 0) { _lastZone = zone; _lastSimKey = simKey; _lastFullScan = now; FullScans++; } return (byte)num != 0; } public void Drain(List<TId> into, Func<TId, bool> exists, Func<TId, bool> inArea, Func<TId, bool> shouldSend, Func<TId, bool> deferSend = null) { _prune.Clear(); foreach (TId item in Pending) { if (!exists(item) || !inArea(item) || !shouldSend(item)) { _prune.Add(item); } else if (deferSend == null || !deferSend(item)) { into.Add(item); } } foreach (TId item2 in _prune) { Pending.Remove(item2); } } } public static class FloatingDrops { private static readonly Dictionary<int, bool> s_isCandidate = new Dictionary<int, bool>(); private static bool IsFloatingCandidate(int hash) { if (s_isCandidate.TryGetValue(hash, out var value)) { return value; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(hash) : null); value = (Object)(object)val != (Object)null && ((Object)(object)val.GetComponent<ItemDrop>() != (Object)null || (Object)(object)val.GetComponent<TreeLog>() != (Object)null) && (Object)(object)val.GetComponent<Fish>() == (Object)null; s_isCandidate[hash] = value; return value; } public static (int found, int deleted) Run(bool delete, ManualLogSource log) { //IL_0092: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) ZDOMan instance = ZDOMan.instance; if (instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || WorldGenerator.instance == null) { log.LogWarning((object)"[ValheimTune] floating drops: world not ready"); return (found: 0, deleted: 0); } float waterLevel = ZoneSystem.instance.m_waterLevel; List<ZDOID> list = new List<ZDOID>(); ChurnTally churnTally = new ChurnTally(); List<(string, Vector3)> list2 = new List<(string, Vector3)>(); foreach (KeyValuePair<ZDOID, ZDO> item in instance.m_objectsByID) { ZDO value = item.Value; if (!IsFloatingCandidate(value.GetPrefab())) { continue; } Vector3 position = value.GetPosition(); if (!(position.y > waterLevel + 0.25f) && !(WorldGenerator.instance.GetHeight(position.x, position.z) >= waterLevel)) { list.Add(item.Key); churnTally.Add(value.GetPrefab()); if (list2.Count < 10) { GameObject prefab = ZNetScene.instance.GetPrefab(value.GetPrefab()); list2.Add((((Object)(object)prefab != (Object)null) ? ((Object)prefab).name : value.GetPrefab().ToString(), position)); } } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("[ValheimTune] floating drops: ").Append(list.Count).Append(delete ? " found, deleting:" : " found (dry run):"); foreach (KeyValuePair<int, int> item2 in churnTally.Top(10)) { GameObject prefab2 = ZNetScene.instance.GetPrefab(item2.Key); stringBuilder.Append(' ').Append(((Object)(object)prefab2 != (Object)null) ? ((Object)prefab2).name : item2.Key.ToString()).Append('=') .Append(item2.Value); } log.LogInfo((object)stringBuilder.ToString()); if (list2.Count > 0) { StringBuilder stringBuilder2 = new StringBuilder("[ValheimTune] floating drops sample:"); foreach (var item3 in list2) { stringBuilder2.Append(' ').Append(item3.Item1).Append('@') .Append('(') .Append((int)item3.Item2.x) .Append(',') .Append((int)item3.Item2.y) .Append(',') .Append((int)item3.Item2.z) .Append(')'); } log.LogInfo((object)stringBuilder2.ToString()); } int num = 0; if (delete) { long sessionID = ZDOMan.GetSessionID(); foreach (ZDOID item4 in list) { ZDO zDO = instance.GetZDO(item4); if (zDO != null) { zDO.SetOwner(sessionID); instance.DestroyZDO(zDO); num++; } } log.LogInfo((object)$"[ValheimTune] floating drops: {num} queued for destruction"); } return (found: list.Count, deleted: num); } } [BepInPlugin("akoozie.valheimtune", "ValheimTune", "0.7.10")] public class Plugin : BaseUnityPlugin { public const string Guid = "akoozie.valheimtune"; public const string Version = "0.7.10"; private const float WatchdogWindowSeconds = 10f; public static ManualLogSource Log; public static Plugin Instance; private Harmony _harmony; private float _logTimer; private float _reloadTimer; private float _wdTimer; private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Cfg.Bind(((BaseUnityPlugin)this).Config); Compat.GameVersion = ((object)Version.CurrentVersion/*cast due to .constrained prefix*/).ToString(); Compat.ReplacementsAllowed = !Cfg.DisableOnUnknownBuild.Value || Compat.IsKnownOrShipped(Compat.GameVersion, Cfg.KnownGoodBuilds.Value); if (Compat.ReplacementsAllowed && !Compat.IsKnown(Compat.GameVersion, Cfg.KnownGoodBuilds.Value)) { Log.LogInfo((object)("[ValheimTune] game " + Compat.GameVersion + " is not in your KnownGoodBuilds (" + Cfg.KnownGoodBuilds.Value + ") but ships in this release (1.0.7, 1.0.12, 1.0.14, 1.0.15, 1.0.16, 1.0.17, 1.0.20); using the shipped list. Your config is from an older version.")); } if (!Compat.ReplacementsAllowed) { Log.LogWarning((object)("[ValheimTune] game " + Compat.GameVersion + " not in KnownGoodBuilds (" + Cfg.KnownGoodBuilds.Value + "): replacement patches inactive, running vanilla + measurement")); } _harmony = new Harmony("akoozie.valheimtune"); _harmony.PatchAll(typeof(Plugin).Assembly); uint num = 41u; Log.LogInfo((object)string.Format("[ValheimTune] {0} loaded on game {1} (net {2}), {3} methods patched, replacements {4}", "0.7.10", Compat.GameVersion, num, _harmony.GetPatchedMethods().Count(), Compat.ReplacementsAllowed ? "on" : "OFF")); } private void Update() { //IL_058b: Unknown result type (might be due to invalid IL or missing references) //IL_059f: Unknown result type (might be due to invalid IL or missing references) ReloadConfig(); RunFloatingDropsIfRequested(); AssetUnloadPatch.RunIfDue(); MeasurePatches.FrameMs.Add(Time.unscaledDeltaTime * 1000f); int value = Cfg.TargetFrameRate.Value; if (value > 0 && Application.targetFrameRate != value) { Application.targetFrameRate = value; Log.LogInfo((object)$"[ValheimTune] targetFrameRate set to {value}"); } ZDOMan instance = ZDOMan.instance; int num = ((instance != null) ? instance.GetRecvZDOs() : 0); _wdTimer += Time.unscaledDeltaTime; if (_wdTimer >= 10f) { long watchdogRecv = DirtyPatches.WatchdogRecv; long watchdogMarks = DirtyPatches.WatchdogMarks; if (DirtyPatches.WatchdogShouldTrip(Cfg.DirtySets.Value, DirtyPatches.Disabled, watchdogRecv > 0, watchdogMarks)) { DirtyPatches.Disabled = true; Log.LogError((object)$"[ValheimTune] DirtySets: {watchdogRecv} ZDOs deserialized in {10f:F0} s but the revision hook fired 0 times; falling back to vanilla scanning. The ZDO revision setters are probably inlined on this Mono build."); } else if (DirtyPatches.WatchdogShouldRearm(DirtyPatches.Disabled, watchdogMarks)) { DirtyPatches.Disabled = false; Log.LogWarning((object)$"[ValheimTune] DirtySets: revision hook fired {watchdogMarks} times while disabled; re-armed (full scan per peer on the next round)."); } DirtyPatches.WatchdogMarks = 0L; DirtyPatches.WatchdogRecv = 0L; _wdTimer = 0f; } int value2 = Cfg.LogIntervalSeconds.Value; if (value2 <= 0) { MeasurePatches.ResetAll(); DirtyPatches.ResetCounters(); RenderMeshPatch.Skipped = 0L; return; } _logTimer += Time.unscaledDeltaTime; if (_logTimer < (float)value2) { return; } _logTimer = 0f; int num2 = instance?.m_peers.Count ?? 0; int num3 = ((instance != null) ? instance.GetSentZDOs() : 0); int num4 = num; Log.LogInfo((object)($"[ValheimTune] frame avg {MeasurePatches.FrameMs.Avg:F1} max {MeasurePatches.FrameMs.Max:F1} ms " + $"({1000.0 / Math.Max(0.01, MeasurePatches.FrameMs.Avg):F0} fps) | " + $"syncList avg {MeasurePatches.SyncListMs.Avg:F2} max {MeasurePatches.SyncListMs.Max:F2} ms | " + $"send avg {MeasurePatches.SendMs.Avg:F2} ms | Z max {MeasurePatches.MaxZ} | peer-sends {MeasurePatches.Rounds} | " + $"zdos/s sent {num3} recv {num4} | peers {num2}" + string.Format(" | marks {0} full {1} dirtyRounds {2} deferred {3} drained {4}{5}", DirtyPatches.Marks, DirtyPatches.FullScans, DirtyPatches.DirtyRounds, DirtyPatches.Deferred, DirtyPatches.LastDrained, DirtyPatches.Disabled ? " DISABLED" : "") + $" | meshSkips {RenderMeshPatch.Skipped}" + $" | release max {MeasurePatches.ReleaseMaxMs:F1} removePeer max {MeasurePatches.RemovePeerMaxMs:F1} ms | gc {MeasurePatches.GcDelta} heap {MeasurePatches.HeapMb} MB | dead {instance?.m_deadZDOs.Count ?? 0}" + $" | saveMarks {SavePatches.SaveMarks} linkFixes {SavePatches.LinkFixes} keyDedupes {NetPatches.KeyDedupes}")); if (MeasurePatches.Recv.Total > 0) { StringBuilder stringBuilder = new StringBuilder("[ValheimTune] recv by prefab ("); stringBuilder.Append(MeasurePatches.Recv.Total).Append(" in window):"); ZNetScene scene = ZNetScene.instance; foreach (KeyValuePair<int, int> item2 in MeasurePatches.Recv.Top(8)) { string text = null; if ((Object)(object)scene != (Object)null) { GameObject prefab = scene.GetPrefab(item2.Key); if ((Object)(object)prefab != (Object)null) { text = ((Object)prefab).name; } } stringBuilder.Append(' ').Append(text ?? item2.Key.ToString()).Append('=') .Append(item2.Value); } Log.LogInfo((object)stringBuilder.ToString()); HashSet<string> ignoreSet = new HashSet<string>(from s in Cfg.HotObjectsIgnore.Value.Split(',') select s.Trim()); List<KeyValuePair<ZDOID, (int, int, Vector3)>> list = MeasurePatches.HotTop(12).Where(delegate(KeyValuePair<ZDOID, (int count, int prefab, Vector3 pos)> x) { ZNetScene obj3 = scene; object obj4; if (obj3 == null) { obj4 = null; } else { GameObject prefab3 = obj3.GetPrefab(x.Value.prefab); obj4 = ((prefab3 != null) ? ((Object)prefab3).name : null); } if (obj4 == null) { obj4 = x.Value.prefab.ToString(); } string item = (string)obj4; return !ignoreSet.Contains(item); }).Take(6) .ToList(); if (list.Count > 0) { StringBuilder stringBuilder2 = new StringBuilder("[ValheimTune] hot objects:"); foreach (KeyValuePair<ZDOID, (int, int, Vector3)> item3 in list) { ZNetScene obj = scene; object obj2; if (obj == null) { obj2 = null; } else { GameObject prefab2 = obj.GetPrefab(item3.Value.Item2); obj2 = ((prefab2 != null) ? ((Object)prefab2).name : null); } if (obj2 == null) { obj2 = item3.Value.Item2.ToString(); } string value3 = (string)obj2; int value4 = (int)item3.Value.Item3.x; int value5 = (int)item3.Value.Item3.z; stringBuilder2.Append(' ').Append(value3).Append('=') .Append(item3.Value.Item1) .Append('@') .Append('(') .Append(value4) .Append(',') .Append(value5) .Append(')'); } Log.LogInfo((object)stringBuilder2.ToString()); } } RenderMeshPatch.Skipped = 0L; DirtyPatches.ResetCounters(); MeasurePatches.ResetAll(); } private void ReloadConfig() { int value = Cfg.ConfigReloadSeconds.Value; if (value <= 0) { return; } _reloadTimer += Time.unscaledDeltaTime; if (_reloadTimer < (float)value) { return; } _reloadTimer = 0f; try { ((BaseUnityPlugin)this).Config.Reload(); } catch (Exception arg) { Log.LogError((object)$"[ValheimTune] config reload failed: {arg}"); } } private void RunFloatingDropsIfRequested() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || !Cfg.FloatingDropsRun.Value) { return; } try { FloatingDrops.Run(Cfg.FloatingDropsDelete.Value, Log); } catch (Exception arg) { Log.LogError((object)$"[ValheimTune] floating drops run failed: {arg}"); } finally { Cfg.FloatingDropsRun.Value = false; ((BaseUnityPlugin)this).Config.Save(); } } } public sealed class RollingStats { private double _sum; public int Count { get; private set; } public double Max { get; private set; } public double Avg { get { if (Count != 0) { return _sum / (double)Count; } return 0.0; } } public void Add(double v) { _sum += v; Count++; if (v > Max) { Max = v; } } public void Reset() { _sum = 0.0; Count = 0; Max = 0.0; } } public static class TopK { private static class Buf<T> { public static T[] Items; } private static double[] s_rank; private static int[] s_heap; private static bool[] s_taken; public static void Select<T>(List<T> items, Func<T, double> rank, int k) { int count = items.Count; if (count == 0 || k <= 0) { return; } if (k > count) { k = count; } if (s_rank == null || s_rank.Length < count) { s_rank = new double[count]; s_heap = new int[count]; s_taken = new bool[count]; } if (Buf<T>.Items == null || Buf<T>.Items.Length < count) { Buf<T>.Items = new T[count]; } double[] array = s_rank; int[] array2 = s_heap; bool[] array3 = s_taken; T[] items2 = Buf<T>.Items; for (int i = 0; i < count; i++) { items2[i] = items[i]; array[i] = rank(items[i]); array3[i] = false; } int num = 0; for (int j = 0; j < count; j++) { if (num < k) { array2[num] = j; num++; SiftUp(array2, array, num - 1); } else if (array[j] < array[array2[0]]) { array2[0] = j; SiftDown(array2, array, 0, num); } } int num2 = num; for (int num3 = num2 - 1; num3 >= 0; num3--) { int num4 = array2[0]; array3[num4] = true; items[num3] = items2[num4]; num--; array2[0] = array2[num]; SiftDown(array2, array, 0, num); } int num5 = num2; for (int l = 0; l < count; l++) { if (!array3[l]) { items[num5++] = items2[l]; } } } private static void SiftUp(int[] heap, double[] rankArr, int i) { while (i > 0) { int num = (i - 1) / 2; if (!(rankArr[heap[num]] >= rankArr[heap[i]])) { ref int reference = ref heap[num]; ref int reference2 = ref heap[i]; int num2 = heap[i]; int num3 = heap[num]; reference = num2; reference2 = num3; i = num; continue; } break; } } private static void SiftDown(int[] heap, double[] rankArr, int i, int count) { while (true) { int num = 2 * i + 1; int num2 = 2 * i + 2; int num3 = i; if (num < count && rankArr[heap[num]] > rankArr[heap[num3]]) { num3 = num; } if (num2 < count && rankArr[heap[num2]] > rankArr[heap[num3]]) { num3 = num2; } if (num3 != i) { ref int reference = ref heap[i]; ref int reference2 = ref heap[num3]; int num4 = heap[num3]; int num5 = heap[i]; reference = num4; reference2 = num5; i = num3; continue; } break; } } } } namespace ValheimTune.Patches { [HarmonyPatch(typeof(Game), "CollectResources")] public static class AssetUnloadPatch { private static float s_deferredSince = -1f; public static bool Pending => s_deferredSince >= 0f; [HarmonyPrefix] private static bool Prefix() { if (!Compat.ReplacementsAllowed || !Cfg.DeferAssetUnload.Value) { s_deferredSince = -1f; return true; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return true; } int peerConnections = ZNet.instance.GetPeerConnections(); float realtimeSinceStartup = Time.realtimeSinceStartup; double num = ((s_deferredSince < 0f) ? 0.0 : ((double)(realtimeSinceStartup - s_deferredSince))); double maxDeferSecs = (double)Cfg.AssetUnloadMaxDeferMinutes.Value * 60.0; if (AssetUnload.Decide(peerConnections, num, maxDeferSecs) == AssetUnload.Decision.Defer) { if (s_deferredSince < 0f) { s_deferredSince = realtimeSinceStartup; Plugin.Log.LogInfo((object)$"[ValheimTune] asset unload deferred: {peerConnections} player(s) online, will run when the server empties (backstop {Cfg.AssetUnloadMaxDeferMinutes.Value} min)"); } return false; } if (s_deferredSince >= 0f) { Plugin.Log.LogInfo((object)$"[ValheimTune] asset unload running after {num:F0} s deferred ({peerConnections} player(s) online)"); s_deferredSince = -1f; } return true; } public static void RunIfDue() { if (!Cfg.DeferAssetUnload.Value) { s_deferredSince = -1f; } else { if (!Pending) { return; } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return; } double deferredFor = Time.realtimeSinceStartup - s_deferredSince; if (AssetUnload.Decide(instance.GetPeerConnections(), deferredFor, (double)Cfg.AssetUnloadMaxDeferMinutes.Value * 60.0) == AssetUnload.Decision.Run) { Game instance2 = Game.instance; if (instance2 != null) { instance2.CollectResources(false); } } } } } [HarmonyPatch] public static class ConstPatches { private static bool s_allPeersDisabledLogged; private const int VanillaSendRate = 153600; private static bool s_rateLogged; [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> SendZDOsWindow(IEnumerable<CodeInstruction> code) { List<CodeInstruction> list = new List<CodeInstruction>(code); bool value = Cfg.OverrideSendWindow.Value; if (!ConstSwap.ShouldOverride(value, Cfg.SendWindowBytes.Value, 10240) && !ConstSwap.ShouldOverride(value, Cfg.MinHeadroomBytes.Value, 2048)) { Plugin.Log.LogInfo((object)("[ValheimTune] SendZDOs window left vanilla (" + (value ? "values are vanilla" : "OverrideSendWindow = false") + ")")); return list; } if (Cfg.MinHeadroomBytes.Value >= Cfg.SendWindowBytes.Value) { Plugin.Log.LogError((object)$"[ValheimTune] MinHeadroomBytes ({Cfg.MinHeadroomBytes.Value}) >= SendWindowBytes ({Cfg.SendWindowBytes.Value}): no ZDO would ever be sent; leaving SendZDOs vanilla"); return list; } Dictionary<int, int> map = new Dictionary<int, int> { { 10240, Cfg.SendWindowBytes.Value }, { 2048, Cfg.MinHeadroomBytes.Value } }; List<CodeInstruction> result = new List<CodeInstruction>(ConstSwap.Replace(list, map)); if (ConstSwap.LastReplaced != 3) { Plugin.Log.LogError((object)$"[ValheimTune] SendZDOs: {ConstSwap.LastReplaced} constants replaced, expected 3; leaving the method untouched"); return list; } Plugin.Log.LogInfo((object)$"[ValheimTune] SendZDOs window {Cfg.SendWindowBytes.Value}/{Cfg.MinHeadroomBytes.Value}, {ConstSwap.LastReplaced} constants replaced (expected 3)"); return result; } [HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")] [HarmonyPrefix] private static bool AllPeersPerRound(ZDOMan __instance, float dt) { if (!Compat.ReplacementsAllowed || !Cfg.AllPeersPerRound.Value || DirtyPatches.Disabled) { if (DirtyPatches.Disabled && !s_allPeersDisabledLogged) { s_allPeersDisabledLogged = true; Plugin.Log.LogWarning((object)"[ValheimTune] AllPeersPerRound skipped: DirtySets watchdog disabled dirty sets, falling back to vanilla one-peer-per-frame to avoid stacking full scans in one frame."); } return true; } if (__instance.m_peers.Count == 0) { return false; } __instance.m_sendTimer += dt; if (__instance.m_sendTimer < Cfg.RoundSeconds.Value) { return false; } __instance.m_sendTimer = 0f; for (int i = 0; i < __instance.m_peers.Count; i++) { try { __instance.SendZDOs(__instance.m_peers[i], false); } catch (Exception arg) { Plugin.Log.LogError((object)$"[ValheimTune] SendZDOs threw for peer {i}: {arg}"); } } return false; } [HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")] [HarmonyPostfix] private static void SendRate() { bool value = Cfg.OverrideSendRate.Value; bool flag = ConstSwap.ShouldOverride(value, Cfg.SendRateMin.Value, 153600); bool flag2 = ConstSwap.ShouldOverride(value, Cfg.SendRateMax.Value, 153600); if (flag) { SetGlobalInt((ESteamNetworkingConfigValue)10, Cfg.SendRateMin.Value); } if (flag2) { SetGlobalInt((ESteamNetworkingConfigValue)11, Cfg.SendRateMax.Value); } if (!s_rateLogged) { s_rateLogged = true; Plugin.Log.LogInfo((object)((flag || flag2) ? ("[ValheimTune] Steam send rate min " + (flag ? Cfg.SendRateMin.Value.ToString() : "untouched") + " max " + (flag2 ? Cfg.SendRateMax.Value.ToString() : "untouched") + " B/s") : ("[ValheimTune] Steam send rate left untouched (" + (value ? "values are vanilla" : "OverrideSendRate = false") + ")"))); } } private static void SetGlobalInt(ESteamNetworkingConfigValue key, int value) { //IL_000d: 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) GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned); try { if (!SteamGameServerNetworkingUtils.SetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject())) { Plugin.Log.LogError((object)$"[ValheimTune] Steam rejected {key} = {value}"); } } finally { gCHandle.Free(); } } } [HarmonyPatch] public static class DirtyPatches { public static long MarksData; public static long MarksOwner; public static int FullScans; public static int DirtyRounds; public static int Deferred; public static int LastDrained; public static bool Disabled; public static long WatchdogMarks; public static long WatchdogRecv; private static readonly ConditionalWeakTable<ZDOPeer, DirtyPeerState<ZDOID>> s_state = new ConditionalWeakTable<ZDOPeer, DirtyPeerState<ZDOID>>(); private static readonly List<ZDOID> s_ids = new List<ZDOID>(256); private static int s_relayMinMs; public static long Marks => MarksData + MarksOwner; private static bool Active { get { if (Compat.ReplacementsAllowed && Cfg.DirtySets.Value && !Disabled && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public static int ZoneChebyshev(int ax, int ay, int bx, int by) { int num = ax - bx; if (num < 0) { num = -num; } int num2 = ay - by; if (num2 < 0) { num2 = -num2; } if (num <= num2) { return num2; } return num; } internal static bool InNear(Vector2s centre, Vector2s candidate, SimulationDistance sd) { //IL_0000: 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) //IL_000c: 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) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (ZoneChebyshev(centre.x, centre.y, candidate.x, candidate.y) > ((SimulationDistance)(ref sd)).NearSimulationDistance) { return false; } if (!((SimulationDistance)(ref sd)).IsClassic) { return ZoneSystem.instance.ZonesWithinRadius(centre, candidate, ((SimulationDistance)(ref sd)).NearSimulationDistance, false); } return true; } internal static bool InDistant(Vector2s centre, Vector2s candidate, SimulationDistance sd) { //IL_0000: 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) //IL_000c: 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) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (ZoneChebyshev(centre.x, centre.y, candidate.x, candidate.y) > ((SimulationDistance)(ref sd)).TotalSimulationDistance) { return false; } if (!((SimulationDistance)(ref sd)).IsClassic) { return ZoneSystem.instance.ZonesWithinRadius(centre, candidate, ((SimulationDistance)(ref sd)).TotalSimulationDistance, true); } return true; } public static DirtyPeerState<ZDOID> StateFor(ZDOPeer peer) { return s_state.GetValue(peer, (ZDOPeer _) => new DirtyPeerState<ZDOID>()); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void DataRevisionSet(ZDO __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Mark(__instance.m_uid, isOwner: false); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void OwnerRevisionSet(ZDO __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Mark(__instance.m_uid, isOwner: true); } [HarmonyPatch(typeof(ZDOMan), "CreateNewZDO", new Type[] { typeof(ZDOID), typeof(Vector3), typeof(int) })] [HarmonyPostfix] private static void CreateNewZDOPost(ZDO __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Mark(__result.m_uid, isOwner: true); } private static void Mark(ZDOID id, bool isOwner) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (id == ZDOID.None) { return; } if (isOwner) { MarksOwner++; } else { MarksData++; WatchdogMarks++; } if (!Active) { return; } ZDOMan instance = ZDOMan.instance; if (instance != null) { List<ZDOPeer> peers = instance.m_peers; for (int i = 0; i < peers.Count; i++) { StateFor(peers[i]).Pending.Add(id); } } } public static void ResetCounters() { MarksData = 0L; MarksOwner = 0L; FullScans = 0; DirtyRounds = 0; Deferred = 0; LastDrained = 0; } public static bool WatchdogShouldTrip(bool dirtySetsOn, bool disabled, bool recvSeen, long marksInWindow) { if (dirtySetsOn && !disabled && recvSeen) { return marksInWindow == 0; } return false; } public static bool WatchdogShouldRearm(bool disabled, long marksInWindow) { if (disabled) { return marksInWindow > 0; } return false; } [HarmonyPatch(typeof(ZDOMan), "CreateSyncList")] [HarmonyPrefix] private static bool CreateSyncListPrefix(ZDOMan __instance, ZDOPeer peer, List<ZDO> toSync, out bool __state) { //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_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_0041: 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_0047: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) __state = false; ZDOID lastId = ZDOID.None; ZDO lastZdo = null; bool active = Active; Vector3 refPos = peer.m_peer.GetRefPos(); Vector2s zone = ZoneSystem.GetZone(refPos); DirtyPeerState<ZDOID> dirtyPeerState = StateFor(peer); SimulationDistance sd = peer.m_peer.m_simulationDistance; int simKey = ((SimulationDistance)(ref sd)).NearSimulationDistance * 4096 + ((SimulationDistance)(ref sd)).TotalSimulationDistance * 2 + (((SimulationDistance)(ref sd)).IsClassic ? 1 : 0); bool flag = dirtyPeerState.NeedsFullScan((x: zone.x, y: zone.y), Time.time, Cfg.ReconcileSeconds.Value, active, simKey); if (!active) { return true; } if (flag) { FullScans++; __state = true; return true; } DirtyRounds++; s_relayMinMs = Cfg.RelayMinIntervalMs.Value; s_ids.Clear(); dirtyPeerState.Drain(s_ids, (ZDOID id) => Z(id) != null, delegate(ZDOID id) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) ZDO val = Z(id); Vector2s sector = val.GetSector(); return InNear(zone, sector, sd) || (val.Distant && InDistant(zone, sector, sd)); }, (ZDOID id) => peer.ShouldSend(Z(id)), (ZDOID id) => RelayThrottled(peer, Z(id))); LastDrained = s_ids.Count; int num = 0; for (int num2 = 0; num2 < s_ids.Count; num2++) { ZDO zDO = __instance.GetZDO(s_ids[num2]); if (InNear(zone, zDO.GetSector(), sd)) { toSync.Add(zDO); num++; } } if (num < 10) { for (int num3 = 0; num3 < s_ids.Count; num3++) { ZDO zDO2 = __instance.GetZDO(s_ids[num3]); if (!InNear(zone, zDO2.GetSector(), sd)) { toSync.Add(zDO2); } } } __instance.ServerSortSendZDOS(toSync, refPos, peer); __instance.AddForceSendZdos(peer, toSync); return false; ZDO Z(ZDOID id) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (id != lastId) { lastZdo = __instance.GetZDO(id); lastId = id; } return lastZdo; } } [HarmonyPatch(typeof(ZDOMan), "CreateSyncList")] [HarmonyPostfix] private static void CreateSyncListPostfix(ZDOMan __instance, ZDOPeer peer, List<ZDO> toSync, bool __state) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!__state) { return; } DirtyPeerState<ZDOID> dirtyPeerState = StateFor(peer); for (int i = 0; i < toSync.Count; i++) { dirtyPeerState.Pending.Add(toSync[i].m_uid); } List<ZDO> tempToSyncDistant = __instance.m_tempToSyncDistant; for (int j = 0; j < tempToSyncDistant.Count; j++) { if (peer.ShouldSend(tempToSyncDistant[j])) { dirtyPeerState.Pending.Add(tempToSyncDistant[j].m_uid); } } } private static bool RelayThrottled(ZDOPeer peer, ZDO z) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) int num = s_relayMinMs; if (num <= 0) { return false; } if ((int)z.Type == 1) { return false; } if (z.GetOwner() == peer.m_peer.m_uid) { return false; } if (!peer.m_zdos.TryGetValue(z.m_uid, out var value)) { return false; } bool num2 = (Time.time - value.m_syncTime) * 1000f < (float)num; if (num2) { Deferred++; } return num2; } } [HarmonyPatch] public static class MeasurePatches { public static readonly RollingStats FrameMs = new RollingStats(); public static readonly RollingStats SyncListMs = new RollingStats(); public static readonly RollingStats SendMs = new RollingStats(); public static readonly ChurnTally Recv = new ChurnTally(); public static int MaxZ; public static int Rounds; private static readonly Stopwatch s_sync = new Stopwatch(); private static readonly Stopwatch s_send = new Stopwatch(); private static readonly Dictionary<ZDOID, (int count, int prefab, Vector3 pos)> s_hot = new Dictionary<ZDOID, (int, int, Vector3)>(); public static double RemovePeerMaxMs; public static double ReleaseMaxMs; private static readonly Stopwatch s_remove = new Stopwatch(); private static readonly Stopwatch s_release = new Stopwatch(); private static int s_gc0 = GC.CollectionCount(0); public static int GcDelta => GC.CollectionCount(0) - s_gc0; public static long HeapMb => GC.GetTotalMemory(forceFullCollection: false) / 1048576; [HarmonyPatch(typeof(ZDOMan), "CreateSyncList")] [HarmonyPrefix] [HarmonyPriority(800)] private static void SyncListPrefix() { s_sync.Restart(); } [HarmonyPatch(typeof(ZDOMan), "CreateSyncList")] [HarmonyPostfix] private static void SyncListPostfix(ZDOMan __instance) { SyncListMs.Add(s_sync.Elapsed.TotalMilliseconds); int count = __instance.m_tempSectorObjects.Count; if (count > MaxZ) { MaxZ = count; } } [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] [HarmonyPrefix] [HarmonyPriority(800)] private static void SendPrefix() { s_send.Restart(); } [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] [HarmonyPostfix] private static void SendPostfix() { SendMs.Add(s_send.Elapsed.TotalMilliseconds); Rounds++; } [HarmonyPatch(typeof(ZDO), "Deserialize")] [HarmonyPostfix] private static void DeserializePostfix(ZDO __instance) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Recv.Add(__instance.GetPrefab()); DirtyPatches.WatchdogRecv++; ZDOID uid = __instance.m_uid; s_hot.TryGetValue(uid, out (int, int, Vector3) value); s_hot[uid] = (value.Item1 + 1, __instance.GetPrefab(), __instance.GetPosition()); } [HarmonyPatch(typeof(ZDOMan), "RemovePeer")] [HarmonyPrefix] [HarmonyPriority(800)] private static void RemovePeerPrefix() { s_remove.Restart(); } [HarmonyPatch(typeof(ZDOMan), "RemovePeer")] [HarmonyPostfix] private static void RemovePeerPostfix() { RemovePeerMaxMs = Math.Max(RemovePeerMaxMs, s_remove.Elapsed.TotalMilliseconds); } [HarmonyPatch(typeof(ZDOMan), "ReleaseZDOS")] [HarmonyPrefix] [HarmonyPriority(800)] private static void ReleasePrefix() { s_release.Restart(); } [HarmonyPatch(typeof(ZDOMan), "ReleaseZDOS")] [HarmonyPostfix] private static void ReleasePostfix() { ReleaseMaxMs = Math.Max(ReleaseMaxMs, s_release.Elapsed.TotalMilliseconds); } public static List<KeyValuePair<ZDOID, (int count, int prefab, Vector3 pos)>> HotTop(int n) { return s_hot.OrderByDescending((KeyValuePair<ZDOID, (int count, int prefab, Vector3 pos)> x) => x.Value.count).Take(n).ToList(); } public static void ResetAll() { FrameMs.Reset(); SyncListMs.Reset(); SendMs.Reset(); MaxZ = 0; Rounds = 0; Recv.Reset(); s_hot.Clear(); RemovePeerMaxMs = 0.0; ReleaseMaxMs = 0.0; s_gc0 = GC.CollectionCount(0); SavePatches.SaveMarks = 0L; SavePatches.LinkFixes = 0L; NetPatches.KeyDedupes = 0L; } } [HarmonyPatch] public static class NetPatches { public static long KeyDedupes; [HarmonyPatch(typeof(ZSteamSocket), "Close")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> CloseNoSleep(IEnumerable<CodeInstruction> code) { List<CodeInstruction> list = new List<CodeInstruction>(code); if (!Compat.ReplacementsAllowed || !Cfg.DisconnectNoSleep.Value) { return list; } if (!CloseSwap.TryApply(list, out var result)) { Plugin.Log.LogError((object)"[ValheimTune] ZSteamSocket.Close: expected one Thread.Sleep and one CloseConnection(..., false) not found; leaving Close untouched"); return list; } Plugin.Log.LogInfo((object)"[ValheimTune] ZSteamSocket.Close: 100 ms sleep removed, linger on"); return result; } [HarmonyPatch(typeof(ZoneSystem), "RPC_SetGlobalKey")] [HarmonyPrefix] private static bool SetGlobalKeyDedupe(ZoneSystem __instance, string name) { if (!Compat.ReplacementsAllowed || !Cfg.GlobalKeyDedupe.Value || name == null) { return true; } string text = default(string); GlobalKeys val = default(GlobalKeys); string keyValue = ZoneSystem.GetKeyValue(name.ToLower(), ref text, ref val); if (__instance.m_globalKeysValues.TryGetValue(keyValue, out var value) && value == text) { KeyDedupes++; return false; } return true; } } [HarmonyPatch(typeof(ZRpc), "Update")] public static class ReceiveCapPatch { [HarmonyPrefix] private static bool Prefix(ZRpc __instance, float dt, ref ErrorCode __result) { int value = Cfg.MaxPacketsPerPeerPerFrame.Value; if (!Compat.ReplacementsAllowed || value <= 0) { return true; } if (!__instance.m_socket.IsConnected()) { __result = (ErrorCode)1; return false; } for (int i = 0; i < value; i++) { ZPackage val = __instance.m_socket.Recv(); if (val == null) { break; } __instance.m_recvPackages++; __instance.m_recvData += val.Size(); try { __instance.HandlePackage(val); } catch (EndOfStreamException ex) { ZLog.LogError((object)("EndOfStreamException in ZRpc::HandlePackage: Assume incompatible version: " + ex.Message)); __result = (ErrorCode)2; return false; } catch (Exception ex2) { ZLog.Log((object)("Exception in ZRpc::HandlePackage: " + ex2)); } } __instance.UpdatePing(dt); __result = (ErrorCode)0; return false; } } [HarmonyPatch(typeof(Heightmap), "RebuildRenderMesh")] public static class RenderMeshPatch { public static long Skipped; [HarmonyPrefix] private static bool Prefix() { if (!Compat.ReplacementsAllowed || !Cfg.ServerSkipRenderMesh.Value) { return true; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return true; } Skipped++; return false; } } [HarmonyPatch] public static class SavePatches { public static long SaveMarks; public static long LinkFixes; private static bool s_inFix; private static bool s_fixErrorLogged; private static readonly List<ZDOID> s_deadKeys = new List<ZDOID>(); [HarmonyPatch(typeof(ZDO), "Deserialize")] [HarmonyPostfix] private static void MarkDirtyOnReceive(ZDO __instance) { if (!Compat.ReplacementsAllowed || !Cfg.SaveDirtyFix.Value) { return; } ZNet instance = ZNet.instance; ZDOMan instance2 = ZDOMan.instance; if (!((Object)(object)instance == (Object)null) && instance2 != null && instance.IsServer() && __instance.Persistent) { if ((Object)(object)Game.instance != (Object)null && Game.instance.PortalPrefabHash.Contains(__instance.GetPrefab())) { instance2.SetDirtyPortals(); } else { instance2.SetDirtySector(__instance); } SaveMarks++; } } [HarmonyPatch(typeof(ZDOMan), "GetSaveClonePerChunk")] [HarmonyPostfix] private static void SaveLinkedChunks(ZDOMan __instance, ref List<Tuple<ChunkIndex, List<ZDO>>> __result) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_00df: Invalid comparison between Unknown and I4 //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) if (s_inFix || !Compat.ReplacementsAllowed || !Cfg.SpawnerLinkFix.Value || (int)__instance.m_currentSaveState != 0) { return; } s_inFix = true; List<Tuple<ChunkIndex, List<ZDO>>> list = __result; try { int num = 0; for (int i = 0; i < 8; i++) { HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); foreach (Tuple<ChunkIndex, List<ZDO>> item in __result) { foreach (ZDO item2 in item.Item2) { hashSet.Add(item2.m_uid); } } int num2 = 0; foreach (KeyValuePair<ZDOID, ZDOConnection> s_connection in ZDOExtraData.s_connections) { ZDOConnection value = s_connection.Value; if ((int)value.m_type == 0 || (int)value.m_type == 1 || value.m_target == ZDOID.None) { continue; } ZDO zDO = __instance.GetZDO(s_connection.Key); ZDO zDO2 = __instance.GetZDO(value.m_target); if (zDO != null && zDO2 != null && zDO.Persistent && zDO2.Persistent) { bool flag = hashSet.Contains(zDO.m_uid); bool flag2 = hashSet.Contains(zDO2.m_uid); if (flag != flag2) { __instance.SetDirtySector(flag ? zDO2 : zDO); num2++; } } } if (num2 == 0) { break; } num += num2; __result = __instance.GetSaveClonePerChunk(); } if (num > 0) { Plugin.Log.LogInfo((object)$"[ValheimTune] save: {num} split spawner link(s): other side marked for this save"); } LinkFixes += num; } catch (Exception arg) { __result = list; if (!s_fixErrorLogged) { s_fixErrorLogged = true; Plugin.Log.LogError((object)$"[ValheimTune] spawner link fix failed, saving as vanilla: {arg}"); } } finally { s_inFix = false; } } [HarmonyPatch(typeof(ZDOMan), "PrepareSave")] [HarmonyPostfix] private static void PruneDead(ZDOMan __instance) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (!Compat.ReplacementsAllowed || !Cfg.DeadZdoPrune.Value || (Object)(object)ZNet.instance == (Object)null) { return; } long num = ZNet.instance.GetTime().Ticks - TimeSpan.FromHours(1.0).Ticks; s_deadKeys.Clear(); foreach (KeyValuePair<ZDOID, long> deadZDO in __instance.m_deadZDOs) { if (deadZDO.Value < num) { s_deadKeys.Add(deadZDO.Key); } } foreach (ZDOID s_deadKey in s_deadKeys) { __instance.m_deadZDOs.Remove(s_deadKey); } s_deadKeys.Clear(); } } [HarmonyPatch] public static class SortPatches { private static bool Active { get { if (Compat.ReplacementsAllowed && Cfg.TopKSort.Value && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } [HarmonyPatch(typeof(ZDOMan), "ServerSortSendZDOS")] [HarmonyPrefix] private static bool Prefix(List<ZDO> objects, Vector3 refPos, ZDOPeer peer) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!Active) { return true; } float time = Time.time; long receiver = peer.m_peer.m_uid; Dictionary<ZDOID, PeerZDOInfo> zdos = peer.m_zdos; int k = ((Cfg.TopK.Value > 0) ? Cfg.TopK.Value : Math.Max(64, Cfg.SendWindowBytes.Value / 64)); TopK.Select(objects, delegate(ZDO z) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_002a: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) int num = (((int)z.Type != 1 || !z.HasOwner() || z.GetOwner() == receiver) ? (1 + (3 - z.Type)) : 0); float num2 = 100f; if (zdos.TryGetValue(z.m_uid, out var value)) { num2 = Mathf.Clamp(time - value.m_syncTime, 0f, 100f); } double num3 = Vector3.Distance(z.GetPosition(), refPos) - num2 * 1.5f; return (double)num * 100000.0 + num3; }, k); return false; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }