Decompiled source of GroundReset v2.7.1
GroundReset.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
#define DEBUG using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CodeMonkey; using GroundReset.Compatibility.WardIsLove; using GroundReset.Compatibility.kgMarketplace; using GroundReset.Patch; using HarmonyLib; using JFUtils; using JFUtils.Valheim.WithPatch; using JFUtils.WithPatch; using JetBrains.Annotations; using Marketplace.Modules.TerritorySystem; using Microsoft.CodeAnalysis; using ServerSync; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: <35c48fc9-d1a9-4c43-b2d6-1899d1b37e6c>IgnoresAccessChecksTo("assembly_guiutils")] [assembly: <35c48fc9-d1a9-4c43-b2d6-1899d1b37e6c>IgnoresAccessChecksTo("assembly_utils")] [assembly: <35c48fc9-d1a9-4c43-b2d6-1899d1b37e6c>IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("GroundReset")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("2.7.0")] [assembly: AssemblyInformationalVersion("1.0.0+aa5696804342b30ae83b7247ffc394e22e210653")] [assembly: AssemblyProduct("GroundReset")] [assembly: AssemblyTitle("GroundReset")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.7.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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] [Microsoft.CodeAnalysis.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; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 CodeMonkey { public class FunctionTimer { private class MonoBehaviourHook : MonoBehaviour { public Action? OnUpdate; private void Update() { OnUpdate?.Invoke(); } } public class FunctionTimerObject { private readonly Action callback; private float timer; public FunctionTimerObject(Action callback, float timer) { this.callback = callback; this.timer = timer; } public bool Update() { return Update(Time.deltaTime); } public bool Update(float deltaTime) { timer -= deltaTime; if (timer <= 0f) { callback(); return true; } return false; } } private static List<FunctionTimer>? TimerList; private static GameObject? InitGameObject; private readonly string functionName = "NoneNameTimer"; private readonly GameObject gameObject; private readonly Action onEndAction; private readonly bool useUnscaledDeltaTime; public float Timer { get; private set; } public FunctionTimer(GameObject gameObject, Action action, float timer, string functionName, bool useUnscaledDeltaTime) { this.gameObject = gameObject; onEndAction = action; Timer = timer; this.functionName = functionName; this.useUnscaledDeltaTime = useUnscaledDeltaTime; } private static void InitIfNeeded() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown if ((Object)(object)InitGameObject == (Object)null) { InitGameObject = new GameObject("FunctionTimer_Global"); TimerList = new List<FunctionTimer>(); } } public static FunctionTimer? Create(Action action, float timer) { return Create(action, timer, "", useUnscaledDeltaTime: false, stopAllWithSameName: false); } public static FunctionTimer? Create(Action action, float timer, string functionName) { return Create(action, timer, functionName, useUnscaledDeltaTime: false, stopAllWithSameName: false); } public static FunctionTimer? Create(Action action, float timer, string functionName, bool useUnscaledDeltaTime) { return Create(action, timer, functionName, useUnscaledDeltaTime, stopAllWithSameName: false); } public static FunctionTimer Create(Action? action, float timer, string functionName, bool useUnscaledDeltaTime, bool stopAllWithSameName) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown if (TimerList == null) { throw new NullReferenceException("TimerList"); } if (action == null) { throw new ArgumentNullException("action"); } InitIfNeeded(); if (stopAllWithSameName) { StopAllTimersWithName(functionName); } GameObject val = new GameObject("FunctionTimer Object " + functionName, new Type[1] { typeof(MonoBehaviourHook) }); FunctionTimer functionTimer = new FunctionTimer(val, action, timer, functionName, useUnscaledDeltaTime); val.GetComponent<MonoBehaviourHook>().OnUpdate = functionTimer.Update; TimerList.Add(functionTimer); return functionTimer; } public static void RemoveTimer(FunctionTimer funcTimer) { InitIfNeeded(); TimerList.Remove(funcTimer); } public static void StopAllTimersWithName(string functionName) { InitIfNeeded(); checked { for (int i = 0; i < TimerList.Count; i++) { if (TimerList[i].functionName == functionName) { TimerList[i].DestroySelf(); i--; } } } } public static void StopFirstTimerWithName(string functionName) { InitIfNeeded(); for (int i = 0; i < TimerList.Count; i = checked(i + 1)) { if (TimerList[i].functionName == functionName) { TimerList[i].DestroySelf(); break; } } } private void Update() { if (useUnscaledDeltaTime) { Timer -= Time.unscaledDeltaTime; } else { Timer -= Time.deltaTime; } if (Timer <= 0f) { onEndAction(); DestroySelf(); } } private void DestroySelf() { RemoveTimer(this); if ((Object)(object)gameObject != (Object)null) { Object.Destroy((Object)(object)gameObject); } } public static FunctionTimerObject CreateObject(Action callback, float timer) { return new FunctionTimerObject(callback, timer); } } public class FunctionUpdater { private class MonoBehaviourHook : MonoBehaviour { public Action? OnUpdate; private void Update() { if (OnUpdate != null) { OnUpdate(); } } } private static List<FunctionUpdater>? UpdaterList; private static GameObject? InitGameObject; private readonly string functionName; private readonly GameObject gameObject; private readonly Func<bool> updateFunc; private bool active; public FunctionUpdater(GameObject gameObject, Func<bool> updateFunc, string functionName, bool active) { this.gameObject = gameObject; this.updateFunc = updateFunc; this.functionName = functionName; this.active = active; } private static void InitIfNeeded() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown if ((Object)(object)InitGameObject == (Object)null) { InitGameObject = new GameObject("FunctionUpdater_Global"); UpdaterList = new List<FunctionUpdater>(); } } public static FunctionUpdater Create(Action updateFunc) { Action updateFunc2 = updateFunc; return Create(delegate { updateFunc2(); return false; }, "", active: true, stopAllWithSameName: false); } public static FunctionUpdater Create(Action updateFunc, string functionName) { Action updateFunc2 = updateFunc; return Create(delegate { updateFunc2(); return false; }, functionName, active: true, stopAllWithSameName: false); } public static FunctionUpdater Create(Func<bool> updateFunc) { return Create(updateFunc, "", active: true, stopAllWithSameName: false); } public static FunctionUpdater Create(Func<bool> updateFunc, string functionName) { return Create(updateFunc, functionName, active: true, stopAllWithSameName: false); } public static FunctionUpdater Create(Func<bool> updateFunc, string functionName, bool active) { return Create(updateFunc, functionName, active, stopAllWithSameName: false); } public static FunctionUpdater Create(Func<bool> updateFunc, string functionName, bool active, bool stopAllWithSameName) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown InitIfNeeded(); if (stopAllWithSameName) { StopAllUpdatersWithName(functionName); } GameObject val = new GameObject("FunctionUpdater Object " + functionName, new Type[1] { typeof(MonoBehaviourHook) }); FunctionUpdater functionUpdater = new FunctionUpdater(val, updateFunc, functionName, active); val.GetComponent<MonoBehaviourHook>().OnUpdate = functionUpdater.Update; UpdaterList.Add(functionUpdater); return functionUpdater; } private static void RemoveUpdater(FunctionUpdater funcUpdater) { InitIfNeeded(); UpdaterList.Remove(funcUpdater); } public static void DestroyUpdater(FunctionUpdater? funcUpdater) { InitIfNeeded(); funcUpdater?.DestroySelf(); } public static void StopUpdaterWithName(string functionName) { InitIfNeeded(); for (int i = 0; i < UpdaterList.Count; i = checked(i + 1)) { if (UpdaterList[i].functionName == functionName) { UpdaterList[i].DestroySelf(); break; } } } public static void StopAllUpdatersWithName(string functionName) { InitIfNeeded(); checked { for (int i = 0; i < UpdaterList.Count; i++) { if (UpdaterList[i].functionName == functionName) { UpdaterList[i].DestroySelf(); i--; } } } } public void Pause() { active = false; } public void Resume() { active = true; } private void Update() { if (active && updateFunc()) { DestroySelf(); } } public void DestroySelf() { RemoveUpdater(this); if ((Object)(object)gameObject != (Object)null) { Object.Destroy((Object)(object)gameObject); } } } } namespace GroundReset { [Serializable] public class ChunkData { public bool[] m_modifiedHeight; public float[] m_levelDelta; public float[] m_smoothDelta; public bool[] m_modifiedPaint; public Color[] m_paintMask; public float m_lastOpRadius; public Vector3 m_lastOpPoint; public int m_operations; public ChunkData() { checked { int num = Reseter.HeightmapWidth + 1; m_modifiedHeight = new bool[num * num]; m_levelDelta = new float[num * num]; m_smoothDelta = new float[num * num]; m_modifiedPaint = new bool[num * num]; m_paintMask = (Color[])(object)new Color[num * num]; } } } public class ConfigsContainer { [CompilerGenerated] [DebuggerBrowsable(DebuggerBrowsableState.Never)] private static ConfigsContainer <Instance>k__BackingField = null; private static bool IsInitialized = false; private static DateTime LastConfigUpdateTime = DateTime.MinValue; private readonly ConfigEntry<float> _triggerIntervalInMinutesConfig; private readonly ConfigEntry<float> _dividerConfig; private readonly ConfigEntry<float> _minHeightToSteppedResetConfig; private readonly ConfigEntry<float> _paintsCompareToleranceConfig; private readonly ConfigEntry<string> _paintsToIgnoreConfig; private readonly ConfigEntry<bool> _resetSmoothingConfig; private readonly ConfigEntry<bool> _resetPaintResetLastlyConfig; private float _lastTriggerIntervalInMinutes = -1f; private readonly List<Color> _paintsToIgnore = new List<Color>(); private readonly Dictionary<string, Color> vanillaPresets = new Dictionary<string, Color> { { "Dirt", Heightmap.m_paintMaskDirt }, { "Cultivated", Heightmap.m_paintMaskCultivated }, { "Paved", Heightmap.m_paintMaskPaved }, { "Nothing", Heightmap.m_paintMaskNothing } }; public static ConfigsContainer Instance { get { Debug.Assert(IsInitialized); return <Instance>k__BackingField; } [CompilerGenerated] private set { <Instance>k__BackingField = value; } } public static float TriggerIntervalInMinutes => Instance._triggerIntervalInMinutesConfig.Value; public static float Divider => Instance._dividerConfig.Value; public static float MinHeightToSteppedReset => Instance._minHeightToSteppedResetConfig.Value; public static float PaintsCompareTolerance => Instance._paintsCompareToleranceConfig.Value; public static List<Color> PaintsToIgnore => Instance._paintsToIgnore; public static bool ResetSmoothing => Instance._resetSmoothingConfig.Value; public static bool ResetPaintResetLastly => Instance._resetPaintResetLastlyConfig.Value; private ConfigsContainer() { //IL_0022: 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_0044: 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) _triggerIntervalInMinutesConfig = ModBase.config("General", "TheTriggerTime", 4320f, "Time in real minutes between reset steps."); _dividerConfig = ModBase.config("General", "Divider", 1.7f, "The divider for the terrain restoration. Current value will be divided by this value. Learn more on mod page."); _minHeightToSteppedResetConfig = ModBase.config("General", "Min Height To Stepped Reset", 0.2f, "If the height delta is lower than this value, it will be counted as zero."); _paintsToIgnoreConfig = ModBase.config("General", "Paint To Ignore", "(Paved), (Cultivated)", "This paints will be ignored in the reset process.\n" + vanillaPresets.Keys.GetString()); _paintsCompareToleranceConfig = ModBase.config("General", "Paints Compair Tolerance", 0.3f, "The accuracy of the comparison of colors. Since the current values of the same paint may differ from the reference in different situations, they have to be compared with the difference in this value."); _resetSmoothingConfig = ModBase.config("General", "Reset Smoothing", value: true, "Should the terrain smoothing be reset"); _resetPaintResetLastlyConfig = ModBase.config("General", "Process Paint Lastly", value: true, "Set to true so that the paint is reset only after the ground height delta and smoothing is completely reset. Otherwise, the paint will be reset at each reset step along with the height delta."); IsInitialized = true; ModBase.OnConfigurationChanged = (Action)Delegate.Combine(ModBase.OnConfigurationChanged, new Action(UpdateConfiguration)); } public static void InitializeConfiguration() { Instance = new ConfigsContainer(); } private void UpdateConfiguration() { if (!(DateTime.Now - LastConfigUpdateTime < TimeSpan.FromSeconds(1.0))) { float num = Math.Abs(_lastTriggerIntervalInMinutes - TriggerIntervalInMinutes); bool flag = Helper.IsMainScene(); ModBase.LogInfo($"diff={num:F00}, isMainScene={flag}"); if (num > 1f && flag) { ResetTerrainTimer.RestartTimer(); } _lastTriggerIntervalInMinutes = TriggerIntervalInMinutes; ResetTerrainTimer.LoadTimePassedFromFile(); ParsePaints(_paintsToIgnoreConfig.Value); if (Object.op_Implicit((Object)(object)ZNetScene.instance)) { InitWardsSettings.RegisterWards(); } ModBase.LogInfo("PaintsToIgnore = " + PaintsToIgnore.GetString()); ModBase.LogInfo("Configuration Received"); } } private void ParsePaints(string str) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) PaintsToIgnore.Clear(); string[] array = str.Split(new string[1] { "), (" }, StringSplitOptions.RemoveEmptyEntries); PaintsToIgnore.Capacity = array.Length; string[] array2 = array; foreach (string text in array2) { string text2 = text.Trim('(', ')'); if (vanillaPresets.TryGetValue(text2.Replace(" ", ""), out var value)) { PaintsToIgnore.Add(value); continue; } string[] array3 = text2.Split(new string[1] { ", " }, StringSplitOptions.RemoveEmptyEntries); if (array3.Length != 4) { ModBase.LogError("Could not parse color: '" + array3.GetString() + "', expected format: (r, b, g, alpha)\n" + vanillaPresets.Keys.GetString()); continue; } string text3 = array3[0]; string text4 = array3[1]; string text5 = array3[2]; string text6 = array3[3]; if (!float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { ModBase.LogError("Could not parse a value: '" + text3 + "'"); continue; } if (!float.TryParse(text4, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { ModBase.LogError("Could not parse b value: '" + text4 + "'"); continue; } if (!float.TryParse(text5, NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { ModBase.LogError("Could not parse g value: '" + text5 + "'"); continue; } if (!float.TryParse(text6, NumberStyles.Float, CultureInfo.InvariantCulture, out var result4)) { ModBase.LogError("Could not parse alpha value: '" + text6 + "'"); continue; } ((Color)(ref value))..ctor(result, result2, result3, result4); PaintsToIgnore.Add(value); } } } public static class Consts { public const string TimerId = "JF_GroundReset"; public const string MainSceneName = "main"; public const string ThorwardPrefabName = "Thorward"; public const string ArcaneWardPrefabName = "ArcaneWard"; public const string ArcaneWardZdoKey = "Radius"; public const string TerrCompPrefabName = "_TerrainCompiler"; public static readonly int RadiusNetKey = StringExtensionMethods.GetStableHashCode("wardRadius"); public const string TimerPassedTimeSaveFileName = "ModName_LastTimerTimePassed.txt"; public static readonly Lazy<string> TimerPassedTimeSaveFilePath = new Lazy<string>(() => Path.Combine(Paths.ConfigPath, "ModName_LastTimerTimePassed.txt")); } public static class Helper { public static bool IsMainScene() { //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) Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).IsValid() && ((Scene)(ref activeScene)).name == "main"; } public static bool IsServer(bool logIsShouldBeOnServerOnly = false) { GameServerClientState gameServerClientState = GetGameServerClientState(); if (logIsShouldBeOnServerOnly && gameServerClientState == GameServerClientState.Client) { ModBase.LogError("ModName is fully server-side, do not install it on clients"); } return gameServerClientState == GameServerClientState.Server; } public static GameServerClientState GetGameServerClientState() { ZNet instance = ZNet.instance; bool? flag = ((instance != null) ? new bool?(instance.IsServer()) : null); if (1 == 0) { } GameServerClientState result = (flag.HasValue ? ((!flag.GetValueOrDefault()) ? GameServerClientState.Client : GameServerClientState.Server) : GameServerClientState.Unknown); if (1 == 0) { } return result; } } public enum GameServerClientState { Unknown, Client, Server } [BepInPlugin("com.Frogger.GroundReset", "GroundReset", "2.7.1")] public class Plugin : BaseUnityPlugin { private const string ModName = "GroundReset"; private const string ModAuthor = "Frogger"; private const string ModVersion = "2.7.1"; private const string ModGuid = "com.Frogger.GroundReset"; private void Awake() { ModBase.CreateMod((BaseUnityPlugin)(object)this, "GroundReset", "Frogger", "2.7.1", "com.Frogger.GroundReset"); ConfigsContainer.InitializeConfiguration(); } } public static class Reseter { public static readonly int HeightmapWidth = 64; public static readonly int HeightmapScale = 1; private static List<ZDO> wards = new List<ZDO>(); public static List<WardSettings> wardsSettingsList = new List<WardSettings>(); public static Stopwatch watch = new Stopwatch(); public static async Task ResetAll(bool checkIfNeed = true, bool checkWards = true, bool ranFromConsole = false) { try { await FindWards(); await Terrains.ResetTerrains(checkWards); if (ranFromConsole) { ((Terminal)Console.instance).AddString("<color=green> Done </color>"); } wards.Clear(); } catch (Exception ex) { Exception e = ex; ModBase.LogError($"ResetAll failed with exception: {e}"); } } private static async Task FindWards() { watch.Restart(); wards.Clear(); for (int i = 0; i < wardsSettingsList.Count; i = checked(i + 1)) { wards = Enumerable.Concat(second: await ZoneSystemExtension.GetWorldObjectsAsync(prefabName: wardsSettingsList[i].prefabName, zoneSystem: ZoneSystem.instance, customFilters: Array.Empty<Func<ZDO, bool>>()), first: wards).ToList(); } ModBase.LogWarning(string.Format(arg1: TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalSeconds, format: "Wards count: {0}. Took {1} seconds", arg0: wards.Count)); watch.Restart(); } public static Vector3 HmapToWorld(Vector3 heightmapPos, int x, int y) { //IL_0027: 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_0039: 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) float num = ((float)x - (float)(HeightmapWidth / 2)) * (float)HeightmapScale; float num2 = ((float)y - (float)(HeightmapWidth / 2)) * (float)HeightmapScale; return heightmapPos + new Vector3(num, 0f, num2); } public static void WorldToVertex(Vector3 worldPos, Vector3 heightmapPos, out int x, out int y) { //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_0003: 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) //IL_000a: 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) Vector3 val = worldPos - heightmapPos; checked { x = Mathf.FloorToInt((float)((double)val.x / (double)HeightmapScale + 0.5)) + unchecked(HeightmapWidth / 2); y = Mathf.FloorToInt((float)((double)val.z / (double)HeightmapScale + 0.5)) + unchecked(HeightmapWidth / 2); } } public static bool IsInWard(Vector3 pos, float checkRadius = 0f) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) return wards.Exists(delegate(ZDO searchWard) { //IL_0069: 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) ZDO searchWard2 = searchWard; WardSettings wardSettings = wardsSettingsList.Find((WardSettings s) => StringExtensionMethods.GetStableHashCode(s.prefabName) == searchWard2.GetPrefab()); if (!searchWard2.GetBool(ZDOVars.s_enabled, true)) { return false; } float num = (wardSettings.dynamicRadius ? wardSettings.getDynamicRadius(searchWard2) : wardSettings.radius); return pos.DistanceXZ(searchWard2.GetPosition()) <= num + checkRadius; }) || MarketplaceTerritorySystem.PointInTerritory(pos); } public static bool IsInWard(Vector3 zoneCenter, int w, int h) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) return IsInWard(HmapToWorld(zoneCenter, w, h)); } } public enum ResetOrder { HeightAndSmoothingAndColor, HeightAndSmoothing_Color, Height_Smoothing_Color, Height_Color_Smoothing } public static class ResetTerrainTimer { private static TimeSpan LastTimerTimePassed = TimeSpan.Zero; private static readonly Action? _onTimer = async delegate { try { ModBase.LogInfo("Timer Triggered, starting chunks reset", showInHud: false, showInConsole: false, insertTimestamp: true); await Reseter.ResetAll(); ModBase.LogInfo("Timer Triggered, chunks have been reset, restarting the timer", showInHud: false, showInConsole: false, insertTimestamp: true); RestartTimer(); } catch (Exception ex) { Exception exception1 = ex; ModBase.LogError($"OnTimer event failed with exception: {exception1}"); RestartTimer(); } }; private static FunctionTimer? Timer { get; set; } = null; public static void RestartTimer() { try { ModBase.LogInfo("ResetTerrainTimer.RestartTimer"); if (!Helper.IsMainScene() || !Helper.IsServer(logIsShouldBeOnServerOnly: true)) { return; } ModBase.LogInfo("Stopping existing timers"); FunctionTimer.StopAllTimersWithName("JF_GroundReset"); Timer = null; TimeSpan timeSpan = TimeSpan.FromMinutes(ConfigsContainer.TriggerIntervalInMinutes); if (LastTimerTimePassed != TimeSpan.Zero) { try { timeSpan -= LastTimerTimePassed; } catch { timeSpan = TimeSpan.FromSeconds(1.0); } if (timeSpan.TotalSeconds <= 0.0) { timeSpan = TimeSpan.FromSeconds(1.0); } } ModBase.LogInfo($"Creating new timer for {timeSpan:hh\\:mm\\:ss}", showInHud: false, showInConsole: false, insertTimestamp: true); try { Timer = FunctionTimer.Create(_onTimer, (float)timeSpan.TotalSeconds, "JF_GroundReset", useUnscaledDeltaTime: true, stopAllWithSameName: true); } catch (Exception arg) { ModBase.LogError($"FunctionTimer.Create failed with exception: {arg}"); } LastTimerTimePassed = TimeSpan.Zero; } catch (Exception arg2) { ModBase.LogError(string.Format("{0} failed with exception: {1}", "RestartTimer", arg2)); } } public static void LoadTimePassedFromFile() { string value = Consts.TimerPassedTimeSaveFilePath.Value; if (!File.Exists(value)) { File.Create(value); File.WriteAllText(value, 0f.ToString(NumberFormatInfo.InvariantInfo)); LastTimerTimePassed = TimeSpan.Zero; return; } string s = File.ReadAllText(value); if (!float.TryParse(s, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out var result)) { ModBase.LogWarning("Failed to read invalid value from timer save file, overwritten with zero"); File.WriteAllText(value, 0f.ToString(NumberFormatInfo.InvariantInfo)); LastTimerTimePassed = TimeSpan.Zero; } else { LastTimerTimePassed = TimeSpan.FromSeconds(result); ModBase.LogInfo($"Loaded last timer passed time: {LastTimerTimePassed:hh\\:mm\\:ss}"); } } public static void SavePassedTimerTimeToFile() { Debug.Assert(Timer != null); if (Timer == null) { ModBase.LogWarning("Can not save timer passed time before its creation"); return; } string value = Consts.TimerPassedTimeSaveFilePath.Value; if (!File.Exists(value)) { File.Create(value); } float timer = Timer.Timer; LastTimerTimePassed = TimeSpan.FromSeconds(timer); File.WriteAllText(value, timer.ToString(NumberFormatInfo.InvariantInfo)); ModBase.LogInfo($"Saved timer passed time to file: {LastTimerTimePassed:hh\\:mm\\:ss}"); } } public static class Terrains { public static async Task<int> ResetTerrains(bool checkWards, int? maxDegreeOfParallelism = null) { MessageHud.instance.MessageAll((MessageType)1, "Идёт сброс ландшафта..."); Reseter.watch.Restart(); List<ZDO> zdos = await ZoneSystem.instance.GetWorldObjectsAsync("_TerrainCompiler"); if (zdos.Count == 0) { ModBase.LogInfo("0 chunks have been reset. Took 0.0 seconds"); Reseter.watch.Stop(); return 0; } ModBase.LogInfo($"Found {zdos.Count} chunks to reset", showInHud: false, showInConsole: false, insertTimestamp: true); checked { int dop = maxDegreeOfParallelism ?? Math.Max(1, Environment.ProcessorCount - 1); SemaphoreSlim semaphore = new SemaphoreSlim(dop, dop); try { ConcurrentBag<(ZDO zdo, ChunkData data)> results = new ConcurrentBag<(ZDO, ChunkData)>(); int completed = 0; int currentBatchIndex = 0; for (int i = 0; i < zdos.Count; i += 32) { int count = Math.Min(32, zdos.Count - i); List<Task> batchTasks = new List<Task>(count); for (int j = 0; j < count; j++) { ZDO zdo = zdos[i + j]; await semaphore.WaitAsync(); Task t = Task.Run(async delegate { try { ChunkData newChunkData = await ResetTerrainComp(zdo, checkWards); if (newChunkData != null) { results.Add((zdo, newChunkData)); Interlocked.Increment(ref completed); } } catch (Exception ex3) { Exception ex2 = ex3; ModBase.LogWarning($"ResetTerrainComp failed for zdo {zdo.m_uid}: {ex2}"); } finally { try { semaphore.Release(); } catch (ObjectDisposedException) { ModBase.LogError("ObjectDisposedException from ResetTerrains for SemaphoreSlim"); } } }); batchTasks.Add(t); } ModBase.LogInfo($"Processing batch {currentBatchIndex}"); await Task.WhenAll(batchTasks); currentBatchIndex++; await Task.Yield(); } ModBase.LogInfo($"New data been generated for {completed} chunks. Applying each to game world...", showInHud: false, showInConsole: false, insertTimestamp: true); int saved = 0; foreach (var (zdo2, data) in results) { try { SaveData(zdo2, data); saved++; } catch (Exception ex) { ModBase.LogWarning($"SaveData failed for zdo {zdo2.m_uid}: {ex}"); } } foreach (TerrainComp comp in TerrainComp.s_instances) { Heightmap hmap = comp.m_hmap; if (hmap != null) { hmap.Poke(false); } } ModBase.LogInfo(string.Format(arg2: TimeSpan.FromMilliseconds(Reseter.watch.ElapsedMilliseconds).TotalSeconds, format: "{0} chunks have been reset, {1} saved. Took {2} seconds", arg0: completed, arg1: saved), showInHud: false, showInConsole: false, insertTimestamp: true); Reseter.watch.Stop(); MessageHud.instance.MessageAll((MessageType)1, "Сброс ландшафта завершён"); return completed; } finally { if (semaphore != null) { ((IDisposable)semaphore).Dispose(); } } } } private static async Task<ChunkData?> ResetTerrainComp(ZDO zdo, bool checkWards) { float divider = ConfigsContainer.Divider; bool resetSmooth = ConfigsContainer.ResetSmoothing; bool resetSmoothingLast = ConfigsContainer.ResetSmoothing; float minHeightToSteppedReset = ConfigsContainer.MinHeightToSteppedReset; Vector3 zoneCenter = ZoneSystem.GetZonePos(ZoneSystem.GetZone(zdo.GetPosition())); ChunkData data; try { data = LoadOldData(zdo); } catch (Exception ex) { Exception e = ex; ModBase.LogError(e); return null; } if (data == null) { return null; } checked { int num = Reseter.HeightmapWidth + 1; for (int h = 0; h < num; h++) { for (int w = 0; w < num; w++) { int idx = h * num + w; if (!data.m_modifiedHeight[idx] || (checkWards && Reseter.IsInWard(zoneCenter, w, h))) { continue; } data.m_levelDelta[idx] /= divider; if (Mathf.Abs(data.m_levelDelta[idx]) < minHeightToSteppedReset) { data.m_levelDelta[idx] = 0f; } if (resetSmooth && (!resetSmoothingLast || data.m_levelDelta[idx] == 0f)) { data.m_smoothDelta[idx] /= divider; if (Mathf.Abs(data.m_smoothDelta[idx]) < minHeightToSteppedReset) { data.m_smoothDelta[idx] = 0f; } } bool flag_b = resetSmooth && data.m_smoothDelta[idx] != 0f; unchecked { data.m_modifiedHeight[idx] = data.m_levelDelta[idx] != 0f || flag_b; if (idx % 1000 == 0) { await Task.Yield(); } } } } int paintLenMun1 = data.m_modifiedPaint.Length - 1; for (int h2 = 0; h2 < num; h2++) { for (int w2 = 0; w2 < num; w2++) { int idx2 = h2 * num + w2; if (idx2 > paintLenMun1 || !data.m_modifiedPaint[idx2]) { continue; } Color currentPaint = data.m_paintMask[idx2]; if (IsPaintIgnored(currentPaint)) { continue; } if (checkWards || ConfigsContainer.ResetPaintResetLastly) { Vector3 worldPos = Reseter.HmapToWorld(zoneCenter, w2, h2); if (checkWards && Reseter.IsInWard(worldPos)) { continue; } if (ConfigsContainer.ResetPaintResetLastly) { Reseter.WorldToVertex(worldPos, zoneCenter, out var _, out var _); int heightIdx = idx2; if (data.m_modifiedHeight.Length > heightIdx && data.m_modifiedHeight[heightIdx]) { continue; } } } data.m_modifiedPaint[idx2] = false; data.m_paintMask[idx2] = Heightmap.m_paintMaskNothing; if (unchecked(idx2 % 1000) == 0) { await Task.Yield(); } } } return data; } } private static bool IsPaintIgnored(Color color) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return ConfigsContainer.PaintsToIgnore.Exists((Color x) => Mathf.Abs(x.r - color.r) < ConfigsContainer.PaintsCompareTolerance && Mathf.Abs(x.b - color.b) < ConfigsContainer.PaintsCompareTolerance && Mathf.Abs(x.g - color.g) < ConfigsContainer.PaintsCompareTolerance && Mathf.Abs(x.a - color.a) < ConfigsContainer.PaintsCompareTolerance); } private static void SaveData(ZDO zdo, ChunkData data) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(1); val.Write(data.m_operations); val.Write(data.m_lastOpPoint); val.Write(data.m_lastOpRadius); val.Write(data.m_modifiedHeight.Length); checked { for (int i = 0; i < data.m_modifiedHeight.Length; i++) { val.Write(data.m_modifiedHeight[i]); if (data.m_modifiedHeight[i]) { val.Write(data.m_levelDelta[i]); val.Write(data.m_smoothDelta[i]); } } val.Write(data.m_modifiedPaint.Length); for (int j = 0; j < data.m_modifiedPaint.Length; j++) { val.Write(data.m_modifiedPaint[j]); if (data.m_modifiedPaint[j]) { val.Write(data.m_paintMask[j].r); val.Write(data.m_paintMask[j].g); val.Write(data.m_paintMask[j].b); val.Write(data.m_paintMask[j].a); } } byte[] array = Utils.Compress(val.GetArray()); zdo.Set(ZDOVars.s_TCData, array); } } private static ChunkData? LoadOldData(ZDO zdo) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //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_0251: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) ChunkData chunkData = new ChunkData(); byte[] byteArray = zdo.GetByteArray(ZDOVars.s_TCData, (byte[])null); if (byteArray == null) { ModBase.LogWarning("ByteArray is null, aborting chunk load"); return null; } ZPackage val = new ZPackage(Utils.Decompress(byteArray)); val.ReadInt(); chunkData.m_operations = val.ReadInt(); chunkData.m_lastOpPoint = val.ReadVector3(); chunkData.m_lastOpRadius = val.ReadSingle(); int num = val.ReadInt(); if (num != chunkData.m_modifiedHeight.Length) { ModBase.LogWarning("Terrain data load error, height array missmatch"); return null; } checked { for (int i = 0; i < num; i++) { chunkData.m_modifiedHeight[i] = val.ReadBool(); if (chunkData.m_modifiedHeight[i]) { chunkData.m_levelDelta[i] = val.ReadSingle(); chunkData.m_smoothDelta[i] = val.ReadSingle(); } else { chunkData.m_levelDelta[i] = 0f; chunkData.m_smoothDelta[i] = 0f; } } int num2 = val.ReadInt(); if (num2 != chunkData.m_modifiedPaint.Length) { ModBase.LogWarning($"Terrain data load error, paint array missmatch, num2={num2}, modifiedPaint.Length={chunkData.m_modifiedPaint.Length}, paintMask.Length={chunkData.m_paintMask.Length}"); num2 = Mathf.Max(new int[3] { num2, chunkData.m_modifiedPaint.Length, chunkData.m_paintMask.Length }); if (chunkData.m_modifiedPaint.Length != num2) { Array.Resize(ref chunkData.m_modifiedPaint, num2); } if (chunkData.m_paintMask.Length != num2) { Array.Resize(ref chunkData.m_paintMask, num2); } } for (int j = 0; j < num2; j++) { chunkData.m_modifiedPaint[j] = val.ReadBool(); if (chunkData.m_modifiedPaint[j]) { chunkData.m_paintMask[j] = new Color { r = val.ReadSingle(), g = val.ReadSingle(), b = val.ReadSingle(), a = val.ReadSingle() }; } else { chunkData.m_paintMask[j] = Color.black; } } if (num2 == Reseter.HeightmapWidth * Reseter.HeightmapWidth) { Color[] array = (Color[])(object)new Color[chunkData.m_paintMask.Length]; chunkData.m_paintMask.CopyTo(array, 0); bool[] array2 = new bool[chunkData.m_modifiedPaint.Length]; chunkData.m_modifiedPaint.CopyTo(array2, 0); int num3 = Reseter.HeightmapWidth + 1; for (int k = 0; k < chunkData.m_paintMask.Length; k++) { int num4; int num5; unchecked { num4 = k / num3; num5 = checked(k + 1) / num3; } int num6 = k - num4; if (num4 == Reseter.HeightmapWidth) { num6 -= Reseter.HeightmapWidth; } if (unchecked(k > 0 && checked(k - num4) % Reseter.HeightmapWidth == 0 && checked(k + 1 - num5) % Reseter.HeightmapWidth == 0)) { num6--; } chunkData.m_paintMask[k] = array[num6]; chunkData.m_modifiedPaint[k] = array2[num6]; } } return chunkData; } } } public struct WardSettings { public string prefabName; public float radius; public bool dynamicRadius; public Func<ZDO, float> getDynamicRadius; public WardSettings(string prefabName, float radius) { getDynamicRadius = null; dynamicRadius = false; this.prefabName = prefabName; this.radius = radius; } public WardSettings(string prefabName, Func<ZDO, float> getDynamicRadius) : this(prefabName, 0f) { dynamicRadius = true; this.getDynamicRadius = getDynamicRadius; } } } namespace GroundReset.Patch { [HarmonyPatch] [HarmonyWrapSafe] public static class InitWardsSettings { [HarmonyPostfix] [HarmonyPatch(typeof(ZNetScene), "Awake")] private static void Init(ZNetScene __instance) { if (Helper.IsMainScene() && Helper.IsServer(logIsShouldBeOnServerOnly: true)) { RegisterWards(); } } public static void RegisterWards() { Reseter.wardsSettingsList.Clear(); AddWard("guard_stone"); AddWardThorward(); AddWardArcaneWard(); if (Object.op_Implicit((Object)(object)ZNetScene.instance) && ZNetScene.instance.m_prefabs != null && ZNetScene.instance.m_prefabs.Count > 0) { List<GameObject> list = ZNetScene.instance.m_prefabs.Where((GameObject x) => Object.op_Implicit((Object)(object)x.GetComponent<PrivateArea>())).ToList(); foreach (GameObject item in list) { AddWard(((Object)item).name); } } ModBase.LogInfo($"Found {Reseter.wardsSettingsList.Count} wards: {Reseter.wardsSettingsList.Select((WardSettings x) => x.prefabName).ToArray().GetString()}"); } private static void AddWard(string name) { string name2 = name; GameObject prefab = ZNetScene.instance.GetPrefab(StringExtensionMethods.GetStableHashCode(name2)); if (Object.op_Implicit((Object)(object)prefab)) { PrivateArea component = prefab.GetComponent<PrivateArea>(); if (!Reseter.wardsSettingsList.Any((WardSettings x) => x.prefabName == name2)) { Reseter.wardsSettingsList.Add(new WardSettings(name2, component.m_radius)); } } } private static void AddWardThorward() { GameObject prefab = ZNetScene.instance.GetPrefab(StringExtensionMethods.GetStableHashCode("Thorward")); if (!Object.op_Implicit((Object)(object)prefab)) { return; } Reseter.wardsSettingsList.Add(new WardSettings("Thorward", delegate(ZDO zdo) { float num = zdo.GetFloat(AzuWardZdoKeys.wardRadius, 0f); if (num == 0f) { num = WardIsLovePlugin.WardRange().Value; } return num; })); } private static void AddWardArcaneWard() { GameObject prefab = ZNetScene.instance.GetPrefab(StringExtensionMethods.GetStableHashCode("ArcaneWard")); if (Object.op_Implicit((Object)(object)prefab)) { Reseter.wardsSettingsList.Add(new WardSettings("ArcaneWard", delegate(ZDO zdo) { int @int = zdo.GetInt("Radius", 0); return @int; })); } } } [HarmonyPatch] internal static class <SaveTimeTime>FC20D5B07E47C0B215C4207E48E5CF2F4A87C0D2F492F2B59F351395439455DB4__SaveTimerTimePatch { [HarmonyPrefix] [HarmonyPatch(typeof(ZNet), "Save")] private static void SaveTime() { if (Helper.IsServer(logIsShouldBeOnServerOnly: true)) { ResetTerrainTimer.SavePassedTimerTimeToFile(); } } } [HarmonyPatch] internal static class <StartTimer>FE6C81AD2EEC2F3B308A0172832A2BA8BCAFC3704370F26938BF6C9DD02E1EF80__StartTimerPatch { [HarmonyPostfix] [HarmonyPatch(typeof(ZNetScene), "Awake")] private static void StartTimer() { if (Helper.IsMainScene() && Helper.IsServer(logIsShouldBeOnServerOnly: true)) { ResetTerrainTimer.RestartTimer(); } } } } namespace GroundReset.Compatibility { public class ModCompat { public static T InvokeMethod<T>(Type type, object instance, string methodName, object[] parameter) { return (T)(type.GetMethod(methodName)?.Invoke(instance, parameter)); } public static T GetField<T>(Type type, object instance, string fieldName) { return (T)(type.GetField(fieldName)?.GetValue(instance)); } } } namespace GroundReset.Compatibility.WardIsLove { public class AzuWardZdoKeys { public static readonly int wardRadius = StringExtensionMethods.GetStableHashCode("wardRadius"); public static readonly int accessMode = StringExtensionMethods.GetStableHashCode("accessMode"); public static readonly int bubbleMode = StringExtensionMethods.GetStableHashCode("bubbleMode"); public static readonly int damageType = StringExtensionMethods.GetStableHashCode("damageType"); public static readonly int healthRegen = StringExtensionMethods.GetStableHashCode("healthRegen"); public static readonly int staminaBoost = StringExtensionMethods.GetStableHashCode("staminaBoost"); public static readonly int bubbleOn = StringExtensionMethods.GetStableHashCode("bubbleOn"); public static readonly int weatherDmgOn = StringExtensionMethods.GetStableHashCode("weatherDmgOn"); public static readonly int autoPickupOn = StringExtensionMethods.GetStableHashCode("autoPickupOn"); public static readonly int autoCloseDoorsOn = StringExtensionMethods.GetStableHashCode("autoCloseDoorsOn"); public static readonly int fireplaceUnlimOn = StringExtensionMethods.GetStableHashCode("fireplaceUnlimOn"); public static readonly int bathingUnlimOn = StringExtensionMethods.GetStableHashCode("bathingUnlimOn"); public static readonly int cookingUnlimOn = StringExtensionMethods.GetStableHashCode("cookingUnlimOn"); public static readonly int wardFireplaceList = StringExtensionMethods.GetStableHashCode("wardFireplaceList"); public static readonly int noDeathPenOn = StringExtensionMethods.GetStableHashCode("noDeathPenOn"); public static readonly int noFoodDrainOn = StringExtensionMethods.GetStableHashCode("noFoodDrainOn"); public static readonly int pushoutPlayersOn = StringExtensionMethods.GetStableHashCode("pushoutPlayersOn"); public static readonly int pushoutCreaturesOn = StringExtensionMethods.GetStableHashCode("pushoutCreaturesOn"); public static readonly int pvpOn = StringExtensionMethods.GetStableHashCode("pvpOn"); public static readonly int pveOn = StringExtensionMethods.GetStableHashCode("pveOn"); public static readonly int teleportOn = StringExtensionMethods.GetStableHashCode("teleportOn"); public static readonly int showFlashOn = StringExtensionMethods.GetStableHashCode("showFlashOn"); public static readonly int showMarkerOn = StringExtensionMethods.GetStableHashCode("showMarkerOn"); public static readonly int wardNotificationsOn = StringExtensionMethods.GetStableHashCode("wardNotificationsOn"); public static readonly int wardNotifyMessageEntry = StringExtensionMethods.GetStableHashCode("wardNotifyMessageEntry"); public static readonly int wardNotifyMessageExit = StringExtensionMethods.GetStableHashCode("wardNotifyMessageExit"); public static readonly int wardDamageAmount = StringExtensionMethods.GetStableHashCode("wardDamageAmount"); public static readonly int itemstandInteractOn = StringExtensionMethods.GetStableHashCode("itemstandInteractOn"); public static readonly int indestructibleOn = StringExtensionMethods.GetStableHashCode("indestructibleOn"); public static readonly int indestructList = StringExtensionMethods.GetStableHashCode("indestructList"); public static readonly int CreatDamageIncreaseAmount = StringExtensionMethods.GetStableHashCode("CreatDamageIncreaseAmount"); public static readonly int structDamageReducAmount = StringExtensionMethods.GetStableHashCode("structDamageReducAmount"); public static readonly int itemInteractOn = StringExtensionMethods.GetStableHashCode("itemInteractOn"); public static readonly int doorInteractOn = StringExtensionMethods.GetStableHashCode("doorInteractOn"); public static readonly int chestInteractOn = StringExtensionMethods.GetStableHashCode("chestInteractOn"); public static readonly int portalInteractOn = StringExtensionMethods.GetStableHashCode("portalInteractOn"); public static readonly int pickableInteractOn = StringExtensionMethods.GetStableHashCode("pickableInteractOn"); public static readonly int shipInteractOn = StringExtensionMethods.GetStableHashCode("shipInteractOn"); public static readonly int signInteractOn = StringExtensionMethods.GetStableHashCode("signInteractOn"); public static readonly int craftingStationInteractOn = StringExtensionMethods.GetStableHashCode("craftingStationInteractOn"); public static readonly int smelterInteractOn = StringExtensionMethods.GetStableHashCode("smelterInteractOn"); public static readonly int beehiveInteractOn = StringExtensionMethods.GetStableHashCode("beehiveInteractOn"); public static readonly int maptableInteractOn = StringExtensionMethods.GetStableHashCode("maptableInteractOn"); public static readonly int OnlyPermOn = StringExtensionMethods.GetStableHashCode("OnlyPermOn"); public static readonly int NotPermOn = StringExtensionMethods.GetStableHashCode("NotPermOn"); public static readonly int ctaMessage = StringExtensionMethods.GetStableHashCode("ctaMessage"); public static readonly int autoRepairOn = StringExtensionMethods.GetStableHashCode("autoRepairOn"); public static readonly int autoRepairAmount = StringExtensionMethods.GetStableHashCode("autoRepairAmount"); public static readonly int autoRepairTime = StringExtensionMethods.GetStableHashCode("autoRepairTime"); public static readonly int raidProtectionOn = StringExtensionMethods.GetStableHashCode("raidProtectionOn"); public static readonly int raidablePlayersNeeded = StringExtensionMethods.GetStableHashCode("raidablePlayersNeeded"); public static readonly int wardIsLoveOn = StringExtensionMethods.GetStableHashCode("wardIsLoveOn"); public static readonly int WILLimitedWard = StringExtensionMethods.GetStableHashCode("WILLimitedWard"); public static readonly int WILLimitedWardTime = StringExtensionMethods.GetStableHashCode("WILLimitedWardTime"); public static readonly int steamID = StringExtensionMethods.GetStableHashCode("steamID"); public static readonly int steamName = StringExtensionMethods.GetStableHashCode("steamName"); public static readonly int wardFresh = StringExtensionMethods.GetStableHashCode("wardFresh"); public static readonly int wardColorCount = StringExtensionMethods.GetStableHashCode("wardColorCount"); public static readonly int wardAlphaCount = StringExtensionMethods.GetStableHashCode("wardAlphaCount"); public static readonly int wardModelKey = StringExtensionMethods.GetStableHashCode("wardModelKey"); } public class CustomCheck : ModCompat { public static Type ClassType() { return Type.GetType("WardIsLove.Util.CustomCheck, WardIsLove"); } public static bool CheckAccess(long playerID, Vector3 point, float radius = 0f, bool flash = true) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) return ModCompat.InvokeMethod<bool>(ClassType(), null, "CheckAccess", new object[4] { playerID, point, radius, flash }); } } public class WardIsLovePlugin : ModCompat { private const string GUID = "Azumatt.WardIsLove"; private static readonly System.Version MinVersion = new System.Version(2, 3, 3); public static Type ClassType() { return Type.GetType("WardIsLove.WardIsLovePlugin, WardIsLove"); } public static bool IsLoaded() { return Chainloader.PluginInfos.ContainsKey("Azumatt.WardIsLove") && Chainloader.PluginInfos["Azumatt.WardIsLove"].Metadata.Version >= MinVersion; } public static ConfigEntry<bool> WardEnabled() { return ModCompat.GetField<ConfigEntry<bool>>(ClassType(), null, "WardEnabled"); } public static ConfigEntry<float> WardRange() { return ModCompat.GetField<ConfigEntry<float>>(ClassType(), null, "WardRange"); } } public class WardMonoscript : ModCompat { public object targetScript; public WardMonoscript(object targetScript) { this.targetScript = targetScript; } public static Type ClassType() { return Type.GetType("WardIsLove.Util.WardMonoscript, WardIsLove"); } public static bool CheckInWardMonoscript(Vector3 point, bool flash = false) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) return ModCompat.InvokeMethod<bool>(ClassType(), null, "CheckInWardMonoscript", new object[2] { point, flash }); } public static bool CheckAccess(Vector3 point, float radius = 0f, bool flash = true, bool wardCheck = false) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) return ModCompat.InvokeMethod<bool>(ClassType(), null, "CheckAccess", new object[4] { point, radius, flash, wardCheck }); } public ZNetView GetZNetView() { if (targetScript == null) { return null; } return ModCompat.GetField<ZNetView>(ClassType(), targetScript, "m_nview"); } public ZDO GetZDO() { ZNetView zNetView = GetZNetView(); return ((Object)(object)zNetView != (Object)null && Object.op_Implicit((Object)(object)zNetView) && zNetView.IsValid()) ? zNetView.GetZDO() : null; } } public static class WardMonoscriptExt { public static Type ClassType() { return Type.GetType("WardIsLove.Extensions.WardMonoscriptExt, WardIsLove"); } public static WardMonoscript GetWardMonoscript(Vector3 pos) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) object targetScript = ModCompat.InvokeMethod<object>(ClassType(), null, "GetWardMonoscript", new object[1] { pos }); return new WardMonoscript(targetScript); } public static float GetWardRadius(this WardMonoscript wrapper) { return ModCompat.InvokeMethod<float>(ClassType(), null, "GetWardRadius", new object[1] { wrapper.targetScript }); } public static bool GetDoorInteractOn(this WardMonoscript wrapper) { return ModCompat.InvokeMethod<bool>(ClassType(), null, "GetDoorInteractOn", new object[1] { wrapper.targetScript }); } } } namespace GroundReset.Compatibility.kgMarketplace { public class MarketplaceTerritorySystem { private const string GUID = "MarketplaceAndServerNPCs"; public static bool IsLoaded() { return Chainloader.PluginInfos.ContainsKey("MarketplaceAndServerNPCs"); } public static bool PointInTerritory(Vector3 pos) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (!IsLoaded()) { return false; } return MarketplaceTerritorySystem_RAW.PointInTerritory(pos); } } public static class MarketplaceTerritorySystem_RAW { public static bool PointInTerritory(Vector3 pos) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) foreach (Territory territory in TerritorySystem_DataTypes.SyncedTerritoriesData.Value.Territories) { if (territory.IsInside(pos)) { return true; } } return false; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] public sealed class <35c48fc9-d1a9-4c43-b2d6-1899d1b37e6c>IgnoresAccessChecksToAttribute : Attribute { public <35c48fc9-d1a9-4c43-b2d6-1899d1b37e6c>IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal class SyncedConfigEntry<T> : OwnConfigEntryBase { public readonly ConfigEntry<T> SourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public SyncedConfigEntry(ConfigEntry<T> sourceConfig) { SourceConfig = sourceConfig; } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } internal abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; public readonly Type Type; private object? boxedValue; protected bool localIsOwner; public readonly int Priority; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] internal class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { try { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; ModBase.LogDebug("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections"); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } } catch (Exception) { } static void SendAdmin(List<ZNetPeer> peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } static IEnumerator WatchAdminListChanges() { SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); List<string> CurrentList = new List<string>(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List<string>(adminList.GetList()); List<ZNetPeer> adminPeer = (from p in ZNet.instance.GetPeers() where adminList.Contains(p.m_rpc.GetSocket().GetHostName()) select p).ToList(); List<ZNetPeer> nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_FromServerConfigSync); } } } private class ParsedConfigs { public readonly Dictionary<OwnConfigEntryBase, object?> configValues = new Dictionary<OwnConfigEntryBase, object>(); public readonly Dictionary<CustomSyncedValueBase, object?> customValues = new Dictionary<CustomSyncedValueBase, object>(); } [HarmonyPatch(typeof(ZNet), "Shutdown")] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List<ZPackage> Package = new List<ZPackage>(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix(ref Dictionary<Assembly, BufferingSocket>? __state, ZNet __instance, ZRpc rpc) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 if (__instance.IsServer()) { BufferingSocket value = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, value); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend > 0) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, value); } if (__state == null) { __state = new Dictionary<Assembly, BufferingSocket>(); } __state[Assembly.GetExecutingAssembly()] = value; } } [HarmonyPostfix] private static void Postfix(Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc) { ZRpc rpc2 = rpc; ZNet __instance2 = __instance; Dictionary<Assembly, BufferingSocket> __state2 = __state; ZNetPeer peer; if (__instance2.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance2).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc2.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc2, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance2, new object[1] { rpc2 }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state2[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i = checked(i + 1)) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } IEnumerator sendAsync() { foreach (ConfigSync configSync in configSyncs) { List<PackageEntry> entries = new List<PackageEntry>(); if (configSync.CurrentVersion != null) { entries.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); entries.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc2.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc2.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance2).StartCoroutine(configSync.sendZPackage(new List<ZNetPeer> { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string section = null; public string key = null; public Type type = null; public object? value; } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { ModBase.LogWarning($"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; public string? DisplayName; public string? CurrentVersion; public string? MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet<ConfigSync> configSyncs; private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); private HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private static bool isServer; private static bool lockExempt; private OwnConfigEntryBase? lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>(); private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0052; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag.GetValueOrDefault(); } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0053: return (byte)result != 0; IL_0052: result = 0; goto IL_0053; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt || isSourceOfTruth; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get; private set; } = false; public event Action<bool>? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet<ConfigSync>(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { ConfigEntry<T> configEntry2 = configEntry; OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry2); SyncedConfigEntry<T> syncedEntry = ownConfigEntryBase as SyncedConfigEntry<T>; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry<T>(configEntry2); AccessTools.DeclaredField(typeof(ConfigDescription), "<Tags>k__BackingField").SetValue(((ConfigEntryBase)configEntry2).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry2).Description.Tags ?? Array.Empty<object>()).Concat(new SyncedConfigEntry<T>[1] { syncedEntry }).ToArray()); configEntry2.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(ZRoutedRpc.Everybody, (ConfigEntryBase)configEntry2); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry<T> AddLockingConfigEntry<T>(ConfigEntry<T> lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry<T>(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry<T>)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { CustomSyncedValueBase customValue2 = customValue; if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue2.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue2); allCustomValues = new HashSet<CustomSyncedValueBase>(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue2.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(ZRoutedRpc.Everybody, customValue2); } }; } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown try { if (isServer && IsLocked) { ZRpc? currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj; if (currentRpc == null) { obj = null; } else { ISocket socket = currentRpc.GetSocket(); obj = ((socket != null) ? socket.GetHostName() : null); } string text = (string)obj; if (text != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text })))) { return false; } } } cacheExpirations.RemoveAll(delegate(KeyValuePair<long, string> kv) { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2u) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out SortedDictionary<int, byte[]> value)) { value = new SortedDictionary<int, byte[]>(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair<long, string>(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value.Add(key, package.ReadByteArray()); if (value.Count < num2) { return false; } configValueCache.Remove(text2); package = new ZPackage(value.Values.SelectMany((byte[] a) => a).ToArray()); b = package.ReadByte(); } ProcessingServerUpdate = true; if ((b & 4u) != 0) { byte[] buffer = package.ReadByteArray(); MemoryStream stream = new MemoryStream(buffer); MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); b = package.ReadByte(); } if ((b & 1) == 0) { resetConfigsFromServer(); } ParsedConfigs parsedConfigs = ReadConfigsFromPackage(package); foreach (KeyValuePair<OwnConfigEntryBase, object> configValue in parsedConfigs.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } configValue.Key.BaseConfig.BoxedValue = configValue.Value; } foreach (KeyValuePair<CustomSyncedValueBase, object> customValue in parsedConfigs.customValues) { if (!isServer) { CustomSyncedValueBase key2 = customValue.Key; if (key2.LocalBaseValue == null) { key2.LocalBaseValue = customValue.Key.BoxedValue; } } customValue.Key.BoxedValue = customValue.Value; } ModBase.LogDebug(string.Format("Received {0} configs and {1} custom values from {2} for mod {3}", parsedConfigs.configValues.Count, parsedConfigs.customValues.Count, (isServer || clientUpdate) ? $"client {sender}" : "the server", DisplayName ?? Name)); if (!isServer) { serverLockedSettingChanged(); } return true; } finally { ProcessingServerUpdate = false; } } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary<string, OwnConfigEntryBase> dictionary = allConfigs.Where((OwnConfigEntryBase c) => c.SynchronizedConfig).ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "_" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); Dictionary<string, CustomSyncedValueBase> dictionary2 = allCustomValues.ToDictionary((CustomSyncedValueBase c) => c.Identifier, (CustomSyncedValueBase c) => c); int num = package.ReadInt(); for (int i = 0; i < num; i = checked(i + 1)) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 == "" || type != null) { object obj; try { obj = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { ModBase.LogWarning("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + ex.expected); continue; } OwnConfigEntryBase value2; if (text == "Internal") { CustomSyncedValueBase value; if (text2 == "serverversion") { if (obj?.ToString() != CurrentVersion) { ModBase.LogWarning("Received server version is not equal: server version = " + (obj?.ToString() ?? "null") + "; local version = " + (CurrentVersion ?? "unknown")); } } else if (text2 == "lockexempt") { if (obj is bool flag) { lockExempt = flag; } } else if (dictionary2.TryGetValue(text2, out value)) { if ((text3 == "" && (!value.Type.IsValueType || Nullable.GetUnderlyingType(value.Type) != null)) || GetZPackageTypeString(value.Type) == text3) { parsedConfigs.customValues[value] = obj; continue; } ModBase.LogWarning("Got unexpected type " + text3 + " for internal value " + text2 + " for mod " + (DisplayName ?? Name) + ", expecting " + value.Type.AssemblyQualifiedName); } } else if (dictionary.TryGetValue(text + "_" + text2, out value2)) { Type type2 = configType(value2.BaseConfig); if ((text3 == "" && (!type2.IsValueType || Nullable.GetUnderlyingType(type2) != null)) || GetZPackageTypeString(type2) == text3) { parsedConfigs.configValues[value2] = obj; continue; } ModBase.LogWarning("Got unexpected type " + text3 + " for " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ", expecting " + type2.AssemblyQualifiedName); } else { ModBase.LogWarning("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ". This may happen if client and server versions of the mod do not match."); } continue; } ModBase.LogWarning("Got invalid type " + text3 + ", abort reading of received configs"); return new ParsedConfigs(); } return parsedConfigs; } private static bool isWritableConfig(OwnConfigEntryBase config) { OwnConfigEntryBase config2 = config; ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config2)); if (configSync == null) { return true; } return configSync.IsSourceOfTruth || !config2.SynchronizedConfig || config2.LocalBaseValue == null || (!configSync.IsLocked && (config2 != configSync.lockedConfig || lockExempt)); } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { configAttribute<ConfigurationManagerAttributes>(allConfig.BaseConfig).ReadOnly = !isWritableConfig(allConfig); } } private void resetConfigsFromServer() { foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase config) => config.LocalBaseValue != null)) { item.BaseConfig.BoxedValue = item.LocalBaseValue; item.LocalBaseValue = null; } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase config) => config.LocalBaseValue != null)) { item2.BoxedValue = item2.LocalBaseValue; item2.LocalBaseValue = null; } lockedConfigChanged -= serverLockedSettingChanged; serverLockedSettingChanged(); } private IEnumerator<bool> distributeConfigToPeers(ZNetPeer peer, ZPackage package) { ZNetPeer peer2 = peer; ZRoutedRpc rpc = ZRoutedRpc.instance; if (rpc == null) { yield break; } byte[] data = package.GetArray(); checked { if (data != null && data.LongLength > 250000) { int fragments = (int)(1 + unchecked(checked(data.LongLength - 1) / 250000)); long packageIdentifier = ++packageCounter; int fragment = 0; while (fragment < fragments) { foreach (bool item in waitForQueue()) { yield return item; } if (peer2.m_socket.IsConnected()) { ZPackage fragmentedPackage = new ZPackage(); fragmentedPackage.Write((byte)2); fragmentedPackage.Write(packageIdentifier); fragmentedPackage.Write(fragment); fragmentedPackage.Write(fragments); fragmentedPackage.Write(data.Skip(250000 * fragment).Take(250000).ToArray()); SendPackage(fragmentedPackage); if (fragment != fragments - 1) { yield return true; } int num = fragment + 1; fragment = num; continue; } break; } yield break; } foreach (bool item2 in waitForQueue()) { yield return item2; } SendPackage(package); } void SendPackage(ZPackage pkg) { string text = Name + " ConfigSync"; if (isServer) { peer2.m_rpc.Invoke(text, new object[1] { pkg }); } else { rpc.InvokeRoutedRPC(peer2.m_server ? 0 : peer2.m_uid, text, new object[1] { pkg }); } } IEnumerable<bool> waitForQueue() { float timeout = Time.time + 30f; while (peer2.m_socket.GetSendQueueSize() > 20000) { if (Time.time > timeout) { ModBase.LogDebug($"Disconnecting {peer2.m_uid} after 30 seconds config sending timeout"); peer2.m_rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)5 }); ZNet.instance.Disconnect(peer2); break; } yield return false; } } } private IEnumerator sendZPackage(long target, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return Enumerable.Empty<object>().GetEnumerator(); } List<ZNetPeer> list = (List<ZNetPeer>)AccessTools.DeclaredField(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance); if (target != ZRoutedRpc.Everybody) { list = list.Where((ZNetPeer p) => p.m_uid == target).ToList(); } return sendZPackage(list, package); } private IEnumerator sendZPackage(List<ZNetPeer> peers, ZPackage package) { ZPackage package2 = package; if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] rawData = package2.GetArray(); if (rawData != null && rawData.LongLength > 10000) { ZPackage compressedPackage = new ZPackage(); compressedPackage.Write((byte)4); MemoryStream output = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(output, CompressionLevel.Optimal)) { deflateStream.Write(rawData, 0, rawData.Length); } compressedPackage.Write(output.ToArray()); package2 = compressedPackage; } List<IEnumerator<bool>> writers = (from peer in peers where peer.IsReady() select peer into p select distributeConfigToPeers(p, package2)).ToList(); writers.RemoveAll((IEnumerator<bool> writer) => !writer.MoveNext()); while (writers.Count > 0) { yield return null; writers.RemoveAll((IEnumerator<bool> writer) => !writer.MoveNext()); } } private void Broadcast(long target, params ConfigEntryBase[] configs) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(configs); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private void Broadcast(long target, params CustomSyncedValueBase[] customValues) { if (!IsLocked || isServer) { ZPackage package = ConfigsToPackage(null, customValues); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(sendZPackage(target, package)); } } } private static OwnConfigEntryBase? configData(ConfigEntryBase config) { return config.Description.Tags?.OfType<OwnConfigEntryBase>().SingleOrDefault(); } public static SyncedConfigEntry<T>? ConfigData<T>(ConfigEntry<T> config) { return ((ConfigEntryBase)config).Description.Tags?.OfType<SyncedConfigEntry<T>>().SingleOrDefault(); } private static T configAttribute<T>(ConfigEntryBase config) { return config.Description.Tags.OfType<T>().First(); } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { return type.IsEnum ? Enum.GetUnderlyingType(type) : type; } private static ZPackage ConfigsToPackage(IEnumerable<ConfigEntryBase>? configs = null, IEnumerable<CustomSyncedValueBase>? customValues = null, IEnumerable<PackageEntry>? packageEntries = null, bool partial = true) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown List<ConfigEntryBase> list = configs?.Where((ConfigEntryBase config) => configData(config).SynchronizedConfig).ToList() ?? new List<ConfigEntryBase>(); List<CustomSyncedValueBase> list2 = customValues?.ToList() ?? new List<CustomSyncedValueBase>(); ZPackage val = new ZPackage(); val.Write((byte)(partial ? 1 : 0)); val.Write(checked(list.Count + list2.Count + (packageEntries?.Count() ?? 0))); foreach (PackageEntry item in packageEntries ?? Array.Empty<PackageEntry>()) { AddEntryToPackage(val, item); } foreach (CustomSyncedValueBase item2 in list2) { AddEntryToPackage(val, new PackageEntry { section = "Internal", key = item2.Identifier, type = item2.Type, value = item2.BoxedValue }); } foreach (ConfigEntryBase item3 in list) { AddEntryToPackage(val, new PackageEntry { section = item3.Definition.Section, key = item3.Definition.Key, type = configType(item3), value = item3.BoxedValue }); } return val; } private static void AddEntryToPackage(ZPackage package, PackageEntry entry) { package.Write(entry.section); package.Write(entry.key); package.Write((entry.value == null) ? "" : GetZPackageTypeString(entry.type)); AddValueToZPackage(package, entry.value); } private static string GetZPackageTypeString(Type type) { return type.AssemblyQualifiedName; } private static void AddValueToZPackage(ZPackage package, object? value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { AddValueToZPackage(package, item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetZPackageTypeString(fieldInfo.FieldType)); AddValueToZPackage(package, fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetZPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetZPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, ReadValueWithTypeFromZPackage(package, fieldInfo.FieldType)); } return uninitializedObject; } checked { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = ReadValueWithTypeFromZPackage(package, type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List<string>) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type)) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List<object> source = new List<object>(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.First(); } } } [PublicAPI] [HarmonyPatch] internal class VersionCheck { private static readonly HashSet<VersionCheck> versionChecks; private static readonly Dictionary<string, string> notProcessedNames; public string Name; private string? displayName; private string? currentVersion; private string? minimumRequiredVersion; public bool ModRequired = true; private string? ReceivedCurrentVersion; private string? ReceivedMinimumRequiredVersion; private readonly List<ZRpc> ValidatedClients = new List<ZRpc>(); private ConfigSync? ConfigSync; public string DisplayName { get { return displayName ?? Name; } set { displayName = value; } } public string CurrentVersion { get { return currentVersion ?? "0.0.0"; } set { currentVersion = value; } } public string MinimumRequiredVersion { get { return minimumRequiredVersion ?? (ModRequired ? CurrentVersion : "0.0.0"); } set { minimumRequiredVersion = value; } } private static void PatchServerSync() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown Patches patchInfo = PatchProcessor.GetPatchInfo((MethodBase)AccessTools.DeclaredMethod(typeof(ZNet), "Awake", (Type[])null, (Type[])null)); if (patchInfo != null && patchInfo.Postfixes.Count((Patch p) => p.PatchMethod.DeclaringType == typeof(ConfigSync.RegisterRPCPatch)) > 0) { return; } Harmony val = new Harmony("org.bepinex.helpers.ServerSync"); foreach (Type item in from t in typeof(ConfigSync).GetNestedTypes(BindingFlags.NonPublic).Concat(new Type[1] { typeof(VersionCheck) }) where t.IsClass select t) { val.PatchAll(item); } } static VersionCheck() { versionChecks = new HashSet<VersionCheck>(); notProcessedNames = new Dictionary<string, string>(); typeof(ThreadingHelper).GetMethod("StartSyncInvoke").Invoke(ThreadingHelper.Instance, new object[1] { new Action(PatchServerSync) }); } public VersionCheck(string name) { Name = name; ModRequired = true; versionChecks.Add(this); } public VersionCheck(ConfigSync configSync) { ConfigSync = configSync; Name = ConfigSync.Name; versionChecks.Add(this); } public void Initialize() { ReceivedCurrentVersion = null; ReceivedMinimumRequiredVersion = null; if (ConfigSync != null) { Name = ConfigSync.Name; DisplayName = ConfigSync.DisplayName; CurrentVersion = ConfigSync.CurrentVersion; MinimumRequiredVersion = ConfigSync.MinimumRequiredVersion; ModRequired = ConfigSync.ModRequired; } } private bool IsVersionOk() { if (ReceivedMinimumRequiredVersion == null || ReceivedCurrentVersion == null) { return !ModRequired; } bool flag = new System.Version(CurrentVersion) >= new System.Version(ReceivedMinimumRequiredVersion); bool flag2 = new System.Version(ReceivedCurrentVersion) >= new System.Version(MinimumRequiredVersion); return flag && flag2; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return "Mod " + DisplayName + " must not be installed."; } return (new System.Version(CurrentVersion) >= new System.Version(ReceivedMinimumRequiredVersion)) ? ("Mod " + DisplayName + " requires maximum " + ReceivedCurrentVersion + ". Installed is version " + CurrentVersion + ".") : ("Mod " + DisplayName + " requires minimum " + ReceivedMinimumRequiredVersion + ". Installed is version " + CurrentVersion + "."); } private string ErrorServer(ZRpc rpc) { return "Disconnect: The client (" + rpc.GetSocket().GetHostName() + ") doesn't have the correct " + DisplayName + " version " + MinimumRequiredVersion; } private string Error(ZRpc? rpc = null) { return (rpc == null) ? ErrorClient() : ErrorServer(rpc); } private static VersionCheck[] GetFailedClient() { return versionChecks.Where((VersionCheck check) => !check.IsVersionOk()).ToArray(); } private static VersionCheck[] GetFailedServer(ZRpc rpc) { ZRpc rpc2 = rpc; return versionChecks.Where((VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc2)).ToArray(); } private static void Logout() { Game.instance.Logout(true, true); AccessTools.DeclaredField(typeof(ZNet), "m_connectionStatus").SetValue(null, (object)(ConnectionStatus)3); } private static void DisconnectClient(ZRpc rpc) { rpc.Invoke("Error", new object[1] { 3 }); } private static void CheckVersion(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, null); } private static void CheckVersion(ZRpc rpc, ZPackage pkg, Action<ZRpc, ZPackage>? original) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string text3 = pkg.ReadString(); bool flag = false; foreach (VersionCheck versionCheck in versionChecks) { if (!(text != versionCheck.Name)) { ModBase.LogDebug("Received " + versionCheck.DisplayName + " version " + text3 + " and minimum version " + text2 + " from the " + (ZNet.instance.IsServer() ? "client" : "server") + "."); versionCheck.ReceivedMinimumRequiredVersion = text2; versionCheck.ReceivedCurrentVersion = text3; if (ZNet.instance.IsServer() && versionCheck.IsVersionOk()) { versionCheck.ValidatedClients.Add(rpc); } flag = true; } } if (flag) { return; } pkg.SetPos(0); if (original != null) { original(rpc, pkg); if (pkg.GetPos() == 0) { notProcessedNames.Add(text, text3); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPrefix] private static bool RPC_PeerInfo(ZRpc rpc, ZNet __instance) { VersionCheck[] array = (__instance.IsServer() ? GetFailedServer(rpc) : GetFailedClient()); if (array.Length == 0) { return true; } VersionC