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 StumpsAreOneHp v0.0.3
StumpsAreOneHp.dll
Decompiled 3 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("StumpsAreOneHp")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+e5b051a86bfd1cf104a7f53b38b22f648e6adf8c")] [assembly: AssemblyProduct("StumpsAreOneHp")] [assembly: AssemblyTitle("StumpsAreOneHp")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 ServerSync { internal class ConfigSync { private class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { 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; Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } 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() { MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); 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 = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return (listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); }).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 Dictionary<string, OwnConfigEntryBase> configEntries = new Dictionary<string, OwnConfigEntryBase>(); public Dictionary<string, CustomSyncedValueBase> customValues = new Dictionary<string, CustomSyncedValueBase>(); } private class PackageEntry { public string section = ""; public string key = ""; public Type? type; public object? value; } [HarmonyPatch(typeof(ZNet), "Shutdown")] private static class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } private static bool ProcessingServerUpdate; private static readonly HashSet<ConfigSync> configSyncs; private static bool isServer; private static bool lockExempt; private static long packageCounter; public string Name; public bool IsSourceOfTruth = true; public bool InitialSyncDone; public string DisplayName = ""; public string CurrentVersion = ""; public string MinimumRequiredVersion = ""; public bool ModRequired; private OwnConfigEntryBase? lockedConfig; private Action lockedConfigChanged = delegate { }; private readonly List<OwnConfigEntryBase> allConfigs = new List<OwnConfigEntryBase>(); internal event Action<bool> SourceOfTruthChanged = delegate { }; 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); } private bool isLockExempt(ZRpc rpc) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown if (lockExempt) { return true; } if (isServer) { SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); string hostName = rpc.GetSocket().GetHostName(); return (methodInfo == null) ? val.Contains(hostName) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, hostName })); } return false; } private OwnConfigEntryBase configData(ConfigEntryBase config) { return allConfigs.First((OwnConfigEntryBase x) => x.BaseConfig == config); } public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { OwnConfigEntryBase ownConfigEntryBase = allConfigs.FirstOrDefault((OwnConfigEntryBase x) => (object)x.BaseConfig == configEntry); SyncedConfigEntry<T> syncedEntry = ownConfigEntryBase as SyncedConfigEntry<T>; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry<T>(configEntry); configEntry.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(0L, (ConfigEntryBase)(object)configEntry); } }; 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 { lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry<T>)lockedConfig; } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { if (isServer) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged = (Action)Delegate.Combine(lockedConfigChanged, new Action(serverLockedSettingChanged)); IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void serverLockedSettingChanged() { if (lockedConfig != null) { ((SyncedConfigEntry<IConvertible>)lockedConfig).AssignLocalValue(((SyncedConfigEntry<IConvertible>)lockedConfig).Value); } } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { ParsedConfigs parsedConfigs = ParseConfigs(package); foreach (KeyValuePair<string, OwnConfigEntryBase> item in parsedConfigs.configEntries.Where((KeyValuePair<string, OwnConfigEntryBase> x) => x.Value.SynchronizedConfig)) { OwnConfigEntryBase value = item.Value; if (value is SyncedConfigEntry<IConvertible> syncedConfigEntry && (value != lockedConfig || isLockExempt(SnatchCurrentlyHandlingRPC.currentRpc))) { syncedConfigEntry.AssignLocalValue(syncedConfigEntry.Value); } } if (clientUpdate && isServer) { Broadcast(0L, package); } return true; } private ParsedConfigs ParseConfigs(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); int num = package.ReadInt(); for (int i = 0; i < num; i++) { string section = package.ReadString(); string key = package.ReadString(); string typeName = package.ReadString(); Type valueType = Type.GetType(typeName); object obj = null; if (valueType != null) { MethodInfo methodInfo = typeof(ZPackage).GetMethods(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "Read" && m.ReturnType == valueType && m.GetParameters().Length == 0); if (methodInfo != null) { obj = methodInfo.Invoke(package, null); } } string key2 = section + "." + key; OwnConfigEntryBase ownConfigEntryBase = allConfigs.FirstOrDefault((OwnConfigEntryBase x) => x.BaseConfig.Definition.Section == section && x.BaseConfig.Definition.Key == key); if (ownConfigEntryBase != null) { parsedConfigs.configEntries[key2] = ownConfigEntryBase; } } return parsedConfigs; } private static ZPackage ConfigsToPackage(IEnumerable<ConfigEntryBase>? configs = null, IEnumerable<CustomSyncedValueBase>? customValues = null, IEnumerable<PackageEntry>? packageEntries = null, bool partial = true) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(partial ? 1 : 0); List<ConfigEntryBase> list = configs?.ToList() ?? new List<ConfigEntryBase>(); List<CustomSyncedValueBase> list2 = customValues?.ToList() ?? new List<CustomSyncedValueBase>(); val.Write(list.Count + list2.Count + (packageEntries?.Count() ?? 0)); foreach (ConfigEntryBase item in list) { val.Write(item.Definition.Section); val.Write(item.Definition.Key); val.Write(item.SettingType.FullName ?? ""); AccessTools.DeclaredMethod(typeof(ZPackage), "Write", new Type[1] { item.SettingType }, (Type[])null)?.Invoke(val, new object[1] { item.BoxedValue }); } foreach (CustomSyncedValueBase item2 in list2) { val.Write("CustomSyncedValue"); val.Write(item2.Identifier); val.Write(item2.Type?.FullName ?? ""); item2.WriteToPackage(val); } if (packageEntries != null) { foreach (PackageEntry packageEntry in packageEntries) { val.Write(packageEntry.section); val.Write(packageEntry.key); val.Write(packageEntry.type?.FullName ?? ""); AccessTools.DeclaredMethod(typeof(ZPackage), "Write", new Type[1] { packageEntry.type }, (Type[])null)?.Invoke(val, new object[1] { packageEntry.value }); } } return val; } private void Broadcast(long target, ConfigEntryBase config) { Broadcast(target, ConfigsToPackage((IEnumerable<ConfigEntryBase>?)(object)new ConfigEntryBase[1] { config })); } private void Broadcast(long target, ZPackage package) { if (ZRoutedRpc.instance != null) { if (isServer) { ZRoutedRpc.instance.InvokeRoutedRPC(target, Name + " ConfigSync", new object[1] { package }); } else { ZRoutedRpc.instance.InvokeRoutedRPC(0L, Name + " ConfigSync", new object[1] { package }); } } } private void BroadcastToPeers(List<ZNetPeer> peers, ZPackage package) { foreach (ZNetPeer peer in peers) { peer.m_rpc.Invoke(Name + " ConfigSync", new object[1] { package }); } } private IEnumerator sendZPackage(List<ZNetPeer> peers, ZPackage package) { yield return null; BroadcastToPeers(peers, package); } private void resetConfigsFromServer() { foreach (OwnConfigEntryBase allConfig in allConfigs) { if (allConfig.SynchronizedConfig && allConfig.LocalBaseValue != null) { if (allConfig is SyncedConfigEntry<IConvertible> syncedConfigEntry) { syncedConfigEntry.AssignLocalValue(syncedConfigEntry.Value); } allConfig.LocalBaseValue = null; } } } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field)] internal class ConfigurationManagerAttributes : Attribute { public bool? ShowRangeButtons; public string? Category; public string? Name; public string? Description; public int? Order; public bool? ReadOnly; public bool? Browsable; public string? CustomDrawer; } internal class CustomSyncedValue<T> : CustomSyncedValueBase { private bool localIsOwner = true; private object? boxedValue; public override object? BoxedValue { get { return boxedValue; } set { boxedValue = value; } } public T Value { get { return (T)boxedValue; } set { 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 abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly ConfigSync Config; public readonly string Identifier; public readonly int Priority; public readonly Type? Type; public bool SynchronizedConfig = true; private bool localIsOwner = true; public abstract object? BoxedValue { get; set; } protected CustomSyncedValueBase(ConfigSync config, string identifier, Type type, int priority = 0) { Config = config; Identifier = identifier; Type = type; Priority = priority; config.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } public void WriteToPackage(ZPackage package) { AccessTools.DeclaredMethod(typeof(ZPackage), "Write", new Type[1] { Type }, (Type[])null)?.Invoke(package, new object[1] { BoxedValue }); } } internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } 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; } } } [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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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 Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion); bool flag2 = new Version(ReceivedCurrentVersion) >= new Version(MinimumRequiredVersion); return flag && flag2; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return DisplayName + " is not installed on the server."; } return (new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion)) ? (DisplayName + " may not be higher than version " + ReceivedCurrentVersion + ". You have version " + CurrentVersion + ".") : (DisplayName + " needs to be at least version " + ReceivedMinimumRequiredVersion + ". You have 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) { return versionChecks.Where((VersionCheck check) => check.ModRequired && !check.ValidatedClients.Contains(rpc)).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)) { Debug.Log((object)("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; } VersionCheck[] array2 = array; VersionCheck[] array3 = array2; foreach (VersionCheck versionCheck in array3) { Debug.LogWarning((object)versionCheck.Error(rpc)); } if (__instance.IsServer()) { DisconnectClient(rpc); } else { Logout(); } return false; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPrefix] private static void RegisterAndCheckVersion(ZNetPeer peer, ZNet __instance) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Expected O, but got Unknown notProcessedNames.Clear(); IDictionary dictionary = (IDictionary)typeof(ZRpc).GetField("m_functions", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(peer.m_rpc); if (dictionary.Contains(StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck"))) { object obj = dictionary[StringExtensionMethods.GetStableHashCode("ServerSync VersionCheck")]; Action<ZRpc, ZPackage> action = (Action<ZRpc, ZPackage>)obj.GetType().GetField("m_action", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(obj); peer.m_rpc.Register<ZPackage>("ServerSync VersionCheck", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage pkg) { CheckVersion(rpc, pkg, action); }); } else { peer.m_rpc.Register<ZPackage>("ServerSync VersionCheck", (Action<ZRpc, ZPackage>)CheckVersion); } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.Initialize(); if (versionCheck.ModRequired || __instance.IsServer()) { Debug.Log((object)("Sending " + versionCheck.DisplayName + " version " + versionCheck.CurrentVersion + " and minimum version " + versionCheck.MinimumRequiredVersion + " to the " + (__instance.IsServer() ? "client" : "server") + ".")); ZPackage val = new ZPackage(); val.Write(versionCheck.Name); val.Write(versionCheck.MinimumRequiredVersion); val.Write(versionCheck.CurrentVersion); peer.m_rpc.Invoke("ServerSync VersionCheck", new object[1] { val }); } } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPrefix] private static void RemoveDisconnected(ZNetPeer peer, ZNet __instance) { if (!__instance.IsServer()) { return; } foreach (VersionCheck versionCheck in versionChecks) { versionCheck.ValidatedClients.Remove(peer.m_rpc); } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] [HarmonyPostfix] private static void ShowConnectionError(FejdStartup __instance) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) if (!__instance.m_connectionFailedPanel.activeSelf || (int)ZNet.GetConnectionStatus() != 3) { return; } bool flag = false; VersionCheck[] failedClient = GetFailedClient(); if (failedClient.Length != 0) { string text = string.Join("\n", failedClient.Select((VersionCheck check) => check.Error())); TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + text; flag = true; } foreach (KeyValuePair<string, string> item in notProcessedNames.OrderBy((KeyValuePair<string, string> kv) => kv.Key)) { if (!__instance.m_connectionFailedError.text.Contains(item.Key)) { TMP_Text connectionFailedError2 = __instance.m_connectionFailedError; connectionFailedError2.text = connectionFailedError2.text + "\nServer expects you to have " + item.Key + " (Version: " + item.Value + ") installed."; flag = true; } } if (flag) { RectTransform component = ((Component)__instance.m_connectionFailedPanel.transform.Find("Image")).GetComponent<RectTransform>(); Vector2 sizeDelta = component.sizeDelta; sizeDelta.x = 675f; component.sizeDelta = sizeDelta; __instance.m_connectionFailedError.ForceMeshUpdate(false, false); float num = __instance.m_connectionFailedError.renderedHeight + 105f; RectTransform component2 = ((Component)((Component)component).transform.Find("ButtonOk")).GetComponent<RectTransform>(); component2.anchoredPosition = new Vector2(component2.anchoredPosition.x, component2.anchoredPosition.y - (num - component.sizeDelta.y) / 2f); sizeDelta = component.sizeDelta; sizeDelta.y = num; component.sizeDelta = sizeDelta; } } } } namespace JetBrains.Annotations { [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] internal sealed class UsedImplicitlyAttribute : Attribute { } [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] internal sealed class PublicAPIAttribute : Attribute { } [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] internal sealed class MeansImplicitUseAttribute : Attribute { } } namespace StumpsAreOneHp { [BepInPlugin("coemt.StumpsAreOneHp", "StumpsAreOneHp", "0.0.2")] public class StumpsAreOneHpPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } internal const string ModName = "StumpsAreOneHp"; internal const string ModVersion = "0.0.2"; internal const string Author = "DaiMinhTri (fork of coemt 0.0.1)"; private const string ModGUID = "coemt.StumpsAreOneHp"; private static string ConfigFileName = "coemt.StumpsAreOneHp.cfg"; private static string ConfigFileFullPath; internal static string ConnectionError; private readonly Harmony _harmony = new Harmony("coemt.StumpsAreOneHp"); private static ManualLogSource _logger; private static readonly ConfigSync ConfigSync; private static ConfigEntry<Toggle> _serverConfigLocked; internal static ManualLogSource StumpsAreOneHpLogger => _logger; public void Awake() { _logger = Logger.CreateLogSource("StumpsAreOneHp"); _serverConfigLocked = config("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only."); ConfigSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); _harmony.PatchAll(Assembly.GetExecutingAssembly()); SetupWatcher(); } private void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void SetupWatcher() { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName) { IncludeSubdirectories = true, SynchronizingObject = ThreadingHelper.SynchronizingObject }; fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(ConfigFileFullPath)) { return; } try { StumpsAreOneHpLogger.LogDebug((object)"ReadConfigValues called"); ((BaseUnityPlugin)this).Config.Reload(); } catch { StumpsAreOneHpLogger.LogError((object)("There was an issue loading your " + ConfigFileName)); StumpsAreOneHpLogger.LogError((object)"Please check your config entries for spelling and format!"); } } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2); syncedConfigEntry.SynchronizedConfig = synchronizedSetting; return val2; } private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } static StumpsAreOneHpPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ConnectionError = ""; ConfigSync = new ConfigSync("coemt.StumpsAreOneHp") { DisplayName = "StumpsAreOneHp", CurrentVersion = "0.0.2", MinimumRequiredVersion = "0.0.1" }; _serverConfigLocked = null; } } public static class StumpHp { [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class ZNetScene_Awake_Patch { [HarmonyPostfix] public static void Postfix(ZNetScene __instance) { if (Object.op_Implicit((Object)(object)__instance)) { string[] stumpPrefabs = StumpPrefabs; foreach (string prefabName in stumpPrefabs) { Stumped(__instance, prefabName); } } } private static void Stumped(ZNetScene instance, string prefabName) { GameObject prefab = instance.GetPrefab(prefabName); Destructible val = default(Destructible); if (!Object.op_Implicit((Object)(object)prefab)) { StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogWarning((object)("Prefab not found: " + prefabName + " (skipped)")); } else if (prefab.TryGetComponent<Destructible>(ref val)) { val.m_health = 1f; } else { StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogWarning((object)("No Destructible on " + prefabName + " (skipped)")); } } } private static readonly string[] StumpPrefabs = new string[9] { "Beech_Stub", "BirchStub", "FirTree_Stub", "OakStub", "Pinetree_01_Stub", "SwampTree1_Stub", "AshlandsTreeStump1", "AshlandsTreeStump2", "AshlandsTreeStump3" }; } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] public static class RegisterAndCheckVersion { private static void Prefix(ZNetPeer peer) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogDebug((object)"Registering version RPC handler"); peer.m_rpc.Register<ZPackage>("StumpsAreOneHp_VersionCheck", (Action<ZRpc, ZPackage>)RpcHandlers.RPC_StumpsAreOneHp_Version); StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogDebug((object)"Invoking version check"); ZPackage val = new ZPackage(); val.Write("0.0.2"); peer.m_rpc.Invoke("StumpsAreOneHp_VersionCheck", new object[1] { val }); } } [HarmonyPatch(typeof(ZNet), "Disconnect")] public static class RemoveDisconnectedPeerFromVerified { private static void Prefix(ZNetPeer peer, ZNet __instance) { if (__instance.IsServer()) { StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogInfo((object)("Peer (" + peer.m_rpc.m_socket.GetHostName() + ") disconnected, removing from validated list")); RpcHandlers.ValidatedPeers.Remove(peer.m_rpc); } } } public static class RpcHandlers { public static readonly List<ZRpc> ValidatedPeers = new List<ZRpc>(); public static void RPC_StumpsAreOneHp_Version(ZRpc rpc, ZPackage pkg) { string text = pkg.ReadString(); StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogInfo((object)("Version check, local: 0.0.2, remote: " + text)); if (text != "0.0.2") { StumpsAreOneHpPlugin.ConnectionError = "StumpsAreOneHp Installed: 0.0.2\n Needed: " + text; if (ZNet.instance.IsServer()) { StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogWarning((object)("Peer (" + rpc.m_socket.GetHostName() + ") has incompatible version, disconnecting...")); rpc.Invoke("Error", new object[1] { 3 }); } } else if (!ZNet.instance.IsServer()) { StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogInfo((object)"Received same version from server!"); } else { StumpsAreOneHpPlugin.StumpsAreOneHpLogger.LogInfo((object)("Adding peer (" + rpc.m_socket.GetHostName() + ") to validated list")); ValidatedPeers.Add(rpc); } } } [HarmonyPatch(typeof(FejdStartup), "ShowConnectError")] public static class ShowConnectionError { private static void Postfix(FejdStartup __instance) { if (__instance.m_connectionFailedPanel.activeSelf) { __instance.m_connectionFailedError.fontSizeMax = 25f; __instance.m_connectionFailedError.fontSizeMin = 15f; TMP_Text connectionFailedError = __instance.m_connectionFailedError; connectionFailedError.text = connectionFailedError.text + "\n" + StumpsAreOneHpPlugin.ConnectionError; } } } }