Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HighSeat v0.2.2
plugins\HighSeat.dll
Decompiled 5 days agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ConfigReload; using HarmonyLib; using Microsoft.CodeAnalysis; using PatchGuard; using Steamworks; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("HighSeat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("HighSeat: the server runs every creature near a player")] [assembly: AssemblyFileVersion("0.2.2.0")] [assembly: AssemblyInformationalVersion("0.2.2+c059968f7e2f4c97229c435352912d3c276ffd65")] [assembly: AssemblyProduct("HighSeat")] [assembly: AssemblyTitle("HighSeat")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.2.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace HighSeat { [HarmonyPatch(typeof(ZNetScene), "OutsideActiveArea", new Type[] { typeof(Vector3) })] internal static class ActiveArea { private static void Postfix(Vector3 point, ref bool __result) { //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) if (!__result || !ServerRun.Active) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady() && ZNetScene.InActiveArea(point, peer.GetRefPos())) { __result = false; break; } } } } [HarmonyPatch(typeof(BaseAI), "Awake")] internal static class AlwaysAnimate { private static void Postfix(BaseAI __instance) { if (ServerRun.Active) { Animator[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<Animator>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].cullingMode = (AnimatorCullingMode)0; } } } } internal static class FrameGuard { private const float Smoothing = 0.05f; private static float _average = 1f / 60f; private static bool _warned; public static bool Busy { get; private set; } public static void Measure() { if (ServerRun.Active) { _average += (Time.unscaledDeltaTime - _average) * 0.05f; bool num = _average * 1000f > (float)Settings.FrameBudgetMs.Value; if (num && !_warned) { HighSeat.Log.LogWarning((object)($"server frames average {_average * 1000f:0} ms, over the {Settings.FrameBudgetMs.Value} ms " + "budget: new creatures are left to players until it recovers")); } _warned = num; Busy = num; } } } [BepInPlugin("com.HighSeat", "HighSeat", "0.2.2")] public class HighSeat : BaseUnityPlugin { public const string PluginGuid = "com.HighSeat"; public const string PluginName = "HighSeat"; public const string PluginVersion = "0.2.2"; public static ManualLogSource Log { get; private set; } private void Awake() { //IL_002e: 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_004e: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Settings.Bind(((BaseUnityPlugin)this).Config); ConfigReloader.Setup(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); Harmony val = new Harmony("com.HighSeat"); val.PatchAll(Assembly.GetExecutingAssembly()); Guard.Install(val, ((BaseUnityPlugin)this).Logger, Assembly.GetExecutingAssembly()); } private void Update() { FrameGuard.Measure(); } } [HarmonyPatch(typeof(ZDOMan), "ReleaseNearbyZDOS")] internal static class Ownership { private static readonly List<ZDO> Near = new List<ZDO>(); private static bool Prefix(ZDOMan __instance, Vector3 refPosition, long uid) { //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_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_0026: 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_0083: Unknown result type (might be due to invalid IL or missing references) if (!ServerRun.Active) { return true; } Vector2s zone = ZoneSystem.GetZone(refPosition); SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance(); Near.Clear(); __instance.FindSectorObjects(zone, new SimulationDistance(((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance, 0, ((SimulationDistance)(ref syncedSimulationDistance)).IsClassic), Near, (List<ZDO>)null); long sessionID = ZDOMan.GetSessionID(); bool take = !FrameGuard.Busy; foreach (ZDO item in Near) { if (item.Persistent && !Claim(item, sessionID, take)) { Vanilla(__instance, item, zone, uid); } } Near.Clear(); return false; } private static bool Claim(ZDO zdo, long server, bool take) { if (!ServerRun.Is(zdo)) { return false; } if (zdo.GetOwner() == server) { return true; } if (!take) { return false; } zdo.SetOwner(server); return true; } private static void Vanilla(ZDOMan man, ZDO zdo, Vector2s zone, long uid) { //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_0010: 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_0039: 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_002b: Unknown result type (might be due to invalid IL or missing references) Vector3 position = zdo.GetPosition(); if (zdo.GetOwner() == uid) { if (!ZNetScene.InActiveArea(position, zone)) { zdo.SetOwner(0L); } } else if ((!zdo.HasOwner() || !man.IsInPeerActiveArea(position, zdo.GetOwner())) && ZNetScene.InActiveArea(position, zone)) { zdo.SetOwner(uid); } } } [HarmonyPatch(typeof(ZNetScene), "CreateDestroyObjects")] internal static class PlayerAreas { private static List<ZDO> Near = null; private static List<ZDO> Distant = null; private static readonly HashSet<SectorIndex> Visited = new HashSet<SectorIndex>(); private static bool Prefix(ZNetScene __instance) { if (!ServerRun.Active) { return true; } Near = __instance.m_tempCurrentObjects; Distant = __instance.m_tempCurrentDistantObjects; Near.Clear(); Distant.Clear(); Collect(); __instance.CreateObjects(Near, Distant); __instance.RemoveObjects(Near, Distant); return false; } private static void Collect() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //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) //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_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_005a: 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_0087: Unknown result type (might be due to invalid IL or missing references) Visited.Clear(); SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance(); Vector2s zone = ZoneSystem.GetZone(ZNet.instance.GetReferencePosition()); AddNear(zone, syncedSimulationDistance, groundFirst: false); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady()) { AddNear(ZoneSystem.GetZone(peer.GetRefPos()), syncedSimulationDistance, groundFirst: true); } } if (!ZNet.instance.IsDedicated()) { AddDistant(zone, syncedSimulationDistance); } } private static void AddNear(Vector2s center, SimulationDistance sim, bool groundFirst) { //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_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_0065: 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) int nearSimulationDistance = ((SimulationDistance)(ref sim)).NearSimulationDistance; Vector2s val = default(Vector2s); for (int i = -nearSimulationDistance; i <= nearSimulationDistance; i++) { for (int j = -nearSimulationDistance; j <= nearSimulationDistance; j++) { ((Vector2s)(ref val))..ctor(center.x + j, center.y + i); if (((j == 0 && i == 0) || ((SimulationDistance)(ref sim)).IsClassic || ZoneSystem.instance.ZonesWithinRadius(center, val, nearSimulationDistance, false)) && (!groundFirst || ZoneSystem.instance.m_zones.ContainsKey(val))) { ZDOMan.instance.FindObjects(val, Near, Visited); } } } } private static void AddDistant(Vector2s center, SimulationDistance sim) { //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_0047: 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) int totalSimulationDistance = ((SimulationDistance)(ref sim)).TotalSimulationDistance; Vector2s val = default(Vector2s); for (int i = -totalSimulationDistance; i <= totalSimulationDistance; i++) { for (int j = -totalSimulationDistance; j <= totalSimulationDistance; j++) { ((Vector2s)(ref val))..ctor(center.x + j, center.y + i); if (((SimulationDistance)(ref sim)).IsClassic || ZoneSystem.instance.ZonesWithinRadius(center, val, totalSimulationDistance, true)) { ZDOMan.instance.FindDistantObjects(val, Distant, Visited); } } } } } [HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")] internal static class SendRate { private const int Rate = 512000; private static bool GameServer => Application.isBatchMode; private static void Postfix() { try { Raise(); } catch (Exception ex) { HighSeat.Log.LogWarning((object)("send rate left as it was: " + ex.Message)); } } private static void Raise() { IntPtr intPtr = Marshal.AllocHGlobal(4); try { if (Current(intPtr) < 512000) { Marshal.WriteInt32(intPtr, 512000); Set((ESteamNetworkingConfigValue)10, intPtr); Set((ESteamNetworkingConfigValue)11, intPtr); } } finally { Marshal.FreeHGlobal(intPtr); } } private static int Current(IntPtr data) { //IL_0005: 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_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_002e: 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_0010: 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_0034: Invalid comparison between Unknown and I4 ulong num = 4uL; ESteamNetworkingConfigValue val = (ESteamNetworkingConfigValue)11; ESteamNetworkingConfigScope val2 = (ESteamNetworkingConfigScope)1; ESteamNetworkingConfigDataType val3 = default(ESteamNetworkingConfigDataType); if ((int)(GameServer ? SteamGameServerNetworkingUtils.GetConfigValue(val, val2, IntPtr.Zero, ref val3, data, ref num) : SteamNetworkingUtils.GetConfigValue(val, val2, IntPtr.Zero, ref val3, data, ref num)) != 1) { return 0; } return Marshal.ReadInt32(data); } private static void Set(ESteamNetworkingConfigValue value, IntPtr data) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: 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_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) //IL_000b: 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) ESteamNetworkingConfigScope val = (ESteamNetworkingConfigScope)1; ESteamNetworkingConfigDataType val2 = (ESteamNetworkingConfigDataType)1; if (GameServer) { SteamGameServerNetworkingUtils.SetConfigValue(value, val, IntPtr.Zero, val2, data); } else { SteamNetworkingUtils.SetConfigValue(value, val, IntPtr.Zero, val2, data); } } } internal static class ServerRun { private static readonly Dictionary<int, bool> Kinds = new Dictionary<int, bool>(); public static bool Active { get { if (Settings.ServerRunsCreatures.Value && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public static bool Is(ZDO zdo) { int prefab = zdo.GetPrefab(); if (!Kinds.TryGetValue(prefab, out var value)) { value = Decide(prefab); Kinds[prefab] = value; } return value; } private static bool Decide(int prefab) { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<Player>() != (Object)null || (Object)(object)val.GetComponent<Fish>() != (Object)null) { return false; } if (!((Object)(object)val.GetComponent<BaseAI>() != (Object)null) && !((Object)(object)val.GetComponent<SpawnSystem>() != (Object)null) && !((Object)(object)val.GetComponent<CreatureSpawner>() != (Object)null) && !((Object)(object)val.GetComponent<SpawnArea>() != (Object)null) && !((Object)(object)val.GetComponent<TriggerSpawner>() != (Object)null)) { return (Object)(object)val.GetComponent<OfferingBowl>() != (Object)null; } return true; } } [HarmonyPatch(typeof(SpawnSystem), "UpdateSpawning")] internal static class ServerSpawning { private static List<Player> InZone => SpawnSystem.m_tempNearPlayers; private static bool Prefix(SpawnSystem __instance) { if (!ServerRun.Active) { return true; } if (__instance.m_nview.IsValid() && __instance.m_nview.IsOwner()) { InZone.Clear(); __instance.GetPlayersInZone(InZone); if (InZone.Count > 0) { Spawn(__instance, ZNet.instance.GetTime()); } InZone.Clear(); } return false; } private static void Spawn(SpawnSystem system, DateTime time) { foreach (SpawnSystemList spawnList in system.m_spawnLists) { system.UpdateSpawnList(spawnList.m_spawners, time, false, "b_"); } List<SpawnData> list = RaidSpawners(); if (list != null) { system.UpdateSpawnList(list, time, true, "e_"); } for (int i = 0; i < system.m_heightmap.m_cornerAltBiomes.Count; i++) { AltBiome val = system.m_heightmap.m_cornerAltBiomes[i]; if (val.m_spawn.Count > 0) { system.UpdateSpawnList(val.m_spawn, time, false, $"m{i}"); } } } private static List<SpawnData> RaidSpawners() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) RandEventSystem instance = RandEventSystem.instance; RandomEvent val = instance.m_forcedEvent ?? instance.m_randomEvent; if (val == null || val.m_time <= val.m_spawnerDelay) { return null; } if (instance.m_forcedEvent != null) { return val.m_spawn; } foreach (Player item in InZone) { if (instance.IsInsideRandomEventArea(val, ((Component)item).transform.position)) { return val.m_spawn; } } return null; } } internal static class Settings { private const string General = "1 - General"; public static ConfigEntry<bool> ServerRunsCreatures; public static ConfigEntry<int> FrameBudgetMs; public static void Bind(ConfigFile config) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown ServerRunsCreatures = config.Bind<bool>("1 - General", "Server runs creatures", true, "The server loads every player's area and runs every creature and spawner in it, tamed creatures and bosses included. Off: the game's own rule, where the nearest player's game runs each creature."); FrameBudgetMs = config.Bind<int>("1 - General", "Server frame budget ms", 50, new ConfigDescription("While the server's frames take longer than this, creatures it does not run yet are left to players as the game does; those it runs stay with it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 500), Array.Empty<object>())); } } [HarmonyPatch(typeof(ZoneSystem), "UpdateTTL")] internal static class ZoneKeeping { private static void Prefix(ZoneSystem __instance) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!ServerRun.Active) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady()) { __instance.CreateLocalZones(peer.GetRefPos()); } } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace ConfigReload { internal static class ConfigReloader { private sealed class Poller : IDisposable { private const int IntervalMilliseconds = 5000; private readonly ConfigFile config; private readonly ManualLogSource? log; private readonly string fileName; private readonly Timer timer; private DateTime lastWriteTime; private long lastLength; private string lastContent; private int reloading; public Poller(ConfigFile config, ManualLogSource? log) { this.config = config; this.log = log; fileName = Path.GetFileName(config.ConfigFilePath); Snapshot(out lastWriteTime, out lastLength); lastContent = ReadOrNull(config.ConfigFilePath) ?? ""; timer = new Timer(delegate { Poll(); }, null, 5000, 5000); } private void Poll() { try { if (SnapshotChanged()) { string text = ReadOrNull(config.ConfigFilePath); if (text != null && !(text == lastContent)) { lastContent = text; ScheduleCheck(); } } } catch (Exception ex) { ManualLogSource? obj = log; if (obj != null) { obj.LogError((object)("Watching " + fileName + " failed: " + ex.Message)); } } } private bool SnapshotChanged() { Snapshot(out var writeTime, out var length); if (writeTime == lastWriteTime && length == lastLength) { return false; } lastWriteTime = writeTime; lastLength = length; return true; } private void ScheduleCheck() { if (Interlocked.CompareExchange(ref reloading, 1, 0) == 0) { ThreadingHelper.SynchronizingObject.BeginInvoke(new Action(CheckAndReload), null); } } private void CheckAndReload() { try { if (!LoadedValues.Matches(config, lastContent)) { ManualLogSource? obj = log; if (obj != null) { obj.LogInfo((object)(fileName + " changed on disk, reloading")); } ReloadQuietly(); } } catch (Exception ex) { ManualLogSource? obj2 = log; if (obj2 != null) { obj2.LogError((object)("Failed to reload " + fileName + ", please check it for typos and formatting: " + ex.Message)); } } finally { Resnapshot(); Interlocked.Exchange(ref reloading, 0); } } private void ReloadQuietly() { bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { config.Reload(); } finally { config.SaveOnConfigSet = saveOnConfigSet; } if (saveOnConfigSet) { config.Save(); } } private void Resnapshot() { Snapshot(out lastWriteTime, out lastLength); lastContent = ReadOrNull(config.ConfigFilePath) ?? lastContent; } private void Snapshot(out DateTime writeTime, out long length) { FileInfo fileInfo = new FileInfo(config.ConfigFilePath); if (fileInfo.Exists) { writeTime = fileInfo.LastWriteTimeUtc; length = fileInfo.Length; } else { writeTime = DateTime.MinValue; length = -1L; } } private static string? ReadOrNull(string path) { try { return File.Exists(path) ? File.ReadAllText(path) : null; } catch (IOException) { return null; } } public void Dispose() { timer.Dispose(); } } private static readonly HashSet<ConfigFile> held = new HashSet<ConfigFile>(); public static void HoldSaves(ConfigFile config) { if (config.SaveOnConfigSet && held.Add(config)) { config.SaveOnConfigSet = false; } } public static IDisposable Setup(ConfigFile config, ManualLogSource? log = null, bool saveNow = true) { if (held.Remove(config)) { config.SaveOnConfigSet = true; } if (saveNow) { config.Save(); } return new Poller(config, log); } } internal static class LoadedValues { private static readonly PropertyInfo? orphans = typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic); public static bool Matches(ConfigFile config, string content) { Dictionary<ConfigDefinition, string> dictionary = Parse(content); Dictionary<ConfigDefinition, string> dictionary2 = Loaded(config); if (dictionary2 == null) { return false; } if (dictionary.Count != dictionary2.Count) { return false; } foreach (KeyValuePair<ConfigDefinition, string> item in dictionary) { if (!dictionary2.TryGetValue(item.Key, out var value) || !string.Equals(value, item.Value, StringComparison.Ordinal)) { return false; } } return true; } private static Dictionary<ConfigDefinition, string>? Loaded(ConfigFile config) { if (!(orphans?.GetValue(config) is Dictionary<ConfigDefinition, string> dictionary)) { return null; } Dictionary<ConfigDefinition, string> dictionary2 = new Dictionary<ConfigDefinition, string>(dictionary); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config) { dictionary2[item.Key] = item.Value.GetSerializedValue(); } return dictionary2; } private static Dictionary<ConfigDefinition, string> Parse(string content) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown Dictionary<ConfigDefinition, string> dictionary = new Dictionary<ConfigDefinition, string>(); string text = ""; string[] array = content.Split(new char[1] { '\n' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0 || text2.StartsWith("#", StringComparison.Ordinal)) { continue; } if (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal)) { text = text2.Substring(1, text2.Length - 2); continue; } int num = text2.IndexOf('='); if (num > 0) { dictionary[new ConfigDefinition(text, text2.Substring(0, num).Trim())] = text2.Substring(num + 1).Trim(); } } return dictionary; } } } namespace PatchGuard { internal static class Guard { private const string Marker = "PatchGuard.Logged"; private static ManualLogSource? log; private static Assembly? modAssembly; private static string[] namespaces = Array.Empty<string>(); public static int Install(Harmony harmony, ManualLogSource logger, Assembly assembly) { log = logger; modAssembly = assembly; namespaces = RootNamespaces(assembly); return 0; } public static void Run(string context, Action action) { try { action(); } catch (Exception exception) { Report(exception, context); throw; } } public static T Run<T>(string context, Func<T> func) { try { return func(); } catch (Exception exception) { Report(exception, context); throw; } } public static void Run<TArg>(string context, Action<TArg> action, TArg arg) { try { action(arg); } catch (Exception exception) { Report(exception, context); throw; } } public static void Run<T1, T2>(string context, Action<T1, T2> action, T1 first, T2 second) { try { action(first, second); } catch (Exception exception) { Report(exception, context); throw; } } public static T Run<TArg, T>(string context, Func<TArg, T> func, TArg arg) { try { return func(arg); } catch (Exception exception) { Report(exception, context); throw; } } public static T Run<T1, T2, T>(string context, Func<T1, T2, T> func, T1 first, T2 second) { try { return func(first, second); } catch (Exception exception) { Report(exception, context); throw; } } public static Action Wrap(string context, Action action) { return delegate { Run(context, action); }; } public static EventHandler Wrap(string context, EventHandler handler) { return delegate(object sender, EventArgs args) { Run(context, delegate { handler(sender, args); }); }; } public static void Report(Exception exception, string context) { if (log != null && !exception.Data.Contains("PatchGuard.Logged") && IsFromMod(exception)) { try { exception.Data["PatchGuard.Logged"] = true; } catch (Exception) { } log.LogError((object)$"Exception in {context} (thrown by this mod's code):\n{exception}"); } } internal static bool HasPatchFrom(MethodBase method, Assembly assembly) { Patches patchInfo = Harmony.GetPatchInfo(method); return patchInfo?.Prefixes.Concat(patchInfo.Postfixes).Concat(patchInfo.Transpilers).Concat(patchInfo.Finalizers) .Any((Patch p) => p.PatchMethod.DeclaringType?.Assembly == assembly) ?? false; } private static bool IsFromMod(Exception exception) { for (Exception ex = exception; ex != null; ex = ex.InnerException) { StackFrame[] array = null; try { array = new StackTrace(ex, fNeedFileInfo: false).GetFrames(); } catch (Exception) { } if (array != null && array.Any((StackFrame f) => f.GetMethod()?.DeclaringType?.Assembly == modAssembly)) { return true; } string text = ex.StackTrace; if (text != null && namespaces.Any((string ns) => text.Contains(" " + ns + ".") || text.Contains("<" + ns + "."))) { return true; } } return false; } private static string[] RootNamespaces(Assembly assembly) { try { return (from t in assembly.GetTypes() select t.Namespace into ns where !string.IsNullOrEmpty(ns) select ns.Split(new char[1] { '.' })[0]).Distinct().ToArray(); } catch (ReflectionTypeLoadException ex) { return (from t in ex.Types where t?.Namespace != null select t.Namespace.Split(new char[1] { '.' })[0]).Distinct().ToArray(); } } internal static string Describe(MethodBase method) { return method.DeclaringType?.Name + "." + method.Name; } } internal static class Profiler { private sealed class Sample { public long Ticks; public long Calls; } private static ManualLogSource? log; private static readonly HashSet<MethodBase> profiled = new HashSet<MethodBase>(); private static readonly HashSet<MethodBase> ownPatches = new HashSet<MethodBase>(); private static readonly ConcurrentDictionary<MethodBase, Sample> samples = new ConcurrentDictionary<MethodBase, Sample>(); private static int intervalSeconds; [ThreadStatic] private static Stack<long>? starts; public static IDisposable Install(Harmony harmony, ManualLogSource logger, Assembly assembly, int reportEverySeconds = 5, int top = 12) { //IL_0029: 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_003a: Expected O, but got Unknown //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_0057: Expected O, but got Unknown log = logger; intervalSeconds = reportEverySeconds; HarmonyMethod prefix = new HarmonyMethod(typeof(Profiler), "Prefix", (Type[])null) { priority = 800 }; HarmonyMethod postfix = new HarmonyMethod(typeof(Profiler), "Postfix", (Type[])null) { priority = 0 }; int num = TimeGameMethods(harmony, assembly, prefix, postfix); logger.LogInfo((object)string.Format(arg1: TimeOwnPatches(harmony, assembly, prefix, postfix), format: "PatchGuard profiler: timing {0} patched game methods and {1} of the mod's own patch methods, report every {2} s", arg0: num, arg2: reportEverySeconds)); return new Timer(delegate { Report(top); }, null, reportEverySeconds * 1000, reportEverySeconds * 1000); } private static int TimeGameMethods(Harmony harmony, Assembly assembly, HarmonyMethod prefix, HarmonyMethod postfix) { int num = 0; foreach (MethodBase item in Harmony.GetAllPatchedMethods().ToList()) { if (!profiled.Contains(item) && Guard.HasPatchFrom(item, assembly) && TryTime(harmony, item, prefix, postfix, "")) { profiled.Add(item); num++; } } return num; } private static int TimeOwnPatches(Harmony harmony, Assembly assembly, HarmonyMethod prefix, HarmonyMethod postfix) { int num = 0; foreach (MethodInfo item in OwnPatchMethods(assembly)) { if (ownPatches.Add(item) && TryTime(harmony, item, prefix, postfix, "patch ")) { num++; } } return num; } private static IEnumerable<MethodInfo> OwnPatchMethods(Assembly assembly) { foreach (MethodBase item in profiled.ToList()) { Patches patchInfo = Harmony.GetPatchInfo(item); if (patchInfo == null) { continue; } foreach (Patch item2 in patchInfo.Prefixes.Concat(patchInfo.Postfixes).Concat(patchInfo.Finalizers)) { MethodInfo patchMethod = item2.PatchMethod; if (patchMethod.DeclaringType?.Assembly == assembly && patchMethod.DeclaringType != typeof(Profiler)) { yield return patchMethod; } } } } private static bool TryTime(Harmony harmony, MethodBase method, HarmonyMethod prefix, HarmonyMethod postfix, string kind) { try { harmony.Patch(method, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { ManualLogSource? obj = log; if (obj != null) { obj.LogWarning((object)("PatchGuard profiler: could not time " + kind + Guard.Describe(method) + ": " + ex.Message)); } return false; } } private static void Prefix() { if (starts == null) { starts = new Stack<long>(); } starts.Push(Stopwatch.GetTimestamp()); } private static void Postfix(MethodBase __originalMethod) { if (starts != null && starts.Count != 0) { long value = Stopwatch.GetTimestamp() - starts.Pop(); Sample orAdd = samples.GetOrAdd(__originalMethod, (MethodBase _) => new Sample()); Interlocked.Add(ref orAdd.Ticks, value); Interlocked.Increment(ref orAdd.Calls); } } private static void Report(int top) { try { KeyValuePair<MethodBase, Sample>[] array = samples.ToArray(); samples.Clear(); if (array.Length != 0) { double toMs = 1000.0 / (double)Stopwatch.Frequency; double num = (double)array.Sum((KeyValuePair<MethodBase, Sample> kv) => kv.Value.Ticks) * toMs; IEnumerable<string> values = from kv in array.OrderByDescending((KeyValuePair<MethodBase, Sample> kv) => kv.Value.Ticks).Take(top) select string.Format("{0,8:0.0} ms {1}{2} x{3}", (double)kv.Value.Ticks * toMs, ownPatches.Contains(kv.Key) ? "[mod patch] " : "", Guard.Describe(kv.Key), kv.Value.Calls); ManualLogSource? obj = log; if (obj != null) { obj.LogInfo((object)($"PatchGuard profile: {num:0} ms inside patched methods in the last {intervalSeconds} s ({num / ((double)intervalSeconds * 10.0):0.0}% of wall time)\n " + string.Join("\n ", values))); } } } catch (Exception ex) { ManualLogSource? obj2 = log; if (obj2 != null) { obj2.LogWarning((object)("PatchGuard profiler report failed: " + ex.Message)); } } } } }