using 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.0.0")] [assembly: AssemblyInformationalVersion("0.7.0+4d181ca723f884dfd05a2c443aacc630925d1ce3")] [assembly: AssemblyProduct("ValheimTune")] [assembly: AssemblyTitle("ValheimTune")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.7.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; } } } 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 LogIntervalSeconds; public static ConfigEntry SendWindowBytes; public static ConfigEntry MinHeadroomBytes; public static ConfigEntry AllPeersPerRound; public static ConfigEntry RoundSeconds; public static ConfigEntry SendRateMax; public static ConfigEntry SendRateMin; public static ConfigEntry MaxPacketsPerPeerPerFrame; public static ConfigEntry TargetFrameRate; public static ConfigEntry ConfigReloadSeconds; public static ConfigEntry FloatingDropsRun; public static ConfigEntry FloatingDropsDelete; public static ConfigEntry HotObjectsIgnore; public static ConfigEntry DirtySets; public static ConfigEntry ReconcileSeconds; public static ConfigEntry RelayMinIntervalMs; public static ConfigEntry KnownGoodBuilds; public static ConfigEntry DisableOnUnknownBuild; public static ConfigEntry TopKSort; public static ConfigEntry TopK; public static ConfigEntry ServerSkipRenderMesh; public static ConfigEntry DeferAssetUnload; public static ConfigEntry AssetUnloadMaxDeferMinutes; 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_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Expected O, but got Unknown //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Expected O, but got Unknown LogIntervalSeconds = c.Bind("Measure", "LogIntervalSeconds", 10, "How often to print the stats line. 0 disables."); ConfigReloadSeconds = c.Bind("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("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.", (AcceptableValueBase)(object)new AcceptableValueRange(4096, 1048576), Array.Empty())); MinHeadroomBytes = c.Bind("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(256, 262144), Array.Empty())); AllPeersPerRound = c.Bind("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("Sync", "RoundSeconds", 0.05f, new ConfigDescription("Round period when AllPeersPerRound is on. Vanilla 0.05.", (AcceptableValueBase)(object)new AcceptableValueRange(0.01f, 1f), Array.Empty())); SendRateMax = c.Bind("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("Steam", "SendRateMinBytesPerSec", 153600, "Leave at vanilla so the estimator can back off on loss."); MaxPacketsPerPeerPerFrame = c.Bind("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("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("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("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("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("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("Sync", "ReconcileSeconds", 30f, "Safety-net full scan interval per peer when DirtySets is on."); RelayMinIntervalMs = c.Bind("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("Compat", "KnownGoodBuilds", "1.0.7", "Game versions (Version.CurrentVersion) this build of the plugin was verified against. Comma-separated."); DisableOnUnknownBuild = c.Bind("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("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("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("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("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(10, 1440), Array.Empty())); TopK = c.Bind("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(0, 8192), Array.Empty())); } } public sealed class ChurnTally { private readonly Dictionary _counts = new Dictionary(); public int Total { get; private set; } public void Add(int key) { _counts.TryGetValue(key, out var value); _counts[key] = value + 1; Total++; } public List> Top(int n) { return _counts.OrderByDescending((KeyValuePair kv) => kv.Value).Take(n).ToList(); } public void Reset() { _counts.Clear(); Total = 0; } } public static class Compat { public static bool ReplacementsAllowed = true; public static string GameVersion = "?"; 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 IEnumerable Replace(IEnumerable code, IReadOnlyDictionary 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 { private (int x, int y) _lastZone = (x: int.MinValue, y: int.MinValue); private float _lastFullScan = float.NegativeInfinity; private bool _wasActive; private readonly List _prune = new List(); public HashSet Pending { get; } = new HashSet(); public int FullScans { get; private set; } public bool NeedsFullScan((int x, int y) zone, float now, float reconcileSeconds, bool active) { int num; if (_wasActive) { (int, int) tuple = zone; (int, int) lastZone = _lastZone; if (tuple.Item1 == lastZone.Item1 && tuple.Item2 == lastZone.Item2) { num = ((now - _lastFullScan > reconcileSeconds) ? 1 : 0); goto IL_003b; } } num = 1; goto IL_003b; IL_003b: _wasActive = active; if (num != 0) { _lastZone = zone; _lastFullScan = now; FullScans++; } return (byte)num != 0; } public void Drain(List into, Func exists, Func inArea, Func shouldSend, Func 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 s_isCandidate = new Dictionary(); 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() != (Object)null || (Object)(object)val.GetComponent() != (Object)null) && (Object)(object)val.GetComponent() == (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 list = new List(); ChurnTally churnTally = new ChurnTally(); List<(string, Vector3)> list2 = new List<(string, Vector3)>(); foreach (KeyValuePair 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 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.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "akoozie.valheimtune"; public const string Version = "0.7.0"; 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Cfg.Bind(((BaseUnityPlugin)this).Config); if (Cfg.MinHeadroomBytes.Value >= Cfg.SendWindowBytes.Value) { Log.LogError((object)"[ValheimTune] MinHeadroomBytes >= SendWindowBytes: no ZDO will ever be sent. Fix the config."); } Compat.GameVersion = ((object)Version.CurrentVersion/*cast due to .constrained prefix*/).ToString(); Compat.ReplacementsAllowed = !Cfg.DisableOnUnknownBuild.Value || Compat.IsKnown(Compat.GameVersion, Cfg.KnownGoodBuilds.Value); 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 = 39u; Log.LogInfo((object)string.Format("[ValheimTune] {0} loaded on game {1} (net {2}), {3} methods patched, replacements {4}", "0.7.0", Compat.GameVersion, num, _harmony.GetPatchedMethods().Count(), Compat.ReplacementsAllowed ? "on" : "OFF")); } private void Update() { //IL_04ff: Unknown result type (might be due to invalid IL or missing references) //IL_0513: 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}")); 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 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 ignoreSet = new HashSet(from s in Cfg.HotObjectsIgnore.Value.Split(',') select s.Trim()); List> list = MeasurePatches.HotTop(12).Where(delegate(KeyValuePair 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 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 { public static T[] Items; } private static double[] s_rank; private static int[] s_heap; private static bool[] s_taken; public static void Select(List items, Func 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.Items == null || Buf.Items.Length < count) { Buf.Items = new T[count]; } double[] array = s_rank; int[] array2 = s_heap; bool[] array3 = s_taken; T[] items2 = Buf.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) { 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 (!Pending || !Cfg.DeferAssetUnload.Value) { 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 static bool s_rateLogged; [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] [HarmonyTranspiler] private static IEnumerable SendZDOsWindow(IEnumerable code) { Dictionary map = new Dictionary { { 10240, Cfg.SendWindowBytes.Value }, { 2048, Cfg.MinHeadroomBytes.Value } }; List list = new List(code); List result = new List(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() { SetGlobalInt((ESteamNetworkingConfigValue)10, Cfg.SendRateMin.Value); SetGlobalInt((ESteamNetworkingConfigValue)11, Cfg.SendRateMax.Value); if (!s_rateLogged) { s_rateLogged = true; Plugin.Log.LogInfo((object)$"[ValheimTune] Steam send rate min {Cfg.SendRateMin.Value} max {Cfg.SendRateMax.Value} B/s"); } } 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> s_state = new ConditionalWeakTable>(); private static readonly List s_ids = new List(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; } } internal static int ZoneChebyshev(Vector2s a, Vector2s b) { //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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) int num = a.x - b.x; if (num < 0) { num = -num; } int num2 = a.y - b.y; 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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (ZoneChebyshev(centre, candidate) > ((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_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (ZoneChebyshev(centre, candidate) > ((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 StateFor(ZDOPeer peer) { return s_state.GetValue(peer, (ZDOPeer _) => new DirtyPeerState()); } [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); } 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 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 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_00bd: 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_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: 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 dirtyPeerState = StateFor(peer); bool flag = dirtyPeerState.NeedsFullScan((x: zone.x, y: zone.y), Time.time, Cfg.ReconcileSeconds.Value, active); if (!active) { return true; } if (flag) { FullScans++; __state = true; return true; } DirtyRounds++; SimulationDistance sd = peer.m_peer.m_simulationDistance; 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(ZDOPeer peer, List toSync, bool __state) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (__state) { DirtyPeerState dirtyPeerState = StateFor(peer); for (int i = 0; i < toSync.Count; i++) { dirtyPeerState.Pending.Add(toSync[i].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 s_hot = new Dictionary(); [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()); } public static List> HotTop(int n) { return s_hot.OrderByDescending((KeyValuePair 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(); } } [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 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 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 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) { } } }