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 SmartWishboneUpdated v1.0.6
SmartWishbone.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
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 BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using ServerSync; using SmartWishbone.ServerSync; using UnityEngine; using UnityEngine.SceneManagement; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; 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.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SmartWishbone")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyProduct("SmartWishbone")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("90f14423-a2aa-4f92-86ea-ed4772443835")] [assembly: AssemblyFileVersion("1.0.6.0")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.6.0")] [module: UnverifiableCode] namespace ServerSync { internal static class AdminSyncing { [HarmonyPatch(typeof(ZNet), "Awake")] private static class AdminStatusSyncPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { _isServer = __instance.IsServer(); if (!_isServer) { ConfigSync.lockExempt = false; } ZRoutedRpc.instance.Register<ZPackage>(AdminStatusRpcName, (Action<long, ZPackage>)RPC_AdminStatusSync); if (_isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } } } private const string AdminStatusRpcSuffix = " AdminStatusSync"; private const float AdminListPollIntervalSeconds = 4f; private const int CompressedPackageThresholdBytes = 10000; private const int CompressedPackageMagic = 4; private const long ServerPeerLoopbackId = 0L; private static bool _isServer; private static readonly string AdminStatusRpcName = typeof(AdminSyncing).Assembly.GetName().Name + " AdminStatusSync"; private static readonly HashSet<long> _sentPeers = new HashSet<long>(); internal static event Action<bool> AdminStatusChanged; internal static bool IsAdmin(long senderId) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return false; } ZNetPeer peer = ZNet.instance.GetPeer(senderId); if (peer == null) { return false; } return AdminListContains(peer.m_rpc.GetSocket().GetHostName()); } internal static bool IsLocalAdmin() { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { return false; } try { if (instance.IsServer()) { return true; } } catch { } try { if (instance.LocalPlayerIsAdminOrHost()) { return true; } } catch { } try { if (ConfigSync.lockExempt) { return true; } } catch { } return false; } internal static bool AdminListContains(string hostName) { if (string.IsNullOrEmpty(hostName)) { return false; } SyncedList adminList = GetAdminList(); if (adminList == null) { return false; } if (adminList.Contains(hostName)) { return true; } string value = NormalizeId(hostName); foreach (string item in adminList.GetList()) { if (NormalizeId(item).Equals(value, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static string NormalizeId(string id) { if (string.IsNullOrEmpty(id)) { return string.Empty; } int num = id.IndexOf('_'); return ((num >= 0) ? id.Substring(num + 1) : id).Trim(); } private static IEnumerator WatchAdminListChanges() { SyncedList adminList = GetAdminList(); HashSet<string> currentAdmins = new HashSet<string>(adminList.GetList()); bool first = true; while (true) { HashSet<string> hashSet = new HashSet<string>(adminList.GetList()); if (!hashSet.SetEquals(currentAdmins) || first) { _sentPeers.Clear(); PushAdminStatus(from p in ZNet.instance.GetPeers() where p.IsReady() select p); currentAdmins = hashSet; first = false; } else { PushAdminStatus(from p in ZNet.instance.GetPeers() where p.IsReady() && !_sentPeers.Contains(p.m_uid) select p); } _sentPeers.RemoveWhere((long uid) => !ZNet.instance.GetPeers().Any((ZNetPeer p) => p.m_uid == uid)); yield return (object)new WaitForSeconds(4f); } } internal static void PushAdminStatus(IEnumerable<ZNetPeer> targets) { List<ZNetPeer> list = targets?.ToList(); if (list == null || list.Count == 0) { return; } List<ZNetPeer> list2 = list.Where((ZNetPeer p) => AdminListContains(p.m_rpc.GetSocket().GetHostName())).ToList(); SendAdminStatus(list.Except(list2).ToList(), isAdmin: false); SendAdminStatus(list2, isAdmin: true); foreach (ZNetPeer item in list) { _sentPeers.Add(item.m_uid); } } private static void SendAdminStatus(List<ZNetPeer> peers, bool isAdmin) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown if (peers.Any()) { ZPackage val = new ZPackage(); val.Write(isAdmin); ((MonoBehaviour)ZNet.instance).StartCoroutine(SendZPackage(peers, val)); } } private static void RPC_AdminStatusSync(long sender, ZPackage package) { if (_isServer) { HandleServerSideAdminRequest(sender); } else { HandleClientSideAdminStatusReceived(package); } } private static void HandleServerSideAdminRequest(long sender) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null && AdminListContains(peer.m_rpc.GetSocket().GetHostName())) { ZPackage val = new ZPackage(); val.Write(true); ZRoutedRpc.instance.InvokeRoutedRPC(sender, AdminStatusRpcName, new object[1] { val }); } } private static void HandleClientSideAdminStatusReceived(ZPackage package) { bool obj = (ConfigSync.lockExempt = package.ReadBool()); ConfigSync.RefreshReadOnlyFlagsForAll(); AdminSyncing.AdminStatusChanged?.Invoke(obj); } private static IEnumerator SendZPackage(List<ZNetPeer> peers, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] array = package.GetArray(); if (array.Length > 10000) { package = BuildCompressedPackage(array); } foreach (ZNetPeer item in peers.Where((ZNetPeer p) => p.IsReady())) { long num = (item.m_server ? 0 : item.m_uid); ZRoutedRpc.instance.InvokeRoutedRPC(num, AdminStatusRpcName, new object[1] { package }); } } private static ZPackage BuildCompressedPackage(byte[] data) { //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(4); using MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Optimal)) { deflateStream.Write(data, 0, data.Length); } val.Write(memoryStream.ToArray()); return val; } private static SyncedList GetAdminList() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown return (SyncedList)AccessTools.Field(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); } } internal class ConfigSync { private const byte PartialConfigs = 1; private const byte FragmentedConfig = 2; private const byte CompressedConfig = 4; private const int CompressionThresholdBytes = 10000; private const int FragmentChunkBytes = 250000; private const int PeerSendQueueBackpressureBytes = 20000; private const float PeerSendQueueTimeoutSeconds = 30f; private const int FragmentCacheLifetimeSeconds = 60; private const double ReceivedLogThrottleSeconds = 12.0; private const string InternalSection = "Internal"; private const string ReservedServerVersionIdentifier = "serverversion"; private const string ConfigSyncRpcSuffix = " ConfigSync"; private const int ManifestKeyHashPrime = 397; public static bool ProcessingServerUpdate = false; public static bool isServer; public static bool lockExempt = false; public static HashSet<ConfigSync> configSyncs = new HashSet<ConfigSync>(); public readonly string Name; public string DisplayName; public string CurrentVersion; public string MinimumRequiredVersion; public bool ModRequired; public HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); public HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private bool? forceConfigLocking; private bool isSourceOfTruth = true; private bool initialSyncDone; private OwnConfigEntryBase lockedConfig; private DateTime lastConfigLogTime = DateTime.MinValue; 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 = 0L; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { OwnConfigEntryBase ownConfigEntryBase = lockedConfig; if (!(((ownConfigEntryBase != null) ? ownConfigEntryBase.BaseConfig.BoxedValue : null) is IConvertible convertible)) { goto IL_0055; } num = convertible.ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag == true; } if (num) { return !lockExempt; } goto IL_0055; IL_0055: return false; } set { forceConfigLocking = value; } } public bool IsAdmin { get { if (!lockExempt && !((Object)(object)ZNet.instance == (Object)null)) { return ZNet.instance.IsServer(); } return true; } } public bool IsSourceOfTruth { get { return isSourceOfTruth; } internal set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get { return initialSyncDone; } internal set { initialSyncDone = value; } } public event Action<bool> SourceOfTruthChanged; public event Action lockedConfigChanged; public ConfigSync(string name) { Name = name; DisplayName = name; configSyncs.Add(this); } public void RequestSync() { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer()) { ZNetPeer val = ((IEnumerable<ZNetPeer>)(List<ZNetPeer>)AccessTools.Field(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance)).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_server)); if (val != null) { ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, Name + " ConfigSync", new object[1] { (object)new ZPackage() }); } } } internal void SendAllValuesToJoiningPeer(ZNetPeer peer) { List<ZNetPeer> peers = new List<ZNetPeer> { peer }; ((MonoBehaviour)ZNet.instance).StartCoroutine(SendZPackage(peers, BuildAllValuesPackage(), waitForSendQueue: false)); } internal void RestoreLocalConfigs() { resetConfigsFromServer(); IsSourceOfTruth = true; InitialSyncDone = false; } internal static void RefreshReadOnlyFlagsForAll() { foreach (ConfigSync configSync in configSyncs) { configSync.serverLockedSettingChanged(); } } private ZPackage BuildAllValuesPackage() { return ConfigsToPackage(allConfigs.Select((OwnConfigEntryBase config) => config.BaseConfig), allCustomValues, null, partial: false); } private void SendAllValuesToRequestingPeer(long sender) { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer != null) { List<ZNetPeer> peers = new List<ZNetPeer> { peer }; ((MonoBehaviour)ZNet.instance).StartCoroutine(SendZPackage(peers, BuildAllValuesPackage())); } } public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { SyncedConfigEntry<T> syncedEntry = (configData((ConfigEntryBase)(object)configEntry) as SyncedConfigEntry<T>) ?? new SyncedConfigEntry<T>(configEntry); object[] array = ((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty<object>(); if (!array.OfType<ConfigurationManagerAttributes>().Any()) { array = new object[1] { new ConfigurationManagerAttributes() }.Concat(array).ToArray(); } array = array.Concat(new object[1] { syncedEntry }).ToArray(); AccessTools.Field(typeof(ConfigDescription), "<Tags>k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, array); 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 { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry<T>)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Any((CustomSyncedValueBase v) => v.Identifier == customValue.Identifier) || customValue.Identifier == "serverversion") { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet<CustomSyncedValueBase>(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(0L, customValue); } }; } public void Broadcast(long target, ConfigEntryBase config) { if (!IsLocked || IsAdmin) { ZPackage package = ConfigsToPackage((IEnumerable<ConfigEntryBase>)(object)new ConfigEntryBase[1] { config }); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(SendZPackage(target, package)); } } } public void Broadcast(long target, CustomSyncedValueBase customValue) { if (!IsLocked || IsAdmin) { ZPackage package = ConfigsToPackage(null, new CustomSyncedValueBase[1] { customValue }); ZNet instance = ZNet.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(SendZPackage(target, package)); } } } internal void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } internal void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { if (isServer && package.Size() == 0) { SendAllValuesToRequestingPeer(sender); } else { HandleConfigSyncRPC(sender, package, clientUpdate: true); } } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { try { if (!IsCallerAllowedWhenLocked()) { return false; } ExpireStaleFragmentCacheEntries(); byte flags = package.ReadByte(); if (!TryReassembleFragments(sender, ref package, ref flags)) { return false; } ProcessingServerUpdate = true; if ((flags & 4) != 0) { DecompressPackage(ref package, out flags); } if ((flags & 1) == 0) { resetConfigsFromServer(); } ParsedConfigs parsed = ReadConfigsFromPackage(package); ApplyParsedConfigsAndCustomValues(parsed); MaybeLogReceived(parsed, sender, clientUpdate); if (!isServer) { serverLockedSettingChanged(); } return true; } finally { ProcessingServerUpdate = false; } } private bool IsCallerAllowedWhenLocked() { if (!isServer || !IsLocked) { return true; } 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) { return AdminSyncing.AdminListContains(text); } return true; } private void ExpireStaleFragmentCacheEntries() { cacheExpirations.RemoveAll((KeyValuePair<long, string> kv) => kv.Key < DateTimeOffset.Now.Ticks && configValueCache.Remove(kv.Value)); } private bool TryReassembleFragments(long sender, ref ZPackage package, ref byte flags) { //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Expected O, but got Unknown if ((flags & 2) == 0) { return true; } long num = package.ReadLong(); string text = sender.ToString() + num; if (!configValueCache.TryGetValue(text, out var value)) { value = new SortedDictionary<int, byte[]>(); configValueCache[text] = value; cacheExpirations.Add(new KeyValuePair<long, string>(DateTimeOffset.Now.Ticks + 600000000, text)); } int key = package.ReadInt(); int num2 = package.ReadInt(); value[key] = package.ReadByteArray(); if (value.Count < num2) { return false; } configValueCache.Remove(text); package = new ZPackage(value.Values.SelectMany((byte[] a) => a).ToArray()); flags = package.ReadByte(); return true; } private static void DecompressPackage(ref ZPackage package, out byte flags) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown using (MemoryStream stream = new MemoryStream(package.ReadByteArray())) { using MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress)) { deflateStream.CopyTo(memoryStream); } package = new ZPackage(memoryStream.ToArray()); } flags = package.ReadByte(); } private void ApplyParsedConfigsAndCustomValues(ParsedConfigs parsed) { ConfigFile val = null; bool saveOnConfigSet = false; bool flag = false; foreach (KeyValuePair<OwnConfigEntryBase, object> configValue in parsed.configValues) { if (!isServer && configValue.Key.LocalBaseValue == null) { configValue.Key.LocalBaseValue = configValue.Key.BaseConfig.BoxedValue; } if (val == null) { val = configValue.Key.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } object boxedValue = configValue.Key.BaseConfig.BoxedValue; configValue.Key.BaseConfig.BoxedValue = configValue.Value; flag |= !object.Equals(boxedValue, configValue.Key.BaseConfig.BoxedValue); } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; if (flag) { val.Save(); } } foreach (KeyValuePair<CustomSyncedValueBase, object> customValue in parsed.customValues) { if (!isServer && !customValue.Key.HasLocalBaseValue) { customValue.Key.LocalBaseValue = customValue.Key.BoxedValue; } customValue.Key.BoxedValue = customValue.Value; } } private void MaybeLogReceived(ParsedConfigs parsed, long sender, bool clientUpdate) { if (!((DateTime.Now - lastConfigLogTime).TotalSeconds <= 12.0)) { string text = ((isServer || clientUpdate) ? $"client {sender}" : "server"); Debug.Log((object)($"Received {parsed.configValues.Count} configs and {parsed.customValues.Count} " + "custom values from " + text + " for mod " + (DisplayName ?? Name))); lastConfigLogTime = DateTime.Now; } } private void serverLockedSettingChanged() { foreach (OwnConfigEntryBase allConfig in allConfigs) { ConfigurationManagerAttributes configurationManagerAttributes = allConfig.BaseConfig.Description.Tags?.OfType<ConfigurationManagerAttributes>().FirstOrDefault(); if (configurationManagerAttributes != null) { configurationManagerAttributes.ReadOnly = !isWritableConfig(allConfig); } } } internal static bool isWritableConfig(OwnConfigEntryBase config) { ConfigSync configSync = configSyncs.FirstOrDefault((ConfigSync cs) => cs.allConfigs.Contains(config)); if (configSync == null || configSync.IsSourceOfTruth || !config.SynchronizedConfig || config.LocalBaseValue == null) { return true; } if (configSync.IsLocked) { return false; } if (config == configSync.lockedConfig) { return lockExempt; } return true; } internal void resetConfigsFromServer() { ConfigFile val = null; bool saveOnConfigSet = false; bool flag = false; foreach (OwnConfigEntryBase item in allConfigs.Where((OwnConfigEntryBase c) => c.LocalBaseValue != null)) { if (val == null) { val = item.BaseConfig.ConfigFile; saveOnConfigSet = val.SaveOnConfigSet; val.SaveOnConfigSet = false; } object boxedValue = item.BaseConfig.BoxedValue; item.BaseConfig.BoxedValue = item.LocalBaseValue; flag |= !object.Equals(boxedValue, item.BaseConfig.BoxedValue); item.LocalBaseValue = null; } if (val != null) { val.SaveOnConfigSet = saveOnConfigSet; if (flag) { val.Save(); } } foreach (CustomSyncedValueBase item2 in allCustomValues.Where((CustomSyncedValueBase c) => c.HasLocalBaseValue)) { item2.BoxedValue = item2.LocalBaseValue; item2.ClearLocalBaseValue(); } lockedConfigChanged -= serverLockedSettingChanged; serverLockedSettingChanged(); } private ParsedConfigs ReadConfigsFromPackage(ZPackage package) { ParsedConfigs parsedConfigs = new ParsedConfigs(); Dictionary<string, OwnConfigEntryBase> dictionary = allConfigs.ToDictionary((OwnConfigEntryBase c) => c.BaseConfig.Definition.Section + "*" + c.BaseConfig.Definition.Key, (OwnConfigEntryBase c) => c); int num = package.ReadInt(); for (int num2 = 0; num2 < num; num2++) { string text = package.ReadString(); string text2 = package.ReadString(); string text3 = package.ReadString(); Type type = Type.GetType(text3); if (text3 != "" && type == null) { Debug.LogWarning((object)("Got invalid type " + text3 + ", abort reading of received configs")); return new ParsedConfigs(); } object value; try { value = ((text3 == "") ? null : ReadValueWithTypeFromZPackage(package, type)); } catch (InvalidDeserializationTypeException ex) { Debug.LogWarning((object)("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; } if (text == "Internal") { HandleInternalEntry(parsedConfigs, text2, text3, value); continue; } if (dictionary.TryGetValue(text + "*" + text2, out var value2)) { HandleConfigEntry(parsedConfigs, value2, text, text2, text3, value); continue; } Debug.LogWarning((object)("Received unknown config entry " + text2 + " in section " + text + " for mod " + (DisplayName ?? Name) + ".")); } return parsedConfigs; } private void HandleInternalEntry(ParsedConfigs parsed, string key, string typeName, object value) { CustomSyncedValueBase customSyncedValueBase = allCustomValues.FirstOrDefault((CustomSyncedValueBase v) => v.Identifier == key); if (customSyncedValueBase != null) { if ((typeName == "" && (!customSyncedValueBase.Type.IsValueType || Nullable.GetUnderlyingType(customSyncedValueBase.Type) != null)) | (GetZPackageTypeString(CustomValueWireType(customSyncedValueBase.Type)) == typeName)) { parsed.customValues[customSyncedValueBase] = FromCustomValueWireValue(customSyncedValueBase.Type, value); return; } Debug.LogWarning((object)("Got unexpected type " + typeName + " for internal value " + key + " for mod " + (DisplayName ?? Name) + ", expecting " + customSyncedValueBase.Type.AssemblyQualifiedName)); } } private void HandleConfigEntry(ParsedConfigs parsed, OwnConfigEntryBase config, string section, string key, string typeName, object value) { Type type = configType(config.BaseConfig); if ((typeName == "" && (!type.IsValueType || Nullable.GetUnderlyingType(type) != null)) | (GetZPackageTypeString(type) == typeName)) { parsed.configValues[config] = value; return; } Debug.LogWarning((object)("Got unexpected type " + typeName + " for " + key + " in section " + section + " for mod " + (DisplayName ?? Name) + ", expecting " + type.AssemblyQualifiedName)); } 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(type), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { WriteCollectionToPackage(package, collection); return; } if (type != null && type.IsValueType && !type.IsPrimitive) { WriteStructFieldsToPackage(package, value, type); return; } } ZRpc.Serialize(new object[1] { value }, ref package); } private static void WriteCollectionToPackage(ZPackage package, ICollection collection) { package.Write(collection.Count); foreach (object item in collection) { AddValueToZPackage(package, item); } } private static void WriteStructFieldsToPackage(ZPackage package, object value, Type type) { 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)); } } private static object ReadValueWithTypeFromZPackage(ZPackage package, Type type) { if (type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { return ReadStructFromPackage(package, type); } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { return ReadDictionaryFromPackage(package, type); } if (type.IsGenericType) { Type type2 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if (type2.IsAssignableFrom(type)) { return ReadCollectionFromPackage(package, type, type2); } } return ReadScalarFromPackage(package, type); } private static object ReadStructFromPackage(ZPackage package, Type type) { 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; } private static object ReadDictionaryFromPackage(ZPackage package, Type dictType) { int num = package.ReadInt(); Type type = typeof(KeyValuePair<, >).MakeGenericType(dictType.GenericTypeArguments); IDictionary dictionary = (IDictionary)Activator.CreateInstance(dictType); FieldInfo field = type.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int i = 0; i < num; i++) { object obj = ReadValueWithTypeFromZPackage(package, type); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } private static object ReadCollectionFromPackage(ZPackage package, Type type, Type collectionType) { int num = package.ReadInt(); object obj = Activator.CreateInstance(type); MethodInfo method = collectionType.GetMethod("Add"); for (int i = 0; i < num; i++) { method.Invoke(obj, new object[1] { ReadValueWithTypeFromZPackage(package, type.GenericTypeArguments[0]) }); } return obj; } private static object ReadScalarFromPackage(ZPackage package, Type type) { ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.Field(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(); } internal ZPackage ConfigsToPackage(IEnumerable<ConfigEntryBase> configs = null, IEnumerable<CustomSyncedValueBase> customValues = null, IEnumerable<PackageEntry> packageEntries = null, bool partial = true) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown List<ConfigEntryBase> list = configs?.Where((ConfigEntryBase c) => configData(c).SynchronizedConfig).ToList() ?? new List<ConfigEntryBase>(); List<CustomSyncedValueBase> list2 = customValues?.ToList() ?? new List<CustomSyncedValueBase>(); List<PackageEntry> list3 = packageEntries?.ToList() ?? new List<PackageEntry>(); ZPackage val = new ZPackage(); val.Write(partial ? ((byte)1) : ((byte)0)); val.Write(list.Count + list2.Count + list3.Count); foreach (PackageEntry item in list3) { val.Write(item.section); val.Write(item.key); val.Write((item.value == null) ? "" : GetZPackageTypeString(item.type)); AddValueToZPackage(val, item.value); } foreach (CustomSyncedValueBase item2 in list2) { val.Write("Internal"); val.Write(item2.Identifier); val.Write((item2.BoxedValue == null) ? "" : GetZPackageTypeString(CustomValueWireType(item2.Type))); AddValueToZPackage(val, item2.BoxedValue); } foreach (ConfigEntryBase item3 in list) { val.Write(item3.Definition.Section); val.Write(item3.Definition.Key); val.Write(GetZPackageTypeString(configType(item3))); AddValueToZPackage(val, item3.BoxedValue); } return val; } private static Type configType(ConfigEntryBase config) { return configType(config.SettingType); } private static Type configType(Type type) { if (!type.IsEnum) { return type; } return Enum.GetUnderlyingType(type); } private static Type CustomValueWireType(Type type) { return configType(Nullable.GetUnderlyingType(type) ?? type); } private static object FromCustomValueWireValue(Type type, object wireValue) { Type type2 = Nullable.GetUnderlyingType(type) ?? type; if (wireValue == null || !type2.IsEnum) { return wireValue; } return Enum.ToObject(type2, wireValue); } public IEnumerator SendZPackage(long target, ZPackage package) { if (Object.op_Implicit((Object)(object)ZNet.instance)) { List<ZNetPeer> peers = ((List<ZNetPeer>)AccessTools.Field(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance)).Where((ZNetPeer p) => target == 0L || p.m_uid == target).ToList(); IEnumerator enumerator = SendZPackage(peers, package); while (enumerator.MoveNext()) { yield return enumerator.Current; } } } public IEnumerator SendZPackage(List<ZNetPeer> peers, ZPackage package) { return SendZPackage(peers, package, waitForSendQueue: true); } private IEnumerator SendZPackage(List<ZNetPeer> peers, ZPackage package, bool waitForSendQueue) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { yield break; } byte[] array = package.GetArray(); if (array.Length > 10000) { package = CompressPackage(array); } List<IEnumerator> writers = (from p in peers where p.IsReady() select distributeConfigToPeers(p, package, waitForSendQueue)).ToList(); writers.RemoveAll((IEnumerator w) => !w.MoveNext()); while (writers.Any()) { yield return null; writers.RemoveAll((IEnumerator w) => !w.MoveNext()); } } private static ZPackage CompressPackage(byte[] data) { //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((byte)4); using MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Optimal)) { deflateStream.Write(data, 0, data.Length); } val.Write(memoryStream.ToArray()); return val; } private IEnumerator distributeConfigToPeers(ZNetPeer peer, ZPackage package, bool waitForSendQueue) { byte[] data = package.GetArray(); if (data.Length > 250000) { int fragments = (data.Length + 249999) / 250000; long packageId = ++packageCounter; for (int i = 0; i < fragments; i++) { if (waitForSendQueue) { foreach (bool item in waitForQueue()) { yield return item; } } if (peer.m_socket.IsConnected()) { ZPackage val = new ZPackage(); val.Write((byte)2); val.Write(packageId); val.Write(i); val.Write(fragments); val.Write(data.Skip(250000 * i).Take(250000).ToArray()); SendPackage(val); if (i < fragments - 1) { yield return true; } continue; } break; } yield break; } if (waitForSendQueue) { foreach (bool item2 in waitForQueue()) { yield return item2; } } SendPackage(package); void SendPackage(ZPackage pkg) { if (isServer) { peer.m_rpc.Invoke(Name + " ConfigSync", new object[1] { pkg }); } else { ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_server ? 0 : peer.m_uid, Name + " ConfigSync", new object[1] { pkg }); } } IEnumerable<bool> waitForQueue() { float timeout = Time.time + 30f; while (peer.m_socket.GetSendQueueSize() > 20000) { if (Time.time > timeout) { Debug.Log((object)$"Disconnecting {peer.m_uid} after 30 seconds config sending timeout"); peer.m_rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)5 }); ZNet.instance.Disconnect(peer); break; } yield return false; } } } public static OwnConfigEntryBase configData(ConfigEntryBase config) { return config.Description.Tags?.OfType<OwnConfigEntryBase>().SingleOrDefault(); } private static T configAttribute<T>(ConfigEntryBase config) where T : class { object[] tags = config.Description.Tags; if (tags == null) { return null; } return tags.OfType<T>().FirstOrDefault(); } } internal abstract class OwnConfigEntryBase { public readonly ConfigEntryBase BaseConfig; public object LocalBaseValue; public bool SynchronizedConfig = true; protected OwnConfigEntryBase(ConfigEntryBase config) { BaseConfig = config; } } internal class SyncedConfigEntry<T> : OwnConfigEntryBase { public T Value { get { if (!(BaseConfig is ConfigEntry<T> val)) { throw new InvalidOperationException("Cannot cast BaseConfig to ConfigEntry<" + typeof(T).Name + ">"); } return val.Value; } set { ((ConfigEntry<T>)(object)BaseConfig).Value = value; } } public T DefaultValue => (T)BaseConfig.BoxedValue; public SyncedConfigEntry(ConfigEntry<T> config) : base((ConfigEntryBase)(object)config) { } } internal abstract class CustomSyncedValueBase { private readonly ConfigSync configSync; private object boxedValue; private object localBaseValue; public string Identifier { get; } public Type Type { get; } public object BoxedValue { get { return boxedValue; } set { if (!object.Equals(boxedValue, value)) { boxedValue = value; this.ValueChanged?.Invoke(); } } } public object LocalBaseValue { get { return localBaseValue; } set { localBaseValue = value; HasLocalBaseValue = true; } } public int Priority { get; } internal bool HasLocalBaseValue { get; private set; } internal bool LocalIsOwner => configSync.IsSourceOfTruth; public event Action ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { this.configSync = configSync; Identifier = identifier; Type = type; Priority = priority; configSync.AddCustomValue(this); } internal void ClearLocalBaseValue() { localBaseValue = null; HasLocalBaseValue = false; } } 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 (base.LocalIsOwner) { Value = value; } else { base.LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { public bool? ReadOnly; public Action<ConfigEntryBase> CustomDrawer; } internal class ParsedConfigs { public readonly Dictionary<OwnConfigEntryBase, object> configValues = new Dictionary<OwnConfigEntryBase, object>(); public readonly Dictionary<CustomSyncedValueBase, object> customValues = new Dictionary<CustomSyncedValueBase, object>(); } internal class PackageEntry { public string section; public string key; public Type type; public object value; } internal class InvalidDeserializationTypeException : Exception { public string expected; public string received; public string field = ""; } [HarmonyPatch(typeof(ZRpc), "HandlePackage")] internal class SnatchCurrentlyHandlingRPC { public static ZRpc currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { ConfigSync.isServer = __instance.IsServer(); foreach (ConfigSync configSync in ConfigSync.configSyncs) { ZRoutedRpc.instance.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<long, ZPackage>)configSync.RPC_FromOtherClientConfigSync); if (ConfigSync.isServer) { Debug.Log((object)("Registered '" + configSync.Name + " ConfigSync' RPC - waiting for incoming connections")); } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in ConfigSync.configSyncs) { peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_FromServerConfigSync); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal class SendServerConfigsToJoiningPeer { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(ZNet __instance, ZRpc rpc) { if (!__instance.IsServer()) { return; } ZNetPeer val = __instance.GetPeers().Find((ZNetPeer candidate) => candidate.m_rpc == rpc); if (val == null || !val.IsReady()) { return; } AdminSyncing.PushAdminStatus((IEnumerable<ZNetPeer>)(object)new ZNetPeer[1] { val }); foreach (ConfigSync configSync in ConfigSync.configSyncs) { configSync.SendAllValuesToJoiningPeer(val); } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] internal class RestoreLocalConfigsOnDisconnect { [HarmonyPostfix] private static void Postfix() { if (ConfigSync.isServer) { return; } ConfigSync.ProcessingServerUpdate = true; try { foreach (ConfigSync configSync in ConfigSync.configSyncs) { configSync.RestoreLocalConfigs(); } } finally { ConfigSync.ProcessingServerUpdate = false; } } } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] internal class PreventSavingServerInfo { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = ConfigSync.configData(__instance); if (ownConfigEntryBase == null || ConfigSync.isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] internal class PreventConfigRereadChangingValues { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = ConfigSync.configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)($"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. " + "Reason: " + ex.Message + "; Value: " + value)); } return false; } } } namespace SmartWishbone { internal static class DataFileWriter { private const int drainTimeoutMs = 5000; private const string threadName = "SmartWishbone.DataFile"; private static readonly BlockingCollection<Action> jobs = new BlockingCollection<Action>(); private static readonly object startLock = new object(); private static Thread thread; private static string lastFailure; internal static void Enqueue(string path, string text) { ReportFailure(); Action action = delegate { File.WriteAllText(path, text); }; EnsureThread(); try { jobs.Add(action); } catch (InvalidOperationException) { Run(action); } } internal static void Drain() { if (thread == null) { return; } ManualResetEventSlim done = new ManualResetEventSlim(initialState: false); try { jobs.Add(delegate { done.Set(); }); } catch (InvalidOperationException) { return; } if (!done.Wait(5000)) { Helper.LogWarning($"Data file still saving after {5} s at shutdown."); } ReportFailure(); } private static void ReportFailure() { string text = Interlocked.Exchange(ref lastFailure, null); if (text != null) { Helper.LogWarning(text); } } private static void EnsureThread() { lock (startLock) { if (thread == null) { thread = new Thread(Loop) { IsBackground = true, Name = "SmartWishbone.DataFile" }; thread.Start(); } } } private static void Loop() { foreach (Action item in jobs.GetConsumingEnumerable()) { Run(item); } } private static void Run(Action job) { try { job(); } catch (Exception ex) { Interlocked.Exchange(ref lastFailure, "Failed saving the data file in the background: " + ex.Message); } } } internal class Target { public string targetName; public SharedData optionalItem; public string displayName; public Sprite displaySprite; public Dictionary<string, Trackable> possibleTargets; public Target(string targetName, Dictionary<string, Trackable> possibleTargets) { this.targetName = targetName; this.possibleTargets = possibleTargets; } } internal static class TrackableData { internal static Target[] targets; private static Dictionary<string, Trackable> personalData = new Dictionary<string, Trackable>(); private static Dictionary<string, Trackable> syncedData = new Dictionary<string, Trackable>(); internal static int receivedFromServerCount; internal static Dictionary<string, Trackable> Data { get { if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsServer()) { return syncedData; } return personalData; } } internal static void ToggleTarget(string prefabName) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return; } if (!ZNet.instance.IsServer()) { RPCHandler.SendTrackableToggleRPC(prefabName); return; } if (personalData.ContainsKey(prefabName)) { Helper.Log("Removed trackable " + prefabName); personalData.Remove(prefabName); } else { Helper.Log("Added trackable " + prefabName); personalData.Add(prefabName, new Trackable(prefabName, "Auto")); if (!ZNet.instance.IsDedicated()) { TryAddBeacons(new List<string> { prefabName }); } } string text = TrackableDataLoader.ParseCustomDictToString(personalData); TrackableDataLoader.SaveTrackableData(text); WishboneConfig.CurrentDataCache.Value = text; UpdateTargets(); } internal static void SetPersonalData(Dictionary<string, Trackable> newData) { personalData = newData; } internal static void ClearSyncedData() { syncedData.Clear(); receivedFromServerCount = 0; } internal static void ReceiveNewDataFromServer(Dictionary<string, Trackable> newData) { receivedFromServerCount++; List<string> list = newData.Keys.Except(syncedData.Keys).ToList(); Helper.Log($"Previous data count: {syncedData.Count}, new data count: {newData.Count}, difference count {list.Count}"); TryAddBeacons(list); syncedData = newData; UpdateTargets(); } internal static void TryAddBeacons(List<string> newNames) { if (newNames == null || newNames.Count == 0) { return; } Destructible[] array = Object.FindObjectsByType<Destructible>((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (Destructible val in array) { if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)((Component)val).gameObject) && newNames.Contains(Utils.GetPrefabName(((Component)val).gameObject))) { BeaconAddPatches.TryAddBeacon(val); } } } internal static void UpdateTargets() { Dictionary<string, Dictionary<string, Trackable>> dictionary = new Dictionary<string, Dictionary<string, Trackable>>(); foreach (KeyValuePair<string, Trackable> datum in Data) { string[] array = datum.Value.displayItem.Split(new char[1] { '|' }); foreach (string key in array) { if (dictionary.TryGetValue(key, out var value)) { value[datum.Key] = datum.Value; continue; } dictionary[key] = new Dictionary<string, Trackable> { [datum.Key] = datum.Value }; } } List<Target> list = new List<Target>(); foreach (KeyValuePair<string, Dictionary<string, Trackable>> item in dictionary) { Target target = new Target(item.Key, item.Value); SharedData possibleItem = GetPossibleItem(target.targetName); if (possibleItem != null) { target.optionalItem = possibleItem; target.displaySprite = possibleItem.m_icons[0]; target.displayName = Localization.instance.Localize(possibleItem.m_name); } else if (target.targetName.IndexOf('$') == 0) { target.displayName = Localization.instance.Localize(target.targetName); } else { target.displayName = target.targetName; } list.Add(target); } targets = (from a in list orderby a.optionalItem == null, a.displayName select a).ToArray(); } private static SharedData GetPossibleItem(string itemName) { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(StringExtensionMethods.GetStableHashCode(itemName)); ItemDrop val = default(ItemDrop); if ((Object)(object)itemPrefab != (Object)null && itemPrefab.TryGetComponent<ItemDrop>(ref val)) { return val.m_itemData.m_shared; } return null; } } internal static class BeaconHelper { public static Beacon FindClosestBeaconInRange(Vector3 point, Target target) { //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) Beacon val = null; float num = 999999f; Dictionary<string, Trackable> targetList = ((target != null) ? target.possibleTargets : TrackableData.Data); foreach (Beacon instance in Beacon.m_instances) { if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)((Component)instance).gameObject)) { continue; } double num2 = 20.0; if (TryGetTrackable(targetList, instance, out var trackable) && trackable.FulFillsCondition()) { num2 = trackable.range; if (WishboneConfig.SearchDistanceOverride.Value != 0f) { num2 = ((WishboneConfig.SearchDistanceOverrideStyle.Value != WishboneConfig.RangeStyle.SetTo) ? (num2 + (double)WishboneConfig.SearchDistanceOverride.Value) : ((double)WishboneConfig.SearchDistanceOverride.Value)); } float num3 = Vector3.Distance(point, ((Component)instance).transform.position); if ((double)num3 < num2 && ((Object)(object)val == (Object)null || num3 < num)) { val = instance; num = num3; } } } return val; } internal static string GetOwnerPrefabName(Component component) { ZNetView componentInParent = component.GetComponentInParent<ZNetView>(); return Utils.GetPrefabName(Object.op_Implicit((Object)(object)componentInParent) ? ((Component)componentInParent).gameObject : ((Component)component.transform.root).gameObject); } internal static bool TryGetTrackable(Dictionary<string, Trackable> targetList, Beacon beacon, out Trackable trackable) { if (!targetList.TryGetValue(GetOwnerPrefabName((Component)(object)beacon), out trackable)) { return targetList.TryGetValue(Utils.GetPrefabName(((Component)beacon).gameObject), out trackable); } return true; } } internal class Interactions { internal static void TrackObject(Player player) { GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if (!Object.op_Implicit((Object)(object)hoverObject)) { return; } GameObject gameObject = ((Component)hoverObject.transform.root).gameObject; if (!Object.op_Implicit((Object)(object)gameObject)) { return; } Destructible component = gameObject.GetComponent<Destructible>(); if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)((Component)component).gameObject)) { Helper.LogWarning("Invalid hover object: not destructible"); return; } string text = BeaconHelper.GetOwnerPrefabName((Component)(object)component); Beacon componentInChildren = gameObject.GetComponentInChildren<Beacon>(); if (Object.op_Implicit((Object)(object)componentInChildren) && !TrackableData.Data.ContainsKey(text)) { string prefabName = Utils.GetPrefabName(((Component)componentInChildren).gameObject); if (TrackableData.Data.ContainsKey(prefabName)) { text = prefabName; } } Helper.Log("Valid hover object: " + text); TrackableData.ToggleTarget(text); } } [HarmonyPatch] internal static class KeybindChecker { [HarmonyPatch(typeof(Player), "Update")] public static class Player_Update_Patch { internal static void Prefix(Player __instance) { //IL_001b: 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_004b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Player.m_localPlayer != (Object)(object)__instance) && !IgnoreKeyPresses()) { if (WishboneConfig.NextTargetKey.Value.IsKeyDown()) { TrackableSwitcher.SwitchTarget(forward: true); } else if (WishboneConfig.PreviousTargetKey.Value.IsKeyDown()) { TrackableSwitcher.SwitchTarget(forward: false); } else if (WishboneConfig.ToggleTrackingObjectKey.Value.IsKeyDown()) { Interactions.TrackObject(__instance); } } } } public static bool IgnoreKeyPresses() { if (!IgnoreKeyPressesDueToPlayer(Player.m_localPlayer) && Object.op_Implicit((Object)(object)ZNetScene.instance) && !Minimap.IsOpen() && !Menu.IsVisible() && !Console.IsVisible() && !StoreGui.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.InPasswordDialog())) { if (Object.op_Implicit((Object)(object)TextViewer.instance)) { return TextViewer.instance.IsVisible(); } return false; } return true; } private static bool IgnoreKeyPressesDueToPlayer(Player player) { if (Object.op_Implicit((Object)(object)player) && !((Character)player).InCutscene() && !((Character)player).IsTeleporting() && !((Character)player).IsDead()) { return ((Character)player).InPlaceMode(); } return true; } public static bool IsKeyDown(this KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } public static bool IsKeyHeld(this KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey); } return false; } } internal static class LocalizationLoader { private const string keyPrefix = "smartwishbone_"; public static string[] supportedEmbeddedLanguages = new string[1] { "English" }; private const string embeddedLanguagePathFormat = "SmartWishbone.Translations.SmartWishbone.{0}.json"; private const string loadingLog = "Loading {0} translation file for language: {1}"; private const string failedLoadLog = "Failed loading {0} translation file for language: {1}"; private const string external = "external"; private const string embedded = "embedded"; internal static string GetWishboneTranslation(this Localization localization, string key) { return localization.Localize("$smartwishbone_" + key.ToLower()); } internal static void SetupTranslations() { string selectedLanguage = Localization.instance.GetSelectedLanguage(); string[] files = Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), "SmartWishbone.*.json", SearchOption.AllDirectories); bool flag = false; string[] array = files; foreach (string path in array) { if (Path.GetFileNameWithoutExtension(path).Split(new char[1] { '.' })[1] == selectedLanguage) { Helper.Log(string.Format("Loading {0} translation file for language: {1}", "external", selectedLanguage)); if (!LoadExternalLanguageFile(selectedLanguage, path)) { Helper.LogWarning(string.Format("Failed loading {0} translation file for language: {1}", "external", selectedLanguage)); } else { flag = true; } break; } } if (!flag && selectedLanguage != "English" && supportedEmbeddedLanguages.Contains(selectedLanguage)) { Helper.Log(string.Format("Loading {0} translation file for language: {1}", "embedded", selectedLanguage)); if (!LoadEmbeddedLanguageFile(selectedLanguage)) { Helper.LogWarning(string.Format("Failed loading {0} translation file for language: {1}", "embedded", selectedLanguage)); } } Helper.Log(string.Format("Loading {0} translation file for language: {1}", "embedded", "English")); if (!LoadEmbeddedLanguageFile("English")) { Helper.LogWarning(string.Format("Failed loading {0} translation file for language: {1}", "embedded", "English")); } } internal static bool LoadExternalLanguageFile(string language, string path) { string text = File.ReadAllText(path); if (text == null) { return false; } return ParseStringToLanguage(language, text); } internal static bool LoadEmbeddedLanguageFile(string language) { string text = ReadEmbeddedTextFile($"SmartWishbone.Translations.SmartWishbone.{language}.json"); if (text == null) { return false; } return ParseStringToLanguage(language, text); } internal static bool ParseStringToLanguage(string language, string translationAsString) { Dictionary<string, string> dictionary = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(translationAsString); if (dictionary == null || dictionary.Count == 0) { return false; } foreach (KeyValuePair<string, string> item in dictionary) { AddForLanguage(language, item.Key, item.Value); } return true; } internal static void AddForLanguage(string language, string key, string value) { string text = "smartwishbone_" + key.ToLower(); bool num = Localization.instance.GetSelectedLanguage() == language; bool flag = language == "English" && !Localization.instance.m_translations.ContainsKey(text); if (num || flag) { Localization.instance.AddWord(text, value); } } public static string ReadEmbeddedTextFile(string path) { Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(path); if (manifestResourceStream == null) { return null; } using MemoryStream memoryStream = new MemoryStream(); manifestResourceStream.CopyTo(memoryStream); byte[] array = ((memoryStream.Length > 0) ? memoryStream.ToArray() : null); return (array != null) ? Encoding.UTF8.GetString(array) : null; } } public static class Helper { internal static void Log(object s) { if (WishboneConfig.EnableDebugLogs.Value) { Debug.Log((object)("Smart Wishbone 1.0.6: " + ((s != null) ? s.ToString() : "null"))); } } internal static void LogWarning(object s) { Debug.LogWarning((object)("Smart Wishbone 1.0.6: " + ((s != null) ? s.ToString() : "null"))); } internal static void LogError(Exception e) { Debug.LogError((object)("Smart Wishbone 1.0.6: " + ((e != null) ? (e.Message + "\n" + e.StackTrace) : "null"))); } } internal class Trackable { public string prefabName; public string displayItem; public string condition; public double range = 20.0; public Trackable() { } public Trackable(string prefabName, string displayItem) : this() { this.prefabName = prefabName; this.displayItem = displayItem; } public Trackable(string prefabName, string displayItem, string condition, double range) : this(prefabName, displayItem) { this.condition = condition; this.range = range; } } internal static class TrackableExtension { internal static bool FulFillsCondition(this Trackable trackable) { if (!WishboneConfig.EnforceWorldConditions.Value) { return true; } if (!Utility.IsNullOrWhiteSpace(trackable.condition)) { return ZoneSystem.instance.GetGlobalKey(trackable.condition); } return true; } } internal class TrackableDataLoader { private const string embeddedPathFormat = "SmartWishbone.Data.{0}.yaml"; private const string dataFileInfix = "SmartWishbone.Data"; private const string loadingLog = "Loading {0} data file '{1}'"; private const string failedLoadLog = "Failed loading {0} data file '{1}'"; private const string savingLog = "Saving {0} data file '{1}'"; private const string failedSaveLog = "Failed saving {0} data file '{1}'"; private const string external = "external"; private const string embedded = "embedded"; private static string externalDataFilePath; internal static Dictionary<string, Trackable> LoadTrackableData() { Dictionary<string, Trackable> dictionary = ValidateData(LoadDataFiles()); Helper.Log($"trackable count after validation: {dictionary.Count}"); return dictionary; } internal static void SaveTrackableData(string trackablesAsString) { if (externalDataFilePath == null || !File.Exists(externalDataFilePath)) { externalDataFilePath = FindExternalDataFile(); } if (externalDataFilePath == null) { Helper.LogWarning("No data file to save to found, aborting saving."); return; } if (trackablesAsString == null) { Helper.LogWarning(string.Format("Failed saving {0} data file '{1}'", "external", externalDataFilePath)); return; } Helper.Log(string.Format("Saving {0} data file '{1}'", "external", externalDataFilePath)); DataFileWriter.Enqueue(externalDataFilePath, trackablesAsString); } private static string FindExternalDataFile() { List<string> list = Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), "SmartWishbone.Data.yaml", SearchOption.AllDirectories).ToList(); if (list.Count == 0) { return null; } list.Sort(); return list[0]; } internal static List<Trackable> LoadDataFiles() { List<string> list = Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), "SmartWishbone.Data.yaml", SearchOption.AllDirectories).ToList(); List<Trackable> list2 = new List<Trackable>(); if (list.Count > 0) { list.Sort(); string text = (externalDataFilePath = list[0]); if (list.Count > 1) { Helper.LogWarning($"Found {list.Count} data files instead of only 1. Please remove the duplicate entries. Only reading entry: '{text}'"); } Helper.Log(string.Format("Loading {0} data file '{1}'", "external", text)); List<Trackable> list3 = LoadExternalDataFile(text); if (list3 != null && list3.Count > 0) { list2 = list3; } else { Helper.LogWarning(string.Format("Failed loading {0} data file '{1}'", "external", text)); } } if (list2 == null || list2.Count == 0) { Helper.Log(string.Format("Loading {0} data file '{1}'", "embedded", "SmartWishbone.Data")); List<Trackable> list4 = LoadEmbeddedDataFile("SmartWishbone.Data"); if (list4 != null && list4.Count > 0) { list2 = list4; } else { Helper.LogWarning(string.Format("Failed loading {0} data file '{1}'", "embedded", "SmartWishbone.Data")); } } if (list2 == null || list2.Count == 0) { return new List<Trackable>(); } return list2; } internal static Dictionary<string, Trackable> ValidateData(List<Trackable> trackables) { Dictionary<string, Trackable> dictionary = new Dictionary<string, Trackable>(); if (trackables == null) { return dictionary; } foreach (Trackable trackable in trackables) { if (dictionary.ContainsKey(trackable.prefabName)) { Helper.LogWarning("There were multiple definitions for the same prefab name: " + trackable.prefabName + "; only picking the first one."); } else { dictionary[trackable.prefabName] = trackable; } } foreach (KeyValuePair<string, Trackable> item in dictionary.ToList()) { if (item.Key == null || item.Key != item.Value.prefabName) { Helper.LogWarning("invalid trackable"); dictionary.Remove(item.Key); continue; } Trackable value = item.Value; if (Utility.IsNullOrWhiteSpace(value.prefabName)) { Helper.LogWarning("PrefabName is not a valid"); dictionary.Remove(item.Key); continue; } if (Utility.IsNullOrWhiteSpace(value.displayItem)) { Helper.LogWarning("DisplayItem is not a valid"); dictionary.Remove(item.Key); continue; } if (Utility.IsNullOrWhiteSpace(value.condition)) { value.condition = null; } if (value.range <= 0.0) { Helper.LogWarning("Trackable range is not positive, resetting to default of 20"); value.range = 20.0; } } return dictionary; } internal static List<Trackable> LoadExternalDataFile(string path) { string text = File.ReadAllText(path); if (text == null) { return null; } return ParseStringToCustomList(text); } internal static List<Trackable> LoadEmbeddedDataFile(string infix) { string text = ReadEmbeddedTextFile($"SmartWishbone.Data.{infix}.yaml"); if (text == null || text.IndexOf('-') == -1) { return null; } text = text.Substring(text.IndexOf('-')); return ParseStringToCustomList(text); } internal static List<Trackable> ParseStringToCustomList(string translationAsString) { List<Trackable> list = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build().Deserialize<List<Trackable>>(translationAsString); if (list == null || list.Count == 0) { return null; } return list; } internal static string ParseCustomDictToString(Dictionary<string, Trackable> data) { string text = new SerializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build().Serialize(data.Values.ToList()); if (text == null) { return null; } return text; } public static string ReadEmbeddedTextFile(string path) { Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(path); if (manifestResourceStream == null) { return null; } using MemoryStream memoryStream = new MemoryStream(); manifestResourceStream.CopyTo(memoryStream); byte[] array = ((memoryStream.Length > 0) ? memoryStream.ToArray() : null); return (array != null) ? Encoding.UTF8.GetString(array) : null; } } [HarmonyPatch(typeof(Player))] internal class PlayerPatch { [HarmonyPatch("SetLocalPlayer")] [HarmonyPostfix] public static void SetLocalPlayer_Postfix(Player __instance) { if (!((Object)(object)Player.m_localPlayer != (Object)(object)__instance)) { TrackableSwitcher.UpdateTarget(); } } } internal class TrackableSwitcher { internal static Target currentTarget; private const string playerPrefsCurrentTargetKey = "goldenrevolver.smartWishbone.currentTarget"; internal static string GetCurrentTarget() { return PlayerPrefs.GetString("goldenrevolver.smartWishbone.currentTarget", (string)null); } internal static void RemoveCurrentTarget() { currentTarget = null; PlayerPrefs.DeleteKey("goldenrevolver.smartWishbone.currentTarget"); } internal static void SetCurrentTarget(Target target) { currentTarget = target; PlayerPrefs.SetString("goldenrevolver.smartWishbone.currentTarget", target.targetName); } internal static void UpdateTarget() { InternalSwitchTarget(0); } internal static void RestoreTarget(string targetName) { if (Utility.IsNullOrWhiteSpace(targetName)) { RemoveCurrentTarget(); } else { PlayerPrefs.SetString("goldenrevolver.smartWishbone.currentTarget", targetName); } UpdateTarget(); } internal static void SwitchTarget(bool forward) { InternalSwitchTarget(forward ? 1 : (-1)); } private static void InternalSwitchTarget(int positionChange) { TrySwitchToNextTarget(positionChange); if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || ((Character)Player.m_localPlayer).m_seman == null) { return; } for (int num = ((Character)Player.m_localPlayer).m_seman.m_statusEffects.Count - 1; num >= 0; num--) { StatusEffect val = ((Character)Player.m_localPlayer).m_seman.m_statusEffects[num]; if (val is SE_CustomFinder) { ((Character)Player.m_localPlayer).m_seman.RemoveStatusEffect(val, true); ((Character)Player.m_localPlayer).m_seman.AddStatusEffect(val, false, 0, 0f, (short)(-1)); } } } internal static void TrySwitchToNextTarget(int positionChange) { if (!InternalTrySwitchToNextTarget(positionChange)) { RemoveCurrentTarget(); } } internal static bool InternalTrySwitchToNextTarget(int positionChange) { string text = GetCurrentTarget(); if (Utility.IsNullOrWhiteSpace(text)) { if (positionChange == 0) { return false; } if (positionChange > 0) { return SearchForNextUsefulIndex(-1, forward: true); } return SearchForNextUsefulIndex(TrackableData.targets.Length, forward: false); } for (int i = 0; i < TrackableData.targets.Length; i++) { Target target = TrackableData.targets[i]; if (text != target.targetName) { continue; } if (positionChange == 0) { if (target.possibleTargets.Values.Any(TrackableExtension.FulFillsCondition)) { SetCurrentTarget(target); return true; } return false; } return SearchForNextUsefulIndex(i, positionChange > 0); } return false; } internal static bool SearchForNextUsefulIndex(int index, bool forward) { int num = index; bool flag = forward && num == TrackableData.targets.Length - 1; bool flag2 = !forward && num == 0; while (!flag && !flag2) { num = ((!forward) ? (num - 1) : (num + 1)); Target target = TrackableData.targets[num]; if (target.possibleTargets.Values.Any(TrackableExtension.FulFillsCondition)) { SetCurrentTarget(target); return true; } flag = forward && num == TrackableData.targets.Length - 1; flag2 = !forward && num == 0; } return false; } } public class WishboneConfig { public enum RangeStyle { SetTo, AddTo } public enum UserLevel { Noone, OnlyAdmins, Everyone } public enum Target { Always, OnlyInUniversalMode, Never } internal static ConfigEntry<KeyboardShortcut> ToggleTrackingObjectKey; internal static ConfigEntry<KeyboardShortcut> NextTargetKey; internal static ConfigEntry<KeyboardShortcut> PreviousTargetKey; internal static ConfigEntry<bool> EnableDebugLogs; internal static ConfigEntry<ServerSyncWrapper.Toggle> UseServerSync; internal static ConfigEntry<float> SearchDistanceOverride; internal static ConfigEntry<RangeStyle> SearchDistanceOverrideStyle; internal static ConfigEntry<bool> EnforceWorldConditions; internal static ConfigEntry<UserLevel> UsersAllowedToAddTrackables; internal static ConfigEntry<string> CurrentDataCache; internal static ConfigFile config; private static BaseUnityPlugin plugin; internal static ConfigSync serverSyncInstance; public static void LoadConfig(BaseUnityPlugin plugin) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) config = plugin.Config; WishboneConfig.plugin = plugin; serverSyncInstance = ServerSyncWrapper.CreateRequiredConfigSync("goldenrevolver.SmartWishbone", "Smart Wishbone", "1.0.6"); string text = "0 - Client Settings"; NextTargetKey = config.Bind<KeyboardShortcut>(text, "NextTargetKey", new KeyboardShortcut((KeyCode)110, Array.Empty<KeyCode>()), (ConfigDescription)null); PreviousTargetKey = config.Bind<KeyboardShortcut>(text, "PreviousTargetKey", new KeyboardShortcut((KeyCode)98, Array.Empty<KeyCode>()), (ConfigDescription)null); ToggleTrackingObjectKey = config.Bind<KeyboardShortcut>(text, "ToggleTrackingObjectKey", new KeyboardShortcut((KeyCode)106, Array.Empty<KeyCode>()), (ConfigDescription)null); text = "1 - Host/ Server Settings"; UseServerSync = config.BindForceEnabledSyncLocker(serverSyncInstance, text, "UseServerSync"); SearchDistanceOverride = config.BindSynced(serverSyncInstance, text, "SearchDistanceOverride", 0f, "Only does something while it's not equal to zero, affected by SearchDistanceOverrideStyle."); SearchDistanceOverrideStyle = config.BindSynced(serverSyncInstance, text, "SearchDistanceOverrideStyle", RangeStyle.AddTo); EnforceWorldConditions = config.BindSynced(serverSyncInstance, text, "EnforceWorldConditions", value: true); EnforceWorldConditions.SettingChanged += EnforceWorldConditions_SettingChanged; UsersAllowedToAddTrackables = config.BindSynced(serverSyncInstance, text, "UsersAllowedToAddTrackables", UserLevel.OnlyAdmins); text = "9 - Other Settings"; CurrentDataCache = config.BindSynced(serverSyncInstance, text, "CurrentDataCache", string.Empty, ServerSyncWrapper.HiddenDisplay("You can ignore this. This is just for runtime synchronization.")); CurrentDataCache.SettingChanged += CurrentDataCache_SettingChanged; EnableDebugLogs = config.Bind<bool>(text, "EnableDebugLogs", true, (ConfigDescription)null); } private static void EnforceWorldConditions_SettingChanged(object sender, EventArgs e) { ((MonoBehaviour)plugin).StartCoroutine(WaitForUpdate()); } internal static void CurrentDataCache_SettingChanged(object sender, EventArgs e) { Helper.Log("Received settíng changed event"); if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsServer()) { Helper.Log("Received new trackable data from server"); TrackableData.ReceiveNewDataFromServer(TrackableDataLoader.ValidateData(TrackableDataLoader.ParseStringToCustomList(CurrentDataCache.Value))); } } internal static void SetConfigDataWithoutEvent(string newValue) { bool processingServerUpdate = ConfigSync.ProcessingServerUpdate; ConfigSync.ProcessingServerUpdate = true; CurrentDataCache.SettingChanged -= CurrentDataCache_SettingChanged; try { CurrentDataCache.Value = newValue; } finally { CurrentDataCache.SettingChanged += CurrentDataCache_SettingChanged; ConfigSync.ProcessingServerUpdate = processingServerUpdate; } } private static IEnumerator WaitForUpdate() { yield return (object)new WaitForSeconds(0.1f); TrackableSwitcher.UpdateTarget(); } } [HarmonyPatch] internal class BeaconAddPatches { [HarmonyPostfix] [HarmonyPatch(typeof(Destructible), "Awake")] public static void Awake_Postfix(Destructible __instance) { if (Enumerable.Contains(TrackableData.Data.Keys, Utils.GetPrefabName(((Component)__instance).gameObject))) { TryAddBeacon(__instance); } } public static void TryAddBeacon(Destructible destructible) { Beacon componentInChildren = ((Component)destructible).gameObject.GetComponentInChildren<Beacon>(); Beacon componentInParent = ((Component)destructible).gameObject.GetComponentInParent<Beacon>(); if (!Object.op_Implicit((Object)(object)componentInChildren) && !Object.op_Implicit((Object)(object)componentInParent)) { ((Component)destructible).gameObject.AddComponent<Beacon>(); } } } [HarmonyPatch(typeof(ObjectDB))] internal class ObjectDBPatch { internal const string wishboneEffectName = "Wishbone"; [HarmonyPostfix] [HarmonyPatch("Awake")] public static void Awake_Postfix(ObjectDB __instance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name != "main") { return; } SE_Finder val = null; SE_Finder val2 = null; for (int i = 0; i < __instance.m_StatusEffects.Count; i++) { StatusEffect val3 = __instance.m_StatusEffects[i]; SE_Finder val4 = (SE_Finder)(object)((val3 is SE_Finder) ? val3 : null); if (val4 == null || ((Object)val3).name != "Wishbone") { continue; } val = val4; val2 = (SE_Finder)(object)ScriptableObject.CreateInstance<SE_CustomFinder>(); SE_CustomFinder.originalWishboneSprite = ((StatusEffect)val).m_icon; try { FieldInfo[] fields = typeof(SE_Finder).GetFields(); foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(val2, fieldInfo.GetValue(val4)); } fields = typeof(StatusEffect).GetFields(); foreach (FieldInfo fieldInfo2 in fields) { fieldInfo2.SetValue(val2, fieldInfo2.GetValue(val4)); } ((Object)val2).name = ((Object)val4).name; __instance.m_StatusEffects[i] = (StatusEffect)(object)val2; } catch (Exception) { Debug.LogError((object)"Copying wishbone buff failed, aborting."); __instance.m_StatusEffects[i] = val3; } break; } if ((Object)(object)val2 == (Object)null || (Object)(object)val == (Object)null) { Debug.LogError((object)"Couldn't find vanilla wishbone buff. Something is wrong."); return; } foreach (GameObject item in __instance.m_items) { ItemDrop val5 = (Object.op_Implicit((Object)(object)item) ? item.GetComponent<ItemDrop>() : null); if (Object.op_Implicit((Object)(object)val5)) { SharedData shared = val5.m_itemData.m_shared; if ((Object)(object)shared.m_equipStatusEffect == (Object)(object)val) { shared.m_equipStatusEffect = (StatusEffect)(object)val2; Helper.Log("Replaced equip effect of " + shared.m_name); } if ((Object)(object)shared.m_setStatusEffect == (Object)(object)val) { shared.m_setStatusEffect = (StatusEffect)(object)val2; Helper.Log("Replaced equip effect of " + shared.m_name); } } } TrackableData.UpdateTargets(); } } internal sealed class ConfigurationManagerAttributes { public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func<object, string> ObjToStr; public Func<string, object> StrToObj; } internal static class ServerSyncWrapper { public enum Toggle { On = 1 } internal static ConfigSync CreateOptionalConfigSync(string guid, string displayName, string version) { return new ConfigSync(guid) { DisplayName = displayName, CurrentVersion = version, MinimumRequiredVersion = version, ModRequired = false }; } internal static ConfigSync CreateRequiredConfigSync(string guid, string displayName, string version) { return new ConfigSync(guid) { DisplayName = displayName, CurrentVersion = version, MinimumRequiredVersion = version, ModRequired = true }; } internal static ConfigEntry<Toggle> BindForceEnabledSyncLocker(this ConfigFile configFile, ConfigSync serverSyncInstance, string group, string name) { ConfigEntry<Toggle> val = configFile.Bind<Toggle>(group, name, Toggle.On, ForceEnabledDisplay("It's required that the mod is installed on the server and all clients. Changing this setting does nothing, ServerSync is always enabled.")); val.Value = Toggle.On; serverSyncInstance.AddLockingConfigEntry<Toggle>(val); return val; } internal static ConfigEntry<bool> BindHiddenForceEnabledSyncLocker(this ConfigFile configFile, ConfigSync serverSyncInstance, string group, string name) { ConfigEntry<bool> val = configFile.Bind<bool>(group, name, true, HiddenDisplay("It's required that the mod is installed on the server and all clients. Changing this setting does nothing, ServerSync is always enabled.")); val.Value = true; serverSyncInstance.AddLockingConfigEntry<bool>(val); return val; } internal static ConfigDescription ForceEnabledDisplay(string description, AcceptableValueBase acceptableValues = null) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown return new ConfigDescription(description, acceptableValues, new object[1] { new ConfigurationManagerAttributes { ReadOnly = true, CustomDrawer = delegate { CustomLabelDrawer("Enabled"); } } }); } internal static ConfigDescription ForceDisabledDisplay(string description, AcceptableValueBase acceptableValues = null) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown return new ConfigDescription(description, acceptableValues, new object[1] { new ConfigurationManagerAttributes { ReadOnly = true, CustomDrawer = delegate { CustomLabelDrawer("Disabled"); } } }); } internal static ConfigDescription HiddenDisplay(string description, AcceptableValueBase acceptableValues = null) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown return new ConfigDescription(description, acceptableValues, new object[1] { new ConfigurationManagerAttributes { ReadOnly = true, Browsable = false } }); } private static void CustomLabelDrawer(string labelText) { GUILayout.Label(labelText, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); } internal static ConfigEntry<T> BindSyncLocker<T>(this ConfigFile configFile, ConfigSync serverSyncInstance, string group, string name, T value, ConfigDescription description) where T : IConvertible { ConfigEntry<T> val = configFile.Bind<T>(group, name, value, description); serverSyncInstance.AddLockingConfigEntry<T>(val); return val; } internal static ConfigEntry<T> BindSyncLocker<T>(this ConfigFile configFile, ConfigSync serverSyncInstance, string group, string name, T value, string description) where T : IConvertible { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return configFile.BindSyncLocker(serverSyncInstance, group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>())); } internal static ConfigEntry<T> BindSynced<T>(this ConfigFile configFile, ConfigSync serverSyncInstance, string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { ConfigEntry<T> val = configFile.Bind<T>(group, name, value, description); serverSyncInstance.AddConfigEntry<T>(val).SynchronizedConfig = synchronizedSetting; return val; } internal static ConfigEntry<T> BindSynced<T>(this ConfigFile configFile, ConfigSync serverSyncInstance, string group, string name, T value, string description = "", bool synchronizedSetting = true) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown return configFile.BindSynced(serverSyncInstance, group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } } internal class SE_CustomFinder : SE_Finder { private float m_customUpdateBeaconTimer; internal static Sprite originalWishboneSprite; public static string GetDefaultMessageForTarget() { return GetMessageForTarget(Localization.instance.GetWishboneTranslation("WishboneStartMessageDefault")); } public static string GetMessageForTarget(string target) { return string.Format(Localization.instance.GetWishboneTranslation("WishboneStartMessage"), target); } public override void Setup(Character character) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) bool flag = false; ((StatusEffect)this).m_startMessageType = (MessageType)1; if (TrackableSwitcher.currentTarget != null) { ((StatusEffect)this).m_startMessage = GetMessageForTarget(TrackableSwitcher.currentTarget.displayName); if ((Object)(object)TrackableSwitcher.currentTarget.displaySprite != (Object)null) { ((StatusEffect)this).m_icon = TrackableSwitcher.currentTarget.displaySprite; flag = true; } } else { ((StatusEffect)this).m_startMessage = GetDefaultMessageForTarget(); } if (!flag) { ((StatusEffect)this).m_icon = originalWishboneSprite; } ((StatusEffect)this).Setup(character); } public override void UpdateStatusEffect(float dt) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) m_customUpdateBeaconTimer += dt; if (m_customUpdateBeaconTimer > 1f) { m_customUpdateBeaconTimer = 0f; Beacon val = BeaconHelper.FindClosestBeaconInRange(((Component)((StatusEffect)this).m_character).transform.position, TrackableSwitcher.currentTarget); if ((Object)(object)val != (Object)(object)base.m_beacon) { base.m_beacon = val; if (Object.op_Implicit((Object)(object)base.m_beacon)) { base.m_lastDistance = Utils.DistanceXZ(((Component)((StatusEffect)this).m_character).transform.position, ((Component)base.m_beacon).transform.position); base.m_pingTimer = 0f; } } } base.m_updateBeaconTimer = 0f - dt; ((SE_Finder)this).UpdateStatusEffect(dt); } } [BepInPlugin("goldenrevolver.SmartWishbone", "Smart Wishbone", "1.0.6")] public class SmartWishbonePlugin : BaseUnityPlugin { public const string GUID = "goldenrevolver.SmartWishbone"; public const string NAME = "Smart Wishbone"; public const string VERSION = "1.0.6"; internal static SmartWishbonePlugin Instance; internal static ManualLogSource Log; protected void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; WishboneConfig.LoadConfig((BaseUnityPlugin)(object)this); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); } } [HarmonyPatch(typeof(Game))] internal static class PatchGame { [HarmonyPrefix] [HarmonyPatch("OnApplicationQuit")] internal static void OnApplicationQuitPatch() { WishboneConfig.CurrentDataCache.SettingChanged -= WishboneConfig.CurrentDataCache_SettingChanged; WishboneConfig.config.Remove(((ConfigEntryBase)WishboneConfig.CurrentDataCache).Definition); DataFileWriter.Drain(); } [HarmonyPrefix] [HarmonyPatch("Logout")] internal static void ResetOnLogout() { TrackableData.ClearSyncedData(); WishboneConfig.SetConfigDataWithoutEvent(string.Empty); } } [HarmonyPatch(typeof(FejdStartup))] internal class FejdStartupPatch { private static bool isFirstStartup = true; [HarmonyPostfix] [HarmonyPatch("Awake")] private static void FejdStartupAwakePatch() { WishboneConfig.SetConfigDataWithoutEvent(string.Empty); if (!isFirstStartup) { return; } isFirstStartup = false; try { LocalizationLoader.SetupTranslations(); TrackableData.SetPersonalData(TrackableDataLoader.LoadTrackableData()); } catch (Exception e) { Helper.LogError(e); } } } } namespace SmartWishbone.ServerSync { [HarmonyPatch(typeof(ZNet))] internal class RPCHandler { private const string rpc_server_toggle = "goldenrevolver.SmartWishboneServerToggleRPC"; [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(ZNet __instance) { if (__instance.IsServer()) { WishboneConfig.SetConfigDataWithoutEvent(TrackableDataLoader.ParseCustomDictToString(TrackableData.Data)); ZRoutedRpc.instance.Register<string>("goldenrevolver.SmartWishboneServerToggleRPC", (Action<long, string>)ReceiveTrackableToggleRPC); } } internal static void SendTrackableToggleRPC(string trackableName) { if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsServer()) { Helper.Log("Send new trackable toggle request for: " + trackableName); ZRoutedRpc.instance.InvokeRoutedRPC("goldenrevolver.SmartWishboneServerToggleRPC", new object[1] { trackableName }); } } private static void ReceiveTrackableToggleRPC(long sender, string trackableName) { if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer()) { return; } switch (WishboneConfig.UsersAllowedToAddTrackables.Value) { case WishboneConfig.UserLevel.Noone: return; case WishboneConfig.UserLevel.OnlyAdmins: { ZNetPeer peer = ZNet.instance.GetPeer(sender); if (peer == null || !ZNet.instance.ListContainsId(ZNet.instance.m_adminList, peer.m_socket.GetHostName())) { Helper.Log("Received trackable request for '" + trackableName + "' but sender is not an admin"); return; } break; } } Helper.Log("Received valid trackable request for '" + trackableName + "'"); TrackableData.ToggleTarget(trackableName); } } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [Embedded] [CompilerGenerated] internal sealed class IsReadOnlyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] [Embedded] [CompilerGenerated] 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; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] [Embedded] [CompilerGenerated] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace YamlDotNet { internal sealed class CultureInfoAdapter : CultureInfo { private readonly IFormatProvider provider; public CultureInfoAdapter(CultureInfo baseCulture, IFormatProvider provider) : base(baseCulture.LCID) { this.provider = provider; } public override object? GetFormat(Type? formatType) { return provider.GetFormat(formatType); } } internal static class ReflectionExtensions { private static readonly FieldInfo? RemoteStackTraceField = typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic); public static Type? BaseType(this Type type) { return type.BaseType; } public static bool IsValueType(this Type type) { return type.IsValueType; } public static bool IsGenericType(this Type type) { return type.IsGenericType; } public static bool IsGenericTypeDefinition(this Type type) { return type.IsGenericTypeDefinition; } public static bool IsInterface(this Type type) { return type.IsInterface; } public static bool IsEnum(this Type type) { return type.IsEnum; } public static bool IsDbNull(this object value) { return value is DBNull; } public static bool HasDefaultConstructor(this Type type, bool allowPrivateConstructors) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (allowPrivateConstructors) { bindingFlags |= BindingFlags.NonPublic; } if (!type.IsValueType) { return type.GetConstructor(bindingFlags, null, Type.EmptyTypes, null) != null; } return true; } public static TypeCode GetTypeCode(this Type type) { return Type.GetTypeCode(type); } public static PropertyInfo? GetPublicProperty(this Type type, string name) { return type.GetProperty(name); } public static FieldInfo? GetPublicStaticField(this Type type, string name) { return type.GetField(name, BindingFlags.Static | BindingFlags.Public); } public static IEnumerable<PropertyInfo> GetProperties(this Type type, bool includeNonPublic) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (includeNonPublic) { bindingFlags |= BindingFlags.NonPublic; } if (!type.IsInterface) { return type.GetProperties(bindingFlags); } return new Type[1] { type }.Concat(type.GetInterfaces()).SelectMany((Type i) => i.GetProperties(bindingFlags)); } public static IEnumerable<PropertyInfo> GetPublicProperties(this Type type) { return type.GetProperties(includeNonPublic: false); } public static IEnumerable<FieldInfo> GetPublicFields(this Type type) { return type.GetFields(BindingFlags.Instance | BindingFlags.Public); } public static IEnumerable<MethodInfo> GetPublicStaticMethods(this Type type) { return type.GetMethods(BindingFlags.Static | BindingFlags.Public); } public static MethodInfo GetPrivateStaticMethod(this Type type, string name) { return type.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic) ?? throw new MissingMethodException("Expected to find a method named '" + name + "' in '" + type.FullName + "'."); } public static MethodInfo? GetPublicStaticMethod(this Type type, string name, params Type[] parameterTypes) { return type.GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, parameterTypes, null); } public static MethodInfo? GetPublicInstanceMethod(this Type type, string name) { return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public); } public static Exception Unwrap(this TargetInvocationException ex) { Exception innerException = ex.InnerException; if (innerException == null) { return ex; } if (RemoteStackTraceField != null) { RemoteStackTraceField.SetValue(innerException, innerException.StackTrace + "\r\n"); } return innerException; } public static bool IsInstanceOf(this Type type, object o) { return type.IsInstanceOfType(o); } public static Attribute[] GetAllCustomAttributes<TAttribute>(this PropertyInfo property) { return Attribute.GetCustomAttributes(property, typeof(TAttribute), inherit: true); } } internal static class PropertyInfoExtensions { public static object? ReadValue(this PropertyInfo property, object target) { return property.GetValue(target, null); } } internal static class StandardRegexOptions { public const RegexOptions Compiled = RegexOptions.Compiled; } } namespace YamlDotNet.Serialization { internal abstract class BuilderSkeleton<TBuilder> where TBuilder : BuilderSkeleton<TBuilder> { internal INamingConvention namingConvention = NullNamingConvention.Instance; internal ITypeResolver typeResolver; internal readonly YamlAttributeOverrides overrides; internal readonly LazyComponentRegistrationList<Nothing, IYamlTypeConverter> typeConverterFactories; internal readonly LazyComponentRegistrationList<ITypeInspector, ITypeInspector> typeInspectorFactories; internal bool ignoreFields; internal bool includeNonPublicProperties; internal Settings settings; protected abstract TBuilder Self { get; } internal BuilderSkeleton(ITypeResolver typeResolver) { overrides = new YamlAttributeOverrides(); typeConverterFactories = new LazyComponentRegistrationList<Nothing, IYamlTypeConverter> { { typeof(YamlDotNet.Serialization.Converters.GuidConverter), (Nothing _) => new YamlDotNet.Serialization.Converters.GuidConverter(jsonCompatible: false) }, { typeof(SystemTypeConverter), (Nothing _) => new SystemTypeConverter() } }; typeInspectorFactories = new LazyComponentRegistrationList<ITypeInspector, ITypeInspector>(); this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); settings = new Settings(); } public TBuilder IgnoreFields() { ignoreFields = true; return Self; } public TBuilder IncludeNonPublicProperties() { includeNonPublicProperties = true; return Self; } public TBuilder EnablePrivateConstructors() { settings.AllowPrivateConstructors = true; return Self; } public TBuilder WithNamingConvention(INamingConvention namingConvention) { this.namingConvention = namingConvention ?? throw new ArgumentNullException("namingConvention"); return Self; } public TBuilder WithTypeResolver(ITypeResolver typeResolver) { this.typeResolver = typeResolver ?? throw new ArgumentNullException("typeResolver"); return Self; } public abstract TBuilder WithTagMapping(TagName tag, Type type); public TBuilder WithAttributeOverride<TClass>(Expression<Func<TClass, object>> propertyAccessor, Attribute attribute) { overrides.Add(propertyAccessor, attribute); return Self; } public TBuilder WithAttributeOverride(Type type, string member, Attribute attribute) { overrides.Add(type, member, attribute); return Self; } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter) { return WithTypeConverter(typeConverter, delegate(IRegistrationLocationSelectionSyntax<IYamlTypeConverter> w) { w.OnTop(); }); } public TBuilder WithTypeConverter(IYamlTypeConverter typeConverter, Action<IRegistrationLocationSelectionSyntax<IYamlTypeConverter>> where) { if (typeConverter == null) { throw new ArgumentNullException("typeConverter"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateRegistrationLocationSelector(typeConverter.GetType(), (Nothing _) => typeConverter)); return Self; } public TBuilder WithTypeConverter<TYamlTypeConverter>(WrapperFactory<IYamlTypeConverter, IYamlTypeConverter> typeConverterFactory, Action<ITrackingRegistrationLocationSelectionSyntax<IYamlTypeConverter>> where) where TYamlTypeConverter : IYamlTypeConverter { if (typeConverterFactory == null) { throw new ArgumentNullException("typeConverterFactory"); } if (where == null) { throw new ArgumentNullException("where"); } where(typeConverterFactories.CreateTrackingRegistrationLocationSelector(typeof(TYamlTypeConverter), (IYamlTypeConverter wrapped, Nothing _) => typeConverterFactory(wrapped))); return Self; } public TBuilder WithoutTypeConverter<TYamlTypeConverter>() where TYamlTypeConverter : IYamlTypeConverter { return WithoutTypeConverter(typeof(TYamlTypeConverter)); } public TBuilder WithoutTypeConverter(Type converterType) { if (converterType == null) { throw new ArgumentNullException("converterType"); } typeConverterFactories.Remove(converterType); return Self; } public TBuilder WithTypeInspector<TTypeInspector>(Func<ITypeInspector, TTypeInspector> typeInspectorFactory) where TTypeInspector : ITypeInspector { return WithTypeInspector(typeInspectorFactory, delegate(IRegistrationLocationSelectionSyntax<ITypeInspector> w) { w.OnTop(); }); } public TBuilder WithTypeInspector<TTypeInspector>(Func<ITypeInspector, TTypeInspector> typeInspectorFactory, Action<IRegistrationLocationSelectionSyntax<ITypeInspector>> where) where TTypeInspector : ITypeInspector { if (typeInspectorFactory == null) { throw new ArgumentNullException("typeInspectorFactory"); } if (where == null) { thro