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 SNEAKer v1.2.0
SNEAKer.dll
Decompiled 2 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using SNEAKer.Utils; using ServerSync; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("SNEAKer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+3fa64cf70dcd0eb8cab2e6eae73c87e1908e3bb9")] [assembly: AssemblyProduct("SNEAKer")] [assembly: AssemblyTitle("SNEAKer")] [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> list = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return (!(listContainsId == null)) ? ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })) : adminList.Contains(hostName); }).ToList(); SendAdmin(ZNet.instance.GetPeers().Except(list).ToList(), isAdmin: false); SendAdmin(list, 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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(); if (!(methodInfo == null)) { return (bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, hostName }); } return val.Contains(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) { foreach (KeyValuePair<string, OwnConfigEntryBase> item in ParseConfigs(package).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); 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) { 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_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: 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 { string text = minimumRequiredVersion; if (text == null) { if (!ModRequired) { return "0.0.0"; } text = CurrentVersion; } return text; } set { minimumRequiredVersion = value; } } private static void PatchServerSync() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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 num = new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion); bool flag = new Version(ReceivedCurrentVersion) >= new Version(MinimumRequiredVersion); return num && flag; } private string ErrorClient() { if (ReceivedMinimumRequiredVersion == null) { return DisplayName + " is not installed on the server."; } if (!(new Version(CurrentVersion) >= new Version(ReceivedMinimumRequiredVersion))) { return DisplayName + " needs to be at least version " + ReceivedMinimumRequiredVersion + ". You have version " + CurrentVersion + "."; } return DisplayName + " may not be higher than version " + ReceivedCurrentVersion + ". 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) { if (rpc != null) { return ErrorServer(rpc); } return ErrorClient(); } 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; for (int i = 0; i < array2.Length; i++) { Debug.LogWarning((object)array2[i].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_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016a: 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_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: 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 SNEAKer { [BepInPlugin("blacks7ar.SNEAKer", "SNEAKer", "1.2.0")] public class Plugin : BaseUnityPlugin { [HarmonyPatch(typeof(Character), "UpdateWalking")] public static class SneakSpeedPatch { [HarmonyPrefix] public static void Prefix(Character __instance, out float __state) { __state = __instance.m_crouchSpeed; if (__instance.IsPlayer() && __instance.IsCrouching() && !__instance.IsEncumbered() && __instance.m_name.Contains("Human")) { float skillFactor = __instance.GetSkillFactor((SkillType)101); __instance.m_crouchSpeed *= Mathf.Lerp(1f, _sneakSpeedAtMaxLevel.Value, skillFactor); } } [HarmonyFinalizer] public static Exception Finalizer(Character __instance, float __state, Exception __exception) { __instance.m_crouchSpeed = __state; return __exception; } } [HarmonyPatch(typeof(Skills), "RaiseSkill")] public static class RaiseSkillPatch { private static void Prefix(ref SkillType skillType, ref float factor) { if (_enableExpMultiplier.Value == Toggle.On && (int)skillType == 101) { factor *= _sneakExpMultiplier.Value; } } private static void Postfix(Skills __instance, SkillType skillType) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (_enableExpMultiplier.Value != Toggle.On || _displayExpGained.Value != Toggle.On || (int)skillType != 101) { return; } try { if (!((Object)(object)Player.m_localPlayer == (Object)null) && __instance.GetSkillLevel((SkillType)101) < 100f) { Skill skill = __instance.GetSkill((SkillType)101); float value = skill.m_accumulator / (skill.GetNextLevelRequirement() / 100f); ((Character)Player.m_localPlayer).Message((MessageType)1, $"Level {skill.m_level.tFloat(0)} {skill.m_info.m_skill} [{skill.m_accumulator.tFloat(2)} / {skill.GetNextLevelRequirement().tFloat(2)}] ({value.tFloat(0)}%)", 0, skill.m_info.m_icon, false); } } catch (Exception ex) { Debug.LogWarning((object)("[SNEAKer] Failed to display exp: " + ex.Message)); } } } private const SkillType SneakSkill = (SkillType)101; public const string modName = "SNEAKer"; public const string modAuthor = "blacks7ar"; public const string modVersion = "1.2.0"; public const string modLink = "https://valheim.thunderstore.io/package/blacks7ar/SNEAKer/"; private static readonly Harmony _harmony = new Harmony("blacks7ar.SNEAKer"); private static readonly ConfigSync _configSync = new ConfigSync("blacks7ar.SNEAKer") { DisplayName = "SNEAKer", CurrentVersion = "1.2.0", MinimumRequiredVersion = "1.2.0" }; private static ConfigEntry<Toggle> _serverConfigLocked; private static ConfigEntry<Toggle> _enableExpMultiplier; private static ConfigEntry<float> _sneakExpMultiplier; private static ConfigEntry<Toggle> _displayExpGained; private static ConfigEntry<float> _sneakSpeedAtMaxLevel; private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: 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); _configSync.AddConfigEntry<T>(val2).SynchronizedConfig = synchronizedSetting; return val2; } public void Awake() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; _serverConfigLocked = config("1- ServerSync", "Lock Configuration", Toggle.On, new ConfigDescription("If On, the configuration is locked and can be changed by server admins only.", (AcceptableValueBase)null, Array.Empty<object>())); _configSync.AddLockingConfigEntry<Toggle>(_serverConfigLocked); _sneakSpeedAtMaxLevel = config("2- General", "Max Sneak Speed", 3f, new ConfigDescription("Max sneak speed at Sneak Level 100.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>())); _enableExpMultiplier = config("3- Skill Exp", "Enable Exp Multiplier", Toggle.On, new ConfigDescription("Enable/Disable exp multiplier.", (AcceptableValueBase)null, Array.Empty<object>())); _sneakExpMultiplier = config("3- Skill Exp", "Exp Gain Multiplier", 1f, new ConfigDescription("Sneak exp multiplier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>())); _displayExpGained = config("3- Skill Exp", "Display Exp Gained", Toggle.On, new ConfigDescription("Enable/Disable exp gained notifications.", (AcceptableValueBase)null, Array.Empty<object>())); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; ((BaseUnityPlugin)this).Config.Save(); _harmony.PatchAll(typeof(Plugin).Assembly); } private void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } } } namespace SNEAKer.Utils { internal static class Helper { public static float tFloat(this float value, int digits) { double num = Math.Pow(10.0, digits); return (float)Math.Truncate(num * (double)value / num); } } public enum Toggle { On = 1, Off = 0 } }