Decompiled source of SmartWishboneUpdated v1.0.6

SmartWishbone.dll

Decompiled 2 days ago
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