using System; using System.Collections.Generic; using System.Diagnostics; 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.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Utils; using SoftReferenceableAssets; using Unity.Collections; using UnityEngine; using UnityEngine.Rendering; using ValheimCommunityPatch.Patches.Correctness; using ValheimCommunityPatch.Patches.Performance; using ValheimCommunityPatch.Patches.Terrain; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ValheimCommunityPatch")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ValheimCommunityPatch")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.22.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.22.1.0")] namespace ValheimCommunityPatch { internal static class HeightmapSampling { internal static bool TryGetHeight(Heightmap hmap, Vector3 position, out float height) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) Vector3 normal; return Sample(hmap, ((Component)hmap).transform.position, position, out height, out normal, wantNormal: false); } internal static bool TryGetHeight(Heightmap hmap, Vector3 origin, Vector3 position, out float height) { //IL_0001: 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) Vector3 normal; return Sample(hmap, origin, position, out height, out normal, wantNormal: false); } internal static bool TryGetSurface(Heightmap hmap, Vector3 position, out float height, out Vector3 normal) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return Sample(hmap, ((Component)hmap).transform.position, position, out height, out normal, wantNormal: true); } internal static bool TryGetSurface(Heightmap hmap, Vector3 origin, Vector3 position, out float height, out Vector3 normal) { //IL_0001: 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) return Sample(hmap, origin, position, out height, out normal, wantNormal: true); } private static bool Sample(Heightmap hmap, Vector3 origin, Vector3 position, out float height, out Vector3 normal, bool wantNormal) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) normal = Vector3.up; int width = hmap.m_width; float scale = hmap.m_scale; float num = (position.x - origin.x) / scale + (float)width * 0.5f; float num2 = (position.z - origin.z) / scale + (float)width * 0.5f; if (num < 0f || num > (float)width || num2 < 0f || num2 > (float)width) { height = 0f; return false; } int num3 = Mathf.Min((int)num, width - 1); int num4 = Mathf.Min((int)num2, width - 1); float num5 = num - (float)num3; float num6 = num2 - (float)num4; float height2 = hmap.GetHeight(num3, num4); float height3 = hmap.GetHeight(num3 + 1, num4); float height4 = hmap.GetHeight(num3, num4 + 1); float height5 = hmap.GetHeight(num3 + 1, num4 + 1); Vector3 val; if (num5 + num6 <= 1f) { height = height2 + (height3 - height2) * num5 + (height4 - height2) * num6 + origin.y; if (wantNormal) { val = new Vector3(height2 - height3, scale, height2 - height4); normal = ((Vector3)(ref val)).normalized; } } else { height = height5 + (height4 - height5) * (1f - num5) + (height3 - height5) * (1f - num6) + origin.y; if (wantNormal) { val = new Vector3(height4 - height5, scale, height3 - height5); normal = ((Vector3)(ref val)).normalized; } } return true; } } internal static class Logger { private static bool _debug; internal static bool DebugEnabled => _debug; internal static void SetDebug(bool enabled) { _debug = enabled; } internal static void LogDebug(string message) { if (_debug) { ValheimCommunityPatch.Log.LogInfo((object)("[DEBUG]" + message)); } } internal static void LogInfo(string message) { ValheimCommunityPatch.Log.LogInfo((object)message); } internal static void LogWarning(string message) { ValheimCommunityPatch.Log.LogWarning((object)message); } internal static void LogError(string message) { ValheimCommunityPatch.Log.LogError((object)message); } internal static void DebugSink(object message) { if (_debug) { LogDebug(message?.ToString() ?? string.Empty); } } } internal static class PatchHelper { internal const int AnyCount = -1; internal static List Copy(IEnumerable instructions) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown List list = ((instructions is ICollection collection) ? new List(collection.Count) : new List()); foreach (CodeInstruction instruction in instructions) { list.Add(new CodeInstruction(instruction)); } return list; } internal static IEnumerable ReplaceCalls(IEnumerable instructions, MethodInfo original, MethodInfo replacement, string site, int expected = -1) { if (original == null || replacement == null) { Logger.LogWarning(site + ": a method this fix needs could not be resolved, so it is inactive here."); return instructions; } List list = Copy(instructions); int num = 0; for (int i = 0; i < list.Count; i++) { if (CodeInstructionExtensions.Calls(list[i], original)) { list[i].opcode = OpCodes.Call; list[i].operand = replacement; num++; } } if ((expected == -1) ? (num > 0) : (num == expected)) { return list; } string text = ((expected == -1) ? "at least 1" : expected.ToString()); Logger.LogWarning(site + ": expected " + text + " call(s) to " + original.DeclaringType?.Name + "." + original.Name + ", " + $"found {num}, so this fix is inactive here. Another mod has most likely already " + "rewritten the method - if so, nothing is wrong."); return instructions; } internal static bool HasHook(MethodBase target, Type hookClass) { Patches val = ((target == null) ? null : Harmony.GetPatchInfo(target)); if (val == null) { return false; } if (!DeclaredBy(val.Prefixes, hookClass)) { return DeclaredBy(val.Postfixes, hookClass); } return true; } private static bool DeclaredBy(IReadOnlyList patches, Type hookClass) { foreach (Patch patch in patches) { if (!(patch.owner != "MidnightsFX.ValheimCommunityPatch") && patch.PatchMethod?.DeclaringType == hookClass) { return true; } } return false; } } internal sealed class HookHealth { private readonly string _fixName; private readonly Func _allAttached; private bool _checked; private bool _healthy; internal bool Healthy { get { if (_checked) { return _healthy; } _checked = true; _healthy = _allAttached(); if (!_healthy) { Logger.LogError(_fixName + ": a maintenance hook is not attached, so this fix stands down to vanilla for this session. A Valheim update has most likely changed one of the patched methods - look for the patch failure logged at startup."); } return _healthy; } } internal HookHealth(string fixName, Func allAttached) { _fixName = fixName; _allAttached = allAttached; } } internal enum Side { Client, Server, Both } [AttributeUsage(AttributeTargets.Class, Inherited = false)] internal sealed class PatchSideAttribute : Attribute { internal Side Side { get; } internal PatchSideAttribute(Side side) { Side = side; } internal static Side Of(Type type) { Type type2 = type; while (type2 != null) { object[] customAttributes = type2.GetCustomAttributes(typeof(PatchSideAttribute), inherit: false); if (customAttributes.Length != 0) { return ((PatchSideAttribute)customAttributes[0]).Side; } type2 = type2.DeclaringType; } return Side.Both; } internal static string Tag(Side side) { return side switch { Side.Client => "(client)", Side.Server => "(server)", _ => "(both)", }; } } internal static class RunMode { private static readonly bool _headless = (int)SystemInfo.graphicsDeviceType == 4; private static ZNet _resolvedFor; private static bool _isServer; private static bool _isDedicated; internal static bool IsHeadless => _headless; internal static bool IsServer { get { Resolve(); return _isServer; } } internal static bool IsDedicated { get { Resolve(); return _isDedicated; } } private static void Resolve() { ZNet instance = ZNet.instance; if (instance != _resolvedFor) { if (instance == null) { _resolvedFor = null; _isServer = false; _isDedicated = false; } else { _isServer = instance.IsServer(); _isDedicated = instance.IsDedicated(); _resolvedFor = instance; } } } } [PatchSide(Side.Both)] internal static class TeardownHooks { [HarmonyPatch(typeof(ZNetView))] internal static class ViewHook { [HarmonyPostfix] [HarmonyPatch("OnDestroy")] private static void OnDestroyPostfix(ZNetView __instance) { if (_statsOn) { _viewDestroys++; } if (__instance.m_ghost) { if (_statsOn) { _ghostDestroys++; } } else { SectorInstanceIndexPatch.OnViewDestroyed(__instance); } } } [HarmonyPatch(typeof(WearNTear))] internal static class PieceHook { [HarmonyPostfix] [HarmonyPatch("OnDestroy")] private static void OnDestroyPostfix(WearNTear __instance) { int instanceID = ((Object)__instance).GetInstanceID(); SupportSleepPatch.OnPieceDestroyed(__instance, instanceID); WearSupportLookupPatch.OnPieceDestroyed(instanceID); WearCacheEventPatch.OnPieceDestroyed(__instance, instanceID); } } [HarmonyPatch(typeof(ZNetScene))] internal static class FrameHook { [HarmonyPrefix] [HarmonyPatch("Update")] private static void UpdatePrefix() { if (LogStormStats == null || !LogStormStats.Value) { if (_statsOn) { LogSummary("final"); Clear(); _statsOn = false; } return; } if (!_statsOn) { _statsOn = true; Clear(); _statsSince = Time.unscaledTime; Logger.LogInfo("Destroy storm stats: on. Bucketing frames by teardown count; a summary follows every 30 s."); return; } RecordFrame(_viewDestroys, Time.unscaledDeltaTime * 1000f); _viewDestroys = 0; if (Time.unscaledTime - _statsSince >= 30f) { LogSummary("periodic"); Clear(); _statsSince = Time.unscaledTime; } } } internal static ConfigEntry LogStormStats; private static bool _statsOn; private static float _statsSince; private static int _viewDestroys; private static int _ghostDestroys; private static int _unloadPasses; private static int _unloadObjects; private static int _unloadWorstObjects; private static double _unloadMs; private static double _unloadWorstMs; private static int _unloadWorstBacklog; private static int _unloadDeferredPasses; private static readonly int[] BucketFloor = new int[5] { 0, 1, 25, 100, 500 }; private static readonly string[] BucketName = new string[5] { "0", "1-24", "25-99", "100-499", "500+" }; private static readonly long[] BucketFrames = new long[5]; private static readonly double[] BucketMs = new double[5]; private static readonly double[] BucketWorstMs = new double[5]; private const float ReportIntervalSeconds = 30f; internal static bool StatsOn => _statsOn; internal static void BindConfig() { LogStormStats = ValConfig.BindServerConfig("Debug", "Log Destroy Storm Stats", value: false, "Diagnostic. Buckets every frame by how many networked objects were torn down in it and reports the frame time each bucket ran at, so a teardown burst can be told apart from a steady drain. Also reports the unload pass's own object count and wall-clock. Costs a counter increment per destroyed object; leave it off unless you are measuring.", null, advanced: true); } internal static void NoteUnloadPass(int processed, int backlog, double milliseconds) { if (_statsOn) { _unloadPasses++; _unloadObjects += processed; _unloadMs += milliseconds; if (processed > _unloadWorstObjects) { _unloadWorstObjects = processed; } if (milliseconds > _unloadWorstMs) { _unloadWorstMs = milliseconds; } if (backlog > _unloadWorstBacklog) { _unloadWorstBacklog = backlog; } if (backlog > processed) { _unloadDeferredPasses++; } } } private static void RecordFrame(int destroys, float frameMs) { int num = 0; for (int num2 = BucketFloor.Length - 1; num2 >= 0; num2--) { if (destroys >= BucketFloor[num2]) { num = num2; break; } } BucketFrames[num]++; BucketMs[num] += frameMs; if ((double)frameMs > BucketWorstMs[num]) { BucketWorstMs[num] = frameMs; } } private static void Clear() { _viewDestroys = 0; _ghostDestroys = 0; _unloadPasses = 0; _unloadObjects = 0; _unloadWorstObjects = 0; _unloadMs = 0.0; _unloadWorstMs = 0.0; _unloadWorstBacklog = 0; _unloadDeferredPasses = 0; for (int i = 0; i < BucketFrames.Length; i++) { BucketFrames[i] = 0L; BucketMs[i] = 0.0; BucketWorstMs[i] = 0.0; } } private static void LogSummary(string kind) { long num = 0L; for (int i = 0; i < BucketFrames.Length; i++) { num += BucketFrames[i]; } if (num == 0L) { return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append($"Destroy storm stats ({kind}), {num} frame(s) over ").Append($"{Time.unscaledTime - _statsSince:F0} s:"); for (int j = 0; j < BucketFrames.Length; j++) { if (BucketFrames[j] != 0L) { stringBuilder.Append($" | {BucketName[j]} destroys: {BucketFrames[j]} frame(s), ").Append($"mean {BucketMs[j] / (double)BucketFrames[j]:F1} ms, worst {BucketWorstMs[j]:F0} ms"); } } stringBuilder.Append($" || ghost destroys {_ghostDestroys}"); if (_unloadPasses > 0) { stringBuilder.Append($" || unload pass: {_unloadPasses} pass(es), {_unloadObjects} object(s), ").Append($"{_unloadMs:F0} ms managed, worst pass {_unloadWorstObjects} object(s) / ").Append($"{_unloadWorstMs:F1} ms") .Append($", worst backlog {_unloadWorstBacklog}, {_unloadDeferredPasses} pass(es) capped"); } Logger.LogInfo(stringBuilder.ToString()); } } internal static class ValConfig { public static ConfigFile cfg; public static ConfigEntry EnableDebugMode; public static ConfigEntry PatchEverySide; public const string SectionPerformance = "Fixes - Performance"; public const string SectionCorrectness = "Fixes - Correctness"; public const string SectionTerrain = "Fixes - Terrain"; public const string SectionDebug = "Debug"; internal static void Bind(ConfigFile file) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown cfg = file; cfg.SaveOnConfigSet = false; EnableDebugMode = cfg.Bind("Client config", "EnableDebugMode", false, new ConfigDescription("Enables Debug logging.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); Logger.SetDebug(EnableDebugMode.Value); EnableDebugMode.SettingChanged += delegate { Logger.SetDebug(EnableDebugMode.Value); }; PatchEverySide = cfg.Bind("Client config", "Patch Every Side", false, new ConfigDescription("Applies every fix regardless of which side it is for. Normally the client-only fixes are not applied on a dedicated server, because nothing there could ever reach them. Turn this on only if this machine has a display but was detected as headless. Requires a game restart.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); BindFixConfig(); cfg.SaveOnConfigSet = true; cfg.Save(); } private static void BindFixConfig() { TeardownHooks.BindConfig(); OrphanZdoIndexPatch.BindConfig(); ZdoPrefabIndexPatch.BindConfig(); HeightmapLookupPatch.BindConfig(); StaticPhysicsCachePatch.BindConfig(); ClutterRebuildCapPatch.BindConfig(); HeightmapBuilderThroughputPatch.BindConfig(); ZoneGenPacingPatch.BindConfig(); TerrainLodSpreadPatch.BindConfig(); WearSupportLookupPatch.BindConfig(); SceneIdleSkipPatch.BindConfig(); LightCostPatch.BindConfig(); RemoveSweepPacingPatch.BindConfig(); SpawnQueueCachePatch.BindConfig(); SectorInstanceIndexPatch.BindConfig(); SupportSleepPatch.BindConfig(); ZoneDiffRemovalPatch.BindConfig(); ReflectionSlicePatch.BindConfig(); PhysicsCatchupPatch.BindConfig(); SpawnEventQueuePatch.BindConfig(); RecipeGetAmountNrePatch.BindConfig(); ProjectileZeroVelocityPatch.BindConfig(); SpawnAreaNullPrefabPatch.BindConfig(); ZdoLoadDuplicatePatch.BindConfig(); RemoveObjectsNrePatch.BindConfig(); EffectAreaPatch.BindConfig(); FuelLossPatch.BindConfig(); BossKeySharePatch.BindConfig(); ItemIconVariantPatch.BindConfig(); SendFailureLogSpamPatch.BindConfig(); ContainerLogSpamPatch.BindConfig(); NegativeStaminaPatch.BindConfig(); DungeonZoneLoadPinPatch.BindConfig(); SeamlessNormalsPatch.BindConfig(); PaintSeamReconcilePatch.BindConfig(); TerrainOpPaintFanoutPatch.BindConfig(); PaintMaskStridePatch.BindConfig(); TerrainCompNullHmapPatch.BindConfig(); } public static ConfigEntry BindFixToggle(Type patchClass, string category, string key, bool value, string description, bool advanced = false) { return BindServerConfig(category, key, value, PatchSideAttribute.Tag(PatchSideAttribute.Of(patchClass)) + " " + description, null, advanced); } public static ConfigEntry BindServerConfig(string category, string key, bool value, string description, AcceptableValueBase acceptableValues = null, bool advanced = false) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown return cfg.Bind(category, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry BindServerConfig(string category, string key, int value, string description, bool advanced = false, int valMin = 0, int valMax = 150) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown return cfg.Bind(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry BindServerConfig(string category, string key, float value, string description, bool advanced = false, float valMin = 0f, float valMax = 150f) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown return cfg.Bind(category, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valMin, valMax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } } [BepInPlugin("MidnightsFX.ValheimCommunityPatch", "ValheimCommunityPatch", "0.22.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class ValheimCommunityPatch : BaseUnityPlugin { public const string PluginGUID = "MidnightsFX.ValheimCommunityPatch"; public const string PluginName = "ValheimCommunityPatch"; public const string PluginVersion = "0.22.1"; internal static ManualLogSource Log; private readonly Harmony harmony = new Harmony("MidnightsFX.ValheimCommunityPatch"); public void Awake() { Log = ((BaseUnityPlugin)this).Logger; ValConfig.Bind(((BaseUnityPlugin)this).Config); CollisionCallbackReusePatch.Apply(); ApplyPatches(); } private void ApplyPatches() { int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; bool flag = ValConfig.PatchEverySide != null && ValConfig.PatchEverySide.Value; Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length == 0) { continue; } Side side = PatchSideAttribute.Of(type); bool flag2 = type.DeclaringType == null; if (side == Side.Client && RunMode.IsHeadless && !flag) { if (flag2) { num3++; } Log.LogDebug((object)("Skipped " + type.Name + ": client-only, and this process is headless.")); continue; } try { harmony.CreateClassProcessor(type).Patch(); if (flag2) { num++; switch (side) { case Side.Client: num4++; break; case Side.Server: num5++; break; default: num6++; break; } } Log.LogDebug((object)("Applied " + type.Name + " " + PatchSideAttribute.Tag(side) + ".")); } catch (Exception arg) { num2++; Log.LogError((object)$"Could not apply {type.Name}: {arg}"); } } Log.LogInfo((object)$"{num} fix(es) applied: {num5} server, {num6} both, {num4} client."); if (num3 > 0) { Log.LogInfo((object)($"{num3} client-only fix(es) not applied: this process has no graphics device, " + "so it is a dedicated server and nothing would ever reach them. If that is wrong, set 'Patch Every Side' in the config.")); } if (num2 > 0) { Log.LogWarning((object)($"{num2} fix(es) failed. The failures above are usually caused by a Valheim " + "update changing a patched method; the remaining fixes are unaffected.")); } } public void OnDestroy() { ZsfxIdleDormancyPatch.RestoreAll(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } } } namespace ValheimCommunityPatch.Patches.Terrain { [PatchSide(Side.Client)] [HarmonyPatch] internal static class PaintMaskStridePatch { internal static ConfigEntry Enabled; internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(PaintMaskStridePatch), "Fixes - Terrain", "Fix Terrain Paint Mask Indexing", value: true, "Corrects the array stride and bounds used when reading and writing terrain paint. Vanilla indexes a 33-wide paint array with a stride of 32, which skews the paint diagonally, and refuses to write the row and column each zone shares with its neighbour."); } [HarmonyPrefix] [HarmonyPatch(typeof(Heightmap), "SetPaintMask")] private static bool SetPaintMaskPrefix(Heightmap __instance, int x, int y, Color paint) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (Enabled == null || !Enabled.Value) { return true; } int num = __instance.m_width + 1; if (x < 0 || y < 0 || x >= num || y >= num) { return false; } __instance.m_paintMask.SetPixel(x, y, paint); return false; } [HarmonyPrefix] [HarmonyPatch(typeof(TerrainComp), "UpdatePaintMask")] private static bool UpdatePaintMaskPrefix(TerrainComp __instance, Heightmap hmap) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (Enabled == null || !Enabled.Value) { return true; } if (!__instance.m_initialized) { return false; } int num = __instance.m_width + 1; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { int num2 = i * num + j; if (__instance.m_modifiedPaint[num2]) { Color val = __instance.m_paintMask[num2]; val.a = hmap.GetPaintMask(j, i).a; __instance.m_paintMask[num2] = val; } } } __instance.Save(false); hmap.Poke(0, false); return false; } [HarmonyPrefix] [HarmonyPatch(typeof(Heightmap), "UpdateTerrainAlpha", new Type[] { typeof(Heightmap) })] private static bool UpdateTerrainAlphaPrefix(Heightmap hmap, ref bool __result) { //IL_0020: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (Enabled == null || !Enabled.Value) { return true; } HMBuildData val = HeightmapBuilder.instance.RequestTerrainSync(((Component)hmap).transform.position, hmap.m_width, hmap.m_scale, hmap.IsDistantLod, WorldGenerator.instance); int num = hmap.m_width + 1; int num2 = 0; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { float a = val.m_baseMask[i * num + j].a; Color paintMask = hmap.GetPaintMask(j, i); if (a != paintMask.a) { paintMask.a = a; hmap.SetPaintMask(j, i, paintMask); num2++; } } } if (num2 > 0) { hmap.GetAndCreateTerrainCompiler().UpdatePaintMask(hmap); Logger.LogInfo($"Corrected {num2} terrain alpha pixel(s) at {((Component)hmap).transform.position}."); } __result = num2 > 0; return false; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(Heightmap))] internal static class PaintSeamReconcilePatch { private enum Edge { East, West, North, South } private struct Corner { internal Heightmap Map; internal int X; internal int Y; internal Color Boundary; internal Color Inward; } internal static ConfigEntry Enabled; private static readonly List DirtyNeighbours = new List(); private static readonly Corner[] CornerBuffer = new Corner[4]; internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(PaintSeamReconcilePatch), "Fixes - Terrain", "Fix Terrain Paint Seams", value: true, "Makes terrain paint agree along zone boundaries. Vanilla can end up with dirt painted on only one side of a 64m zone border, which draws a hard straight line across the ground. Paint that the ground next to the border already carries is carried across the boundary; a lone stripe sitting on the border with nothing behind it on either side is removed."); } [HarmonyPostfix] [HarmonyPatch("ApplyModifiers")] private static void ApplyModifiersPostfix(Heightmap __instance) { if (Enabled == null || !Enabled.Value || RunMode.IsDedicated || __instance.IsDistantLod || (Object)(object)__instance.m_paintMask == (Object)null) { return; } int width = __instance.m_width; if (width >= 2) { float num = (float)width * __instance.m_scale; DirtyNeighbours.Clear(); if ((0u | (ReconcileEdge(__instance, FindNeighbour(__instance, num, 0f), Edge.East, width) ? 1u : 0u) | (ReconcileEdge(__instance, FindNeighbour(__instance, 0f - num, 0f), Edge.West, width) ? 1u : 0u) | (ReconcileEdge(__instance, FindNeighbour(__instance, 0f, num), Edge.North, width) ? 1u : 0u) | (ReconcileEdge(__instance, FindNeighbour(__instance, 0f, 0f - num), Edge.South, width) ? 1u : 0u) | (ReconcileCorner(__instance, num, highX: true, highZ: true, width) ? 1u : 0u) | (ReconcileCorner(__instance, num, highX: true, highZ: false, width) ? 1u : 0u) | (ReconcileCorner(__instance, num, highX: false, highZ: true, width) ? 1u : 0u) | (ReconcileCorner(__instance, num, highX: false, highZ: false, width) ? 1u : 0u)) != 0) { __instance.m_paintMask.Apply(); } for (int i = 0; i < DirtyNeighbours.Count; i++) { DirtyNeighbours[i].m_paintMask.Apply(); } DirtyNeighbours.Clear(); } } private static bool ReconcileEdge(Heightmap ours, Heightmap theirs, Edge edge, int width) { //IL_00ab: 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_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: 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_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)theirs == (Object)null || (Object)(object)theirs.m_paintMask == (Object)null) { return false; } bool result = false; bool flag = false; for (int i = 1; i < width; i++) { int num; int num2; int num3; int num4; int num5; int num6; int num7; int num8; switch (edge) { case Edge.East: num = width; num2 = i; num3 = width - 1; num4 = i; num5 = 0; num6 = i; num7 = 1; num8 = i; break; case Edge.West: num = 0; num2 = i; num3 = 1; num4 = i; num5 = width; num6 = i; num7 = width - 1; num8 = i; break; case Edge.North: num = i; num2 = width; num3 = i; num4 = width - 1; num5 = i; num6 = 0; num7 = i; num8 = 1; break; default: num = i; num2 = 0; num3 = i; num4 = 1; num5 = i; num6 = width; num7 = i; num8 = width - 1; break; } Color pixel = ours.m_paintMask.GetPixel(num, num2); Color pixel2 = theirs.m_paintMask.GetPixel(num5, num6); if (!SamePaint(pixel, pixel2)) { Color val = Merge(pixel, pixel2, ours.m_paintMask.GetPixel(num3, num4), theirs.m_paintMask.GetPixel(num7, num8)); if (!SamePaint(val, pixel)) { ours.m_paintMask.SetPixel(num, num2, new Color(val.r, val.g, val.b, pixel.a)); result = true; } if (!SamePaint(val, pixel2)) { theirs.m_paintMask.SetPixel(num5, num6, new Color(val.r, val.g, val.b, pixel2.a)); flag = true; } } } if (flag) { MarkDirty(theirs); } return result; } private static bool ReconcileCorner(Heightmap ours, float zoneSize, bool highX, bool highZ, int width) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) float dx = (highX ? zoneSize : (0f - zoneSize)); float dz = (highZ ? zoneSize : (0f - zoneSize)); int count = 0; count = AddCorner(count, ours, width, highX, highZ); count = AddCorner(count, FindNeighbour(ours, dx, 0f), width, !highX, highZ); count = AddCorner(count, FindNeighbour(ours, 0f, dz), width, highX, !highZ); count = AddCorner(count, FindNeighbour(ours, dx, dz), width, !highX, !highZ); if (count < 2) { return false; } Color boundary = CornerBuffer[0].Boundary; Color boundary2 = CornerBuffer[0].Boundary; Color inward = CornerBuffer[0].Inward; for (int i = 1; i < count; i++) { Color boundary3 = CornerBuffer[i].Boundary; Color inward2 = CornerBuffer[i].Inward; ((Color)(ref boundary))..ctor(Mathf.Min(boundary.r, boundary3.r), Mathf.Min(boundary.g, boundary3.g), Mathf.Min(boundary.b, boundary3.b)); ((Color)(ref boundary2))..ctor(Mathf.Max(boundary2.r, boundary3.r), Mathf.Max(boundary2.g, boundary3.g), Mathf.Max(boundary2.b, boundary3.b)); ((Color)(ref inward))..ctor(Mathf.Max(inward.r, inward2.r), Mathf.Max(inward.g, inward2.g), Mathf.Max(inward.b, inward2.b)); } Color val = default(Color); ((Color)(ref val))..ctor(Mathf.Clamp(inward.r, boundary.r, boundary2.r), Mathf.Clamp(inward.g, boundary.g, boundary2.g), Mathf.Clamp(inward.b, boundary.b, boundary2.b)); bool result = false; for (int j = 0; j < count; j++) { Corner corner = CornerBuffer[j]; if (!SamePaint(val, corner.Boundary)) { corner.Map.m_paintMask.SetPixel(corner.X, corner.Y, new Color(val.r, val.g, val.b, corner.Boundary.a)); if ((Object)(object)corner.Map == (Object)(object)ours) { result = true; } else { MarkDirty(corner.Map); } } } return result; } private static int AddCorner(int count, Heightmap hmap, int width, bool highX, bool highZ) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hmap == (Object)null || (Object)(object)hmap.m_paintMask == (Object)null) { return count; } int num = (highX ? width : 0); int num2 = (highZ ? width : 0); int num3 = ((!highX) ? 1 : (width - 1)); int num4 = ((!highZ) ? 1 : (width - 1)); CornerBuffer[count] = new Corner { Map = hmap, X = num, Y = num2, Boundary = hmap.m_paintMask.GetPixel(num, num2), Inward = hmap.m_paintMask.GetPixel(num3, num4) }; return count + 1; } private static Color Merge(Color a, Color b, Color supportA, Color supportB) { //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_001d: 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_002f: 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_0040: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) return new Color(MergeChannel(a.r, b.r, supportA.r, supportB.r), MergeChannel(a.g, b.g, supportA.g, supportB.g), MergeChannel(a.b, b.b, supportA.b, supportB.b)); } private static float MergeChannel(float a, float b, float supportA, float supportB) { return Mathf.Clamp(Mathf.Max(supportA, supportB), Mathf.Min(a, b), Mathf.Max(a, b)); } private static bool SamePaint(Color merged, Color current) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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 (merged.r == current.r && merged.g == current.g) { return merged.b == current.b; } return false; } private static void MarkDirty(Heightmap hmap) { if (!DirtyNeighbours.Contains(hmap)) { DirtyNeighbours.Add(hmap); } } private static Heightmap FindNeighbour(Heightmap hmap, float dx, float dz) { //IL_0006: 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_0017: Unknown result type (might be due to invalid IL or missing references) Heightmap val = Heightmap.FindHeightmap(((Component)hmap).transform.position + new Vector3(dx, 0f, dz)); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)hmap || val.IsDistantLod) { return null; } if (val.m_width != hmap.m_width || val.m_scale != hmap.m_scale) { return null; } return val; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(Heightmap))] internal static class SeamlessNormalsPatch { [HarmonyPatch(typeof(MonoUpdaters), "LateUpdate")] internal static class ProcessDirtyHook { [HarmonyPostfix] private static void Postfix() { if (Dirty.Count == 0) { return; } if (Enabled == null || !Enabled.Value || RunMode.IsDedicated) { foreach (Heightmap item in Dirty) { if ((Object)(object)item != (Object)null && (Object)(object)item.m_renderMesh != (Object)null) { item.m_renderMesh.RecalculateTangents(); } } Dirty.Clear(); return; } RebuiltScratch.Clear(); AffectedScratch.Clear(); foreach (Heightmap item2 in Dirty) { if (!((Object)(object)item2 == (Object)null)) { RebuiltScratch.Add(item2); AffectedScratch.Add(item2); float num = (float)item2.m_width * item2.m_scale; AddNeighbour(item2, 0f - num, 0f); AddNeighbour(item2, num, 0f); AddNeighbour(item2, 0f, 0f - num); AddNeighbour(item2, 0f, num); } } Dirty.Clear(); foreach (Heightmap item3 in AffectedScratch) { if (!ApplyNormals(item3) && RebuiltScratch.Contains(item3)) { ApplyFallbackTangents(item3); } } RebuiltScratch.Clear(); AffectedScratch.Clear(); } private static void AddNeighbour(Heightmap origin, float dx, float dz) { Heightmap val = FindNeighbour(origin, dx, dz); if ((Object)(object)val != (Object)null) { AffectedScratch.Add(val); } } } [HarmonyPatch(typeof(Heightmap), "OnDestroy")] internal static class OnDestroyHook { [HarmonyPostfix] private static void Postfix(Heightmap __instance) { Dirty.Remove(__instance); } } internal static ConfigEntry Enabled; internal static ConfigEntry VerifyTangents; private static readonly List NormalBuffer = new List(); private static readonly List TangentBuffer = new List(); private static readonly List VerifyBuffer = new List(); private static readonly HashSet Dirty = new HashSet(); private static readonly HashSet RebuiltScratch = new HashSet(); private static readonly HashSet AffectedScratch = new HashSet(); private static readonly MethodInfo RecalculateTangentsMethod = AccessTools.Method(typeof(Mesh), "RecalculateTangents", new Type[0], (Type[])null); private static readonly MethodInfo TangentsOrDeferMethod = AccessTools.Method(typeof(SeamlessNormalsPatch), "TangentsOrDefer", (Type[])null, (Type[])null); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(SeamlessNormalsPatch), "Fixes - Terrain", "Fix Terrain Seams", value: true, "Computes terrain lighting normals across zone boundaries instead of per zone. Vanilla shades the same ground differently on each side of a 64m zone border, which shows up as a hard crease running through flat terrain - most noticeable in the Plains and Meadows."); VerifyTangents = ValConfig.BindServerConfig("Debug", "Verify Terrain Tangents", value: false, "Diagnostic. Uses Unity's tangent recalculation instead of the analytic tangents the seam fix normally computes, compares the two on a sample of vertices, and logs any disagreement. Costs the mesh pass the analytic version exists to avoid, so leave it off unless terrain lighting looks wrong.", null, advanced: true); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("RebuildRenderMesh")] private static IEnumerable RebuildRenderMeshTranspiler(IEnumerable instructions) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown List list = PatchHelper.Copy(instructions); int num = 0; for (int i = 0; i < list.Count; i++) { if (CodeInstructionExtensions.Calls(list[i], RecalculateTangentsMethod)) { list[i].opcode = OpCodes.Ldarg_0; list[i].operand = null; list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)TangentsOrDeferMethod)); num++; i++; } } if (num != 1) { Logger.LogWarning($"Heightmap.RebuildRenderMesh: expected 1 RecalculateTangents call, found {num}, " + "so the tangent half of the terrain seam fix is inactive. Another mod has most likely already rewritten the method - if so, nothing is wrong."); return instructions; } return list; } private static void TangentsOrDefer(Mesh mesh, Heightmap hmap) { if (!WillProcess(hmap)) { mesh.RecalculateTangents(); } } private static bool WillProcess(Heightmap hmap) { if (Enabled != null && Enabled.Value && !RunMode.IsDedicated && !hmap.IsDistantLod) { return (Object)(object)MonoUpdaters.s_instance != (Object)null; } return false; } [HarmonyPostfix] [HarmonyPatch("RebuildRenderMesh")] private static void RebuildRenderMeshPostfix(Heightmap __instance) { if (WillProcess(__instance)) { Dirty.Add(__instance); } } private static Vector4 AnalyticTangent(Vector3 normal) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) float num = 1f - normal.x * normal.x; float num2 = (0f - normal.x) * normal.y; float num3 = (0f - normal.x) * normal.z; float num4 = 1f / Mathf.Sqrt(num * num + num2 * num2 + num3 * num3); return new Vector4(num * num4, num2 * num4, num3 * num4, -1f); } private static bool ApplyNormals(Heightmap hmap) { //IL_009e: 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_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) Mesh renderMesh = hmap.m_renderMesh; if ((Object)(object)renderMesh == (Object)null) { return false; } int width = hmap.m_width; int num = width + 1; if (renderMesh.vertexCount != num * num) { return false; } float num2 = (float)width * hmap.m_scale; Heightmap val = FindNeighbour(hmap, 0f - num2, 0f); Heightmap val2 = FindNeighbour(hmap, num2, 0f); Heightmap val3 = FindNeighbour(hmap, 0f, 0f - num2); Heightmap val4 = FindNeighbour(hmap, 0f, num2); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null) { return false; } float y = ((Component)hmap).transform.position.y; float y2 = ((Component)val).transform.position.y; float y3 = ((Component)val2).transform.position.y; float y4 = ((Component)val3).transform.position.y; float y5 = ((Component)val4).transform.position.y; float num3 = 1f / (2f * hmap.m_scale); bool flag = VerifyTangents == null || !VerifyTangents.Value; NormalBuffer.Clear(); TangentBuffer.Clear(); Vector3 val5 = default(Vector3); for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { float num4 = (Sample(hmap, j + 1, i, val, val2, val3, val4, y, y2, y3, y4, y5) - Sample(hmap, j - 1, i, val, val2, val3, val4, y, y2, y3, y4, y5)) * num3; float num5 = (Sample(hmap, j, i + 1, val, val2, val3, val4, y, y2, y3, y4, y5) - Sample(hmap, j, i - 1, val, val2, val3, val4, y, y2, y3, y4, y5)) * num3; float num6 = 1f / Mathf.Sqrt(num4 * num4 + 1f + num5 * num5); ((Vector3)(ref val5))..ctor((0f - num4) * num6, num6, (0f - num5) * num6); NormalBuffer.Add(val5); if (flag) { TangentBuffer.Add(AnalyticTangent(val5)); } } } renderMesh.SetNormals(NormalBuffer); if (flag) { renderMesh.SetTangents(TangentBuffer); } else { renderMesh.RecalculateTangents(); CompareTangents(renderMesh); } return true; } private static void ApplyFallbackTangents(Heightmap hmap) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) Mesh renderMesh = hmap.m_renderMesh; if ((Object)(object)renderMesh == (Object)null) { return; } if (VerifyTangents != null && VerifyTangents.Value) { renderMesh.RecalculateTangents(); return; } NormalBuffer.Clear(); renderMesh.GetNormals(NormalBuffer); TangentBuffer.Clear(); for (int i = 0; i < NormalBuffer.Count; i++) { TangentBuffer.Add(AnalyticTangent(NormalBuffer[i])); } renderMesh.SetTangents(TangentBuffer); } private static void CompareTangents(Mesh mesh) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) VerifyBuffer.Clear(); mesh.GetTangents(VerifyBuffer); if (VerifyBuffer.Count != NormalBuffer.Count) { return; } int num = 0; float num2 = 1f; for (int i = 0; i < VerifyBuffer.Count; i += 173) { Vector4 val = AnalyticTangent(NormalBuffer[i]); Vector4 val2 = VerifyBuffer[i]; float num3 = val.x * val2.x + val.y * val2.y + val.z * val2.z; if (num3 < num2) { num2 = num3; } if (num3 < 0.99f || val2.w > 0f) { num++; } } if (num == 0) { Logger.LogDebug($"Tangent verify: agreed (worst dot {num2:F4})."); } else { Logger.LogWarning($"Tangent verify: {num} sampled vertex(es) diverged (worst dot {num2:F4}). " + "Unity's tangents were used. Please report this."); } } private static float Sample(Heightmap hmap, int x, int y, Heightmap west, Heightmap east, Heightmap south, Heightmap north, float selfY, float westY, float eastY, float southY, float northY) { int width = hmap.m_width; if (x < 0) { return west.GetHeight(width - 1, y) + westY; } if (x > width) { return east.GetHeight(1, y) + eastY; } if (y < 0) { return south.GetHeight(x, width - 1) + southY; } if (y > width) { return north.GetHeight(x, 1) + northY; } return hmap.GetHeight(x, y) + selfY; } private static Heightmap FindNeighbour(Heightmap hmap, float dx, float dz) { //IL_0006: 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_0017: Unknown result type (might be due to invalid IL or missing references) Heightmap val = Heightmap.FindHeightmap(((Component)hmap).transform.position + new Vector3(dx, 0f, dz)); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)hmap || val.IsDistantLod) { return null; } if (val.m_width != hmap.m_width || val.m_scale != hmap.m_scale) { return null; } return val; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(TerrainComp))] internal static class TerrainCompNullHmapPatch { internal static ConfigEntry Enabled; private static readonly HashSet RecoveryFailed = new HashSet(); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(TerrainCompNullHmapPatch), "Fixes - Terrain", "Fix Terrain Compiler Init Race", value: true, "Recovers a terrain compiler that loaded before its zone's heightmap existed. In vanilla it throws a NullReferenceException every frame from then on and that zone stops accepting terrain edits entirely."); } [HarmonyPrefix] [HarmonyPatch("Update")] private static bool UpdatePrefix(TerrainComp __instance) { if (Enabled == null || !Enabled.Value) { return true; } if ((Object)(object)__instance.m_hmap != (Object)null && __instance.m_initialized) { return true; } if (RecoveryFailed.Count > 0 && RecoveryFailed.Contains(__instance)) { return false; } TryRecover(__instance); return false; } private static bool TryRecover(TerrainComp comp) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) Heightmap val = (((Object)(object)comp.m_hmap != (Object)null) ? comp.m_hmap : Heightmap.FindHeightmap(((Component)comp).transform.position)); if ((Object)(object)val == (Object)null) { return false; } if ((Object)(object)comp.m_nview == (Object)null || !comp.m_nview.IsValid()) { return false; } comp.m_hmap = val; try { if (!comp.m_initialized) { TerrainComp val2 = TerrainComp.FindTerrainCompiler(((Component)comp).transform.position); if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)comp && (Object)(object)ZNetScene.instance != (Object)null) { Logger.LogWarning($"Found another terrain compiler at {((Component)comp).transform.position}, removing it. " + "Two compilers in one zone means one of their saved terrain edits would be discarded at random, so this resolves it the way TerrainComp.Awake does."); ZNetScene.instance.Destroy(((Component)val2).gameObject); } if (!TerrainComp.s_instances.Contains(comp)) { TerrainComp.s_instances.Add(comp); } comp.m_nview.Register("ApplyOperation", (Action)comp.RPC_ApplyOperation); comp.Initialize(); } else if (!TerrainComp.s_instances.Contains(comp)) { TerrainComp.s_instances.Add(comp); } comp.CheckLoad(); } catch (Exception arg) { RecoveryFailed.RemoveWhere((TerrainComp failed) => (Object)(object)failed == (Object)null); RecoveryFailed.Add(comp); Logger.LogError($"Failed to recover terrain compiler at {((Component)comp).transform.position}, and it will not " + $"be retried: {arg}"); return false; } Logger.LogInfo($"Recovered terrain compiler at {((Component)comp).transform.position} after its heightmap loaded."); return true; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(TerrainOp))] internal static class TerrainOpPaintFanoutPatch { internal static ConfigEntry Enabled; private static readonly List Reached = new List(); private static readonly List PaintReached = new List(); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(TerrainOpPaintFanoutPatch), "Fixes - Terrain", "Fix Terrain Paint Zone Fanout", value: true, "Sends terrain paint to every zone the paint actually covers. Vanilla measures which zones an edit touches from its radius, but the paint itself reaches about a metre further west and south, so the neighbouring zone never records paint applied to ground it shares - which is what leaves dirt stopping dead along a 64m zone border."); } [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(TerrainOp __instance, bool __runOriginal) { //IL_0161: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if (!__runOriginal || Enabled == null || !Enabled.Value || TerrainOp.m_forceDisableTerrainOps) { return; } Settings settings = __instance.m_settings; if (settings == null || !settings.m_paintCleared || settings.m_paintRadius <= 0f) { return; } Vector3 position = ((Component)__instance).transform.position; Heightmap val = Heightmap.FindHeightmap(position); float num = (((Object)(object)val != (Object)null) ? val.m_scale : 1f); float radius = settings.GetRadius(); float num2 = settings.m_paintRadius + num; if (num2 <= radius) { return; } Reached.Clear(); PaintReached.Clear(); try { Heightmap.FindHeightmap(position, radius, Reached); Heightmap.FindHeightmap(position, num2, PaintReached); bool level = settings.m_level; bool raise = settings.m_raise; bool smooth = settings.m_smooth; settings.m_level = false; settings.m_raise = false; settings.m_smooth = false; try { for (int i = 0; i < PaintReached.Count; i++) { Heightmap val2 = PaintReached[i]; if (!((Object)(object)val2 == (Object)null) && !val2.IsDistantLod && !Reached.Contains(val2) && IsBehindOperation(val2, position)) { val2.GetAndCreateTerrainCompiler().ApplyOperation(__instance); } } } finally { settings.m_level = level; settings.m_raise = raise; settings.m_smooth = smooth; } } catch (Exception arg) { Logger.LogWarning($"Could not extend terrain paint past {position} into neighbouring zones: {arg}"); } finally { Reached.Clear(); PaintReached.Clear(); } } private static bool IsBehindOperation(Heightmap hmap, Vector3 pos) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) float num = (float)hmap.m_width * hmap.m_scale * 0.5f; Vector3 position = ((Component)hmap).transform.position; if (!(position.x + num <= pos.x)) { return position.z + num <= pos.z; } return true; } } } namespace ValheimCommunityPatch.Patches.Performance { [PatchSide(Side.Client)] [HarmonyPatch(typeof(Heightmap))] internal static class AsyncColliderBakePatch { private sealed class PendingBake { public Heightmap m_hmap; public MeshCollider m_collider; public Mesh m_mesh; public int m_meshId; public MeshColliderCookingOptions m_options; public volatile bool m_done; } [HarmonyPatch(typeof(ZoneSystem), "SpawnZone")] internal static class SpawnZoneContextHook { [HarmonyPrefix] private static void Prefix(Vector2s zoneID, SpawnMode mode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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) _deferContext = (int)mode == 1 && (Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)ZNet.instance != (Object)null && zoneID != ZoneSystem.GetZone(ZNet.instance.GetReferencePosition()); } [HarmonyFinalizer] private static void Finalizer() { _deferContext = false; } } [HarmonyPatch(typeof(Heightmap), "CustomLateUpdate")] internal static class LateUpdateContextHook { [HarmonyPrefix] private static void Prefix() { _lateUpdateContext = (Object)(object)Player.m_localPlayer != (Object)null; } [HarmonyFinalizer] private static void Finalizer() { _lateUpdateContext = false; } } [HarmonyPatch(typeof(MonoUpdaters), "LateUpdate")] internal static class AssignBakedHook { [HarmonyPostfix] private static void Postfix() { for (int num = Pending.Count - 1; num >= 0; num--) { PendingBake pendingBake = Pending[num]; if (pendingBake.m_done) { Pending.RemoveAt(num); if (!((Object)(object)pendingBake.m_hmap == (Object)null) && !((Object)(object)pendingBake.m_collider == (Object)null)) { pendingBake.m_collider.sharedMesh = pendingBake.m_mesh; } } } } } [HarmonyPatch(typeof(Heightmap), "OnDestroy")] internal static class DestroyGuardHook { [HarmonyPrefix] private static void Prefix(Heightmap __instance) { PendingBake pendingBake = FindPending(__instance); if (pendingBake != null) { WaitOut(pendingBake); Pending.Remove(pendingBake); } } } private static readonly List Pending = new List(); private static bool _deferContext; private static bool _lateUpdateContext; private static PendingBake FindPending(Heightmap hmap) { for (int i = 0; i < Pending.Count; i++) { if (Pending[i].m_hmap == hmap) { return Pending[i]; } } return null; } private static void WaitOut(PendingBake bake) { while (!bake.m_done) { Thread.Sleep(0); } } [HarmonyPrefix] [HarmonyPatch("RebuildCollisionMesh")] private static void RebuildCollisionMeshPrefix(Heightmap __instance, out MeshCollider __state) { __state = null; PendingBake pendingBake = FindPending(__instance); if (pendingBake != null) { WaitOut(pendingBake); Pending.Remove(pendingBake); } if ((_deferContext || _lateUpdateContext) && !((Object)(object)__instance.m_collider == (Object)null)) { __state = __instance.m_collider; __instance.m_collider = null; } } [HarmonyPostfix] [HarmonyPatch("RebuildCollisionMesh")] private static void RebuildCollisionMeshPostfix(Heightmap __instance, MeshCollider __state) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__state == (Object)null) { return; } __instance.m_collider = __state; Mesh collisionMesh = __instance.m_collisionMesh; if ((Object)(object)collisionMesh == (Object)null) { return; } PendingBake bake = new PendingBake { m_hmap = __instance, m_collider = __state, m_mesh = collisionMesh, m_meshId = ((Object)collisionMesh).GetInstanceID(), m_options = __state.cookingOptions }; Pending.Add(bake); ThreadPool.QueueUserWorkItem(delegate { //IL_0012: Unknown result type (might be due to invalid IL or missing references) try { Physics.BakeMesh(bake.m_meshId, false, bake.m_options); } finally { bake.m_done = true; } }); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(ClutterSystem))] internal static class ClutterGroundDataPatch { [HarmonyPrefix] [HarmonyPatch("GetGroundInfo")] private static bool GetGroundInfoPrefix(Vector3 p, out Vector3 point, out Vector3 normal, out Heightmap hmap, out Biome biome, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Expected I4, but got Unknown if (!HeightmapLookupPatch.TryGetCached(p, out var hmap2, out var origin)) { hmap2 = Heightmap.FindHeightmap(p); origin = (((Object)(object)hmap2 != (Object)null) ? ((Component)hmap2).transform.position : Vector3.zero); } float height = 0f; Vector3 normal2 = Vector3.up; if ((Object)(object)hmap2 != (Object)null && HeightmapSampling.TryGetSurface(hmap2, origin, p, out height, out normal2) && height <= p.y + 500f && height >= p.y - 500f) { point = new Vector3(p.x, height, p.z); normal = normal2; hmap = hmap2; biome = (Biome)(int)hmap2.GetBiome(point, 0.02f, false); __result = true; return false; } point = p; normal = Vector3.up; hmap = null; biome = (Biome)1; __result = false; return false; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(ClutterSystem))] internal static class ClutterRebuildCapPatch { internal static ConfigEntry Budget; internal static void BindConfig() { Budget = ValConfig.BindServerConfig("Fixes - Performance", "Grass Rebuild Budget", 8, "How many grass patches a full rebuild may regenerate per frame. Higher finishes the rebuild sooner but hitches more; 64 is effectively vanilla.", advanced: true, 1, 64); } [HarmonyPrefix] [HarmonyPatch("GeneratePatches")] private static bool GeneratePatchesPrefix(ClutterSystem __instance, bool rebuildAll, Vector3 center) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!rebuildAll) { return true; } int num = ((Budget != null) ? Budget.Value : 8); bool flag = false; for (int i = 0; i < num; i++) { bool generated = false; RunRing(__instance, center, ref generated); flag = generated; if (!generated) { break; } } if (flag) { __instance.m_forceRebuild = true; } return false; } private static void RunRing(ClutterSystem clutter, Vector3 center, ref bool generated) { //IL_0001: 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_0007: 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_000a: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) Vector2Int vegPatch = clutter.GetVegPatch(center); clutter.GeneratePatch(center, vegPatch, ref generated, false); int num = Mathf.CeilToInt((clutter.m_distance - clutter.m_grassPatchSize / 2f) / clutter.m_grassPatchSize); for (int i = 1; i <= num; i++) { for (int j = ((Vector2Int)(ref vegPatch)).x - i; j <= ((Vector2Int)(ref vegPatch)).x + i; j++) { clutter.GeneratePatch(center, new Vector2Int(j, ((Vector2Int)(ref vegPatch)).y - i), ref generated, false); clutter.GeneratePatch(center, new Vector2Int(j, ((Vector2Int)(ref vegPatch)).y + i), ref generated, false); } for (int k = ((Vector2Int)(ref vegPatch)).y - i + 1; k <= ((Vector2Int)(ref vegPatch)).y + i - 1; k++) { clutter.GeneratePatch(center, new Vector2Int(((Vector2Int)(ref vegPatch)).x - i, k), ref generated, false); clutter.GeneratePatch(center, new Vector2Int(((Vector2Int)(ref vegPatch)).x + i, k), ref generated, false); } } } } [PatchSide(Side.Both)] internal static class CollisionCallbackReusePatch { internal static void Apply() { if (Physics.reuseCollisionCallbacks) { Logger.LogInfo("Collision callback reuse was already enabled by the game, so 'Fix Collision Callback Allocation' changes nothing on this build."); } else { Physics.reuseCollisionCallbacks = true; } } } [PatchSide(Side.Both)] [HarmonyPatch] internal static class CollisionContactsAllocPatch { private const int MaxCachedContacts = 64; private static readonly ContactPoint[][] Buffers = new ContactPoint[65][]; private static readonly ContactPoint[] Empty = (ContactPoint[])(object)new ContactPoint[0]; private static readonly MethodInfo ContactsGetter = AccessTools.PropertyGetter(typeof(Collision), "contacts"); private static readonly MethodInfo ReusedContactsMethod = AccessTools.Method(typeof(CollisionContactsAllocPatch), "ReusedContacts", (Type[])null, (Type[])null); private static ContactPoint[] ReusedContacts(Collision collision) { if (collision == null) { return Empty; } int contactCount = collision.contactCount; if (contactCount <= 0) { return Empty; } if (contactCount > 64) { return collision.contacts; } ContactPoint[] array = Buffers[contactCount]; if (array == null) { array = (ContactPoint[])(object)new ContactPoint[contactCount]; Buffers[contactCount] = array; } collision.GetContacts(array); return array; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(Character), "OnCollisionStay")] private static IEnumerable CharacterOnCollisionStayTranspiler(IEnumerable instructions) { return PatchHelper.ReplaceCalls(instructions, ContactsGetter, ReusedContactsMethod, "Character.OnCollisionStay", 1); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(ImpactEffect), "OnCollisionEnter")] private static IEnumerable ImpactEffectOnCollisionEnterTranspiler(IEnumerable instructions) { return PatchHelper.ReplaceCalls(instructions, ContactsGetter, ReusedContactsMethod, "ImpactEffect.OnCollisionEnter", 2); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(FloatingTerrain), "OnCollisionStay")] private static IEnumerable FloatingTerrainOnCollisionStayTranspiler(IEnumerable instructions) { return PatchHelper.ReplaceCalls(instructions, ContactsGetter, ReusedContactsMethod, "FloatingTerrain.OnCollisionStay", 1); } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(CookingStation))] internal static class CookingSlotKeyPatch { private static int[] SlotKeys = new int[0]; private static int[] StatusKeys = new int[0]; private static void EnsureKeys(int count) { if (SlotKeys.Length < count) { int[] array = new int[count]; int[] array2 = new int[count]; for (int i = 0; i < count; i++) { array[i] = StringExtensionMethods.GetStableHashCode("slot" + i); array2[i] = StringExtensionMethods.GetStableHashCode("slotstatus" + i); } SlotKeys = array; StatusKeys = array2; } } private static bool TryPrepare(CookingStation station, int slot) { if (slot < 0) { return false; } Transform[] slots = station.m_slots; EnsureKeys(Mathf.Max(slot + 1, (slots != null) ? slots.Length : 0)); return true; } [HarmonyPrefix] [HarmonyPatch("GetSlot")] private static bool GetSlotPrefix(CookingStation __instance, int slot, ref string itemName, ref float cookedTime, ref Status status, ref bool cheated) { if (!TryPrepare(__instance, slot)) { return true; } if (!__instance.m_nview.IsValid()) { itemName = ""; status = (Status)0; cookedTime = 0f; cheated = false; return false; } ZDO zDO = __instance.m_nview.GetZDO(); itemName = zDO.GetString(SlotKeys[slot], ""); cookedTime = zDO.GetFloat(SlotKeys[slot], 0f); status = (Status)zDO.GetInt(StatusKeys[slot], 0); cheated = zDO.GetBool(ZDOVars.s_cheatedQueued + slot, false); return false; } [HarmonyPrefix] [HarmonyPatch("SetSlot")] private static bool SetSlotPrefix(CookingStation __instance, int slot, string itemName, float cookedTime, Status status, bool cheated) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected I4, but got Unknown if (!TryPrepare(__instance, slot)) { return true; } if (!__instance.m_nview.IsValid()) { return false; } ZDO zDO = __instance.m_nview.GetZDO(); zDO.Set(SlotKeys[slot], itemName); zDO.Set(SlotKeys[slot], cookedTime); zDO.Set(StatusKeys[slot], (int)status, false); zDO.Set(ZDOVars.s_cheatedQueued + slot, cheated); return false; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(HeightmapBuilder))] internal static class HeightmapBuilderThroughputPatch { internal static ConfigEntry ReadyCap; internal static void BindConfig() { ReadyCap = ValConfig.BindServerConfig("Fixes - Performance", "Terrain Builder Ready Cap", 32, "How many finished terrain results the build thread may hold before discarding the oldest. Each is roughly 100 KB. Vanilla holds 16, which the distant-terrain ring alone nearly fills.", advanced: true, 16, 128); } [HarmonyPrepare] private static bool Prepare() { if (HeightmapBuilder.m_instance != null) { Logger.LogWarning("The terrain build thread was already running before this patch applied, so 'Fix Terrain Builder Throughput' is inert this session."); } return true; } [HarmonyPrefix] [HarmonyPatch("BuildThread")] private static bool BuildThreadPrefix(HeightmapBuilder __instance) { ZLog.Log((object)"Builder started"); bool flag = false; while (!flag) { bool flag2; lock (__instance.m_lock) { flag2 = __instance.m_toBuild.Count > 0; } if (flag2) { HMBuildData val; lock (__instance.m_lock) { val = __instance.m_toBuild[0]; } __instance.Build(val); lock (__instance.m_lock) { __instance.m_toBuild.Remove(val); __instance.m_ready.Add(val); int num = ((ReadyCap != null) ? ReadyCap.Value : 16); while (__instance.m_ready.Count > num) { __instance.m_ready.RemoveAt(0); } } } if (!flag2) { Thread.Sleep(10); } lock (__instance.m_lock) { flag = __instance.m_stop; } } return false; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(Heightmap))] internal static class HeightmapLookupPatch { private struct Entry { public Heightmap m_hmap; public float m_cx; public float m_cy; public float m_cz; public float m_half; } [HarmonyPatch(typeof(Heightmap), "Awake")] internal static class AwakeHook { [HarmonyPostfix] private static void Postfix(Heightmap __instance) { if (!__instance.m_isDistantLod) { Entry entry = MakeEntry(__instance); Registered.Add(entry); FileByZone(entry); } } } [HarmonyPatch(typeof(Heightmap), "OnDestroy")] internal static class DestroyHook { [HarmonyPostfix] private static void Postfix(Heightmap __instance) { for (int i = 0; i < Registered.Count; i++) { if (Registered[i].m_hmap == __instance) { Unfile(__instance, Registered[i].m_cx, Registered[i].m_cz); Registered.RemoveAt(i); break; } } } } [HarmonyPatch(typeof(Heightmap), "Regenerate")] internal static class RegenerateHook { [HarmonyPrefix] private static void Prefix(Heightmap __instance) { if (__instance.m_isDistantLod) { return; } for (int i = 0; i < Registered.Count; i++) { if (Registered[i].m_hmap == __instance) { Entry entry = Registered[i]; Entry entry2 = MakeEntry(__instance); if (entry2.m_cx != entry.m_cx || entry2.m_cy != entry.m_cy || entry2.m_cz != entry.m_cz || entry2.m_half != entry.m_half) { Unfile(__instance, entry.m_cx, entry.m_cz); Registered[i] = entry2; FileByZone(entry2); } break; } } } } internal static ConfigEntry Verify; private static readonly List Registered = new List(); private static readonly Dictionary ByZone = new Dictionary(); private static readonly HookHealth Hooks = new HookHealth("Heightmap registry", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(Heightmap), "Awake", (Type[])null, (Type[])null), typeof(AwakeHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(Heightmap), "OnDestroy", (Type[])null, (Type[])null), typeof(DestroyHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(Heightmap), "Regenerate", (Type[])null, (Type[])null), typeof(RegenerateHook))); internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Heightmap Registry", value: false, "Diagnostic. Runs both the zone-keyed lookup and vanilla's scan on every terrain tile lookup, acts on vanilla's result, and logs any real disagreement. Costs the scan this fix exists to avoid, so leave it off unless you are validating the registry.", null, advanced: true); } private static Entry MakeEntry(Heightmap hmap) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)hmap).transform.position; return new Entry { m_hmap = hmap, m_cx = position.x, m_cy = position.y, m_cz = position.z, m_half = (float)hmap.m_width * hmap.m_scale * 0.5f }; } internal static bool TryGetCached(Vector3 point, out Heightmap hmap, out Vector3 origin) { //IL_0004: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) hmap = null; origin = default(Vector3); if (!Hooks.Healthy) { return false; } if (ByZone.TryGetValue(ZoneSystem.GetZone(point), out var value) && Contains(in value, point)) { hmap = value.m_hmap; origin = new Vector3(value.m_cx, value.m_cy, value.m_cz); return true; } for (int i = 0; i < Registered.Count; i++) { if (Contains(Registered[i], point)) { hmap = Registered[i].m_hmap; origin = new Vector3(Registered[i].m_cx, Registered[i].m_cy, Registered[i].m_cz); return true; } } return true; } private static void FileByZone(Entry entry) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) Vector2s zone = ZoneSystem.GetZone(new Vector3(entry.m_cx, 0f, entry.m_cz)); if (ByZone.TryGetValue(zone, out var value) && value.m_hmap != entry.m_hmap) { Logger.LogDebug($"Two heightmaps registered for zone {zone}; keeping the newest."); } ByZone[zone] = entry; } private static void Unfile(Heightmap hmap, float cx, float cz) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_002f: Unknown result type (might be due to invalid IL or missing references) Vector2s zone = ZoneSystem.GetZone(new Vector3(cx, 0f, cz)); if (ByZone.TryGetValue(zone, out var value) && value.m_hmap == hmap) { ByZone.Remove(zone); } } private static bool Contains(in Entry entry, Vector3 point) { //IL_0000: 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_002a: 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) if (point.x >= entry.m_cx - entry.m_half && point.x <= entry.m_cx + entry.m_half && point.z >= entry.m_cz - entry.m_half) { return point.z <= entry.m_cz + entry.m_half; } return false; } private static Heightmap FastFind(Vector3 point) { //IL_0005: 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_0016: 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 (ByZone.TryGetValue(ZoneSystem.GetZone(point), out var value) && Contains(in value, point)) { return value.m_hmap; } for (int i = 0; i < Registered.Count; i++) { if (Contains(Registered[i], point)) { return Registered[i].m_hmap; } } return null; } [HarmonyPrefix] [HarmonyPatch("FindHeightmap", new Type[] { typeof(Vector3) })] private static bool FindHeightmapPrefix(Vector3 point, ref Heightmap __result) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_005d: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) if (!Hooks.Healthy) { return true; } Heightmap val = FastFind(point); if (Verify != null && Verify.Value) { Heightmap val2 = VanillaFind(point); if (val != val2) { if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null && val.IsPointInside(point, 0f) && val2.IsPointInside(point, 0f)) { Logger.LogDebug($"Heightmap registry verify: shared-edge tie at {point}."); } else { Logger.LogError($"Heightmap registry verify: DIVERGED at {point} (fast: " + (((Object)(object)val == (Object)null) ? "null" : ((object)((Component)val).transform.position/*cast due to .constrained prefix*/).ToString()) + ", vanilla: " + (((Object)(object)val2 == (Object)null) ? "null" : ((object)((Component)val2).transform.position/*cast due to .constrained prefix*/).ToString()) + "). Vanilla's result was used. Please report this - leave 'Verify Heightmap Registry' on until it is understood, since the verify pass acts on vanilla's answer."); } } __result = val2; return false; } __result = val; return false; } private static Heightmap VanillaFind(Vector3 point) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) foreach (Heightmap s_heightmap in Heightmap.s_heightmaps) { if (s_heightmap.IsPointInside(point, 0f)) { return s_heightmap; } } return null; } [HarmonyPrefix] [HarmonyPatch("HaveQueuedRebuild", new Type[] { typeof(Vector3), typeof(float) })] private static bool HaveQueuedRebuildPrefix(Vector3 point, float radius, ref bool __result) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if (!Hooks.Healthy) { return true; } __result = false; for (int i = 0; i < Registered.Count; i++) { Entry entry = Registered[i]; if (point.x + radius >= entry.m_cx - entry.m_half && point.x - radius <= entry.m_cx + entry.m_half && point.z + radius >= entry.m_cz - entry.m_half && point.z - radius <= entry.m_cz + entry.m_half && entry.m_hmap.HaveQueuedRebuild()) { __result = true; break; } } return false; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(LightFlicker))] internal static class LightCostPatch { private struct Anchor { public int m_frame; public bool m_skip; } [HarmonyPatch(typeof(LightFlicker), "OnDisable")] internal static class DisableHook { [HarmonyPostfix] private static void Postfix(LightFlicker __instance) { Anchors.Remove(((Object)__instance).GetInstanceID()); } } internal static ConfigEntry FlickerDistance; internal static ConfigEntry PointLightLimit; private const int AnchorRefreshFrames = 10; private static readonly Dictionary Anchors = new Dictionary(); private static int _playerPosFrame = -1; private static Vector3 _playerPos; internal static void BindConfig() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown FlickerDistance = ValConfig.cfg.Bind("Client config", "Light Flicker Distance", 45f, new ConfigDescription("Metres beyond which torch flicker stops updating. The game's own light LOD fades the light itself out at 40, so flicker past that is invisible anyway.", (AcceptableValueBase)(object)new AcceptableValueRange(10f, 200f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); PointLightLimit = ValConfig.cfg.Bind("Client config", "Point Light Limit", -1, new ConfigDescription("Caps how many of the nearest point lights are enabled at once, using the game's own dormant light-priority system with its smooth fade. -1 (default) is exactly vanilla: no cap. Try 30-50 in torch-heavy bases.", (AcceptableValueBase)(object)new AcceptableValueRange(-1, 200), new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); ApplyLightLimit(); PointLightLimit.SettingChanged += delegate { ApplyLightLimit(); }; } private static void ApplyLightLimit() { LightLod.m_lightLimit = PointLightLimit.Value; } [HarmonyPrefix] [HarmonyPatch("CustomUpdate")] private static bool CustomUpdatePrefix(LightFlicker __instance) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (__instance.m_ttl > 0f) { return true; } Player localPlayer = Player.m_localPlayer; if (localPlayer == null) { return true; } int frameCount = Time.frameCount; if (frameCount != _playerPosFrame) { _playerPosFrame = frameCount; _playerPos = ((Component)localPlayer).transform.position; } int instanceID = ((Object)__instance).GetInstanceID(); if (!Anchors.TryGetValue(instanceID, out var value) || frameCount - value.m_frame >= 10) { Vector3 position = ((Component)__instance).transform.position; float num = position.x - _playerPos.x; float num2 = position.z - _playerPos.z; float num3 = ((FlickerDistance != null) ? FlickerDistance.Value : 45f); value = new Anchor { m_frame = frameCount, m_skip = (num * num + num2 * num2 > num3 * num3) }; Anchors[instanceID] = value; } return !value.m_skip; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(LightFlicker))] internal static class LightSettingsEventPatch { [HarmonyPatch(typeof(GraphicsSettingsManager))] internal static class SettingsHook { [HarmonyPostfix] [HarmonyPatch("ApplyGraphicsSettingsToCurrentSession")] private static void Postfix() { foreach (LightFlicker value in Subscribed.Values) { value.ApplySettings(); } } } [HarmonyPatch(typeof(ZNetScene), "Shutdown")] internal static class ShutdownHook { [HarmonyPostfix] private static void Postfix() { Subscribed.Clear(); } } private static readonly Dictionary Subscribed = new Dictionary(); private static readonly HookHealth Hooks = new HookHealth("Light settings subscription", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(LightFlicker), "OnDisable", (Type[])null, (Type[])null), typeof(LightSettingsEventPatch)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(GraphicsSettingsManager), "ApplyGraphicsSettingsToCurrentSession", (Type[])null, (Type[])null), typeof(SettingsHook))); [HarmonyPrefix] [HarmonyPatch("OnEnable")] private static bool OnEnablePrefix(LightFlicker __instance) { if (!Hooks.Healthy) { return true; } __instance.m_time = 0f; if ((Object)(object)__instance.m_light == (Object)null) { return false; } Subscribed[((Object)__instance).GetInstanceID()] = __instance; __instance.ApplySettings(); return false; } [HarmonyPostfix] [HarmonyPatch("OnDisable")] private static void OnDisablePostfix(LightFlicker __instance) { Subscribed.Remove(((Object)__instance).GetInstanceID()); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(LiquidVolume))] internal static class LiquidVolumeLeakPatch { private static readonly MethodInfo SafeDisposeMethod = AccessTools.Method(typeof(LiquidVolumeLeakPatch), "SafeDispose", (Type[])null, (Type[])null); private static void SafeDispose(ref NativeArray array) where T : struct { if (array.IsCreated) { array.Dispose(); } } private static bool IsNativeArrayOf(Type type) { if (type != null && type.IsGenericType) { return type.GetGenericTypeDefinition() == typeof(NativeArray<>); } return false; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("OnDestroy")] private static IEnumerable OnDestroyTranspiler(IEnumerable instructions) { List list = PatchHelper.Copy(instructions); int num = 0; for (int i = 0; i < list.Count; i++) { if ((!(list[i].opcode != OpCodes.Call) || !(list[i].opcode != OpCodes.Callvirt)) && list[i].operand is MethodInfo methodInfo && !(methodInfo.Name != "Dispose") && IsNativeArrayOf(methodInfo.DeclaringType)) { Type type = methodInfo.DeclaringType.GetGenericArguments()[0]; list[i].opcode = OpCodes.Call; list[i].operand = SafeDisposeMethod.MakeGenericMethod(type); num++; } } if (num == 0) { Logger.LogWarning("LiquidVolume.OnDestroy: found no NativeArray.Dispose calls to guard, so this fix is inactive. Another mod has most likely already rewritten the method - if so, nothing is wrong."); return instructions; } return list; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(MaterialFader))] internal static class MaterialFaderSettlePatch { [HarmonyPrefix] [HarmonyPatch("Update")] private static bool UpdatePrefix(MaterialFader __instance) { if (!__instance.m_started) { return true; } List fadeProperties = __instance.m_fadeProperties; if (fadeProperties == null || fadeProperties.Count == 0) { return true; } for (int i = 0; i < fadeProperties.Count; i++) { FadeProperty val = fadeProperties[i]; if (!val.m_finished) { if (!val.m_startedFade) { return true; } if (!((val.m_fadeTimer - val.m_delay) / val.m_fadeTime >= 1f)) { return true; } } } return false; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(Mister))] internal static class MisterCachePatch { private struct MisterSnap { public Mister m_mister; public Vector3 m_pos; public float m_radius; public float m_height; } private struct DemisterSnap { public Demister m_demister; public Vector3 m_pos; public float m_endRange; } [HarmonyPatch(typeof(ParticleMist))] internal static class DemisterQueryHooks { [HarmonyPrefix] [HarmonyPatch("IsInsideOtherDemister")] private static bool IsInsideOtherDemisterPrefix(List fields, Vector3 p, float radius, Demister ignore, ref bool __result) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) EnsureDemisters(); __result = false; for (int i = 0; i < DemisterCount; i++) { ref DemisterSnap reference = ref Demisters[i]; if (reference.m_demister != ignore && Vector3.Distance(reference.m_pos, p) + radius < reference.m_endRange) { __result = true; break; } } return false; } [HarmonyPrefix] [HarmonyPatch("InsideDemister")] private static bool InsideDemisterPrefix(Vector3 p, ref bool __result) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) EnsureDemisters(); __result = false; for (int i = 0; i < DemisterCount; i++) { ref DemisterSnap reference = ref Demisters[i]; if (Vector3.Distance(reference.m_pos, p) < reference.m_endRange) { __result = true; break; } } return false; } [HarmonyPrefix] [HarmonyPatch("FindMaxMistAlltitude")] private static bool FindMaxMistAlltitudePrefix(ParticleMist __instance, float testRange, out float minMistHeight, out float maxMistHeight) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_0022: 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_0024: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)__instance).transform.position; float num = 0f; minMistHeight = 99999f; for (int i = 0; i < 20; i++) { Vector2 insideUnitCircle = Random.insideUnitCircle; float num2 = GroundHeight(position + new Vector3(insideUnitCircle.x, 0f, insideUnitCircle.y) * testRange); num += num2; if (num2 < minMistHeight) { minMistHeight = num2; } } maxMistHeight = num / 20f + __instance.m_maxMistAltitude; return false; } private static float GroundHeight(Vector3 probe) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) if (!HeightmapLookupPatch.TryGetCached(probe, out var hmap, out var origin)) { if (!((Object)(object)ZoneSystem.instance != (Object)null)) { return probe.y; } return ZoneSystem.instance.GetGroundHeight(probe); } if ((Object)(object)hmap == (Object)null) { return probe.y; } if (!HeightmapSampling.TryGetHeight(hmap, origin, probe, out var height)) { return probe.y; } return height; } } private const float BucketMargin = 64f; private const int SafetyRefreshFrames = 300; private static MisterSnap[] Misters = new MisterSnap[64]; private static int MisterCount; private static DemisterSnap[] Demisters = new DemisterSnap[64]; private static int DemisterCount; private static readonly Dictionary> MisterBuckets = new Dictionary>(); private static readonly Stack> BucketPool = new Stack>(); private static readonly List EmptyBucket = new List(); private static bool _mistersDirty = true; private static int _misterRebuildFrame = int.MinValue; private static int _demisterSnapFrame = -1; [HarmonyPostfix] [HarmonyPatch("OnEnable")] private static void OnEnablePostfix() { _mistersDirty = true; } [HarmonyPostfix] [HarmonyPatch("OnDisable")] private static void OnDisablePostfix() { _mistersDirty = true; } private static void EnsureMisters() { //IL_00cb: 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_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) int frameCount = Time.frameCount; if (!_mistersDirty && frameCount - _misterRebuildFrame < 300) { return; } _mistersDirty = false; _misterRebuildFrame = frameCount; List misters = Mister.GetMisters(); if (Misters.Length < misters.Count) { Misters = new MisterSnap[Mathf.NextPowerOfTwo(misters.Count)]; } MisterCount = misters.Count; foreach (KeyValuePair> misterBucket in MisterBuckets) { misterBucket.Value.Clear(); BucketPool.Push(misterBucket.Value); } MisterBuckets.Clear(); Vector2s key = default(Vector2s); for (int i = 0; i < misters.Count; i++) { Mister val = misters[i]; Vector3 position = ((Component)val).transform.position; Misters[i] = new MisterSnap { m_mister = val, m_pos = position, m_radius = val.m_radius, m_height = val.m_height }; float num = val.m_radius + 64f; Vector2s zone = ZoneSystem.GetZone(new Vector3(position.x - num, 0f, position.z - num)); Vector2s zone2 = ZoneSystem.GetZone(new Vector3(position.x + num, 0f, position.z + num)); for (int j = zone.x; j <= zone2.x; j++) { for (int k = zone.y; k <= zone2.y; k++) { ((Vector2s)(ref key))..ctor(j, k); if (!MisterBuckets.TryGetValue(key, out var value)) { value = ((BucketPool.Count > 0) ? BucketPool.Pop() : new List()); MisterBuckets.Add(key, value); } value.Add(i); } } } } private static List BucketAt(Vector3 p) { //IL_0005: 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) if (!MisterBuckets.TryGetValue(ZoneSystem.GetZone(p), out var value)) { return EmptyBucket; } return value; } private static void EnsureDemisters() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) int frameCount = Time.frameCount; if (frameCount != _demisterSnapFrame) { _demisterSnapFrame = frameCount; List demisters = Demister.GetDemisters(); if (Demisters.Length < demisters.Count) { Demisters = new DemisterSnap[Mathf.NextPowerOfTwo(demisters.Count)]; } DemisterCount = demisters.Count; for (int i = 0; i < demisters.Count; i++) { Demister val = demisters[i]; ParticleSystemForceField forceField = val.m_forceField; Demisters[i] = new DemisterSnap { m_demister = val, m_pos = ((Component)val).transform.position, m_endRange = (((Object)(object)forceField != (Object)null) ? forceField.endRange : 0f) }; } } } [HarmonyPrefix] [HarmonyPatch("InsideMister")] private static bool InsideMisterPrefix(Vector3 p, float radius, ref bool __result) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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) EnsureMisters(); __result = false; if (radius > 64f) { for (int i = 0; i < MisterCount; i++) { ref MisterSnap reference = ref Misters[i]; if (Vector3.Distance(reference.m_pos, p) < reference.m_radius + radius && p.y - radius < reference.m_pos.y + reference.m_height) { __result = true; break; } } return false; } List list = BucketAt(p); for (int j = 0; j < list.Count; j++) { ref MisterSnap reference2 = ref Misters[list[j]]; if (Vector3.Distance(reference2.m_pos, p) < reference2.m_radius + radius && p.y - radius < reference2.m_pos.y + reference2.m_height) { __result = true; break; } } return false; } [HarmonyPrefix] [HarmonyPatch("IsInsideOtherMister")] private static bool IsInsideOtherMisterPrefix(Vector3 p, Mister ignore, ref bool __result) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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_0042: Unknown result type (might be due to invalid IL or missing references) EnsureMisters(); __result = false; List list = BucketAt(p); for (int i = 0; i < list.Count; i++) { ref MisterSnap reference = ref Misters[list[i]]; if (reference.m_mister != ignore && Vector3.Distance(p, reference.m_pos) < reference.m_radius && p.y < reference.m_pos.y + reference.m_height) { __result = true; break; } } return false; } [HarmonyPrefix] [HarmonyPatch("IsCompletelyInsideOtherMister")] private static bool IsCompletelyInsideOtherMisterPrefix(Mister __instance, float thickness, ref bool __result) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) EnsureMisters(); Vector3 position = ((Component)__instance).transform.position; float radius = __instance.m_radius; float height = __instance.m_height; __result = false; List list = BucketAt(position); for (int i = 0; i < list.Count; i++) { ref MisterSnap reference = ref Misters[list[i]]; if (reference.m_mister != __instance && Vector3.Distance(position, reference.m_pos) + radius + thickness < reference.m_radius && position.y + height < reference.m_pos.y + reference.m_height) { __result = true; break; } } return false; } } [PatchSide(Side.Server)] [HarmonyPatch(typeof(ZDOMan))] internal static class OrphanZdoIndexPatch { [HarmonyPatch(typeof(ZDO), "SetOwnerInternal")] internal static class SetOwnerInternalHook { [HarmonyPostfix] private static void Postfix(ZDO __instance, long uid) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (!__instance.Persistent) { Track(__instance.m_uid, uid); } } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class PersistentSetterHook { [HarmonyPostfix] private static void Postfix(ZDO __instance) { //IL_0015: 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) if (__instance.Persistent) { Untrack(__instance.m_uid); } else { Track(__instance.m_uid, __instance.HasOwner() ? __instance.GetOwner() : 0); } } } [HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")] internal static class HandleDestroyedZdoHook { [HarmonyPostfix] private static void Postfix(ZDOID uid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Untrack(uid); } } [HarmonyPatch(typeof(ZDOMan), "Load")] internal static class ZdoManLoadHook { [HarmonyPostfix] private static void Postfix(ZDOMan __instance) { RebuildIndex(__instance); } } [HarmonyPatch(typeof(ZDOMan), "ShutDown")] internal static class ZdoManShutDownHook { [HarmonyPostfix] private static void Postfix() { ClearIndex(); } } internal static ConfigEntry Verify; private static readonly Dictionary OwnerOf = new Dictionary(); private static readonly Dictionary> ByOwner = new Dictionary>(); private static readonly HashSet Unowned = new HashSet(); private static readonly HashSet ConnectedScratch = new HashSet(); private static readonly List OrphanScratch = new List(); private static readonly List VanillaScratch = new List(); private static readonly List StaleScratch = new List(); private const long NoOwner = 0L; private static readonly HookHealth Hooks = new HookHealth("Orphan index", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDO), "SetOwnerInternal", (Type[])null, (Type[])null), typeof(SetOwnerInternalHook)) && PatchHelper.HasHook(AccessTools.DeclaredPropertySetter(typeof(ZDO), "Persistent"), typeof(PersistentSetterHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "HandleDestroyedZDO", (Type[])null, (Type[])null), typeof(HandleDestroyedZdoHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "Load", (Type[])null, (Type[])null), typeof(ZdoManLoadHook))); internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Orphan Index", value: false, "Diagnostic. Runs both the indexed sweep and vanilla's full scan on every disconnect, acts on vanilla's result, and logs any disagreement. Costs the full scan this fix exists to avoid, so leave it off unless you are validating the index.", null, advanced: true); } private static void Track(ZDOID uid, long owner) { //IL_0008: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (!RunMode.IsServer || uid == ZDOID.None) { return; } if (OwnerOf.TryGetValue(uid, out var value)) { if (value == owner) { return; } Untrack(uid); } OwnerOf[uid] = owner; if (owner == 0L) { Unowned.Add(uid); return; } if (!ByOwner.TryGetValue(owner, out var value2)) { value2 = new HashSet(); ByOwner.Add(owner, value2); } value2.Add(uid); } private static void Untrack(ZDOID uid) { //IL_000d: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!RunMode.IsServer || !OwnerOf.TryGetValue(uid, out var value)) { return; } OwnerOf.Remove(uid); HashSet value2; if (value == 0L) { Unowned.Remove(uid); } else if (ByOwner.TryGetValue(value, out value2)) { value2.Remove(uid); if (value2.Count == 0) { ByOwner.Remove(value); } } } private static void ClearIndex() { OwnerOf.Clear(); ByOwner.Clear(); Unowned.Clear(); } private static void RebuildIndex(ZDOMan zdoMan) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) ClearIndex(); foreach (KeyValuePair item in zdoMan.m_objectsByID) { ZDO value = item.Value; if (!value.Persistent) { Track(value.m_uid, value.HasOwner() ? value.GetOwner() : 0); } } } [HarmonyPrefix] [HarmonyPatch("RemoveOrphanNonPersistentZDOS")] private static bool RemoveOrphanNonPersistentZDOSPrefix(ZDOMan __instance) { if (!Hooks.Healthy) { return true; } bool flag = Verify != null && Verify.Value; ConnectedScratch.Clear(); ConnectedScratch.Add(__instance.m_sessionID); List peers = __instance.m_peers; for (int i = 0; i < peers.Count; i++) { ConnectedScratch.Add(peers[i].m_peer.m_uid); } OrphanScratch.Clear(); CollectIndexed(__instance); if (flag) { VanillaScratch.Clear(); CollectFullScan(__instance); ReportDivergence(); Destroy(__instance, VanillaScratch); VanillaScratch.Clear(); OrphanScratch.Clear(); return false; } Destroy(__instance, OrphanScratch); OrphanScratch.Clear(); return false; } private static void CollectIndexed(ZDOMan zdoMan) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) StaleScratch.Clear(); foreach (ZDOID item in Unowned) { ZDO zDO = zdoMan.GetZDO(item); if (zDO == null) { StaleScratch.Add(item); } else { OrphanScratch.Add(zDO); } } foreach (KeyValuePair> item2 in ByOwner) { if (ConnectedScratch.Contains(item2.Key)) { continue; } foreach (ZDOID item3 in item2.Value) { ZDO zDO2 = zdoMan.GetZDO(item3); if (zDO2 == null) { StaleScratch.Add(item3); } else { OrphanScratch.Add(zDO2); } } } for (int i = 0; i < StaleScratch.Count; i++) { Untrack(StaleScratch[i]); } StaleScratch.Clear(); } private static void CollectFullScan(ZDOMan zdoMan) { foreach (KeyValuePair item in zdoMan.m_objectsByID) { ZDO value = item.Value; if (!value.Persistent && (!value.HasOwner() || !ConnectedScratch.Contains(value.GetOwner()))) { VanillaScratch.Add(value); } } } private static void ReportDivergence() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); for (int i = 0; i < OrphanScratch.Count; i++) { hashSet.Add(OrphanScratch[i].m_uid); } int num = 0; int num2 = 0; ZDOID val = ZDOID.None; ZDOID val2 = ZDOID.None; HashSet hashSet2 = new HashSet(); for (int j = 0; j < VanillaScratch.Count; j++) { ZDOID uid = VanillaScratch[j].m_uid; hashSet2.Add(uid); if (!hashSet.Contains(uid)) { if (num == 0) { val = uid; } num++; } } foreach (ZDOID item in hashSet) { if (!hashSet2.Contains(item)) { if (num2 == 0) { val2 = item; } num2++; } } if (num == 0 && num2 == 0) { Logger.LogInfo($"Orphan index verify: agreed on {VanillaScratch.Count} orphan(s) out of " + $"{OwnerOf.Count} tracked non-persistent ZDO(s)."); } else { Logger.LogError($"Orphan index verify: DIVERGED. The full scan found {num} orphan(s) the index " + $"missed (first {val}), and the index claimed {num2} the full scan did not " + $"(first {val2}). Vanilla's result was used. Please report this - leave " + "'Verify Orphan Index' on until it is understood, since the verify pass acts on vanilla's answer."); } } private static void Destroy(ZDOMan zdoMan, List orphans) { for (int i = 0; i < orphans.Count; i++) { ZDO val = orphans[i]; ZLog.Log((object)("Destroying abandoned non persistent zdo " + ((object)Unsafe.As(ref val.m_uid)/*cast due to .constrained prefix*/).ToString() + " owner " + val.GetOwner())); val.SetOwner(zdoMan.m_sessionID); zdoMan.DestroyZDO(val); } } } [PatchSide(Side.Both)] internal static class PhysicsCatchupPatch { internal static ConfigEntry MaxSteps; internal static void BindConfig() { MaxSteps = ValConfig.BindServerConfig("Fixes - Performance", "Max Physics Steps Per Frame", 8, "How many fixed physics steps a single frame may run while catching up after a stall. Lower recovers from hitches faster but drops more simulated time during them; vanilla's effective value is ~16.", advanced: true, 4, 15); Apply(); MaxSteps.SettingChanged += delegate { Apply(); }; } private static void Apply() { if (MaxSteps != null) { Time.maximumDeltaTime = (float)MaxSteps.Value * Time.fixedDeltaTime; } } } [PatchSide(Side.Server)] [HarmonyPatch(typeof(Game))] internal static class PortalConnectionPatch { private static readonly Dictionary> UnconnectedByTag = new Dictionary>(); private static readonly Stack> ListPool = new Stack>(); private static readonly HashSet ToForceSend = new HashSet(); [HarmonyPrefix] [HarmonyPatch("ConnectPortals")] private static bool ConnectPortalsPrefix(Game __instance) { ZDOMan instance = ZDOMan.instance; if (instance == null) { return true; } __instance.ClearCurrentlyConnectingPortals(); ConnectPortals(instance); return false; } private static void ConnectPortals(ZDOMan zdoMan) { long sessionID = ZDOMan.GetSessionID(); ClearCaches(); CollectUnconnected(zdoMan, sessionID); int num = PairByTag(sessionID); FlushForceSend(zdoMan); ClearCaches(); if (num > 0) { Logger.LogInfo($"Connected {num} portal(s)."); } } private static void CollectUnconnected(ZDOMan zdoMan, long sessionId) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //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) foreach (KeyValuePair> portalObject in zdoMan.m_portalObjects) { List value = portalObject.Value; if (value == null) { continue; } for (int i = 0; i < value.Count; i++) { ZDO val = value[i]; if (val == null) { continue; } string text = val.GetString(ZDOVars.s_tag, ""); ZDOID connectionZDOID = val.GetConnectionZDOID((ConnectionType)1); if (!((ZDOID)(ref connectionZDOID)).IsNone()) { zdoMan.m_objectsByID.TryGetValue(connectionZDOID, out var value2); if (value2 != null && value2.GetString(ZDOVars.s_tag, "") == text) { ZDOID connectionZDOID2 = value2.GetConnectionZDOID((ConnectionType)1); if (!((ZDOID)(ref connectionZDOID2)).IsNone()) { continue; } } Disconnect(val, sessionId); } Bucket(text).Add(val); } } } private static int PairByTag(long sessionId) { int num = 0; foreach (KeyValuePair> item in UnconnectedByTag) { List value = item.Value; for (int i = 0; i + 1 < value.Count; i += 2) { Connect(value[i], value[i + 1], sessionId); num++; } } return num; } private static List Bucket(string tag) { if (UnconnectedByTag.TryGetValue(tag, out var value)) { return value; } value = ((ListPool.Count > 0) ? ListPool.Pop() : new List()); UnconnectedByTag.Add(tag, value); return value; } private static void Disconnect(ZDO portal, long sessionId) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) portal.SetOwner(sessionId); portal.UpdateConnection((ConnectionType)1, ZDOID.None); ToForceSend.Add(portal.m_uid); } private static void Connect(ZDO a, ZDO b, long sessionId) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002e: 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) a.SetOwner(sessionId); b.SetOwner(sessionId); a.SetConnection((ConnectionType)1, b.m_uid); b.SetConnection((ConnectionType)1, a.m_uid); ToForceSend.Add(a.m_uid); ToForceSend.Add(b.m_uid); } private static void FlushForceSend(ZDOMan zdoMan) { if (ToForceSend.Count != 0) { List peers = zdoMan.m_peers; for (int i = 0; i < peers.Count; i++) { peers[i].m_forceSend.UnionWith(ToForceSend); } } } private static void ClearCaches() { foreach (KeyValuePair> item in UnconnectedByTag) { item.Value.Clear(); ListPool.Push(item.Value); } UnconnectedByTag.Clear(); ToForceSend.Clear(); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(TeleportWorld))] internal static class PortalIdleUpdatePatch { [HarmonyPatch(typeof(EffectFade))] internal static class FadeHooks { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(EffectFade __instance) { WriteEndpoint(__instance); } [HarmonyPrefix] [HarmonyPatch("Update")] private static bool UpdatePrefix(EffectFade __instance) { return __instance.m_intensity != (__instance.m_active ? 1f : 0f); } private static void WriteEndpoint(EffectFade fade) { if ((Object)(object)fade.m_light != (Object)null) { fade.m_light.intensity = fade.m_intensity * fade.m_lightBaseIntensity; ((Behaviour)fade.m_light).enabled = fade.m_light.intensity > 0f; } if ((Object)(object)fade.m_audioSource != (Object)null) { fade.m_audioSource.volume = fade.m_intensity * fade.m_baseVolume; } } } private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor"); [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(TeleportWorld __instance) { //IL_003c: 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_004d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.GetZDO() != null && !((Object)(object)__instance.m_model == (Object)null)) { ((Renderer)__instance.m_model).material.SetColor(EmissionColor, Color.Lerp(__instance.m_colorUnconnected, __instance.m_colorTargetfound, __instance.m_colorAlpha)); } } [HarmonyPrefix] [HarmonyPatch("Update")] private static bool UpdatePrefix(TeleportWorld __instance) { return __instance.m_colorAlpha != (__instance.m_hadTarget ? 1f : 0f); } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(RandomMaterialValues))] internal static class RandomMaterialPollPatch { private struct Entry { public RandomMaterialValues m_rmv; public float m_next; } [HarmonyPatch(typeof(ZNetScene))] internal static class PumpHook { [HarmonyPostfix] [HarmonyPatch("Update")] private static void Postfix() { if (Queue.Count == 0) { return; } float time = Time.time; for (int num = Queue.Count - 1; num >= 0; num--) { Entry value = Queue[num]; if (!(time < value.m_next)) { if ((Object)(object)value.m_rmv == (Object)null || Poll(value.m_rmv)) { Queue[num] = Queue[Queue.Count - 1]; Queue.RemoveAt(Queue.Count - 1); } else { value.m_next = time + 0.2f; Queue[num] = value; } } } } [HarmonyPostfix] [HarmonyPatch("Shutdown")] private static void ShutdownPostfix() { Queue.Clear(); } } private const float PollInterval = 0.2f; private const int MaxChecks = 5; private static readonly List Queue = new List(); private static readonly Dictionary PropertyIds = new Dictionary(); private static readonly HookHealth Hooks = new HookHealth("Piece material polling", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZNetScene), "Update", (Type[])null, (Type[])null), typeof(PumpHook))); [HarmonyPrefix] [HarmonyPatch("Start")] private static bool StartPrefix(RandomMaterialValues __instance) { if (!Hooks.Healthy) { return true; } __instance.m_nview = ((Component)__instance).GetComponentInParent(); __instance.m_piece = ((Component)__instance).GetComponentInParent(); if (!Object.op_Implicit((Object)(object)__instance.m_nview)) { ZLog.LogError((object)("Missing nview on '" + ((Object)((Component)((Component)__instance).transform).gameObject).name + "'")); } Queue.Add(new Entry { m_rmv = __instance, m_next = Time.time }); return false; } private static bool Poll(RandomMaterialValues rmv) { //IL_00fa: Unknown result type (might be due to invalid IL or missing references) if (((!rmv.m_isSet && rmv.m_randomSeed < 0) || (rmv.m_isSet && (!Object.op_Implicit((Object)(object)rmv.m_piece) || !Player.IsPlacementGhost(((Component)rmv.m_piece).gameObject)))) && Object.op_Implicit((Object)(object)rmv.m_nview) && rmv.m_nview.GetZDO() != null) { rmv.m_randomSeed = rmv.m_nview.GetZDO().GetInt(RandomMaterialValues.s_randSeedString, -1); if (rmv.m_randomSeed < 0 && rmv.m_nview.IsOwner()) { rmv.m_nview.GetZDO().Set(RandomMaterialValues.s_randSeedString, Random.Range(0, 12345)); } if (rmv.m_randomSeed >= 0) { for (int i = 0; i < rmv.m_vectorProperties.Count; i++) { VectorVariationProperty val = rmv.m_vectorProperties[i]; foreach (string propertyName in ((MaterialVariationProperty)(object)val).m_propertyNames) { MaterialMan.instance.SetValue(((Component)rmv).gameObject, PropertyId(propertyName), ((MaterialVariationProperty)(object)val).GetValue(rmv.m_randomSeed + i), false); } } rmv.m_isSet = true; } } rmv.m_checks++; if (!rmv.m_isSet) { return rmv.m_checks >= 5; } return true; } private static int PropertyId(string name) { if (!PropertyIds.TryGetValue(name, out var value)) { value = Shader.PropertyToID(name); PropertyIds.Add(name, value); } return value; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(ReflectionUpdate))] internal static class ReflectionSlicePatch { internal static ConfigEntry Resolution; internal static ConfigEntry FrameBudgetMs; private const int FaceIdle = -1; private const int MaxConsecutiveDefers = 3; private const int CooldownFrames = 4; private static Camera _camera; private static RenderTexture _cube1; private static RenderTexture _cube2; private static int _nextFace = -1; private static int _deferStreak; private static int _cooldownFrames; private static bool _finished; private static Vector3 _renderPosition; private static int _excludeMask; private static float[] _layerCullDistances; internal static void BindConfig() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown Resolution = ValConfig.cfg.Bind("Client config", "Reflection Resolution", 128, new ConfigDescription("Cubemap face resolution for the sliced reflection renderer. Higher is sharper reflections and more per-face cost.", (AcceptableValueBase)(object)new AcceptableValueList(new int[4] { 64, 128, 256, 512 }), new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); FrameBudgetMs = ValConfig.cfg.Bind("Client config", "Reflection Frame Budget", 33, new ConfigDescription("Milliseconds: a reflection face is held back when the previous frame ran longer than this, so it lands on a quiet frame instead of piling onto one that was already struggling. The reflection is never shown half-built - only the frame that pays for a face moves. 0 renders a face every frame.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), new object[1] { (object)new ConfigurationManagerAttributes { IsAdvanced = true } })); } [HarmonyPrefix] [HarmonyPatch("Update")] private static bool UpdatePrefix(ReflectionUpdate __instance) { //IL_019a: 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) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) __instance.m_updateTimer += Time.deltaTime; if (_nextFace == -1 && __instance.m_updateTimer > __instance.m_interval) { __instance.m_updateTimer = 0f; BeginRender(__instance); } if (_nextFace >= 0 && !DeferFace()) { long timestamp = Stopwatch.GetTimestamp(); RenderFace(__instance, _nextFace); double num = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency; int num2 = ((FrameBudgetMs != null) ? FrameBudgetMs.Value : 0); if (num2 > 0 && num > (double)num2) { _cooldownFrames = 4; } _nextFace++; if (_nextFace > 5) { _nextFace = -1; _finished = true; Current(__instance).realtimeTexture = TargetFor(__instance); } } if (_finished) { float num3 = Mathf.Pow(Mathf.Clamp01(__instance.m_updateTimer / __instance.m_transitionDuration), __instance.m_power); if ((Object)(object)__instance.m_probe1 == (Object)(object)Current(__instance)) { __instance.m_probe1.importance = 1; __instance.m_probe2.importance = 0; __instance.m_probe1.size = new Vector3(2000f * num3, 1000f * num3, 2000f * num3); __instance.m_probe2.size = new Vector3(2001f, 1001f, 2001f); } else { __instance.m_probe1.importance = 0; __instance.m_probe2.importance = 1; __instance.m_probe2.size = new Vector3(2000f * num3, 1000f * num3, 2000f * num3); __instance.m_probe1.size = new Vector3(2001f, 1001f, 2001f); } } return false; } [HarmonyPrefix] [HarmonyPatch("UpdateReflection")] private static bool UpdateReflectionPrefix(ReflectionUpdate __instance) { __instance.m_updateTimer = 0f; BeginRender(__instance); return false; } [HarmonyPostfix] [HarmonyPatch("OnDestroy")] private static void OnDestroyPostfix() { if ((Object)(object)_cube1 != (Object)null) { _cube1.Release(); _cube1 = null; } if ((Object)(object)_cube2 != (Object)null) { _cube2.Release(); _cube2 = null; } _camera = null; _nextFace = -1; _deferStreak = 0; _finished = false; } private static ReflectionProbe Current(ReflectionUpdate update) { return update.m_current; } private static RenderTexture TargetFor(ReflectionUpdate update) { if (!((Object)(object)update.m_current == (Object)(object)update.m_probe1)) { return _cube2; } return _cube1; } private static void BeginRender(ReflectionUpdate update) { //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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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) EnsureResources(update); if (_nextFace == -1) { update.m_current = (((Object)(object)update.m_current == (Object)(object)update.m_probe1) ? update.m_probe2 : update.m_probe1); } _renderPosition = ZNet.instance.GetReferencePosition() + Vector3.up * update.m_reflectionHeight; ((Component)update.m_current).transform.position = _renderPosition; _nextFace = 0; _finished = false; _deferStreak = 0; _cooldownFrames = 0; } private static void EnsureResources(ReflectionUpdate update) { if ((Object)(object)_camera == (Object)null) { _camera = ((Component)update).gameObject.GetComponent(); if ((Object)(object)_camera == (Object)null) { _camera = ((Component)update).gameObject.AddComponent(); } ((Behaviour)_camera).enabled = false; _camera.farClipPlane = 1000f; _excludeMask = (1 << LayerMask.NameToLayer("character")) | (1 << LayerMask.NameToLayer("effect")) | (1 << LayerMask.NameToLayer("item")) | (1 << LayerMask.NameToLayer("TransparentFX")); _layerCullDistances = new float[32]; _layerCullDistances[LayerMask.NameToLayer("piece")] = 500f; } int num = ((Resolution != null) ? Resolution.Value : 128); if ((Object)(object)_cube1 == (Object)null || ((Texture)_cube1).width != num) { if ((Object)(object)_cube1 != (Object)null) { _cube1.Release(); } if ((Object)(object)_cube2 != (Object)null) { _cube2.Release(); } _cube1 = CreateCube(num); _cube2 = CreateCube(num); } } private static RenderTexture CreateCube(int size) { //IL_0004: 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_0010: 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_001f: Expected O, but got Unknown return new RenderTexture(size, size, 16) { dimension = (TextureDimension)4, useMipMap = true, autoGenerateMips = true }; } private static bool DeferFace() { int num = ((FrameBudgetMs != null) ? FrameBudgetMs.Value : 0); if (num <= 0 || _deferStreak >= 3) { _deferStreak = 0; _cooldownFrames = 0; return false; } if (!(Time.unscaledDeltaTime * 1000f > (float)num) && _cooldownFrames <= 0) { _deferStreak = 0; return false; } if (_cooldownFrames > 0) { _cooldownFrames--; } _deferStreak++; return true; } private static void RenderFace(ReflectionUpdate update, int face) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) ((Component)_camera).transform.position = _renderPosition; float lodBias = QualitySettings.lodBias; int shadowCascades = QualitySettings.shadowCascades; float shadowDistance = QualitySettings.shadowDistance; int maximumLODLevel = QualitySettings.maximumLODLevel; try { QualitySettings.lodBias = 5f; QualitySettings.shadowCascades = 2; QualitySettings.shadowDistance = 80f; QualitySettings.maximumLODLevel = 1; _camera.cullingMask = update.m_probe1.cullingMask & ~_excludeMask; _camera.layerCullDistances = _layerCullDistances; _camera.RenderToCubemap(TargetFor(update), 1 << face); } finally { QualitySettings.lodBias = lodBias; QualitySettings.shadowCascades = shadowCascades; QualitySettings.shadowDistance = shadowDistance; QualitySettings.maximumLODLevel = maximumLODLevel; } } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZNetScene))] internal static class RemoveSweepPacingPatch { internal static ConfigEntry SweepIntervalMs; private static float _lastSweep; internal static void BindConfig() { SweepIntervalMs = ValConfig.BindServerConfig("Fixes - Performance", "Object Unload Sweep Interval", 100, "Milliseconds between object-unload sweeps. Higher recovers more frame time in object-heavy areas but lets departed objects linger longer before despawning. 0 sweeps every pass, exactly like vanilla.", advanced: true, 0, 1000); } [HarmonyPrefix] [HarmonyPriority(600)] [HarmonyPatch("RemoveObjects")] private static bool RemoveObjectsPrefix() { int num = ((SweepIntervalMs != null) ? SweepIntervalMs.Value : 100); if (num <= 0) { return true; } float unscaledTime = Time.unscaledTime; if (unscaledTime - _lastSweep < (float)num / 1000f) { return false; } _lastSweep = unscaledTime; return true; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZNetScene))] internal static class SceneIdleSkipPatch { [HarmonyPatch(typeof(ZDOMan), "AddToSector")] internal static class AddToSectorHook { [HarmonyPostfix] private static void Postfix(ZDO zdo, SectorIndex sectorIndex) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) OnSectorTouched(zdo, sectorIndex); } } [HarmonyPatch(typeof(ZDOMan), "RemoveFromSector")] internal static class RemoveFromSectorHook { [HarmonyPostfix] private static void Postfix(ZDO zdo, SectorIndex sectorIndex) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) OnSectorTouched(zdo, sectorIndex); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class CreatedSetterHook { [HarmonyPostfix] private static void Postfix() { _createdVersion++; } } internal static ConfigEntry Verify; private const int HygieneInterval = 30; private static long _ringHash; private static long _createdVersion; private static bool _ringValid; private static int _ringMinX; private static int _ringMaxX; private static int _ringMinY; private static int _ringMaxY; private static ZNetScene _snapshotScene; private static Vector2s _snapshotZone; private static long _snapshotRingHash; private static long _snapshotCreatedVersion; private static SimulationDistance _snapshotSimulationDistance; private static bool _idle; private static int _skipsSinceFullPass; private static Vector2s _zoneAtPrefix; private static long _ringHashAtPrefix; private static long _createdAtPrefix; private static bool _ranFullPass; private static bool _wouldSkip; private static readonly HookHealth Hooks = new HookHealth("Scene idle skip", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "AddToSector", (Type[])null, (Type[])null), typeof(AddToSectorHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "RemoveFromSector", (Type[])null, (Type[])null), typeof(RemoveFromSectorHook)) && PatchHelper.HasHook(AccessTools.DeclaredPropertySetter(typeof(ZDO), "Created"), typeof(CreatedSetterHook))); private const int VerifyReportInterval = 900; private static bool _verifyActive; private static int _verifyPasses; private static int _verifyWouldSkip; private static int _verifyDivergences; private static int _passesSinceReport; internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Scene Idle Skip", value: false, "Diagnostic. Predicts whether each object pass could be skipped, always runs it anyway, and logs whenever a pass predicted skippable did real work. Costs the passes this fix exists to avoid, so leave it off unless you are validating the skip conditions.", null, advanced: true); } private static void OnSectorTouched(ZDO zdo, SectorIndex sectorIndex) { //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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Vector2s val = ZoneSystem.IndexToSector(sectorIndex.Sector); if (!_ringValid || (val.x >= _ringMinX && val.x <= _ringMaxX && val.y >= _ringMinY && val.y <= _ringMaxY)) { _ringHash ^= (uint)((object)Unsafe.As(ref zdo.m_uid)/*cast due to .constrained prefix*/).GetHashCode(); } } [HarmonyPrefix] [HarmonyPatch("CreateDestroyObjects")] private static bool CreateDestroyObjectsPrefix(ZNetScene __instance) { //IL_0027: 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) //IL_0038: 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) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0132: Unknown result type (might be due to invalid IL or missing references) _ranFullPass = false; if (!Hooks.Healthy) { return true; } ZoneSystem instance = ZoneSystem.instance; ZNet instance2 = ZNet.instance; if (instance == null || instance2 == null) { return true; } Vector2s zone = ZoneSystem.GetZone(instance2.GetReferencePosition()); SimulationDistance syncedSimulationDistance = instance2.GetSyncedSimulationDistance(); bool flag = _idle && __instance == _snapshotScene && zone == _snapshotZone && _ringHash == _snapshotRingHash && _createdVersion == _snapshotCreatedVersion && ((SimulationDistance)(ref syncedSimulationDistance)).Equals(_snapshotSimulationDistance) && _skipsSinceFullPass < 30; bool flag2 = Verify != null && Verify.Value; if (_verifyActive && !flag2) { _verifyActive = false; LogVerifySummary("final"); _verifyPasses = 0; _verifyWouldSkip = 0; _verifyDivergences = 0; _passesSinceReport = 0; } _verifyActive = flag2; if (flag && !flag2) { _skipsSinceFullPass++; return false; } _idle = false; _wouldSkip = flag; _zoneAtPrefix = zone; int totalSimulationDistance = ((SimulationDistance)(ref syncedSimulationDistance)).TotalSimulationDistance; _ringMinX = zone.x - totalSimulationDistance; _ringMaxX = zone.x + totalSimulationDistance; _ringMinY = zone.y - totalSimulationDistance; _ringMaxY = zone.y + totalSimulationDistance; _ringValid = true; _ringHashAtPrefix = _ringHash; _createdAtPrefix = _createdVersion; _skipsSinceFullPass = 0; _ranFullPass = true; return true; } [HarmonyPostfix] [HarmonyPatch("CreateDestroyObjects")] private static void CreateDestroyObjectsPostfix(ZNetScene __instance) { //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) if (!_ranFullPass) { return; } _ranFullPass = false; bool flag = _ringHash == _ringHashAtPrefix && _createdVersion == _createdAtPrefix; bool wouldSkip = _wouldSkip; _wouldSkip = false; if (_verifyActive) { _verifyPasses++; if (wouldSkip) { _verifyWouldSkip++; } if (++_passesSinceReport >= 900) { _passesSinceReport = 0; LogVerifySummary("periodic"); } } if (flag || wouldSkip) { int num = CountPending(__instance, __instance.m_tempCurrentObjects2); int num2 = CountPending(__instance, __instance.m_tempCurrentDistantObjects); bool flag2 = (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.IsActiveAreaLoaded(); if (wouldSkip && (!flag || num > 0 || num2 > 0 || __instance.m_tempRemoved.Count > 0)) { _verifyDivergences++; Logger.LogError("Scene idle skip verify: DIVERGED - a pass predicted skippable did work " + $"(ring hash changed {_ringHash != _ringHashAtPrefix}, created delta " + $"{_createdVersion - _createdAtPrefix}, pending near {num}, " + $"pending distant {num2}, removed " + $"{__instance.m_tempRemoved.Count}). Vanilla ran, so nothing was lost. " + "Please report this - leave 'Verify Scene Idle Skip' on until it is understood, since verify mode always runs the full pass."); } _idle = flag && num == 0 && num2 == 0 && flag2; if (_idle) { _snapshotScene = __instance; _snapshotZone = _zoneAtPrefix; _snapshotRingHash = _ringHash; _snapshotCreatedVersion = _createdVersion; _snapshotSimulationDistance = ZNet.instance.GetSyncedSimulationDistance(); } } } private static void LogVerifySummary(string kind) { Logger.LogInfo($"Idle skip verify ({kind}): would have skipped {_verifyWouldSkip} of " + $"{_verifyPasses} passes, {_verifyDivergences} divergence(s). A large skip share " + "with zero divergences means the fix will engage in this area once Verify is off."); } private static int CountPending(ZNetScene scene, List zdos) { int num = 0; for (int i = 0; i < zdos.Count; i++) { ZDO val = zdos[i]; if (!val.Created && !((Object)(object)scene.GetPrefab(val.GetPrefab()) == (Object)null)) { num++; } } return num; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZNetScene))] internal static class SectorInstanceIndexPatch { internal struct Slot { public Vector2s m_zone; public int m_index; } [HarmonyPatch(typeof(ZDOMan))] internal static class SectorMoveHooks { [HarmonyPostfix] [HarmonyPatch("AddToSector")] private static void AddToSectorPostfix(ZDO zdo, SectorIndex sectorIndex) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0051: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; if (instance != null && instance.m_instances.TryGetValue(zdo, out var value)) { Vector2s val = ZoneOf(sectorIndex); int instanceID = ((Object)value).GetInstanceID(); if (Slots.TryGetValue(instanceID, out var value2) && !(value2.m_zone == val)) { IndexRemove(value, instanceID); IndexAdd(value, instanceID, val); } } } } [HarmonyPatch(typeof(ZNetScene), "Shutdown")] internal static class ShutdownHook { [HarmonyPostfix] private static void Postfix() { NonDistantCount.Clear(); ByZone.Clear(); Slots.Clear(); } } internal static ConfigEntry Verify; private static readonly Dictionary NonDistantCount = new Dictionary(); internal static readonly Dictionary> ByZone = new Dictionary>(); internal static readonly Dictionary Slots = new Dictionary(); private static readonly HookHealth Hooks = new HookHealth("Sector instance index", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZNetScene), "AddInstance", (Type[])null, (Type[])null), typeof(SectorInstanceIndexPatch)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZNetView), "OnDestroy", (Type[])null, (Type[])null), typeof(TeardownHooks.ViewHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "AddToSector", (Type[])null, (Type[])null), typeof(SectorMoveHooks))); private const int VerifyReportInterval = 250; private static bool _verifyActive; private static long _verifyComparisons; private static long _verifyDivergences; private static int _comparisonsSinceReport; internal static bool MaintenanceHealthy => Hooks.Healthy; internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Zone Occupancy", value: false, "Diagnostic. Answers every zone-occupancy check both from the tally and vanilla's full walk, acts on vanilla's answer, and logs disagreements. Costs the walk this fix exists to avoid, so leave it off unless you are validating the tally. Transient one-off disagreements on zones holding a moving creature are expected and harmless; persistent ones are not.", null, advanced: true); } private static Vector2s ZoneOf(SectorIndex sectorIndex) { //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) return ZoneSystem.IndexToSector(sectorIndex.Sector); } private static void Bump(Vector2s sector, int delta) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) NonDistantCount.TryGetValue(sector, out var value); value += delta; if (value > 0) { NonDistantCount[sector] = value; } else { NonDistantCount.Remove(sector); } } private static void IndexAdd(ZNetView view, int id, Vector2s zone) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!ByZone.TryGetValue(zone, out var value)) { value = new List(); ByZone.Add(zone, value); } value.Add(view); Slots[id] = new Slot { m_zone = zone, m_index = value.Count - 1 }; if (!view.m_distant) { Bump(zone, 1); } } private static void IndexRemove(ZNetView view, int id) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) if (!Slots.TryGetValue(id, out var value)) { return; } Slots.Remove(id); if (ByZone.TryGetValue(value.m_zone, out var value2)) { int num = value2.Count - 1; if (value.m_index < num) { ZNetView val = value2[num]; value2[value.m_index] = val; Slots[((Object)val).GetInstanceID()] = new Slot { m_zone = value.m_zone, m_index = value.m_index }; } value2.RemoveAt(num); if (value2.Count == 0) { ByZone.Remove(value.m_zone); } } if (!view.m_distant) { Bump(value.m_zone, -1); } } internal static void OnViewDestroyed(ZNetView view) { IndexRemove(view, ((Object)view).GetInstanceID()); } [HarmonyPostfix] [HarmonyPatch("AddInstance")] private static void AddInstancePostfix(ZDO zdo, ZNetView nview) { //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) if (nview != null && !nview.m_ghost) { int instanceID = ((Object)nview).GetInstanceID(); IndexRemove(nview, instanceID); IndexAdd(nview, instanceID, ZoneOf(zdo.GetSectorIndex())); } } [HarmonyPrefix] [HarmonyPatch("HaveInstanceInSector")] private static bool HaveInstanceInSectorPrefix(ZNetScene __instance, Vector2s sector, ref bool __result) { //IL_0013: 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) if (!Hooks.Healthy) { return true; } bool flag = NonDistantCount.ContainsKey(sector); if (Verify != null && Verify.Value) { _verifyActive = true; _verifyComparisons++; bool flag2 = WalkedAnswer(__instance, sector); if (flag2 != flag) { _verifyDivergences++; Logger.LogWarning($"Zone occupancy verify: DIVERGED on zone {sector} (tally: {flag}, " + $"walk: {flag2}). Vanilla's answer was used. One-off disagreements on " + "zones holding a moving creature are expected; report this if it repeats for the same zone."); } if (++_comparisonsSinceReport >= 250) { _comparisonsSinceReport = 0; LogVerifySummary("periodic"); } __result = flag2; return false; } if (_verifyActive) { _verifyActive = false; LogVerifySummary("final"); _verifyComparisons = 0L; _verifyDivergences = 0L; _comparisonsSinceReport = 0; } __result = flag; return false; } private static bool WalkedAnswer(ZNetScene scene, Vector2s sector) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair instance in scene.m_instances) { if (Object.op_Implicit((Object)(object)instance.Value) && !instance.Value.m_distant && ZoneSystem.GetZone(((Component)instance.Value).transform.position) == sector) { return true; } } return false; } private static void LogVerifySummary(string kind) { Logger.LogInfo($"Zone occupancy verify ({kind}): {_verifyComparisons} comparison(s), " + $"{_verifyDivergences} divergence(s)."); } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(Smelter))] internal static class SmelterCatchupPatch { private const string Site = "Smelter.UpdateSmelter"; private static readonly MethodInfo GetFuelMethod = AccessTools.DeclaredMethod(typeof(Smelter), "GetFuel", (Type[])null, (Type[])null); private static readonly MethodInfo GetQueuedOreMethod = AccessTools.DeclaredMethod(typeof(Smelter), "GetQueuedOre", (Type[])null, (Type[])null); private static readonly MethodInfo FuelMethod = AccessTools.DeclaredMethod(typeof(SmelterCatchupPatch), "Fuel", (Type[])null, (Type[])null); private static readonly MethodInfo QueuedOreMethod = AccessTools.DeclaredMethod(typeof(SmelterCatchupPatch), "QueuedOre", (Type[])null, (Type[])null); private static bool _armed; private static ZDO _zdo; private static uint _revision; private static bool _haveFuel; private static float _fuel; private static bool _haveOre; private static string _ore; [HarmonyPrefix] [HarmonyPatch("UpdateSmelter")] private static void UpdateSmelterPrefix() { _armed = true; _zdo = null; _haveFuel = false; _haveOre = false; } [HarmonyPostfix] [HarmonyPatch("UpdateSmelter")] private static void UpdateSmelterPostfix() { _armed = false; _zdo = null; _ore = null; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("UpdateSmelter")] private static IEnumerable UpdateSmelterTranspiler(IEnumerable instructions) { return PatchHelper.ReplaceCalls(PatchHelper.ReplaceCalls(instructions, GetFuelMethod, FuelMethod, "Smelter.UpdateSmelter", 2), GetQueuedOreMethod, QueuedOreMethod, "Smelter.UpdateSmelter", 2); } private static bool Sync(Smelter smelter) { if (!_armed) { return false; } ZNetView nview = smelter.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return false; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return false; } if (zDO != _zdo || zDO.DataRevision != _revision) { _zdo = zDO; _revision = zDO.DataRevision; _haveFuel = false; _haveOre = false; } return true; } private static float Fuel(Smelter smelter) { if (!Sync(smelter)) { return smelter.GetFuel(); } if (!_haveFuel) { _fuel = smelter.GetFuel(); _haveFuel = true; } return _fuel; } private static string QueuedOre(Smelter smelter) { if (!Sync(smelter)) { return smelter.GetQueuedOre(); } if (!_haveOre) { _ore = smelter.GetQueuedOre(); _haveOre = true; } return _ore; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(Smoke))] internal static class SmokeCostPatch { [HarmonyPatch(typeof(SmokeRenderer))] internal static class RendererHook { private static float _nextTransfer; private const float TransferInterval = 0.25f; [HarmonyPrefix] [HarmonyPatch("LateUpdate")] private static bool LateUpdatePrefix(SmokeRenderer __instance) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; if (time >= _nextTransfer) { _nextTransfer = time + 0.25f; __instance.TransferSmokeBetweenChunks(); } foreach (Vector3Int key in __instance.m_chunkedParticleSystems.Keys) { ParticleSystem val = __instance.m_chunkedParticleSystems[key]; List list = __instance.m_chunkedSmoke[key]; Particle[] array = __instance.m_chunkedParticles[key]; if (list.Count > val.particleCount) { val.Emit(list.Count - val.particleCount); } int num = Mathf.Min(list.Count, array.Length); for (int i = 0; i < num; i++) { Smoke val2 = list[i]; Vector3 position = ((Component)val2).transform.position; ((Particle)(ref val2.m_renderParticle)).remainingLifetime = (((double)val2.m_fadeTimer >= 0.0) ? (val2.m_fadetime - val2.m_fadeTimer) : (val2.m_ttl - val2.m_time)); ((Particle)(ref val2.m_renderParticle)).position = position; Particle renderParticle = val2.m_renderParticle; ((Particle)(ref renderParticle)).startColor = Color32.op_Implicit(__instance.m_smokeColor * new Color(1f, 1f, 1f, val2.GetAlpha())); ((Particle)(ref renderParticle)).startSize = __instance.m_smokeBallSize; array[i] = renderParticle; } int num2 = Mathf.Min(val.particleCount, array.Length); for (int j = num; j < num2; j++) { ((Particle)(ref array[j])).remainingLifetime = -1f; } val.SetParticles(array, num2); } return false; } } [HarmonyPrefix] [HarmonyPatch("CustomUpdate")] private static bool CustomUpdatePrefix(Smoke __instance, float deltaTime, float time) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_00c3: 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) __instance.m_time += deltaTime; if (__instance.m_time > __instance.m_ttl && (double)__instance.m_fadeTimer < 0.0) { __instance.StartFadeOut(); } float num = 1f - Mathf.Clamp01(__instance.m_time / __instance.m_ttl); int num2 = (int)(Mathf.Clamp01(__instance.m_time / __instance.m_ttl) * 50f); int num3 = (int)(Mathf.Clamp01((__instance.m_time - deltaTime) / __instance.m_ttl) * 50f); if (num2 != num3 || __instance.m_time <= deltaTime) { __instance.m_body.mass = num * num; } Vector3 linearVelocity = __instance.m_body.linearVelocity; Vector3 vel = __instance.m_vel; vel.y *= num; __instance.m_body.AddForce((vel - linearVelocity) * (__instance.m_force * deltaTime), (ForceMode)2); if ((double)__instance.m_fadeTimer < 0.0) { return false; } __instance.m_fadeTimer += deltaTime; if (__instance.m_fadeTimer < __instance.m_fadetime) { return false; } Object.Destroy((Object)(object)((Component)__instance).gameObject); return false; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZNetScene))] internal static class SpawnEventQueuePatch { private sealed class SpawnQueue { internal readonly List m_entries = new List(); internal readonly List m_ids = new List(); internal readonly Dictionary m_index = new Dictionary(); internal readonly List m_deferred = new List(); internal int m_head; internal int m_tombstones; internal bool m_sortDirty = true; internal int m_appendedSinceSort; internal int m_passesSinceSplice; internal const int DeferredSlot = -1; internal int Pending => m_index.Count; internal void Enqueue(ZDO zdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) ZDOID uid = zdo.m_uid; if (!m_index.ContainsKey(uid)) { m_index[uid] = m_entries.Count; m_entries.Add(zdo); m_ids.Add(uid); m_appendedSinceSort++; } } internal void Dequeue(ZDOID id) { //IL_0006: 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_0032: 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 (!m_index.TryGetValue(id, out var value)) { return; } m_index.Remove(id); if (value == -1) { for (int i = 0; i < m_deferred.Count; i++) { if (!(m_deferred[i].m_uid != id)) { m_deferred[i] = m_deferred[m_deferred.Count - 1]; m_deferred.RemoveAt(m_deferred.Count - 1); break; } } } else { m_entries[value] = null; m_tombstones++; } } internal void Tombstone(int slot) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (m_entries[slot] != null) { m_index.Remove(m_ids[slot]); m_entries[slot] = null; m_tombstones++; } } internal void Defer(int slot) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) ZDO val = m_entries[slot]; if (val != null) { m_deferred.Add(val); m_index[m_ids[slot]] = -1; m_entries[slot] = null; m_tombstones++; } } internal void SpliceDeferred() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (m_deferred.Count != 0) { for (int i = 0; i < m_deferred.Count; i++) { ZDO val = m_deferred[i]; m_index[val.m_uid] = m_entries.Count; m_entries.Add(val); m_ids.Add(val.m_uid); } m_appendedSinceSort += m_deferred.Count; m_deferred.Clear(); } } internal void Compact() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) int num = 0; for (int i = 0; i < m_entries.Count; i++) { ZDO val = m_entries[i]; if (val != null) { m_entries[num] = val; m_ids[num] = m_ids[i]; num++; } } m_entries.RemoveRange(num, m_entries.Count - num); m_ids.RemoveRange(num, m_ids.Count - num); m_tombstones = 0; m_head = 0; ReindexFromEntries(); } internal void ReindexFromEntries() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) m_index.Clear(); for (int i = 0; i < m_entries.Count; i++) { m_index[m_ids[i]] = i; } for (int j = 0; j < m_deferred.Count; j++) { m_index[m_deferred[j].m_uid] = -1; } } internal void Clear() { m_entries.Clear(); m_ids.Clear(); m_index.Clear(); m_deferred.Clear(); m_head = 0; m_tombstones = 0; m_sortDirty = true; m_appendedSinceSort = 0; m_passesSinceSplice = 0; } } [HarmonyPatch(typeof(ZDOMan), "AddToSector")] internal static class AddToSectorHook { [HarmonyPostfix] private static void Postfix(ZDO zdo, SectorIndex sectorIndex) { //IL_001d: 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) if (!zdo.Created) { if (_inZdoData) { PendingRpc.Add(zdo); } else { EnqueueIfInRing(zdo, ZoneSystem.IndexToSector(sectorIndex.Sector)); } } } } [HarmonyPatch(typeof(ZDOMan), "RemoveFromSector")] internal static class RemoveFromSectorHook { [HarmonyPostfix] private static void Postfix(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Near.Dequeue(zdo.m_uid); Distant.Dequeue(zdo.m_uid); } } [HarmonyPatch(typeof(ZDOMan), "RPC_ZDOData")] internal static class ZdoDataHook { [HarmonyPrefix] private static void Prefix() { _inZdoData = true; PendingRpc.Clear(); } [HarmonyPostfix] private static void Postfix() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) _inZdoData = false; for (int i = 0; i < PendingRpc.Count; i++) { ZDO val = PendingRpc[i]; if (!val.Created) { EnqueueIfInRing(val, val.GetSector()); } } PendingRpc.Clear(); } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class CreatedSetterHook { [HarmonyPostfix] private static void Postfix(ZDO __instance) { //IL_002b: 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_001e: Unknown result type (might be due to invalid IL or missing references) if (__instance.Created) { Near.Dequeue(__instance.m_uid); Distant.Dequeue(__instance.m_uid); } else { EnqueueIfInRing(__instance, __instance.GetSector()); } } } [HarmonyPatch(typeof(ZNetScene), "Shutdown")] internal static class ShutdownHook { [HarmonyPostfix] private static void Postfix() { ResetSession(null); } } internal static ConfigEntry Verify; private const float ResortDistanceSqr = 64f; private const int DeferredRecheckPasses = 3; private const int CompactTombstoneShare = 4; private const int AppendResortShare = 8; private const int MinAppendsBeforeResort = 32; private static readonly SpawnQueue Near = new SpawnQueue(); private static readonly SpawnQueue Distant = new SpawnQueue(); private static readonly HashSet NearZones = new HashSet(); private static readonly HashSet DistantZones = new HashSet(); private static readonly HashSet ScratchNearZones = new HashSet(); private static readonly HashSet ScratchDistantZones = new HashSet(); private static readonly List ScratchSector = new List(); private static readonly HashSet ScratchVisited = new HashSet(); private static readonly Vector2s NoZone = new Vector2s(short.MinValue, short.MinValue); private static Vector2s _snapshotZone = NoZone; private static SimulationDistance _snapshotSimulationDistance; private static ZNetScene _snapshotScene; private static Vector3 _lastSortPosition; private static bool _sortPositionValid; private static bool _inZdoData; private static readonly List PendingRpc = new List(); private static readonly HookHealth Hooks = new HookHealth("Spawn event queue", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "AddToSector", (Type[])null, (Type[])null), typeof(AddToSectorHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "RemoveFromSector", (Type[])null, (Type[])null), typeof(RemoveFromSectorHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "RPC_ZDOData", (Type[])null, (Type[])null), typeof(ZdoDataHook)) && PatchHelper.HasHook(AccessTools.PropertySetter(typeof(ZDO), "Created"), typeof(CreatedSetterHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZNetScene), "CreateObjectsSorted", (Type[])null, (Type[])null), typeof(SpawnEventQueuePatch)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZNetScene), "CreateDistantObjects", (Type[])null, (Type[])null), typeof(SpawnEventQueuePatch))); private const int VerifyReportInterval = 900; private static readonly List VerifyNear = new List(); private static readonly List VerifyDistant = new List(); private static bool _verifyActive; private static long _verifyPasses; private static long _verifyQueued; private static long _verifyExpected; private static long _verifyMissing; private static int _passesSinceReport; internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Spawn Queue", value: false, "Diagnostic. Rebuilds the candidate list the vanilla way on every pass, compares it against the maintained queue, acts on vanilla's answer, and logs anything the queue is missing. Costs the whole scan this fix exists to avoid, so leave it off unless you are validating the queue.", null, advanced: true); } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch("CreateDestroyObjects")] private static bool CreateDestroyObjectsPrefix(ZNetScene __instance) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_0065: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)null && (Object)(object)ZoneSystem.instance != (Object)null && ZDOMan.instance != null) { if (__instance != _snapshotScene) { ResetSession(__instance); } Vector2s zone = ZoneSystem.GetZone(ZNet.instance.GetReferencePosition()); SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance(); SyncZoneSets(zone, syncedSimulationDistance); if (Verify != null && Verify.Value) { RunVerify(zone, syncedSimulationDistance); return true; } } FinishVerify(); if (!Driving()) { return true; } __instance.m_tempCurrentObjects.Clear(); __instance.m_tempCurrentDistantObjects.Clear(); __instance.CreateObjects(__instance.m_tempCurrentObjects, __instance.m_tempCurrentDistantObjects); __instance.RemoveObjects(__instance.m_tempCurrentObjects, __instance.m_tempCurrentDistantObjects); return false; } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch("CreateObjectsSorted")] private unsafe static bool CreateObjectsSortedPrefix(ZNetScene __instance, int maxCreatedPerFrame, ref int created) { //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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) if (!Driving()) { return true; } if (!ZoneSystem.instance.IsActiveAreaLoaded()) { return false; } Vector3 referencePosition = ZNet.instance.GetReferencePosition(); PrepareForConsume(Near, referencePosition, sorted: true); int num = Mathf.Max(Near.Pending / ((SpawnQueueCachePatch.BurstDivisor != null) ? SpawnQueueCachePatch.BurstDivisor.Value : 100), maxCreatedPerFrame); AdvanceHead(Near); for (int i = Near.m_head; i < Near.m_entries.Count; i++) { ZDO val = Near.m_entries[i]; if (val == null) { continue; } if (val.m_uid != Near.m_ids[i] || val.Created) { Near.Tombstone(i); } else if (!ZoneSystem.instance.IsZoneReadyForType(val.GetSector(), val.Type)) { Near.Defer(i); } else if ((Object)(object)__instance.CreateObject(val) != (Object)null) { Near.Tombstone(i); created++; if (created > num) { break; } } else if (ZNet.instance.IsServer()) { val.SetOwner(ZDOMan.GetSessionID()); ZDOID uid = val.m_uid; ZLog.Log((object)("Destroyed invalid predab ZDO:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString())); ZDOMan.instance.DestroyZDO(val); } else { Near.Defer(i); } } return false; } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch("CreateDistantObjects")] private static bool CreateDistantObjectsPrefix(ZNetScene __instance, int maxCreatedPerFrame, ref int created) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) if (!Driving()) { return true; } PrepareForConsume(Distant, Vector3.zero, sorted: false); if (created > maxCreatedPerFrame) { return false; } AdvanceHead(Distant); for (int i = Distant.m_head; i < Distant.m_entries.Count; i++) { ZDO val = Distant.m_entries[i]; if (val == null) { continue; } if (val.m_uid != Distant.m_ids[i] || val.Created) { Distant.Tombstone(i); } else if ((Object)(object)__instance.CreateObject(val) != (Object)null) { Distant.Tombstone(i); created++; if (created > maxCreatedPerFrame) { break; } } else if (ZNet.instance.IsServer()) { val.SetOwner(ZDOMan.GetSessionID()); ZLog.Log((object)$"Destroyed invalid predab ZDO:{val.m_uid} prefab hash:{val.GetPrefab()}"); ZDOMan.instance.DestroyZDO(val); } else { Distant.Defer(i); } } return false; } private static void AdvanceHead(SpawnQueue queue) { while (queue.m_head < queue.m_entries.Count && queue.m_entries[queue.m_head] == null) { queue.m_head++; } } private static void PrepareForConsume(SpawnQueue queue, Vector3 referencePosition, bool sorted) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: 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_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0158: 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) if (++queue.m_passesSinceSplice >= 3) { queue.m_passesSinceSplice = 0; queue.SpliceDeferred(); } bool flag = sorted && (!_sortPositionValid || Utils.DistanceSqr(_lastSortPosition, referencePosition) >= 64f); if (!sorted) { if (queue.m_tombstones > queue.m_entries.Count / 4) { queue.Compact(); } return; } bool flag2 = queue.m_appendedSinceSort > Mathf.Max(32, queue.m_entries.Count / 8); if (!queue.m_sortDirty && !flag && !flag2) { if (queue.m_tombstones > queue.m_entries.Count / 4) { queue.Compact(); } return; } queue.Compact(); for (int i = 0; i < queue.m_entries.Count; i++) { ZDO val = queue.m_entries[i]; val.m_tempSortValue = Utils.DistanceSqr(referencePosition, val.GetPosition()); } queue.m_entries.Sort((Comparison)ZNetScene.ZDOCompare); queue.m_ids.Clear(); for (int j = 0; j < queue.m_entries.Count; j++) { queue.m_ids.Add(queue.m_entries[j].m_uid); } queue.ReindexFromEntries(); queue.m_sortDirty = false; queue.m_appendedSinceSort = 0; _lastSortPosition = referencePosition; _sortPositionValid = true; } private static void SyncZoneSets(Vector2s zone, SimulationDistance simulationDistance) { //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_001c: 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_0022: 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_0028: 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_000f: 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) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) if (zone == _snapshotZone && ((SimulationDistance)(ref simulationDistance)).Equals(_snapshotSimulationDistance)) { return; } _snapshotZone = zone; _snapshotSimulationDistance = simulationDistance; BuildZoneSets(zone, simulationDistance, ScratchNearZones, ScratchDistantZones); foreach (Vector2s scratchNearZone in ScratchNearZones) { if (!NearZones.Contains(scratchNearZone)) { EnqueueSector(scratchNearZone, Near, distantOnly: false); } } foreach (Vector2s nearZone in NearZones) { if (!ScratchNearZones.Contains(nearZone)) { DequeueSector(nearZone, Near); } } foreach (Vector2s scratchDistantZone in ScratchDistantZones) { if (!DistantZones.Contains(scratchDistantZone)) { EnqueueSector(scratchDistantZone, Distant, distantOnly: true); } } foreach (Vector2s distantZone in DistantZones) { if (!ScratchDistantZones.Contains(distantZone)) { DequeueSector(distantZone, Distant); } } NearZones.Clear(); foreach (Vector2s scratchNearZone2 in ScratchNearZones) { NearZones.Add(scratchNearZone2); } DistantZones.Clear(); foreach (Vector2s scratchDistantZone2 in ScratchDistantZones) { DistantZones.Add(scratchDistantZone2); } } private static void BuildZoneSets(Vector2s center, SimulationDistance simulationDistance, HashSet nearZones, HashSet distantZones) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) nearZones.Clear(); distantZones.Clear(); int nearSimulationDistance = ((SimulationDistance)(ref simulationDistance)).NearSimulationDistance; int totalSimulationDistance = ((SimulationDistance)(ref simulationDistance)).TotalSimulationDistance; bool isClassic = ((SimulationDistance)(ref simulationDistance)).IsClassic; float num = ZoneSystem.instance.m_zoneSize / 64f; float num2 = ((float)nearSimulationDistance + 0.5f) * num; float num3 = ((float)totalSimulationDistance + 0.8f) * num; float num4 = num2 * num2; float num5 = num3 * num3; for (int i = center.x - totalSimulationDistance; i <= center.x + totalSimulationDistance; i++) { for (int j = center.y - totalSimulationDistance; j <= center.y + totalSimulationDistance; j++) { int num6 = i - center.x; int num7 = j - center.y; if (num6 < 0) { num6 = -num6; } if (num7 < 0) { num7 = -num7; } int num8 = ((num6 > num7) ? num6 : num7); int num9 = num6 * num6 + num7 * num7; if (isClassic ? (num8 <= nearSimulationDistance) : ((float)num9 < num4)) { nearZones.Add(new Vector2s(i, j)); } else if (isClassic || (float)num9 < num5) { distantZones.Add(new Vector2s(i, j)); } } } } private static void EnqueueSector(Vector2s sector, SpawnQueue queue, bool distantOnly) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) ScratchSector.Clear(); ScratchVisited.Clear(); if (distantOnly) { ZDOMan.instance.FindDistantObjects(sector, ScratchSector, ScratchVisited); } else { ZDOMan.instance.FindObjects(sector, ScratchSector, ScratchVisited); } for (int i = 0; i < ScratchSector.Count; i++) { ZDO val = ScratchSector[i]; if (!val.Created) { queue.Enqueue(val); } } ScratchSector.Clear(); } private static void DequeueSector(Vector2s sector, SpawnQueue queue) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) ScratchSector.Clear(); ScratchVisited.Clear(); ZDOMan.instance.FindObjects(sector, ScratchSector, ScratchVisited); for (int i = 0; i < ScratchSector.Count; i++) { queue.Dequeue(ScratchSector[i].m_uid); } ScratchSector.Clear(); } private static void EnqueueIfInRing(ZDO zdo, Vector2s sector) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (NearZones.Contains(sector)) { Near.Enqueue(zdo); } else if (zdo.Distant && DistantZones.Contains(sector)) { Distant.Enqueue(zdo); } } private static bool Driving() { if (Verify == null || !Verify.Value) { return Engaged(); } return false; } private static bool Engaged() { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || ZDOMan.instance == null) { return false; } if (!Hooks.Healthy) { return false; } if (!SectorInstanceIndexPatch.MaintenanceHealthy) { return false; } if (ZoneDiffRemovalPatch.Verify != null && ZoneDiffRemovalPatch.Verify.Value) { return false; } return true; } private static void ResetSession(ZNetScene scene) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) Near.Clear(); Distant.Clear(); NearZones.Clear(); DistantZones.Clear(); PendingRpc.Clear(); _inZdoData = false; _sortPositionValid = false; _snapshotZone = NoZone; _snapshotSimulationDistance = default(SimulationDistance); _snapshotScene = scene; } private static void RunVerify(Vector2s zone, SimulationDistance simulationDistance) { //IL_002c: 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) _verifyActive = true; _verifyPasses++; VerifyNear.Clear(); VerifyDistant.Clear(); ZDOMan.instance.FindSectorObjects(zone, simulationDistance, VerifyNear, VerifyDistant); _verifyQueued += Near.Pending + Distant.Pending; CompareSide(VerifyNear, Near, "near"); CompareSide(VerifyDistant, Distant, "distant"); if (++_passesSinceReport >= 900) { _passesSinceReport = 0; LogVerifySummary("periodic"); } } private static void CompareSide(List vanilla, SpawnQueue queue, string side) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < vanilla.Count; i++) { ZDO val = vanilla[i]; if (!val.Created) { _verifyExpected++; if (!queue.m_index.ContainsKey(val.m_uid)) { _verifyMissing++; Logger.LogError($"Spawn queue verify: MISSING from the {side} queue - ZDO {val.m_uid} " + $"(prefab {val.GetPrefab()}, sector {val.GetSector()}) is an uncreated candidate " + "vanilla would spawn. A feed is missing. Please report this - leave 'Fix Object Stream Rescan' off until it is understood."); } } } } private static void FinishVerify() { if (_verifyActive) { _verifyActive = false; LogVerifySummary("final"); _verifyPasses = 0L; _verifyQueued = 0L; _verifyExpected = 0L; _verifyMissing = 0L; _passesSinceReport = 0; } } private static void LogVerifySummary(string kind) { Logger.LogInfo($"Spawn queue verify ({kind}): {_verifyPasses} pass(es), " + $"{_verifyExpected} vanilla candidate(s) seen against " + $"{_verifyQueued} queued, {_verifyMissing} missing."); } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZNetScene))] internal static class SpawnQueueCachePatch { [HarmonyPatch(typeof(ZNetScene), "Shutdown")] internal static class ShutdownHook { [HarmonyPostfix] private static void Postfix() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) CachedIds.Clear(); _cursor = 0; _passesSinceRebuild = int.MaxValue; _rebuildZone = NoZone; } } internal static ConfigEntry BurstDivisor; private const int RebuildInterval = 3; private static readonly Vector2s NoZone = new Vector2s(short.MinValue, short.MinValue); private static int _passesSinceRebuild = int.MaxValue; private static int _cursor; private static Vector2s _rebuildZone = NoZone; private static readonly List CachedIds = new List(); internal static void BindConfig() { BurstDivisor = ValConfig.BindServerConfig("Fixes - Performance", "Spawn Burst Divisor", 100, "The per-frame object spawn budget is the spawn backlog divided by this (minimum 10 for near objects, exactly like vanilla). 100 is vanilla's hardcoded value. Higher spawns fewer objects per frame when entering a built-up area - smaller frame hits, slower pop-in.", advanced: true, 10, 2000); } [HarmonyPrefix] [HarmonyPatch("CreateObjectsSorted")] private unsafe static bool CreateObjectsSortedPrefix(ZNetScene __instance, List currentNearObjects, int maxCreatedPerFrame, ref int created) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) if (!ZoneSystem.instance.IsActiveAreaLoaded()) { return false; } List tempCurrentObjects = __instance.m_tempCurrentObjects2; Vector3 referencePosition = ZNet.instance.GetReferencePosition(); Vector2s zone = ZoneSystem.GetZone(referencePosition); if (_passesSinceRebuild >= 2 || zone != _rebuildZone || tempCurrentObjects.Count != CachedIds.Count) { _passesSinceRebuild = 0; _rebuildZone = zone; _cursor = 0; tempCurrentObjects.Clear(); for (int i = 0; i < currentNearObjects.Count; i++) { ZDO val = currentNearObjects[i]; if (!val.Created) { val.m_tempSortValue = Utils.DistanceSqr(referencePosition, val.GetPosition()); tempCurrentObjects.Add(val); } } tempCurrentObjects.Sort((Comparison)ZNetScene.ZDOCompare); CachedIds.Clear(); for (int j = 0; j < tempCurrentObjects.Count; j++) { CachedIds.Add(tempCurrentObjects[j].m_uid); } } else { _passesSinceRebuild++; } int num = tempCurrentObjects.Count - _cursor; if (num <= 0) { return false; } int num2 = Mathf.Max(num / ((BurstDivisor != null) ? BurstDivisor.Value : 100), maxCreatedPerFrame); while (_cursor < tempCurrentObjects.Count) { ZDO val2 = tempCurrentObjects[_cursor]; ZDOID val3 = CachedIds[_cursor]; _cursor++; if (val2.m_uid != val3 || val2.Created || !ZoneSystem.instance.IsZoneReadyForType(val2.GetSector(), val2.Type)) { continue; } if ((Object)(object)__instance.CreateObject(val2) != (Object)null) { created++; if (created > num2) { break; } } else if (ZNet.instance.IsServer()) { val2.SetOwner(ZDOMan.GetSessionID()); ZDOID uid = val2.m_uid; ZLog.Log((object)("Destroyed invalid predab ZDO:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString())); ZDOMan.instance.DestroyZDO(val2); } } return false; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(StaticPhysics))] internal static class StaticPhysicsCachePatch { internal static ConfigEntry UseHeightmapData; internal static void BindConfig() { UseHeightmapData = ValConfig.BindServerConfig("Fixes - Performance", "Static Ground Checks Use Heightmap Data", value: false, "Answers static objects' terrain-height checks from heightmap data instead of a physics raycast. Evaluates the same surface the ray would hit, without the physics engine. Off by default for one release while it soaks.", null, advanced: true); } [HarmonyPrefix] [HarmonyPatch("SUpdate")] private static bool SUpdatePrefix(StaticPhysics __instance, float time, Vector2s referenceZone) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_0036: 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) if (__instance.m_falling || time <= __instance.m_updateTime) { return false; } Transform transform = ((Component)__instance).transform; Vector3 position = transform.position; if (ZNetScene.OutsideActiveArea(position, referenceZone)) { return false; } if (__instance.m_fall) { CheckFall(__instance, transform, position); } if (__instance.m_pushUp) { PushUp(__instance, transform, position); } return false; } private static void CheckFall(StaticPhysics sp, Transform transform, Vector3 position) { //IL_0000: 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 (!(position.y <= GetFallHeight(sp, transform, position) + 0.05f)) { sp.Fall(); } } private static float GetFallHeight(StaticPhysics sp, Transform transform, Vector3 position) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) float height = default(float); if (sp.m_checkSolids) { if (!ZoneSystem.instance.GetSolidHeight(position, sp.m_fallCheckRadius, ref height, transform)) { return position.y; } return height; } if (!GroundHeight(position, out height)) { return position.y; } return height; } private static void PushUp(StaticPhysics sp, Transform transform, Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0062: Unknown result type (might be due to invalid IL or missing references) if (GroundHeight(position, out var height) && !(position.y >= height - 0.05f)) { GameObject gameObject = ((Component)sp).gameObject; gameObject.isStatic = false; position.y = height; transform.position = position; gameObject.isStatic = true; ZNetView nview = sp.m_nview; if (Object.op_Implicit((Object)(object)nview) && nview.IsValid() && nview.IsOwner()) { nview.GetZDO().SetPosition(position); } } } private static bool GroundHeight(Vector3 position, out float height) { //IL_0018: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (UseHeightmapData == null || !UseHeightmapData.Value) { return ZoneSystem.instance.GetGroundHeight(position, ref height); } if (!HeightmapLookupPatch.TryGetCached(position, out var hmap, out var origin)) { hmap = Heightmap.FindHeightmap(position); origin = (((Object)(object)hmap != (Object)null) ? ((Component)hmap).transform.position : Vector3.zero); } if ((Object)(object)hmap == (Object)null) { height = 0f; return false; } return HeightmapSampling.TryGetHeight(hmap, origin, position, out height); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(CraftingStation))] internal static class StationRangeQueryPatch { [HarmonyPatch(typeof(StationExtension))] internal static class ExtensionHooks { [HarmonyPrefix] [HarmonyPatch("OtherExtensionInRange")] private static bool OtherExtensionInRangePrefix(StationExtension __instance, float radius, ref bool __result) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_003b: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)__instance).transform.position; double num = (double)radius * (double)radius; List allExtensions = StationExtension.m_allExtensions; for (int i = 0; i < allExtensions.Count; i++) { StationExtension val = allExtensions[i]; if (!((Object)(object)val == (Object)(object)__instance)) { Vector3 position2 = ((Component)val).transform.position; float num2 = position2.x - position.x; float num3 = position2.y - position.y; float num4 = position2.z - position.z; if ((double)(num2 * num2 + num3 * num3 + num4 * num4) < num) { __result = true; return false; } } } __result = false; return false; } } [HarmonyPrefix] [HarmonyPatch("HaveBuildStationInRange")] private static bool HaveBuildStationInRangePrefix(string name, Vector3 point, ref CraftingStation __result) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) List allStations = CraftingStation.m_allStations; for (int i = 0; i < allStations.Count; i++) { CraftingStation val = allStations[i]; if (!(val.m_name != name)) { float stationBuildRange = val.GetStationBuildRange(); Vector3 position = ((Component)val).transform.position; float num = position.x - point.x; float num2 = position.z - point.z; if ((double)(num * num + num2 * num2) < (double)stationBuildRange * (double)stationBuildRange) { __result = val; return false; } } } __result = null; return false; } [HarmonyPrefix] [HarmonyPatch("FindClosestStationInRange")] private static bool FindClosestStationInRangePrefix(string name, Vector3 point, float range, ref CraftingStation __result) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) CraftingStation val = null; double num = (double)range * (double)range; double num2 = 9999800001.0; List allStations = CraftingStation.m_allStations; for (int i = 0; i < allStations.Count; i++) { CraftingStation val2 = allStations[i]; if (!(val2.m_name != name)) { Vector3 position = ((Component)val2).transform.position; float num3 = position.x - point.x; float num4 = position.y - point.y; float num5 = position.z - point.z; double num6 = num3 * num3 + num4 * num4 + num5 * num5; if (num6 < num && (num6 < num2 || (Object)(object)val == (Object)null)) { val = val2; num2 = num6; } } } __result = val; return false; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(WearNTear))] internal static class SupportSleepPatch { internal sealed class PieceState { public bool m_computed; public bool m_dirty; public int m_skips; public bool m_hasRealSupport; public float m_realSupport; public int m_quietRuns; public bool m_strongWake; public Cell[] m_cells; public bool m_wetSleepable; public bool m_wearWake; public bool m_geoCached; public float m_x; public float m_y; public float m_z; } private struct Envelope { public PieceState m_state; public int m_x0; public int m_z0; public int m_x1; public int m_z1; public float m_minX; public float m_minZ; public float m_maxX; public float m_maxZ; public float m_minY; public float m_maxY; } internal struct GridEntry { public PieceState m_state; public float m_minX; public float m_minZ; public float m_maxX; public float m_maxZ; public float m_minY; public float m_maxY; } internal sealed class Cell { public GridEntry[] m_entries = new GridEntry[4]; public int m_count; public int m_clean; public void Add(GridEntry entry) { if (m_count == m_entries.Length) { Array.Resize(ref m_entries, m_count * 2); } m_entries[m_count++] = entry; } public void RemoveAt(int index) { m_entries[index] = m_entries[--m_count]; m_entries[m_count] = default(GridEntry); } } private struct WakeBox { public float m_minX; public float m_minZ; public float m_maxX; public float m_maxZ; public float m_minY; public float m_maxY; public int m_x0; public int m_z0; public int m_x1; public int m_z1; } internal struct Snapshot { public PieceState m_state; public float m_prevSupport; public bool m_skipped; public bool m_predictedSkip; } [HarmonyPatch(typeof(WearNTearUpdater))] internal static class UpdaterHook { [HarmonyPrefix] [HarmonyPatch("Update")] private static void UpdatePrefix() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) FlushDestroyWakes(); SampleWakeStats(); bool flag = EnvMan.IsWet(); if (flag != _pendingWet) { _pendingWet = flag; _pendingSince = Time.time; } if (_pendingWet != _worldWet && Time.time - _pendingSince >= 5f) { _worldWet = _pendingWet; } int frameCount = Time.frameCount; if (frameCount != _centerFrame && (Object)(object)ZNet.instance != (Object)null && (Object)(object)ZoneSystem.instance != (Object)null) { _centerFrame = frameCount; _centerZonePos = ZoneSystem.GetZonePos(ZoneSystem.GetZone(ZNet.instance.GetReferencePosition())); SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance(); float zoneSize = ZoneSystem.instance.m_zoneSize; _activeAreaChebyshev = ((((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance == 1) ? 1f : 1.5f) * zoneSize; if (((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance == 2 && !((SimulationDistance)(ref syncedSimulationDistance)).IsClassic) { float num = zoneSize * 1.75f; _activeAreaRadiusSq = num * num; } else { _activeAreaRadiusSq = -1f; } } } } internal struct WearSnapshot { public PieceState m_state; public float m_prevSupport; public float m_prevHealthPct; public bool m_prevRainWet; public bool m_skipped; public bool m_predictedSkip; } [HarmonyPatch(typeof(ZNetScene), "Shutdown")] internal static class ShutdownHook { [HarmonyPostfix] private static void Postfix() { States.Clear(); Grid.Clear(); Registered.Clear(); PendingWakes.Clear(); WakeCells.Clear(); WakeCellPool.Clear(); } } internal static ConfigEntry Verify; internal static ConfigEntry WearVerify; internal static ConfigEntry WakeEpsilon; internal static ConfigEntry QuietBackoff; internal static ConfigEntry WakeStats; private const int MaxSkipStreak = 9; private const int QuietRunsCap = 16; private const float CellSize = 8f; private const float FallbackWakeRadius = 5f; private static readonly Dictionary States = new Dictionary(); private static readonly Dictionary Grid = new Dictionary(); private static readonly Dictionary Registered = new Dictionary(); private static readonly List PendingWakes = new List(); private static readonly Dictionary> WakeCells = new Dictionary>(); private static readonly Stack> WakeCellPool = new Stack>(); private static readonly HookHealth Hooks = new HookHealth("Support sleep", () => HasOwnHook("ClearCachedSupport") && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(WearNTear), "OnDestroy", (Type[])null, (Type[])null), typeof(TeardownHooks.PieceHook)) && HasOwnHook("SetupColliders") && HasOwnHook("UpdateSupport") && HasOwnHook("Awake") && HasOwnHook("UpdateWear") && HasOwnHook("ApplyDamage") && HasOwnHook("RPC_Repair") && HasOwnHook("UpdateCover") && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(WearNTearUpdater), "Update", (Type[])null, (Type[])null), typeof(UpdaterHook))); private static bool _statsOn; private static float _statsSince; private static long _statVisits; private static long _statFirstCompute; private static long _statIdentical; private static long _statRelTenth; private static long _statRelOne; private static long _statRelTen; private static long _statRelHuge; private static long _statWaves; private static long _wakeCandidates; private static long _wakeWoken; private static long _wakeCellsSkipped; private static long _statOutOfAreaStamp; private static long _statLeftMax; private static long _statReachedMax; private static long _statWeakDeferred; private const int ProbeInterval = 32; private static readonly Collider[] ProbeBuffer = (Collider[])(object)new Collider[512]; private static int _probeCountdown; private static long _probeBoxes; private static long _probeSaturated; private static long _probeWorst; private const float WakeStatsIntervalSeconds = 30f; private const int VerifyReportInterval = 25000; private static bool _verifyActive; private static long _verifyEvaluated; private static long _verifyWouldSkip; private static long _verifyDivergences; private static int _evaluatedSinceReport; private static readonly string[] SupportBlockNames = new string[6] { "sleepable", "support-cold", "dirty-structural", "streak-cap", "unsupported", "dirty-unsettled" }; private static readonly long[] SupportBlockCounts = new long[6]; private static WearNTear _supportHandledFor; private const float NeverUnderwaterY = 35f; private const float WeatherDebounceSeconds = 5f; private static bool _worldWet = true; private static bool _pendingWet = true; private static float _pendingSince; private static int _centerFrame = -1; private static Vector3 _centerZonePos; private static float _activeAreaChebyshev; private static float _activeAreaRadiusSq; private static long _wearSkipped; private static long _wearVisits; private static long _wearWouldSkip; private static long _wearDivergences; private static int _wearSinceReport; private static bool _wearVerifyActive; private static readonly string[] WearBlockNames = new string[11] { "sleepable", "damage-wake", "wet-exposed", "support-cold", "geometry/waterline", "biome", "outside-ring", "not-owner", "support-dirty", "streak-cap", "unsupported" }; private static readonly long[] WearBlockCounts = new long[11]; internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Support Sleep", value: false, "Diagnostic. Runs the vanilla support check on every visit while predicting what 'Fix Idle Support Checks' would have skipped, and logs any visit where a predicted-quiet piece's support actually changed. Costs everything the fix saves, so leave it off unless you are validating the predictions.", null, advanced: true); WearVerify = ValConfig.BindServerConfig("Debug", "Verify Wear Sleep", value: false, "Diagnostic. Runs the vanilla wear visit on every piece while predicting what 'Fix Idle Wear Visits' would have skipped, and logs any visit where a predicted-quiet piece's support, health or wetness actually changed. Costs everything the fix saves, so leave it off unless you are validating the predictions.", null, advanced: true); WakeEpsilon = ValConfig.BindServerConfig("Fixes - Performance", "Support Change Threshold", 0.01f, "How much a piece's structural support must change before its neighbours are re-checked. Support propagates through a structure as a wave, and re-checking neighbours on every last-decimal drift keeps the whole structure awake; a difference this small never accumulates, because each hop can only shrink it. Support values run to a thousand, so the default is far below anything that affects whether a build stands. 0 restores the exact comparison.", advanced: true, 0f, 1f); QuietBackoff = ValConfig.BindServerConfig("Fixes - Performance", "Settled Piece Patience", 3, "How many times in a row a building piece must re-check its support and get the same answer before it is allowed to take a slower look when only a neighbour's value drifted. In a large base the re-check signal is almost always on, so without this the skip never happens; a piece that has proven it is not moving can afford to see a small neighbouring drift a little late. Anything structural - building, destroying, damage, repairs, terrain edits - is always immediate, and any real change resets the piece's patience to zero. 0 turns this off.", advanced: true, 0, 10); WakeStats = ValConfig.BindServerConfig("Debug", "Log Support Wake Stats", value: false, "Diagnostic. Periodically logs how the support wake traffic breaks down - first computations, how far recomputed values actually moved, how many neighbours each wake touched, and how often the game's own surroundings scan overflows its fixed buffer. Samples that last one on a fraction of checks because it costs a real physics query, so this is not free - but it is cheap enough to leave on while measuring, and it is how 'Support Change Threshold' gets sized.", null, advanced: true); } private static bool HasOwnHook(string wearNTearMethod) { return PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(WearNTear), wearNTearMethod, (Type[])null, (Type[])null), typeof(SupportSleepPatch)); } private static void SampleWakeStats() { bool flag = WakeStats != null && WakeStats.Value; if (flag != _statsOn) { _statsOn = flag; _statsSince = Time.time; ClearWakeStats(); if (!flag) { return; } } if (flag && !(Time.time - _statsSince < 30f)) { float num = Time.time - _statsSince; Logger.LogInfo($"Support wake stats ({num:F0}s): {_statVisits} recompute(s) - " + $"{_statFirstCompute} first, {_statIdentical} identical; changed by " + $"<=0.1% {_statRelTenth}, <=1% {_statRelOne}, <=10% {_statRelTen}, " + $">10% {_statRelHuge} of max support. {_statWaves} wave(s) fanned out over " + $"{_wakeCandidates} candidate(s), waking {_wakeWoken}; " + $"{_wakeCellsSkipped} fully-awake cell(s) skipped. " + $"Weak wakes deferred by settled pieces: {_statWeakDeferred}. " + $"Out-of-area max-support stamps {_statOutOfAreaStamp}; changes leaving max " + $"{_statLeftMax}, reaching max {_statReachedMax}. " + $"Overlap probe (1 in {32}): {_probeBoxes} box(es), {_probeSaturated} " + $"at or over the {WearNTear.s_tempColliders.Length} limit, worst {_probeWorst}."); _statsSince = Time.time; ClearWakeStats(); } } private static void ProbeOverlapLimit(WearNTear piece) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_002d: 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_0038: Unknown result type (might be due to invalid IL or missing references) List bounds = piece.m_bounds; if (bounds == null || WearNTear.s_rayMask == 0) { return; } int num = WearNTear.s_tempColliders.Length; for (int i = 0; i < bounds.Count; i++) { BoundData val = bounds[i]; int num2 = Physics.OverlapBoxNonAlloc(val.m_pos, val.m_size, ProbeBuffer, val.m_rot, WearNTear.s_rayMask); _probeBoxes++; if (num2 >= num) { _probeSaturated++; } if (num2 > _probeWorst) { _probeWorst = num2; } } } private static void ClearWakeStats() { _statVisits = 0L; _statFirstCompute = 0L; _statIdentical = 0L; _statRelTenth = 0L; _statRelOne = 0L; _statRelTen = 0L; _statRelHuge = 0L; _statWaves = 0L; _wakeCandidates = 0L; _wakeWoken = 0L; _wakeCellsSkipped = 0L; _statOutOfAreaStamp = 0L; _statWeakDeferred = 0L; _statLeftMax = 0L; _statReachedMax = 0L; _probeBoxes = 0L; _probeSaturated = 0L; _probeWorst = 0L; } private static void SetDirty(PieceState state, bool dirty, bool strong = false) { if (dirty && strong) { state.m_strongWake = true; } if (state.m_dirty == dirty) { return; } state.m_dirty = dirty; Cell[] cells = state.m_cells; if (cells != null) { int num = ((!dirty) ? 1 : (-1)); for (int i = 0; i < cells.Length; i++) { cells[i].m_clean += num; } } } private static PieceState GetState(WearNTear piece) { int instanceID = ((Object)piece).GetInstanceID(); if (!States.TryGetValue(instanceID, out var value)) { value = new PieceState(); States.Add(instanceID, value); } return value; } private static int SupportBlockReason(WearNTear piece, PieceState state) { if (!state.m_computed) { return 1; } if (state.m_dirty && !MayDeferWeakWake(state)) { if (!state.m_strongWake) { return 5; } return 2; } if (state.m_skips >= 9) { return 3; } if (piece.m_support < piece.GetMinSupport()) { return 4; } return 0; } private static bool MayDeferWeakWake(PieceState state) { if (state.m_strongWake) { return false; } int num = ((QuietBackoff != null) ? QuietBackoff.Value : 0); if (num > 0) { return state.m_quietRuns >= num; } return false; } [HarmonyPrefix] [HarmonyPatch("UpdateSupport")] private static bool UpdateSupportPrefix(WearNTear __instance, out Snapshot __state) { __state = default(Snapshot); FlushDestroyWakes(); if (!Hooks.Healthy) { return true; } PieceState pieceState = (__state.m_state = GetState(__instance)); __state.m_prevSupport = __instance.m_support; float prevSupport = default(float); if (!pieceState.m_computed && (Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid() && __instance.m_nview.GetZDO().GetFloat(ZDOVars.s_support, ref prevSupport)) { __state.m_prevSupport = prevSupport; } int num = SupportBlockReason(__instance, pieceState); bool flag = num == 0; if (Verify != null && Verify.Value) { _verifyActive = true; _verifyEvaluated++; SupportBlockCounts[num]++; if (flag) { _verifyWouldSkip++; } __state.m_predictedSkip = flag; if (++_evaluatedSinceReport >= 25000) { _evaluatedSinceReport = 0; LogVerifySummary("periodic"); } return true; } if (_verifyActive) { _verifyActive = false; LogVerifySummary("final"); _verifyEvaluated = 0L; _verifyWouldSkip = 0L; _verifyDivergences = 0L; _evaluatedSinceReport = 0; _wearSkipped = 0L; Array.Clear(SupportBlockCounts, 0, SupportBlockCounts.Length); } if (flag) { pieceState.m_skips++; __state.m_skipped = true; if (_statsOn && pieceState.m_dirty) { _statWeakDeferred++; } return false; } return true; } [HarmonyPostfix] [HarmonyPatch("UpdateSupport")] private static void UpdateSupportPostfix(WearNTear __instance, Snapshot __state) { PieceState state = __state.m_state; if (state == null || __state.m_skipped) { return; } _supportHandledFor = __instance; float num = __instance.m_support - __state.m_prevSupport; if (num < 0f) { num = 0f - num; } float num2 = ((WakeEpsilon != null) ? WakeEpsilon.Value : 0f); bool flag = ((num2 > 0f) ? (num > num2) : (num != 0f)); if (_statsOn) { _statVisits++; if (!state.m_computed) { _statFirstCompute++; } else if (num == 0f) { _statIdentical++; } else { float maxSupport = __instance.GetMaxSupport(); float num3 = ((maxSupport > 0f) ? (num / maxSupport) : 1f); if (num3 <= 0.001f) { _statRelTenth++; } else if (num3 <= 0.01f) { _statRelOne++; } else if (num3 <= 0.1f) { _statRelTen++; } else { _statRelHuge++; } if (__state.m_prevSupport.Equals(maxSupport)) { _statLeftMax++; } if (__instance.m_support.Equals(maxSupport)) { _statReachedMax++; } } if (--_probeCountdown <= 0) { _probeCountdown = 32; ProbeOverlapLimit(__instance); } } if (__state.m_predictedSkip && flag) { _verifyDivergences++; Logger.LogError("Support sleep verify: DIVERGED on '" + ((Object)__instance).name + "' - predicted quiet, " + $"but support changed {__state.m_prevSupport} -> {__instance.m_support}. " + "A wake signal is missing. Please report this - leave 'Fix Idle Support Checks' off until it is understood."); } state.m_computed = true; state.m_realSupport = __instance.m_support; state.m_hasRealSupport = true; SetDirty(state, dirty: false); state.m_strongWake = false; state.m_skips = 0; if (flag) { state.m_quietRuns = 0; } else if (state.m_quietRuns < 16) { state.m_quietRuns++; } if (flag) { if (_statsOn) { _statWaves++; } DirtyNeighbours(__instance, state, strong: false); } } [HarmonyPostfix] [HarmonyPatch("ClearCachedSupport")] private static void ClearCachedSupportPostfix(WearNTear __instance) { if (States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { SetDirty(value, dirty: true, strong: true); } } private static int WearBlockReason(WearNTear piece, PieceState state) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Invalid comparison between Unknown and I4 if (state.m_wearWake) { return 1; } if (_worldWet && !state.m_wetSleepable) { return 2; } if (state.m_skips >= 9) { return 9; } if (piece.m_noSupportWear) { if (!state.m_computed) { return 3; } if (state.m_dirty && !MayDeferWeakWake(state)) { return 8; } if (piece.m_support < piece.GetMinSupport()) { return 10; } } if (!state.m_geoCached || state.m_y <= 35f) { return 4; } if ((int)piece.m_biome == 0 || (int)piece.m_biome == 32 || piece.m_inAshlands) { return 5; } float num = state.m_x - _centerZonePos.x; float num2 = state.m_z - _centerZonePos.z; if (num < 0f) { num = 0f - num; } if (num2 < 0f) { num2 = 0f - num2; } if (((num > num2) ? num : num2) > _activeAreaChebyshev) { return 6; } if (_activeAreaRadiusSq >= 0f && num * num + num2 * num2 >= _activeAreaRadiusSq) { return 6; } if (!piece.m_nview.IsValid() || !piece.m_nview.IsOwner()) { return 7; } return 0; } [HarmonyPrefix] [HarmonyPatch("UpdateWear")] private static bool UpdateWearPrefix(WearNTear __instance, out WearSnapshot __state) { __state = default(WearSnapshot); __state.m_prevSupport = __instance.m_support; _supportHandledFor = null; FlushDestroyWakes(); if (!Hooks.Healthy) { return true; } PieceState pieceState = (__state.m_state = GetState(__instance)); int num = WearBlockReason(__instance, pieceState); bool flag = num == 0; if (WearVerify != null && WearVerify.Value) { _wearVerifyActive = true; _wearVisits++; WearBlockCounts[num]++; if (flag) { _wearWouldSkip++; } __state.m_predictedSkip = flag; __state.m_prevHealthPct = __instance.m_healthPercentage; __state.m_prevRainWet = __instance.m_rainWet; if (++_wearSinceReport >= 25000) { _wearSinceReport = 0; LogWearVerifySummary("periodic"); } return true; } if (_wearVerifyActive) { _wearVerifyActive = false; LogWearVerifySummary("final"); _wearVisits = 0L; _wearWouldSkip = 0L; _wearDivergences = 0L; _wearSinceReport = 0; Array.Clear(WearBlockCounts, 0, WearBlockCounts.Length); } if (flag) { pieceState.m_skips++; __state.m_skipped = true; _wearSkipped++; return false; } return true; } [HarmonyPostfix] [HarmonyPatch("UpdateWear")] private static void UpdateWearPostfix(WearNTear __instance, WearSnapshot __state) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) if (__state.m_skipped) { return; } PieceState state = __state.m_state; if (state != null) { if (__state.m_predictedSkip && (!__instance.m_support.Equals(__state.m_prevSupport) || !__instance.m_healthPercentage.Equals(__state.m_prevHealthPct) || __instance.m_rainWet != __state.m_prevRainWet)) { _wearDivergences++; Logger.LogError("Wear sleep verify: DIVERGED on '" + ((Object)__instance).name + "' - predicted quiet, but the visit changed support, health or wetness. A wake signal is missing. Please report this - leave 'Fix Idle Wear Visits' off until it is understood."); } state.m_wetSleepable = __instance.m_haveRoof && !__instance.m_rainWet; state.m_wearWake = false; if (!__instance.m_noSupportWear) { state.m_skips = 0; } if (!state.m_geoCached && (Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()) { Vector3 position = ((Component)__instance).transform.position; state.m_x = position.x; state.m_y = position.y; state.m_z = position.z; state.m_geoCached = true; } } if (_supportHandledFor == __instance) { return; } States.TryGetValue(((Object)__instance).GetInstanceID(), out var value); RepairStampedSupport(__instance, value); if (!__instance.m_support.Equals(__state.m_prevSupport)) { if (_statsOn) { _statOutOfAreaStamp++; } PieceState state2 = GetState(__instance); SetDirty(state2, dirty: true, strong: true); DirtyNeighbours(__instance, state2, strong: true); } } private static void RepairStampedSupport(WearNTear piece, PieceState state) { if (state == null || !state.m_hasRealSupport || (Object)(object)piece.m_nview == (Object)null || !piece.m_nview.IsValid()) { return; } float maxSupport = piece.GetMaxSupport(); if (!state.m_realSupport.Equals(maxSupport)) { ZDO zDO = piece.m_nview.GetZDO(); float num = default(float); if (zDO.GetFloat(ZDOVars.s_support, ref num) && num.Equals(maxSupport)) { zDO.Set(ZDOVars.s_support, state.m_realSupport); } } } [HarmonyPrefix] [HarmonyPatch("UpdateCover")] private static void UpdateCoverPrefix(WearNTear __instance, out bool __state) { __state = __instance.m_haveRoof; } [HarmonyPostfix] [HarmonyPatch("UpdateCover")] private static void UpdateCoverPostfix(WearNTear __instance, bool __state) { if (__instance.m_haveRoof != __state && States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { value.m_wearWake = true; } } [HarmonyPostfix] [HarmonyPatch("ApplyDamage")] private static void ApplyDamagePostfix(WearNTear __instance) { if (States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { value.m_wearWake = true; } } [HarmonyPostfix] [HarmonyPatch("RPC_Repair")] private static void RPC_RepairPostfix(WearNTear __instance) { if (States.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { value.m_wearWake = true; } } [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(WearNTear __instance) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) float support = default(float); if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid() && __instance.m_nview.GetZDO().GetFloat(ZDOVars.s_support, ref support)) { __instance.m_support = support; } States.TryGetValue(((Object)__instance).GetInstanceID(), out var value); Vector3 position = ((Component)__instance).transform.position; WakeOverlapping(position.x - 5f, position.z - 5f, position.x + 5f, position.z + 5f, position.y - 5f, position.y + 5f, value, strong: false); } private static long CellKey(int x, int z) { return ((long)x << 32) | (uint)z; } [HarmonyPostfix] [HarmonyPatch("SetupColliders")] private static void SetupCollidersPostfix(WearNTear __instance) { //IL_0050: 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) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: 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_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: 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_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) List bounds = __instance.m_bounds; if (bounds == null || bounds.Count == 0) { return; } Unregister(((Object)__instance).GetInstanceID()); float num = float.MaxValue; float num2 = float.MaxValue; float num3 = float.MaxValue; float num4 = float.MinValue; float num5 = float.MinValue; float num6 = float.MinValue; for (int i = 0; i < bounds.Count; i++) { BoundData val = bounds[i]; Matrix4x4 val2 = Matrix4x4.Rotate(val.m_rot); Vector3 size = val.m_size; float num7 = Mathf.Abs(val2.m00) * size.x + Mathf.Abs(val2.m01) * size.y + Mathf.Abs(val2.m02) * size.z; float num8 = Mathf.Abs(val2.m10) * size.x + Mathf.Abs(val2.m11) * size.y + Mathf.Abs(val2.m12) * size.z; float num9 = Mathf.Abs(val2.m20) * size.x + Mathf.Abs(val2.m21) * size.y + Mathf.Abs(val2.m22) * size.z; if (val.m_pos.x - num7 < num) { num = val.m_pos.x - num7; } if (val.m_pos.z - num9 < num2) { num2 = val.m_pos.z - num9; } if (val.m_pos.y - num8 < num3) { num3 = val.m_pos.y - num8; } if (val.m_pos.x + num7 > num4) { num4 = val.m_pos.x + num7; } if (val.m_pos.z + num9 > num5) { num5 = val.m_pos.z + num9; } if (val.m_pos.y + num8 > num6) { num6 = val.m_pos.y + num8; } } Envelope value = new Envelope { m_state = GetState(__instance), m_x0 = Mathf.FloorToInt(num / 8f), m_z0 = Mathf.FloorToInt(num2 / 8f), m_x1 = Mathf.FloorToInt(num4 / 8f), m_z1 = Mathf.FloorToInt(num5 / 8f), m_minX = num, m_minZ = num2, m_maxX = num4, m_maxZ = num5, m_minY = num3, m_maxY = num6 }; GridEntry entry = new GridEntry { m_state = value.m_state, m_minX = num, m_minZ = num2, m_maxX = num4, m_maxZ = num5, m_minY = num3, m_maxY = num6 }; PieceState state = value.m_state; Cell[] array = new Cell[(value.m_x1 - value.m_x0 + 1) * (value.m_z1 - value.m_z0 + 1)]; int num10 = 0; for (int j = value.m_x0; j <= value.m_x1; j++) { for (int k = value.m_z0; k <= value.m_z1; k++) { long key = CellKey(j, k); if (!Grid.TryGetValue(key, out var value2)) { value2 = new Cell(); Grid.Add(key, value2); } value2.Add(entry); if (!state.m_dirty) { value2.m_clean++; } array[num10++] = value2; } } state.m_cells = array; Registered[((Object)__instance).GetInstanceID()] = value; } private static void Unregister(int pieceId) { if (!Registered.TryGetValue(pieceId, out var value)) { return; } for (int i = value.m_x0; i <= value.m_x1; i++) { for (int j = value.m_z0; j <= value.m_z1; j++) { long key = CellKey(i, j); if (!Grid.TryGetValue(key, out var value2)) { continue; } for (int k = 0; k < value2.m_count; k++) { if (value2.m_entries[k].m_state == value.m_state) { value2.RemoveAt(k); if (!value.m_state.m_dirty) { value2.m_clean--; } break; } } if (value2.m_count == 0) { Grid.Remove(key); } } } value.m_state.m_cells = null; Registered.Remove(pieceId); } private static void DirtyNeighbours(WearNTear piece, PieceState state, bool strong) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (Registered.TryGetValue(((Object)piece).GetInstanceID(), out var value)) { WakeOverlapping(value.m_minX, value.m_minZ, value.m_maxX, value.m_maxZ, value.m_minY, value.m_maxY, state, strong); return; } Vector3 position = ((Component)piece).transform.position; WakeOverlapping(position.x - 5f, position.z - 5f, position.x + 5f, position.z + 5f, position.y - 5f, position.y + 5f, state, strong); } private static void WakeOverlapping(float minX, float minZ, float maxX, float maxZ, float minY, float maxY, PieceState exclude, bool strong) { int num = Mathf.FloorToInt(minX / 8f); int num2 = Mathf.FloorToInt(maxX / 8f); int num3 = Mathf.FloorToInt(minZ / 8f); int num4 = Mathf.FloorToInt(maxZ / 8f); for (int i = num; i <= num2; i++) { for (int j = num3; j <= num4; j++) { if (!Grid.TryGetValue(CellKey(i, j), out var value)) { continue; } if (value.m_clean == 0) { if (_statsOn) { _wakeCellsSkipped++; } continue; } GridEntry[] entries = value.m_entries; int count = value.m_count; if (_statsOn) { _wakeCandidates += count; } for (int k = 0; k < count; k++) { PieceState state = entries[k].m_state; if (!state.m_dirty && !(entries[k].m_minX > maxX) && !(entries[k].m_maxX < minX) && !(entries[k].m_minZ > maxZ) && !(entries[k].m_maxZ < minZ) && !(entries[k].m_minY > maxY) && !(entries[k].m_maxY < minY) && state != exclude) { SetDirty(state, dirty: true, strong); if (_statsOn) { _wakeWoken++; } } } } } } internal static void OnPieceDestroyed(WearNTear piece, int pieceId) { QueueDestroyWake(piece, pieceId); Unregister(pieceId); States.Remove(pieceId); } private static void QueueDestroyWake(WearNTear piece, int pieceId) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) float num; float num2; float num3; float num4; float minY; float maxY; if (Registered.TryGetValue(pieceId, out var value)) { num = value.m_minX; num2 = value.m_minZ; num3 = value.m_maxX; num4 = value.m_maxZ; minY = value.m_minY; maxY = value.m_maxY; } else { Vector3 position = ((Component)piece).transform.position; num = position.x - 5f; num2 = position.z - 5f; num3 = position.x + 5f; num4 = position.z + 5f; minY = position.y - 5f; maxY = position.y + 5f; } PendingWakes.Add(new WakeBox { m_minX = num, m_minZ = num2, m_maxX = num3, m_maxZ = num4, m_minY = minY, m_maxY = maxY, m_x0 = Mathf.FloorToInt(num / 8f), m_x1 = Mathf.FloorToInt(num3 / 8f), m_z0 = Mathf.FloorToInt(num2 / 8f), m_z1 = Mathf.FloorToInt(num4 / 8f) }); } private static void FlushDestroyWakes() { if (PendingWakes.Count == 0) { return; } if (PendingWakes.Count == 1) { WakeBox wakeBox = PendingWakes[0]; WakeOverlapping(wakeBox.m_minX, wakeBox.m_minZ, wakeBox.m_maxX, wakeBox.m_maxZ, wakeBox.m_minY, wakeBox.m_maxY, null, strong: true); PendingWakes.Clear(); return; } for (int i = 0; i < PendingWakes.Count; i++) { WakeBox wakeBox2 = PendingWakes[i]; for (int j = wakeBox2.m_x0; j <= wakeBox2.m_x1; j++) { for (int k = wakeBox2.m_z0; k <= wakeBox2.m_z1; k++) { long key = CellKey(j, k); if (Grid.ContainsKey(key)) { if (!WakeCells.TryGetValue(key, out var value)) { value = ((WakeCellPool.Count > 0) ? WakeCellPool.Pop() : new List()); WakeCells.Add(key, value); } value.Add(i); } } } } foreach (KeyValuePair> wakeCell in WakeCells) { Cell cell = Grid[wakeCell.Key]; if (cell.m_clean == 0) { if (_statsOn) { _wakeCellsSkipped++; } continue; } GridEntry[] entries = cell.m_entries; int count = cell.m_count; List value2 = wakeCell.Value; if (_statsOn) { _wakeCandidates += count; } for (int l = 0; l < count; l++) { PieceState state = entries[l].m_state; if (state.m_dirty) { continue; } for (int m = 0; m < value2.Count; m++) { WakeBox wakeBox3 = PendingWakes[value2[m]]; if (!(entries[l].m_minX > wakeBox3.m_maxX) && !(entries[l].m_maxX < wakeBox3.m_minX) && !(entries[l].m_minZ > wakeBox3.m_maxZ) && !(entries[l].m_maxZ < wakeBox3.m_minZ) && !(entries[l].m_minY > wakeBox3.m_maxY) && !(entries[l].m_maxY < wakeBox3.m_minY)) { SetDirty(state, dirty: true, strong: true); if (_statsOn) { _wakeWoken++; } break; } } } value2.Clear(); WakeCellPool.Push(value2); } WakeCells.Clear(); PendingWakes.Clear(); } private static void LogVerifySummary(string kind) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 1; i < SupportBlockCounts.Length; i++) { if (SupportBlockCounts[i] != 0L) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(SupportBlockNames[i]).Append(' ').Append(SupportBlockCounts[i]); } } Logger.LogInfo($"Support sleep verify ({kind}): {_verifyEvaluated} visit(s) reached the support " + $"check, would have skipped {_verifyWouldSkip}, {_verifyDivergences} " + "divergence(s). Blocked by: " + ((stringBuilder.Length > 0) ? stringBuilder.ToString() : "nothing") + ". " + $"The wear sleep skipped {_wearSkipped} visit(s) before this point."); } private static void LogWearVerifySummary(string kind) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 1; i < WearBlockCounts.Length; i++) { if (WearBlockCounts[i] != 0L) { if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(WearBlockNames[i]).Append(' ').Append(WearBlockCounts[i]); } } Logger.LogInfo($"Wear sleep verify ({kind}): {_wearVisits} visit(s) over {States.Count} " + $"tracked piece(s), would have skipped {_wearWouldSkip}, " + $"{_wearDivergences} divergence(s). " + "Blocked by: " + ((stringBuilder.Length > 0) ? stringBuilder.ToString() : "nothing") + "."); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(TerrainLod))] internal static class TerrainLodSpreadPatch { internal static ConfigEntry Budget; internal static void BindConfig() { Budget = ValConfig.BindServerConfig("Fixes - Performance", "Distant Terrain Rebuild Budget", 3, "How many of the nine distant-terrain tiles may rebuild per frame. Higher finishes the ring sooner but hitches more; 9 is exactly vanilla.", advanced: true, 1, 9); } [HarmonyPrefix] [HarmonyPatch("RebuildAllHeightmaps")] private static bool RebuildAllHeightmapsPrefix(TerrainLod __instance) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0070: Unknown result type (might be due to invalid IL or missing references) int num = ((Budget != null) ? Budget.Value : 3); if (num >= __instance.m_heightmaps.Count) { return true; } int num2 = 0; bool flag = false; for (int i = 0; i < __instance.m_heightmaps.Count; i++) { HeightmapWithOffset val = __instance.m_heightmaps[i]; if ((int)val.m_state != 2) { if (num2 >= num) { flag = true; break; } __instance.RebuildHeightmap(val); num2++; } } if (!flag) { __instance.m_heightmapState = (HeightmapState)2; } return false; } [HarmonyPrefix] [HarmonyPatch("IsTerrainReady", new Type[] { typeof(HeightmapWithOffset) })] private static bool IsTerrainReadyPrefix(HeightmapWithOffset heightmapWithOffset, ref bool __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)heightmapWithOffset.m_state == 2) { __result = true; return false; } return true; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(VisEquipment))] internal static class VisEquipmentRefreshPatch { internal struct ColorState { internal bool Primed; internal Vector3 Skin; internal Vector3 Hair; internal GameObject Beard; internal GameObject HairItem; internal SkinnedMeshRenderer Body; internal int ModelIndex; internal bool Matches(ColorState other) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (Primed && other.Primed && ((Vector3)(ref Skin)).Equals(other.Skin) && ((Vector3)(ref Hair)).Equals(other.Hair) && Beard == other.Beard && HairItem == other.HairItem && Body == other.Body) { return ModelIndex == other.ModelIndex; } return false; } } [HarmonyPatch(typeof(VisEquipment), "OnDisable")] internal static class DisableHook { [HarmonyPostfix] private static void Postfix(VisEquipment __instance) { Applied.Remove(__instance); } } private static readonly Dictionary Applied = new Dictionary(); private static ZDO _scopeZdo; private static BinarySearchDictionary _scopeTable; private static readonly MethodInfo ZdoGetInt = AccessTools.Method(typeof(ZDO), "GetInt", new Type[2] { typeof(int), typeof(int) }, (Type[])null); private static readonly MethodInfo ScopedGetIntMethod = AccessTools.Method(typeof(VisEquipmentRefreshPatch), "ScopedGetInt", (Type[])null, (Type[])null); [HarmonyPrefix] [HarmonyPatch("UpdateColors")] private static bool UpdateColorsPrefix(VisEquipment __instance, out ColorState __state) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) __state = default(ColorState); if ((Object)(object)__instance.m_nview == (Object)null || (Object)(object)__instance.m_bodyModel == (Object)null) { return true; } Vector3 skin = __instance.m_skinColor; Vector3 hair = __instance.m_hairColor; ZDO zDO = __instance.m_nview.GetZDO(); if (zDO != null) { skin = zDO.GetVec3(ZDOVars.s_skinColor, Vector3.one); hair = zDO.GetVec3(ZDOVars.s_hairColor, Vector3.one); } __state = new ColorState { Primed = true, Skin = skin, Hair = hair, Beard = __instance.m_beardItemInstance, HairItem = __instance.m_hairItemInstance, Body = __instance.m_bodyModel, ModelIndex = __instance.m_currentModelIndex }; if (Applied.TryGetValue(__instance, out var value)) { return !value.Matches(__state); } return true; } [HarmonyPostfix] [HarmonyPatch("UpdateColors")] private static void UpdateColorsPostfix(VisEquipment __instance, ColorState __state) { if (__state.Primed) { Applied[__instance] = __state; } } [HarmonyPrefix] [HarmonyPatch("UpdateEquipmentVisuals")] [HarmonyPriority(800)] private static void EquipmentVisualsPrefix(VisEquipment __instance) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) _scopeZdo = null; _scopeTable = null; if (!((Object)(object)__instance.m_nview == (Object)null)) { ZDO zDO = __instance.m_nview.GetZDO(); if (zDO != null) { _scopeZdo = zDO; ZDOExtraData.s_ints.TryGetValue(zDO.m_uid, out _scopeTable); } } } private static int ScopedGetInt(ZDO zdo, int hash, int defaultValue) { if (zdo != _scopeZdo) { if (zdo != null) { return zdo.GetInt(hash, defaultValue); } return defaultValue; } if (_scopeTable != null) { return _scopeTable.GetValueOrDefault(hash, defaultValue); } return defaultValue; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("UpdateEquipmentVisuals")] private static IEnumerable EquipmentVisualsTranspiler(IEnumerable instructions) { return PatchHelper.ReplaceCalls(instructions, ZdoGetInt, ScopedGetIntMethod, "VisEquipment.UpdateEquipmentVisuals"); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(WaterVolume))] internal static class WaterVolumeMaterialCachePatch { [HarmonyPatch(typeof(WaterVolume), "OnDisable")] internal static class DisableHook { [HarmonyPostfix] private static void Postfix(WaterVolume __instance) { Cache.Remove(__instance); } } private static readonly Dictionary Cache = new Dictionary(); [HarmonyPrefix] [HarmonyPatch("UpdateMaterials")] private static bool UpdateMaterialsPrefix(WaterVolume __instance) { if (!Cache.TryGetValue(__instance, out var value) || (Object)(object)value == (Object)null) { MeshRenderer waterSurface = __instance.m_waterSurface; if ((Object)(object)waterSurface == (Object)null) { return true; } value = ((Renderer)waterSurface).material; Cache[__instance] = value; } value.SetFloat(WaterVolume.s_shaderWaterTime, WaterVolume.s_waterTime); return false; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(WearNTear))] internal static class WearCacheEventPatch { [HarmonyPatch(typeof(Heightmap))] internal static class HeightmapHooks { [HarmonyPostfix] [HarmonyPatch("Regenerate")] private static void RegeneratePostfix(Heightmap __instance) { if (!Registered.TryGetValue(((Object)__instance).GetInstanceID(), out var value)) { return; } foreach (WearNTear value2 in value.Values) { value2.ClearCachedSupport(); } } [HarmonyPostfix] [HarmonyPatch("OnDestroy")] private static void OnDestroyPostfix(Heightmap __instance) { Registered.Remove(((Object)__instance).GetInstanceID()); } } [HarmonyPatch(typeof(ZNetScene), "Shutdown")] internal static class ShutdownHook { [HarmonyPostfix] private static void Postfix() { Registered.Clear(); } } private static readonly Dictionary> Registered = new Dictionary>(); private static readonly HookHealth Hooks = new HookHealth("Piece event fix", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(WearNTear), "OnDestroy", (Type[])null, (Type[])null), typeof(TeardownHooks.PieceHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(Heightmap), "Regenerate", (Type[])null, (Type[])null), typeof(HeightmapHooks)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(Heightmap), "OnDestroy", (Type[])null, (Type[])null), typeof(HeightmapHooks))); [HarmonyPrefix] [HarmonyPatch("Start")] private static bool StartPrefix(WearNTear __instance) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!Hooks.Healthy) { return true; } Heightmap val = (__instance.m_connectedHeightMap = Heightmap.FindHeightmap(((Component)__instance).transform.position)); if ((Object)(object)val == (Object)null) { return false; } int instanceID = ((Object)val).GetInstanceID(); if (!Registered.TryGetValue(instanceID, out var value)) { value = new Dictionary(); Registered.Add(instanceID, value); } value[((Object)__instance).GetInstanceID()] = __instance; return false; } internal static void OnPieceDestroyed(WearNTear piece, int pieceId) { Heightmap connectedHeightMap = piece.m_connectedHeightMap; if (connectedHeightMap == null) { return; } int instanceID = ((Object)connectedHeightMap).GetInstanceID(); if (Registered.TryGetValue(instanceID, out var value)) { value.Remove(pieceId); if (value.Count == 0) { Registered.Remove(instanceID); } } } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(WearNTear))] internal static class WearSupportLookupPatch { [HarmonyPatch(typeof(ZNetScene), "Shutdown")] internal static class ShutdownHook { [HarmonyPostfix] private static void Postfix() { ColliderOwner.Clear(); RegisteredBy.Clear(); } } internal static ConfigEntry Verify; private static readonly Dictionary ColliderOwner = new Dictionary(); private static readonly Dictionary> RegisteredBy = new Dictionary>(); private static readonly HookHealth Hooks = new HookHealth("Support lookup", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(WearNTear), "SetupColliders", (Type[])null, (Type[])null), typeof(WearSupportLookupPatch)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(WearNTear), "OnDestroy", (Type[])null, (Type[])null), typeof(TeardownHooks.PieceHook))); private const int VerifyReportInterval = 25000; private static bool _verifyActive; private static long _verifyComparisons; private static long _verifyDivergences; private static int _comparisonsSinceReport; private static readonly MethodInfo GetComponentInParentMethod = AccessTools.Method(typeof(Component), "GetComponentInParent", new Type[0], new Type[1] { typeof(WearNTear) }); private static readonly MethodInfo ResolveSupportMethod = AccessTools.Method(typeof(WearSupportLookupPatch), "ResolveSupport", (Type[])null, (Type[])null); private static readonly MethodInfo EnumerableContainsMethod = typeof(Enumerable).GetMethods().First((MethodInfo m) => m.Name == "Contains" && m.GetParameters().Length == 2).MakeGenericMethod(typeof(Collider)); private static readonly MethodInfo IsOwnColliderMethod = AccessTools.Method(typeof(WearSupportLookupPatch), "IsOwnCollider", (Type[])null, (Type[])null); internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Support Lookup", value: false, "Diagnostic. Resolves every support collider both through the lookup table and vanilla's hierarchy walk, acts on vanilla's answer, and logs any disagreement. Costs the walk this fix exists to avoid, so leave it off unless you are validating the table.", null, advanced: true); } private static void Register(WearNTear piece, Collider collider) { if (!((Object)(object)collider == (Object)null)) { int instanceID = ((Object)collider).GetInstanceID(); ColliderOwner[instanceID] = piece; int instanceID2 = ((Object)piece).GetInstanceID(); if (!RegisteredBy.TryGetValue(instanceID2, out var value)) { value = new List(); RegisteredBy.Add(instanceID2, value); } value.Add(instanceID); } } [HarmonyPostfix] [HarmonyPatch("SetupColliders")] private static void SetupCollidersPostfix(WearNTear __instance) { Collider[] colliders = __instance.m_colliders; if (colliders != null) { for (int i = 0; i < colliders.Length; i++) { Register(__instance, colliders[i]); } } } internal static void OnPieceDestroyed(int pieceId) { if (!RegisteredBy.TryGetValue(pieceId, out var value)) { return; } for (int i = 0; i < value.Count; i++) { if (ColliderOwner.TryGetValue(value[i], out var value2) && value2 != null && ((Object)value2).GetInstanceID() == pieceId) { ColliderOwner.Remove(value[i]); } } RegisteredBy.Remove(pieceId); } public static WearNTear ResolveSupport(Collider collider) { if (!Hooks.Healthy) { return ((Component)collider).GetComponentInParent(); } if (Verify != null && Verify.Value) { _verifyActive = true; _verifyComparisons++; ColliderOwner.TryGetValue(((Object)collider).GetInstanceID(), out var value); WearNTear componentInParent = ((Component)collider).GetComponentInParent(); bool flag = (Object)(object)value == (Object)null; bool flag2 = (Object)(object)componentInParent == (Object)null; if (!(flag && flag2) && value != componentInParent && !flag) { _verifyDivergences++; Logger.LogError("Support lookup verify: DIVERGED on collider '" + ((Object)collider).name + "' (table: " + (flag ? "null" : ((Object)value).name) + ", walk: " + (flag2 ? "null" : ((Object)componentInParent).name) + "). Vanilla's answer was used. Please report this - leave 'Fix Support Lookup Cost' off until it is understood."); } if (++_comparisonsSinceReport >= 25000) { _comparisonsSinceReport = 0; LogVerifySummary("periodic"); } return componentInParent; } if (_verifyActive) { _verifyActive = false; LogVerifySummary("final"); _verifyComparisons = 0L; _verifyDivergences = 0L; _comparisonsSinceReport = 0; } if (ColliderOwner.TryGetValue(((Object)collider).GetInstanceID(), out var value2)) { return value2; } WearNTear componentInParent2 = ((Component)collider).GetComponentInParent(); if ((Object)(object)componentInParent2 != (Object)null) { Register(componentInParent2, collider); } return componentInParent2; } private static void LogVerifySummary(string kind) { Logger.LogInfo($"Support lookup verify ({kind}): {_verifyComparisons} comparison(s), " + $"{_verifyDivergences} divergence(s)."); } public static bool IsOwnCollider(IEnumerable ownColliders, Collider candidate) { if (Hooks.Healthy && ColliderOwner.TryGetValue(((Object)candidate).GetInstanceID(), out var value)) { return value.m_colliders == ownColliders; } if (ownColliders is Collider[] array) { for (int i = 0; i < array.Length; i++) { if (array[i] == candidate) { return true; } } return false; } return ownColliders.Contains(candidate); } [HarmonyPrefix] [HarmonyPatch("GetCOM")] private static bool GetCOMPrefix(WearNTear __instance, ref Vector3 __result) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)__instance).transform; __result = transform.position + transform.rotation * __instance.m_comOffset; return false; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("UpdateSupport")] private static IEnumerable UpdateSupportTranspiler(IEnumerable instructions) { List list = PatchHelper.Copy(instructions); int num = 0; int num2 = 0; for (int i = 0; i < list.Count; i++) { if (CodeInstructionExtensions.Calls(list[i], GetComponentInParentMethod)) { list[i].opcode = OpCodes.Call; list[i].operand = ResolveSupportMethod; num++; } else if (CodeInstructionExtensions.Calls(list[i], EnumerableContainsMethod)) { list[i].opcode = OpCodes.Call; list[i].operand = IsOwnColliderMethod; num2++; } } if (num != 3 || num2 != 2) { Logger.LogWarning("WearNTear.UpdateSupport: expected 3 GetComponentInParent and 2 " + $"Enumerable.Contains calls, found {num} and {num2}, so " + "this fix is inactive. Another mod has most likely already rewritten the method - if so, nothing is wrong."); return instructions; } return list; } } [PatchSide(Side.Server)] [HarmonyPatch(typeof(ZDOMan))] internal static class ZdoConnectionIndexPatch { private const ConnectionType PortalType = (ConnectionType)1; private const ConnectionType PortalTargetType = (ConnectionType)17; private const ConnectionType SpawnedType = (ConnectionType)3; private const ConnectionType SpawnedTargetType = (ConnectionType)19; private const ConnectionType SyncTransformType = (ConnectionType)2; private const ConnectionType SyncTransformTargetType = (ConnectionType)18; [HarmonyPrefix] [HarmonyPatch("ConnectPortals")] private static bool ConnectPortalsPrefix(ZDOMan __instance) { //IL_001d: 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) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) List allConnectionZDOIDs = ZDOExtraData.GetAllConnectionZDOIDs((ConnectionType)1); List allConnectionZDOIDs2 = ZDOExtraData.GetAllConnectionZDOIDs((ConnectionType)17); Dictionary> dictionary = new Dictionary>(); for (int i = 0; i < allConnectionZDOIDs2.Count; i++) { ZDOID val = allConnectionZDOIDs2[i]; if ((int)ZDOExtraData.GetConnectionType(val) != 0) { continue; } ZDOConnectionHashData connectionHashData = ZDOExtraData.GetConnectionHashData(val, (ConnectionType)17); if (connectionHashData != null) { if (!dictionary.TryGetValue(connectionHashData.m_hash, out var value)) { value = new Queue(); dictionary.Add(connectionHashData.m_hash, value); } value.Enqueue(val); } } long sessionID = ZDOMan.GetSessionID(); int num = 0; for (int j = 0; j < allConnectionZDOIDs.Count; j++) { ZDOID val2 = allConnectionZDOIDs[j]; ZDO zDO = __instance.GetZDO(val2); if (zDO == null) { continue; } ZDOConnectionHashData connectionHashData2 = zDO.GetConnectionHashData((ConnectionType)1); if (connectionHashData2 == null || !dictionary.TryGetValue(connectionHashData2.m_hash, out var value2)) { continue; } ZDO val3 = null; ZDOID val4 = ZDOID.None; while (value2.Count > 0) { ZDOID val5 = value2.Dequeue(); if (!(val5 == val2)) { val3 = __instance.GetZDO(val5); if (val3 != null) { val4 = val5; break; } } } if (val3 != null) { num++; zDO.SetOwner(sessionID); val3.SetOwner(sessionID); zDO.SetConnection((ConnectionType)1, val4); val3.SetConnection((ConnectionType)1, val2); } } if (num > 0) { Logger.LogInfo($"ConnectPortals => Connected {num} portals."); } return false; } [HarmonyPrefix] [HarmonyPatch("ConnectSpawners")] private static bool ConnectSpawnersPrefix(ZDOMan __instance) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_00c7: Unknown result type (might be due to invalid IL or missing references) List allConnectionZDOIDs = ZDOExtraData.GetAllConnectionZDOIDs((ConnectionType)3); List allConnectionZDOIDs2 = ZDOExtraData.GetAllConnectionZDOIDs((ConnectionType)19); Dictionary dictionary = new Dictionary(); for (int i = 0; i < allConnectionZDOIDs2.Count; i++) { ZDOConnectionHashData connectionHashData = ZDOExtraData.GetConnectionHashData(allConnectionZDOIDs2[i], (ConnectionType)19); if (connectionHashData != null && !dictionary.ContainsKey(connectionHashData.m_hash)) { dictionary.Add(connectionHashData.m_hash, allConnectionZDOIDs2[i]); } } long sessionID = ZDOMan.GetSessionID(); int num = 0; int num2 = 0; for (int j = 0; j < allConnectionZDOIDs.Count; j++) { ZDOID val = allConnectionZDOIDs[j]; ZDO zDO = __instance.GetZDO(val); if (zDO != null) { zDO.SetOwner(sessionID); ZDOConnectionHashData connectionHashData2 = zDO.GetConnectionHashData((ConnectionType)3); if (connectionHashData2 != null && dictionary.TryGetValue(connectionHashData2.m_hash, out var value) && value != val) { num++; zDO.SetConnection((ConnectionType)3, value); } else { num2++; zDO.SetConnection((ConnectionType)3, ZDOID.None); } } } if (num > 0 || num2 > 0) { Logger.LogInfo($"ConnectSpawners => Connected {num} spawners and {num2} 'done' spawners."); } return false; } [HarmonyPrefix] [HarmonyPatch("ConnectSyncTransforms")] private static bool ConnectSyncTransformsPrefix() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_009a: Unknown result type (might be due to invalid IL or missing references) List allConnectionZDOIDs = ZDOExtraData.GetAllConnectionZDOIDs((ConnectionType)2); List allConnectionZDOIDs2 = ZDOExtraData.GetAllConnectionZDOIDs((ConnectionType)18); Dictionary dictionary = new Dictionary(); for (int i = 0; i < allConnectionZDOIDs2.Count; i++) { ZDOConnectionHashData connectionHashData = ZDOExtraData.GetConnectionHashData(allConnectionZDOIDs2[i], (ConnectionType)18); if (connectionHashData != null && !dictionary.ContainsKey(connectionHashData.m_hash)) { dictionary.Add(connectionHashData.m_hash, allConnectionZDOIDs2[i]); } } int num = 0; for (int j = 0; j < allConnectionZDOIDs.Count; j++) { ZDOID val = allConnectionZDOIDs[j]; ZDOConnectionHashData connectionHashData2 = ZDOExtraData.GetConnectionHashData(val, (ConnectionType)2); if (connectionHashData2 != null && dictionary.TryGetValue(connectionHashData2.m_hash, out var value)) { num++; ZDOExtraData.SetConnection(val, (ConnectionType)2, value); } } if (num > 0) { Logger.LogInfo($"ConnectSyncTransforms => Connected {num} SyncTransforms."); } return false; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZDOMan))] internal static class ZdoPrefabIndexPatch { [HarmonyPatch(typeof(ZDO), "SetPrefab")] internal static class SetPrefabHook { [HarmonyPrefix] private static void Prefix(ZDO __instance, out int __state) { __state = __instance.m_prefab; } [HarmonyPostfix] private static void Postfix(ZDO __instance, int __state) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (__instance.m_prefab != __state) { Track(__instance.m_uid, __instance.m_prefab); } } } [HarmonyPatch(typeof(ZDO), "Deserialize")] internal static class DeserializeHook { [HarmonyPrefix] private static void Prefix(ZDO __instance, out int __state) { __state = __instance.m_prefab; } [HarmonyPostfix] private static void Postfix(ZDO __instance, int __state) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) if (__instance.m_prefab != __state) { Track(__instance.m_uid, __instance.m_prefab); } } } [HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")] internal static class HandleDestroyedZdoHook { [HarmonyPostfix] private static void Postfix(ZDOID uid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Untrack(uid); } } [HarmonyPatch(typeof(ZDOMan), "Load")] internal static class ZdoManLoadHook { [HarmonyPostfix] private static void Postfix(ZDOMan __instance) { RebuildIndex(__instance); } } [HarmonyPatch(typeof(ZDOMan), "LoadChunks")] internal static class ZdoManLoadChunksHook { [HarmonyPostfix] private static void Postfix(ZDOMan __instance) { RebuildIndex(__instance); } } [HarmonyPatch(typeof(ZDOMan), "ShutDown")] internal static class ZdoManShutDownHook { [HarmonyPostfix] private static void Postfix() { ClearIndex(); } } internal static ConfigEntry Verify; private static readonly Dictionary PrefabOf = new Dictionary(); private static readonly Dictionary> ByPrefab = new Dictionary>(); private static readonly List IndexScratch = new List(); private static readonly List VanillaScratch = new List(); private static readonly List StaleScratch = new List(); private static readonly Predicate InvalidZdo = (ZDO zdo) => !zdo.IsValid(); private static readonly HookHealth Hooks = new HookHealth("Prefab index", () => PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDO), "SetPrefab", (Type[])null, (Type[])null), typeof(SetPrefabHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDO), "Deserialize", (Type[])null, (Type[])null), typeof(DeserializeHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "HandleDestroyedZDO", (Type[])null, (Type[])null), typeof(HandleDestroyedZdoHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "Load", (Type[])null, (Type[])null), typeof(ZdoManLoadHook)) && PatchHelper.HasHook(AccessTools.DeclaredMethod(typeof(ZDOMan), "LoadChunks", (Type[])null, (Type[])null), typeof(ZdoManLoadChunksHook))); internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Prefab Index", value: false, "Diagnostic. Runs both the indexed lookup and vanilla's whole-world scan on every prefab query, acts on vanilla's result, and logs any disagreement. Costs the scan this fix exists to avoid, so leave it off unless you are validating the index.", null, advanced: true); } private static void Track(ZDOID uid, int prefab) { //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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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) if (uid == ZDOID.None) { return; } if (prefab == 0) { Untrack(uid); return; } if (PrefabOf.TryGetValue(uid, out var value)) { if (value == prefab) { return; } Untrack(uid); } PrefabOf[uid] = prefab; if (!ByPrefab.TryGetValue(prefab, out var value2)) { value2 = new HashSet(); ByPrefab.Add(prefab, value2); } value2.Add(uid); } private static void Untrack(ZDOID uid) { //IL_0005: 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_002c: Unknown result type (might be due to invalid IL or missing references) if (!PrefabOf.TryGetValue(uid, out var value)) { return; } PrefabOf.Remove(uid); if (ByPrefab.TryGetValue(value, out var value2)) { value2.Remove(uid); if (value2.Count == 0) { ByPrefab.Remove(value); } } } private static void ClearIndex() { PrefabOf.Clear(); ByPrefab.Clear(); } private static void RebuildIndex(ZDOMan zdoMan) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) ClearIndex(); foreach (KeyValuePair item in zdoMan.m_objectsByID) { ZDO value = item.Value; if (value.m_prefab != 0) { Track(value.m_uid, value.m_prefab); } } } [HarmonyPrefix] [HarmonyPatch("GetAllZDOsWithPrefabIterative")] private static bool GetAllZDOsWithPrefabIterativePrefix(ZDOMan __instance, string prefab, List zdos, ref int index, ref bool __result) { if (!Hooks.Healthy) { return true; } if (index != 0) { return true; } int stableHashCode = StringExtensionMethods.GetStableHashCode(prefab); if (stableHashCode == 0) { return true; } IndexScratch.Clear(); CollectIndexed(__instance, stableHashCode); IndexScratch.RemoveAll(InvalidZdo); List list = IndexScratch; if (Verify != null && Verify.Value) { VanillaScratch.Clear(); CollectFullScan(__instance, stableHashCode); VanillaScratch.RemoveAll(InvalidZdo); ReportDivergence(prefab); list = VanillaScratch; } for (int i = 0; i < list.Count; i++) { zdos.Add(list[i]); } zdos.RemoveAll(InvalidZdo); index = __instance.m_objectsBySector.Length; IndexScratch.Clear(); VanillaScratch.Clear(); __result = true; return false; } private static void CollectIndexed(ZDOMan zdoMan, int hash) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_003b: 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) if (!ByPrefab.TryGetValue(hash, out var value)) { return; } StaleScratch.Clear(); foreach (ZDOID item in value) { ZDO zDO = zdoMan.GetZDO(item); if (zDO == null) { StaleScratch.Add(item); } else { IndexScratch.Add(zDO); } } for (int i = 0; i < StaleScratch.Count; i++) { Untrack(StaleScratch[i]); } StaleScratch.Clear(); } private static void CollectFullScan(ZDOMan zdoMan, int hash) { List[] objectsBySector = zdoMan.m_objectsBySector; foreach (List list in objectsBySector) { if (list == null) { continue; } for (int j = 0; j < list.Count; j++) { if (list[j].GetPrefab() == hash) { VanillaScratch.Add(list[j]); } } } foreach (List value in zdoMan.m_portalObjects.Values) { for (int k = 0; k < value.Count; k++) { if (value[k].GetPrefab() == hash) { VanillaScratch.Add(value[k]); } } } } private static void ReportDivergence(string prefab) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(); for (int i = 0; i < IndexScratch.Count; i++) { hashSet.Add(IndexScratch[i].m_uid); } int num = 0; int num2 = 0; ZDOID val = ZDOID.None; ZDOID val2 = ZDOID.None; HashSet hashSet2 = new HashSet(); for (int j = 0; j < VanillaScratch.Count; j++) { ZDOID uid = VanillaScratch[j].m_uid; hashSet2.Add(uid); if (!hashSet.Contains(uid)) { if (num == 0) { val = uid; } num++; } } foreach (ZDOID item in hashSet) { if (!hashSet2.Contains(item)) { if (num2 == 0) { val2 = item; } num2++; } } if (num == 0 && num2 == 0) { Logger.LogInfo($"Prefab index verify ('{prefab}'): agreed on {VanillaScratch.Count} object(s) " + $"out of {PrefabOf.Count} indexed."); } else { Logger.LogError($"Prefab index verify ('{prefab}'): DIVERGED. The full scan found {num} " + $"object(s) the index missed (first {val}), and the index claimed {num2} " + $"the full scan did not (first {val2}). Vanilla's result was used. Please " + "report this - leave 'Fix Prefab Query Scan' off until it is understood."); } } } [PatchSide(Side.Both)] [HarmonyPatch] internal static class ZdoReadLookupPatch { private static readonly HashSet AccessorNames = new HashSet { "GetFloat", "GetVec3", "GetQuaternion", "GetInt", "GetLong", "GetString", "GetByteArray", "GetBool", "GetConnection", "GetConnectionZDOID", "GetConnectionType", "GetConnectionHashData" }; private const int ExpectedAccessors = 19; private static readonly Dictionary Replacements = BuildReplacements(); [HarmonyTargetMethods] private static IEnumerable TargetMethods() { int found = 0; foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(typeof(ZDOExtraData))) { if (declaredMethod.IsStatic && AccessorNames.Contains(declaredMethod.Name)) { found++; yield return declaredMethod; } } if (found != 19) { Logger.LogWarning($"ZDOExtraData: expected {19} data accessors, found {found}. The ones " + "that were found are still fixed; a Valheim update has most likely added or removed an accessor, and this fix now covers a different share of the read path."); } } private static TType GetValueOrDefault(Dictionary> container, ZDOID zid, int hash, TType defaultValue) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!container.TryGetValue(zid, out var value)) { return defaultValue; } return value.GetValueOrDefault(hash, defaultValue); } private static bool GetValue(Dictionary> container, ZDOID zid, int hash, out TType value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (container.TryGetValue(zid, out var value2)) { return value2.TryGetValue(hash, ref value); } value = default(TType); return false; } private static List> GetValuesOrEmpty(Dictionary> container, ZDOID zid) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!container.TryGetValue(zid, out var value)) { return new List>(); } return ((IEnumerable>)value).ToList(); } private static TValue GetValueOrDefaultPiktiv(IDictionary container, TKey zid, TValue defaultValue) { if (!container.TryGetValue(zid, out var value)) { return defaultValue; } return value; } private static Dictionary BuildReplacements() { Dictionary dictionary = new Dictionary(); Pair(dictionary, "GetValueOrDefault"); Pair(dictionary, "GetValue"); Pair(dictionary, "GetValuesOrEmpty"); Pair(dictionary, "GetValueOrDefaultPiktiv"); return dictionary; } private static void Pair(Dictionary map, string name) { MethodInfo method = typeof(ZDOHelper).GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public); MethodInfo method2 = typeof(ZdoReadLookupPatch).GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic); if (method == null || method2 == null) { Logger.LogWarning("ZDOHelper." + name + " could not be paired with a single-lookup replacement, so reads through it keep vanilla's doubled lookup. The other helpers are unaffected."); } else { map[method] = method2; } } [HarmonyTranspiler] [HarmonyPriority(0)] private static IEnumerable AccessorTranspiler(IEnumerable instructions, MethodBase __originalMethod) { List list = PatchHelper.Copy(instructions); if (Replacements.Count == 0) { return instructions; } int num = 0; for (int i = 0; i < list.Count; i++) { if ((!(list[i].opcode != OpCodes.Call) || !(list[i].opcode != OpCodes.Callvirt)) && list[i].operand is MethodInfo { IsGenericMethod: not false } methodInfo && Replacements.TryGetValue(methodInfo.GetGenericMethodDefinition(), out var value)) { list[i].opcode = OpCodes.Call; list[i].operand = value.MakeGenericMethod(methodInfo.GetGenericArguments()); num++; } } if (num != 1) { Logger.LogWarning("ZDOExtraData." + __originalMethod?.Name + ": expected 1 ZDO lookup helper call, found " + $"{num}, so that accessor keeps vanilla's doubled lookup. Another mod has most " + "likely already rewritten it - if so, nothing is wrong."); return instructions; } return list; } } [PatchSide(Side.Both)] [HarmonyPatch] internal static class ZdoValueWriteAllocPatch { private static readonly Type[] BoxedValueTypes = new Type[5] { typeof(float), typeof(Vector3), typeof(Quaternion), typeof(int), typeof(long) }; private static readonly MethodInfo ObjectEquals = AccessTools.Method(typeof(object), "Equals", new Type[1] { typeof(object) }, (Type[])null); [HarmonyTargetMethods] private static IEnumerable TargetMethods() { Type[] boxedValueTypes = BoxedValueTypes; foreach (Type type in boxedValueTypes) { MethodInfo methodInfo = AccessTools.Method(typeof(BinarySearchDictionary<, >).MakeGenericType(typeof(int), type), "SetValue", new Type[2] { typeof(int), type }, (Type[])null); if (methodInfo == null) { Logger.LogWarning("BinarySearchDictionary.SetValue could not be resolved, so ZDO writes of that type keep vanilla's boxed comparison. The other types are unaffected."); } else { yield return methodInfo; } } } [HarmonyTranspiler] [HarmonyPriority(0)] private static IEnumerable SetValueTranspiler(IEnumerable instructions, MethodBase __originalMethod) { List list = PatchHelper.Copy(instructions); Type type = __originalMethod?.DeclaringType; if (type == null || !type.IsGenericType) { return instructions; } Type type2 = type.GetGenericArguments()[1]; if (!type2.IsValueType) { return instructions; } MethodInfo methodInfo = AccessTools.Method(type2, "Equals", new Type[1] { type2 }, (Type[])null); if (methodInfo == null || ObjectEquals == null) { Logger.LogWarning(type2.Name + " has no Equals(" + type2.Name + ") overload to route the ZDO write comparison through, so that type keeps vanilla's boxed comparison."); return instructions; } int num = 0; for (int i = 1; i < list.Count; i++) { if (!CodeInstructionExtensions.Calls(list[i], ObjectEquals)) { continue; } int num2 = ((list[i - 1].opcode == OpCodes.Constrained) ? (i - 1) : i); int num3 = num2 - 1; if (num3 >= 0 && !(list[num3].opcode != OpCodes.Box) && (!(list[num3].operand is Type type3) || !(type3 != type2) || type3.IsGenericParameter)) { list[num3].opcode = OpCodes.Nop; list[num3].operand = null; if (num2 != i) { list[num2].opcode = OpCodes.Nop; list[num2].operand = null; } list[i].opcode = OpCodes.Call; list[i].operand = methodInfo; num++; } } if (num != 1) { Logger.LogWarning("BinarySearchDictionary.SetValue: expected 1 boxed equality " + $"check, found {num}, so ZDO writes of that type keep vanilla's allocation. " + "Another mod has most likely already rewritten the method - if so, nothing is wrong."); return instructions; } return list; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZNetScene))] internal static class ZoneDiffRemovalPatch { internal static ConfigEntry Verify; internal static ConfigEntry FrameBudget; private static readonly List Removed = new List(); private static readonly HashSet VerifyVanillaSet = new HashSet(); private static readonly HashSet VerifyOurSet = new HashSet(); private const int VerifyReportInterval = 900; private static bool _verifyActive; private static long _verifyPasses; private static long _verifyOurRemovals; private static long _verifyDivergences; private static int _passesSinceReport; private const int StormReportThreshold = 500; private static Vector2s _lastCenter; private static bool _haveLastCenter; internal static void BindConfig() { Verify = ValConfig.BindServerConfig("Debug", "Verify Unload Discovery", value: false, "Diagnostic. Computes every unload pass both from the zone index and from vanilla's full stamped walk, compares the two removal sets, acts on vanilla's, and logs any disagreement. Costs the walk this fix exists to avoid, so leave it off unless you are validating the index.", null, advanced: true); FrameBudget = ValConfig.BindServerConfig("Fixes - Performance", "Object Unload Frame Budget", 250, "How many departed objects may be handed to the engine for destruction in a single unload pass. Destroying an object is mostly engine work that happens in one burst at the end of the frame, so a pass that unloads thousands at once - arriving through a portal, respawning, or a world load settling - is a visible freeze no matter how fast the game's own bookkeeping is. Capping the pass turns that freeze into a short, shallow dip. The remainder unloads on the following passes; until then it lingers at the far edge of the loaded distance, where nothing can see it. Raise it to unload faster and hitch harder, lower it for the reverse. 0 unloads everything at once, exactly like vanilla.", advanced: true, 0, 20000); } [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyPatch("RemoveObjects")] private static bool RemoveObjectsPrefix(ZNetScene __instance, List currentNearObjects, List currentDistantObjects) { //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_0034: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) if (!SectorInstanceIndexPatch.MaintenanceHealthy) { return true; } if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null) { return true; } Vector2s zone = ZoneSystem.GetZone(ZNet.instance.GetReferencePosition()); SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance(); int nearSimulationDistance = ((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance; int totalSimulationDistance = ((SimulationDistance)(ref syncedSimulationDistance)).TotalSimulationDistance; bool isClassic = ((SimulationDistance)(ref syncedSimulationDistance)).IsClassic; float num = ZoneSystem.instance.m_zoneSize / 64f; float num2 = ((float)nearSimulationDistance + 0.5f) * num; float num3 = ((float)totalSimulationDistance + 0.8f) * num; float num4 = num2 * num2; float num5 = num3 * num3; Removed.Clear(); foreach (KeyValuePair> item in SectorInstanceIndexPatch.ByZone) { int num6 = item.Key.x - zone.x; int num7 = item.Key.y - zone.y; if (num6 < 0) { num6 = -num6; } if (num7 < 0) { num7 = -num7; } int num8 = ((num6 > num7) ? num6 : num7); int num9 = num6 * num6 + num7 * num7; if (isClassic ? (num8 <= nearSimulationDistance) : ((float)num9 < num4)) { continue; } List value = item.Value; if (isClassic ? (num8 > totalSimulationDistance) : ((float)num9 >= num5)) { Removed.AddRange(value); continue; } for (int i = 0; i < value.Count; i++) { ZNetView val = value[i]; if (val.m_zdo == null || !val.m_zdo.Distant) { Removed.Add(val); } } } if (TeardownHooks.StatsOn) { if (Removed.Count >= 500) { ReportStorm(__instance, zone, nearSimulationDistance, totalSimulationDistance, Removed.Count); } _lastCenter = zone; _haveLastCenter = true; } if (Verify != null && Verify.Value) { VerifyPass(__instance, currentNearObjects, currentDistantObjects); } else { if (_verifyActive) { _verifyActive = false; LogVerifySummary("final"); _verifyPasses = 0L; _verifyOurRemovals = 0L; _verifyDivergences = 0L; _passesSinceReport = 0; } Execute(__instance, currentNearObjects, currentDistantObjects); } return false; } private static bool InLoadingScreen() { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { return ((Character)Player.m_localPlayer).IsTeleporting(); } return true; } private static void ReportStorm(ZNetScene scene, Vector2s center, int near, int full, int count) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_001b: Unknown result type (might be due to invalid IL or missing references) int num = -1; if (_haveLastCenter) { int num2 = center.x - _lastCenter.x; int num3 = center.y - _lastCenter.y; if (num2 < 0) { num2 = -num2; } if (num3 < 0) { num3 = -num3; } num = ((num2 > num3) ? num2 : num3); } Logger.LogInfo($"Destroy storm: unload pass discovered {count} object(s) at once. " + $"Ring centre zone ({center.x},{center.y}), moved {num} zone(s) since the last " + $"pass; near ring {near}, full ring {full}; " + $"{SectorInstanceIndexPatch.ByZone.Count} zone(s) hold instances, " + $"{scene.m_instances.Count} instance(s) loaded; " + $"loading screen: {InLoadingScreen()}."); } private static void Execute(ZNetScene scene, List currentNearObjects, List currentDistantObjects) { bool statsOn = TeardownHooks.StatsOn; long num = (statsOn ? Stopwatch.GetTimestamp() : 0); int count = Removed.Count; int num2 = ((FrameBudget != null) ? FrameBudget.Value : 0); if (num2 > 0 && !RunMode.IsDedicated && InLoadingScreen()) { num2 = 0; } int num3 = ((num2 > 0 && count > num2) ? num2 : count); try { for (int i = 0; i < num3; i++) { ZNetView obj = Removed[i]; ZDO zdo = obj.m_zdo; obj.ResetZDO(); Object.Destroy((Object)(object)((Component)obj).gameObject); if (!zdo.Persistent && zdo.IsOwner()) { ZDOMan.instance.DestroyZDO(zdo); } scene.m_instances.Remove(zdo); } } catch (Exception ex) { Logger.LogDebug("Zone-diff unload hit an orphaned instance (" + ex.GetType().Name + "); running guarded sweep."); byte b = (byte)(Time.frameCount & 0xFF); foreach (ZNetView value in scene.m_instances.Values) { ZDO val = value?.m_zdo; if (val != null) { val.m_tempRemoveEarmark = b; } } byte tempRemoveEarmark = (byte)(b + 1); for (int j = 0; j < Removed.Count; j++) { ZDO val2 = Removed[j]?.m_zdo; if (val2 != null) { val2.m_tempRemoveEarmark = tempRemoveEarmark; } } RemoveObjectsNrePatch.GuardedSweep(scene, b); } if (statsOn) { double milliseconds = (double)(Stopwatch.GetTimestamp() - num) * 1000.0 / (double)Stopwatch.Frequency; TeardownHooks.NoteUnloadPass(num3, count, milliseconds); } } private static void VerifyPass(ZNetScene scene, List currentNearObjects, List currentDistantObjects) { _verifyActive = true; _verifyPasses++; _verifyOurRemovals += Removed.Count; byte b = (byte)(Time.frameCount & 0xFF); for (int i = 0; i < currentNearObjects.Count; i++) { currentNearObjects[i].m_tempRemoveEarmark = b; } for (int j = 0; j < currentDistantObjects.Count; j++) { currentDistantObjects[j].m_tempRemoveEarmark = b; } try { VerifyVanillaSet.Clear(); foreach (ZNetView value in scene.m_instances.Values) { if (value.m_zdo.m_tempRemoveEarmark != b) { VerifyVanillaSet.Add(value); } } VerifyOurSet.Clear(); for (int k = 0; k < Removed.Count; k++) { VerifyOurSet.Add(Removed[k]); } int num = 0; foreach (ZNetView item in VerifyVanillaSet) { if (!VerifyOurSet.Contains(item)) { num++; } } int num2 = Removed.Count - (VerifyVanillaSet.Count - num); if (num > 0 || num2 > 0) { _verifyDivergences++; Logger.LogError($"Unload discovery verify: DIVERGED - index found {Removed.Count} " + $"removal(s), vanilla found {VerifyVanillaSet.Count} ({num} missed by " + $"the index, {num2} extra). Vanilla's set was used. Please report this - " + "leave 'Fix Unload Discovery Scan' off until it is understood."); } Removed.Clear(); Removed.AddRange(VerifyVanillaSet); Execute(scene, currentNearObjects, currentDistantObjects); } catch (Exception ex) { Logger.LogDebug("Unload discovery verify hit an orphaned instance (" + ex.GetType().Name + "); running guarded sweep."); RemoveObjectsNrePatch.GuardedSweep(scene, b); } if (++_passesSinceReport >= 900) { _passesSinceReport = 0; LogVerifySummary("periodic"); } } private static void LogVerifySummary(string kind) { Logger.LogInfo($"Unload discovery verify ({kind}): {_verifyPasses} pass(es), " + $"{_verifyOurRemovals} index removal(s), {_verifyDivergences} divergence(s)."); } } [PatchSide(Side.Server)] [HarmonyPatch(typeof(ZoneSystem))] internal static class ZoneGenPacingPatch { internal static ConfigEntry BudgetMs; internal static ConfigEntry CooldownTicks; private const int MaxConsecutiveSkips = 4; private static int _decisionFrame = -1; private static bool _skipThisFrame; private static int _consecutiveSkips; private static int _cooldownRemaining; internal static void BindConfig() { BudgetMs = ValConfig.BindServerConfig("Fixes - Performance", "Zone Generation Frame Budget", 30, "Milliseconds: a background zone generation tick is deferred when the previous frame exceeded this, and a generation that itself took longer than this arms the cooldown. Lower spreads generation out more aggressively.", advanced: true, 10, 100); CooldownTicks = ValConfig.BindServerConfig("Fixes - Performance", "Zone Generation Cooldown Ticks", 2, "How many 100ms ticks to wait after an expensive background zone generation before the next one. 0 disables the cooldown and paces on frame pressure alone.", advanced: true, 0, 10); } [HarmonyPrefix] [HarmonyPatch("CreateGhostZones")] private static bool CreateGhostZonesPrefix(ref bool __result, out long __state) { __state = 0L; int frameCount = Time.frameCount; if (frameCount != _decisionFrame) { _decisionFrame = frameCount; float num = Time.unscaledDeltaTime * 1000f; int num2 = ((BudgetMs != null) ? BudgetMs.Value : 30); if ((num > (float)num2 || _cooldownRemaining > 0) && _consecutiveSkips < 4) { _skipThisFrame = true; _consecutiveSkips++; if (_cooldownRemaining > 0) { _cooldownRemaining--; } } else { _skipThisFrame = false; _consecutiveSkips = 0; _cooldownRemaining = 0; } } if (_skipThisFrame) { __result = false; return false; } __state = Stopwatch.GetTimestamp(); return true; } [HarmonyPostfix] [HarmonyPatch("CreateGhostZones")] private static void CreateGhostZonesPostfix(long __state) { if (__state != 0L && CooldownTicks != null && CooldownTicks.Value > 0) { double num = (double)(Stopwatch.GetTimestamp() - __state) * 1000.0 / (double)Stopwatch.Frequency; int num2 = ((BudgetMs != null) ? BudgetMs.Value : 30); if (num > (double)num2) { _cooldownRemaining = CooldownTicks.Value; } } } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZPackage))] internal static class ZPackageWriteAllocPatch { [HarmonyPrefix] [HarmonyPatch("Write", new Type[] { typeof(ZPackage) })] private static bool WritePrefix(ZPackage __instance, ZPackage pkg) { pkg.m_writer.Flush(); pkg.m_stream.Flush(); int num = (int)pkg.m_stream.Length; __instance.m_writer.Write(num); __instance.m_writer.Write(pkg.m_stream.GetBuffer(), 0, num); return false; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(ZSFX))] internal static class ZsfxIdleDormancyPatch { [HarmonyPatch(typeof(ZNetScene))] internal static class SceneHooks { [HarmonyPostfix] [HarmonyPatch("Update")] private static void UpdatePostfix() { if (Sleepers.Count == 0) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextWatchdog) { return; } _nextWatchdog = unscaledTime + 1f; foreach (KeyValuePair sleeper in Sleepers) { ZSFX value = sleeper.Value; if (!((Object)(object)value == (Object)null)) { AudioSource audioSource = value.m_audioSource; if ((Object)(object)audioSource != (Object)null && audioSource.isPlaying) { WatchdogWakes.Add(value); } } } for (int i = 0; i < WatchdogWakes.Count; i++) { Wake(WatchdogWakes[i]); } WatchdogWakes.Clear(); } [HarmonyPostfix] [HarmonyPatch("Shutdown")] private static void ShutdownPostfix() { Sleepers.Clear(); WatchdogWakes.Clear(); _nextWatchdog = 0f; } } private const float WatchdogInterval = 1f; private static readonly Dictionary Sleepers = new Dictionary(); private static readonly List WatchdogWakes = new List(); private static float _nextWatchdog; private static void Wake(ZSFX sfx) { if (Sleepers.Remove(((Object)sfx).GetInstanceID())) { ZSFX.Instances.Add((IMonoUpdater)(object)sfx); } } [HarmonyPostfix] [HarmonyPatch("CustomUpdate")] private static void CustomUpdatePostfix(ZSFX __instance) { AudioSource audioSource = __instance.m_audioSource; if (!((Object)(object)audioSource == (Object)null) && !audioSource.loop && !audioSource.isPlaying && !(__instance.m_delay >= 0f) && !__instance.m_fadeOutOnAwake && ZSFX.Instances.Remove((IMonoUpdater)(object)__instance)) { Sleepers[((Object)__instance).GetInstanceID()] = __instance; } } [HarmonyPostfix] [HarmonyPatch("Play")] private static void PlayPostfix(ZSFX __instance) { Wake(__instance); } [HarmonyPostfix] [HarmonyPatch("FadeOut")] private static void FadeOutPostfix(ZSFX __instance) { Wake(__instance); } [HarmonyPostfix] [HarmonyPatch("OnEnable")] private static void OnEnablePostfix(ZSFX __instance) { Sleepers.Remove(((Object)__instance).GetInstanceID()); } [HarmonyPostfix] [HarmonyPatch("OnDisable")] private static void OnDisablePostfix(ZSFX __instance) { Sleepers.Remove(((Object)__instance).GetInstanceID()); } internal static void RestoreAll() { foreach (KeyValuePair sleeper in Sleepers) { ZSFX value = sleeper.Value; if ((Object)(object)value != (Object)null) { ZSFX.Instances.Add((IMonoUpdater)(object)value); } } Sleepers.Clear(); WatchdogWakes.Clear(); } } } namespace ValheimCommunityPatch.Patches.Correctness { [PatchSide(Side.Client)] [HarmonyPatch] internal static class BossKeySharePatch { internal static ConfigEntry Enabled; internal static ConfigEntry MaxDistance; private const string RpcName = "VCP_ShareDefeatKey"; internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(BossKeySharePatch), "Fixes - Correctness", "Share Boss Defeat Keys", value: true, "Gives every nearby player credit for a boss kill. Vanilla records the per-player defeat key only on whichever client happened to own the boss, so in a group everyone else is left without it - most noticeably for Hildir's quest bosses."); MaxDistance = ValConfig.BindServerConfig("Fixes - Correctness", "Boss Defeat Key Range", 300f, "How far from the boss a player can be and still be credited with the defeat key, in metres.", advanced: false, 0f, 2000f); } [HarmonyPostfix] [HarmonyPatch(typeof(Game), "Start")] private static void GameStartPostfix() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && !instance.m_functions.ContainsKey(StringExtensionMethods.GetStableHashCode("VCP_ShareDefeatKey"))) { instance.Register("VCP_ShareDefeatKey", (Action)RPC_ShareDefeatKey); } } private static void RPC_ShareDefeatKey(long sender, string key, Vector3 position) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (Enabled != null && Enabled.Value && !string.IsNullOrEmpty(key)) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !(Utils.DistanceXZ(((Component)localPlayer).transform.position, position) > MaxDistance.Value)) { ((Humanoid)localPlayer).AddUniqueKey(key); } } } [HarmonyPostfix] [HarmonyPatch(typeof(Character), "OnDeath")] private static void OnDeathPostfix(Character __instance) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (Enabled != null && Enabled.Value && !string.IsNullOrEmpty(__instance.m_defeatSetGlobalKey)) { ZNetView nview = __instance.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC(0L, "VCP_ShareDefeatKey", new object[2] { __instance.m_defeatSetGlobalKey, ((Component)__instance).transform.position }); } } } } [PatchSide(Side.Both)] [HarmonyPatch] internal static class ContainerLogSpamPatch { internal static ConfigEntry Enabled; private static readonly MethodInfo ZLogMethod = AccessTools.Method(typeof(ZLog), "Log", (Type[])null, (Type[])null); private static readonly MethodInfo SinkMethod = AccessTools.Method(typeof(Logger), "DebugSink", (Type[])null, (Type[])null); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(ContainerLogSpamPatch), "Fixes - Correctness", "Fix Container Log Spam", value: true, "Stops every chest open, stack-all and take-all writing four lines to the game log. The messages are still visible with EnableDebugMode on. Changing this requires a game restart."); } private static IEnumerable RedirectLogCalls(IEnumerable instructions, string method) { if (Enabled == null || !Enabled.Value) { return instructions; } return PatchHelper.ReplaceCalls(instructions, ZLogMethod, SinkMethod, "Container." + method); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(Container), "RPC_RequestOpen")] private static IEnumerable RequestOpenTranspiler(IEnumerable instructions) { return RedirectLogCalls(instructions, "RPC_RequestOpen"); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(Container), "RPC_RequestStack")] private static IEnumerable RequestStackTranspiler(IEnumerable instructions) { return RedirectLogCalls(instructions, "RPC_RequestStack"); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(Container), "RPC_RequestTakeAll")] private static IEnumerable RequestTakeAllTranspiler(IEnumerable instructions) { return RedirectLogCalls(instructions, "RPC_RequestTakeAll"); } } [PatchSide(Side.Both)] [HarmonyPatch] internal static class DungeonZoneLoadPinPatch { internal static ConfigEntry Enabled; internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(DungeonZoneLoadPinPatch), "Fixes - Correctness", "Fix Dungeon Load Stall", value: true, "Keeps a dungeon whose room assets fail to load from leaving its zone flagged as loading forever. In vanilla that zone stops spawning objects and anyone who spawns or teleports into it never leaves the loading screen."); } [HarmonyPrefix] [HarmonyPatch(typeof(DungeonGenerator), "OnRoomLoaded")] private static bool OnRoomLoadedPrefix(DungeonGenerator __instance, LoadResult result) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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) if (Enabled == null || !Enabled.Value) { return true; } if ((int)result == 0) { return true; } if ((Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).gameObject == (Object)null) { return true; } Logger.LogWarning($"Dungeon at {((Component)__instance).transform.position}: a room prefab failed to load ({result}). " + "Counting it as finished so the rest of the dungeon still spawns and the zone is not left flagged as loading."); __instance.m_roomsToLoad--; if (__instance.m_roomsToLoad > 0) { return false; } __instance.Spawn(); __instance.ReleaseHeldReferences(); return false; } [HarmonyPrefix] [HarmonyPatch(typeof(DungeonGenerator), "Spawn")] private static void SpawnPrefix(DungeonGenerator __instance) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0104: 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) if (Enabled == null || !Enabled.Value) { return; } RoomPlacementData[] array = __instance.m_loadedRooms; if (array == null || array.Length == 0) { return; } bool[] array2 = new bool[array.Length]; int num = 0; try { for (int i = 0; i < array.Length; i++) { RoomData roomData = array[i].m_roomData; array2[i] = roomData != null && (Object)(object)roomData.m_prefab.Asset != (Object)null; if (array2[i]) { num++; } } } catch (Exception arg) { Logger.LogError($"Could not check which rooms loaded for the dungeon at {((Component)__instance).transform.position}, " + $"so it is being spawned as vanilla would: {arg}"); return; } if (num == array.Length) { return; } Logger.LogWarning($"Dungeon at {((Component)__instance).transform.position}: {array.Length - num} of {array.Length} " + "room(s) did not load and have been dropped. That part of the dungeon will be missing. Placing them anyway throws inside PlaceRoom, which would leave this zone flagged as loading and unenterable."); int num2 = 0; for (int j = 0; j < array.Length; j++) { if (array2[j]) { array[num2++] = array[j]; } } Array.Resize(ref array, num); __instance.m_loadedRooms = array; } [HarmonyPrefix] [HarmonyPatch(typeof(ZoneSystem), "UnsetLoadingInZone")] private static bool UnsetLoadingInZonePrefix(ZoneSystem __instance, ZDO zdo) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (Enabled == null || !Enabled.Value) { return true; } if (zdo == null || __instance.m_loadingObjectsInZones == null) { return false; } Dictionary> loadingObjectsInZones = __instance.m_loadingObjectsInZones; if (RemoveFrom(loadingObjectsInZones, zdo.GetSector(), zdo)) { return false; } foreach (KeyValuePair> item in loadingObjectsInZones) { if (item.Value != null && item.Value.Contains(zdo)) { Vector2s key = item.Key; RemoveFrom(loadingObjectsInZones, key, zdo); Logger.LogWarning($"A dungeon or other loading object reported sector {zdo.GetSector()} but was " + $"registered in {key}, most likely because its ZDO was destroyed and pooled " + "mid-load. Cleared the real entry; vanilla would have left that zone flagged as loading and unenterable."); return false; } } return false; } private static bool RemoveFrom(Dictionary> loading, Vector2s sector, ZDO zdo) { //IL_0001: 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 (!loading.TryGetValue(sector, out var value)) { return false; } if (!value.Remove(zdo)) { return false; } if (value.Count == 0) { loading.Remove(sector); } return true; } } [PatchSide(Side.Both)] [HarmonyPatch] internal static class EffectAreaPatch { internal static ConfigEntry Enabled; private const int BufferGrowth = 128; private const int MaxBuffer = 4096; private static readonly MethodInfo OverlapSphereNonAllocMethod = AccessTools.Method(typeof(Physics), "OverlapSphereNonAlloc", new Type[4] { typeof(Vector3), typeof(float), typeof(Collider[]), typeof(int) }, (Type[])null); private static readonly MethodInfo GrowingOverlapMethod = AccessTools.Method(typeof(EffectAreaPatch), "GrowingOverlapSphereNonAlloc", (Type[])null, (Type[])null); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(EffectAreaPatch), "Fixes - Correctness", "Fix Effect Areas", value: true, "Two fixes: grows the fixed 128-collider buffer that made fire warmth and wetness checks silently miss in dense builds, and drops destroyed characters from effect areas instead of throwing every physics step. Changing this requires a game restart."); } private static int GrowingOverlapSphereNonAlloc(Vector3 position, float radius, Collider[] results, int layerMask) { //IL_0000: 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) int num = Physics.OverlapSphereNonAlloc(position, radius, results, layerMask); while (num == EffectArea.m_tempColliders.Length && EffectArea.m_tempColliders.Length < 4096) { int num2 = EffectArea.m_tempColliders.Length + 128; Array.Resize(ref EffectArea.m_tempColliders, num2); Logger.LogDebug($"Grew the effect area collider buffer to {num2}."); num = Physics.OverlapSphereNonAlloc(position, radius, EffectArea.m_tempColliders, layerMask); } return num; } private static IEnumerable ReplaceOverlapCall(IEnumerable instructions, string method) { if (Enabled == null || !Enabled.Value) { return instructions; } return PatchHelper.ReplaceCalls(instructions, OverlapSphereNonAllocMethod, GrowingOverlapMethod, "EffectArea." + method); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(EffectArea), "IsPointInsideArea")] private static IEnumerable IsPointInsideAreaTranspiler(IEnumerable instructions) { return ReplaceOverlapCall(instructions, "IsPointInsideArea"); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch(typeof(EffectArea), "GetBaseValue")] private static IEnumerable GetBaseValueTranspiler(IEnumerable instructions) { return ReplaceOverlapCall(instructions, "GetBaseValue"); } [HarmonyPrefix] [HarmonyPatch(typeof(EffectArea), "CustomFixedUpdate")] private static void CustomFixedUpdatePrefix(EffectArea __instance) { if (Enabled == null || !Enabled.Value) { return; } List collidedWithCharacter = __instance.m_collidedWithCharacter; if (collidedWithCharacter == null || collidedWithCharacter.Count == 0) { return; } for (int num = collidedWithCharacter.Count - 1; num >= 0; num--) { Character val = collidedWithCharacter[num]; if ((Object)(object)val == (Object)null || !Object.op_Implicit((Object)(object)val.m_nview) || !val.m_nview.IsValid()) { collidedWithCharacter.RemoveAt(num); } } } [HarmonyPostfix] [HarmonyPatch(typeof(Character), "OnDestroy")] private static void CharacterOnDestroyPostfix(Character __instance) { if (Enabled != null && Enabled.Value) { List allAreas = EffectArea.GetAllAreas(); for (int i = 0; i < allAreas.Count; i++) { allAreas[i]?.m_collidedWithCharacter?.Remove(__instance); } } } } [PatchSide(Side.Client)] [HarmonyPatch] internal static class FuelLossPatch { internal static ConfigEntry Enabled; internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(FuelLossPatch), "Fixes - Correctness", "Fix Fuel And Ore Loss", value: true, "Takes ownership of a smelter, kiln or fireplace before adding fuel or ore. Vanilla removes the item from your inventory and then sends a network message that is silently dropped if the owning player is lagging or has disconnected, destroying the item."); } private static void ClaimBeforeInteract(ZNetView nview) { if (Enabled != null && Enabled.Value && !((Object)(object)nview == (Object)null) && nview.IsValid() && !nview.IsOwner()) { nview.ClaimOwnership(); } } [HarmonyPrefix] [HarmonyPatch(typeof(Smelter), "OnAddFuel")] private static void SmelterOnAddFuelPrefix(Smelter __instance) { ClaimBeforeInteract(__instance.m_nview); } [HarmonyPrefix] [HarmonyPatch(typeof(Smelter), "OnAddOre")] private static void SmelterOnAddOrePrefix(Smelter __instance) { ClaimBeforeInteract(__instance.m_nview); } [HarmonyPrefix] [HarmonyPatch(typeof(Fireplace), "Interact")] private static void FireplaceInteractPrefix(Fireplace __instance) { ClaimBeforeInteract(__instance.m_nview); } [HarmonyPrefix] [HarmonyPatch(typeof(Fireplace), "UseItem")] private static void FireplaceUseItemPrefix(Fireplace __instance) { ClaimBeforeInteract(__instance.m_nview); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(ItemData))] internal static class ItemIconVariantPatch { internal static ConfigEntry Enabled; private static readonly HashSet Reported = new HashSet(); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(ItemIconVariantPatch), "Fixes - Correctness", "Fix Item Icon Crash", value: true, "Guards the unchecked array index in ItemDrop.ItemData.GetIcon that throws when an item's stored variant no longer matches its icon list - after a game update or a removed item mod. Without it the inventory and crafting panels break."); } [HarmonyPrefix] [HarmonyPatch("GetIcon")] private static bool GetIconPrefix(ItemData __instance, ref Sprite __result) { if (Enabled == null || !Enabled.Value) { return true; } Sprite[] array = __instance.m_shared?.m_icons; if (array != null && __instance.m_variant >= 0 && __instance.m_variant < array.Length) { return true; } __result = ((array != null && array.Length != 0) ? array[0] : null); string text = __instance.m_shared?.m_name ?? ""; if (Reported.Add(text)) { Logger.LogWarning($"Item '{text}' has variant {__instance.m_variant} but only {((array != null) ? array.Length : 0)} icon(s); " + "falling back to the first. This item was probably saved by a mod or game version that is no longer installed."); } return false; } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(Player))] internal static class NegativeStaminaPatch { internal static ConfigEntry Enabled; private static readonly HashSet Reported = new HashSet(); private static readonly HashSet ReportedRpc = new HashSet(); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(NegativeStaminaPatch), "Fixes - Correctness", "Fix Negative Stamina", value: true, "Floors player stamina at zero after Player.AddStamina, which bounds only the top of the range, and repairs a character that loads in already broken. Also drops a UseStamina network message carrying NaN or infinity, which vanilla would apply and leave your stamina permanently stuck. Only reachable when another mod reduces stamina without checking the value first."); } private static void Repair(Player player, string site) { float stamina = player.m_stamina; if (stamina > 0f) { if (Reported.Count > 0) { Reported.Remove(player.GetPlayerName()); } } else if (stamina != 0f) { player.m_stamina = 0f; if (Reported.Add(player.GetPlayerName())) { Logger.LogWarning($"Player stamina was {stamina} after {site}; floored to 0. Another mod is " + "reducing stamina without checking the value first. Logged once until it recovers."); } } } [HarmonyPostfix] [HarmonyPatch("AddStamina")] private static void AddStaminaPostfix(Player __instance) { if (Enabled != null && Enabled.Value) { Repair(__instance, "AddStamina"); } } [HarmonyPrefix] [HarmonyPatch("RPC_UseStamina")] private static bool RpcUseStaminaPrefix(Player __instance, float v) { if (Enabled == null || !Enabled.Value) { return true; } if (!float.IsNaN(v) && !float.IsInfinity(v)) { return true; } if (ReportedRpc.Add(__instance.GetPlayerName())) { Logger.LogWarning($"Player.RPC_UseStamina was called with {v}; ignoring it. Vanilla would have " + "made this player's stamina permanently NaN. Logged once per player."); } return false; } [HarmonyPostfix] [HarmonyPatch("Load")] private static void LoadPostfix(Player __instance) { if (Enabled != null && Enabled.Value) { Repair(__instance, "Load"); } } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(Projectile))] internal static class ProjectileZeroVelocityPatch { internal static ConfigEntry Enabled; private static readonly MethodInfo LookRotationMethod = AccessTools.Method(typeof(Quaternion), "LookRotation", new Type[1] { typeof(Vector3) }, (Type[])null); private static readonly MethodInfo SafeLookRotationMethod = AccessTools.Method(typeof(ProjectileZeroVelocityPatch), "SafeLookRotation", (Type[])null, (Type[])null); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(ProjectileZeroVelocityPatch), "Fixes - Correctness", "Fix Projectile Rotation Spam", value: true, "Stops the 'Look rotation viewing vector is zero' log spam produced every physics step by each projectile whose velocity reaches exactly zero. Changing this requires a game restart."); } private static Quaternion SafeLookRotation(Vector3 forward) { //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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!(forward == Vector3.zero)) { return Quaternion.LookRotation(forward); } return Quaternion.identity; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("FixedUpdate")] private static IEnumerable FixedUpdateTranspiler(IEnumerable instructions) { if (Enabled == null || !Enabled.Value) { return instructions; } return PatchHelper.ReplaceCalls(instructions, LookRotationMethod, SafeLookRotationMethod, "Projectile.FixedUpdate"); } } [PatchSide(Side.Client)] [HarmonyPatch(typeof(Recipe))] internal static class RecipeGetAmountNrePatch { internal static ConfigEntry Enabled; private static readonly FieldInfo QualityField = AccessTools.Field(typeof(ItemData), "m_quality"); private static readonly MethodInfo SafeQualityMethod = AccessTools.Method(typeof(RecipeGetAmountNrePatch), "SafeQuality", (Type[])null, (Type[])null); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(RecipeGetAmountNrePatch), "Fixes - Correctness", "Fix Recipe Amount Crash", value: true, "Guards the null dereference in Recipe.GetAmount that throws when a 'requires any one of these' recipe is displayed while you carry none of the accepted ingredients. Without it the crafting/upgrade panel breaks. Changing this requires a game restart."); } private static int SafeQuality(ItemData item) { return item?.m_quality ?? 1; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("GetAmount")] private static IEnumerable GetAmountTranspiler(IEnumerable instructions) { if (Enabled == null || !Enabled.Value) { return instructions; } List list = PatchHelper.Copy(instructions); int num = 0; for (int i = 0; i < list.Count; i++) { if (CodeInstructionExtensions.LoadsField(list[i], QualityField, false)) { list[i].opcode = OpCodes.Call; list[i].operand = SafeQualityMethod; num++; } } if (num == 0) { Logger.LogWarning("Recipe.GetAmount: found no m_quality load to guard, so this fix is inactive. Another mod has most likely already rewritten the method - if so, nothing is wrong."); return instructions; } return list; } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZNetScene))] internal static class RemoveObjectsNrePatch { internal static ConfigEntry Enabled; private static readonly List OrphanedKeys = new List(); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(RemoveObjectsNrePatch), "Fixes - Correctness", "Fix Object Unload Crash", value: true, "Recovers from orphaned entries during object unloading instead of throwing. In vanilla a single orphan aborts the whole unload pass every frame, so nothing despawns and the log fills with NullReferenceExceptions."); } [HarmonyPrefix] [HarmonyPatch("RemoveObjects")] private static bool RemoveObjectsPrefix(ZNetScene __instance, List currentNearObjects, List currentDistantObjects, bool __runOriginal) { if (!__runOriginal) { return false; } if (Enabled == null || !Enabled.Value) { return true; } byte b = (byte)(Time.frameCount & 0xFF); for (int i = 0; i < currentNearObjects.Count; i++) { currentNearObjects[i].m_tempRemoveEarmark = b; } for (int j = 0; j < currentDistantObjects.Count; j++) { currentDistantObjects[j].m_tempRemoveEarmark = b; } try { FastPass(__instance, b); } catch (Exception ex) { Logger.LogDebug("Object unload hit an orphaned instance (" + ex.GetType().Name + "); running guarded sweep."); GuardedSweep(__instance, b); } return false; } private static void FastPass(ZNetScene scene, byte earmark) { scene.m_tempRemoved.Clear(); foreach (ZNetView value in scene.m_instances.Values) { if (value.m_zdo.m_tempRemoveEarmark != earmark) { scene.m_tempRemoved.Add(value); } } for (int i = 0; i < scene.m_tempRemoved.Count; i++) { ZNetView obj = scene.m_tempRemoved[i]; ZDO zdo = obj.m_zdo; obj.ResetZDO(); Object.Destroy((Object)(object)((Component)obj).gameObject); if (!zdo.Persistent && zdo.IsOwner()) { ZDOMan.instance.DestroyZDO(zdo); } scene.m_instances.Remove(zdo); } } internal static void GuardedSweep(ZNetScene scene, byte earmark) { scene.m_tempRemoved.Clear(); OrphanedKeys.Clear(); foreach (KeyValuePair instance in scene.m_instances) { ZNetView value = instance.Value; if ((Object)(object)value == (Object)null || value.GetZDO() == null) { OrphanedKeys.Add(instance.Key); if ((Object)(object)value != (Object)null) { try { Object.Destroy((Object)(object)((Component)value).gameObject); } catch (Exception ex) { Logger.LogDebug("Destroying an orphaned view failed: " + ex.GetType().Name + "."); } } } else if (value.GetZDO().TempRemoveEarmark != earmark) { scene.m_tempRemoved.Add(value); } } for (int i = 0; i < scene.m_tempRemoved.Count; i++) { ZNetView obj = scene.m_tempRemoved[i]; ZDO zDO = obj.GetZDO(); obj.ResetZDO(); Object.Destroy((Object)(object)((Component)obj).gameObject); if (!zDO.Persistent && zDO.IsOwner()) { ZDOMan.instance.DestroyZDO(zDO); } scene.m_instances.Remove(zDO); } if (OrphanedKeys.Count > 0) { for (int j = 0; j < OrphanedKeys.Count; j++) { scene.m_instances.Remove(OrphanedKeys[j]); } Logger.LogDebug($"Dropped {OrphanedKeys.Count} orphaned scene instance(s)."); OrphanedKeys.Clear(); } } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(ZSteamSocket))] internal static class SendFailureLogSpamPatch { internal static ConfigEntry Enabled; private static readonly MethodInfo ZLogMethod = AccessTools.Method(typeof(ZLog), "Log", (Type[])null, (Type[])null); private static readonly MethodInfo SinkMethod = AccessTools.Method(typeof(Logger), "DebugSink", (Type[])null, (Type[])null); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(SendFailureLogSpamPatch), "Fixes - Correctness", "Fix Send Failure Log Spam", value: true, "Stops 'Failed to send data' being written to the game log once per socket per frame whenever a peer's send queue backs up - which is exactly when the server can least afford it. The message is still visible with EnableDebugMode on. Changing this requires a game restart."); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("SendQueuedPackages")] private static IEnumerable SendQueuedPackagesTranspiler(IEnumerable instructions) { if (Enabled == null || !Enabled.Value) { return instructions; } return PatchHelper.ReplaceCalls(instructions, ZLogMethod, SinkMethod, "ZSteamSocket.SendQueuedPackages"); } } [PatchSide(Side.Both)] [HarmonyPatch(typeof(SpawnArea))] internal static class SpawnAreaNullPrefabPatch { internal static ConfigEntry Enabled; private static readonly Predicate IsNullPrefab = (SpawnData data) => data == null || (Object)(object)data.m_prefab == (Object)null; internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(SpawnAreaNullPrefabPatch), "Fixes - Correctness", "Fix Spawner Null Prefabs", value: true, "Removes null entries from spawner (SpawnArea) tables on load. Without it a single missing creature prefab - common after a game update or a removed creature mod - throws during spawn selection and silently kills that spawner."); } [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(SpawnArea __instance) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (Enabled != null && Enabled.Value && __instance.m_prefabs != null) { int num = __instance.m_prefabs.RemoveAll(IsNullPrefab); if (num > 0) { Logger.LogInfo($"Removed {num} null spawn entries from {((Object)__instance).name} at {((Component)__instance).transform.position}."); } } } } [PatchSide(Side.Server)] [HarmonyPatch(typeof(ZDOMan))] internal static class ZdoLoadDuplicatePatch { internal static ConfigEntry Enabled; private static readonly MethodInfo DictionaryAddMethod = AccessTools.Method(typeof(Dictionary), "Add", (Type[])null, (Type[])null); private static readonly MethodInfo AddOrReplaceMethod = AccessTools.Method(typeof(ZdoLoadDuplicatePatch), "AddOrReplace", (Type[])null, (Type[])null); internal static void BindConfig() { Enabled = ValConfig.BindFixToggle(typeof(ZdoLoadDuplicatePatch), "Fixes - Correctness", "Tolerate Duplicate ZDOs On Load", value: true, "Recovers a world whose save contains duplicate ZDO ids instead of aborting the load with an exception. Changing this requires a game restart."); } private static void AddOrReplace(Dictionary objectsById, ZDOID uid, ZDO zdo) { //IL_0001: 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_000e: Unknown result type (might be due to invalid IL or missing references) if (objectsById.ContainsKey(uid)) { Logger.LogWarning($"Duplicate ZDO id {uid} in the save file; keeping the later one. " + "This world was saved in a damaged state, but loading will continue."); } objectsById[uid] = zdo; } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("Load")] private static IEnumerable LoadTranspiler(IEnumerable instructions) { if (Enabled == null || !Enabled.Value) { return instructions; } return PatchHelper.ReplaceCalls(instructions, DictionaryAddMethod, AddOrReplaceMethod, "ZDOMan.Load", 3); } [HarmonyTranspiler] [HarmonyPriority(0)] [HarmonyPatch("LoadChunks")] private static IEnumerable LoadChunksTranspiler(IEnumerable instructions) { if (Enabled == null || !Enabled.Value) { return instructions; } return PatchHelper.ReplaceCalls(instructions, DictionaryAddMethod, AddOrReplaceMethod, "ZDOMan.LoadChunks", 2); } } }