Decompiled source of QuestHeim v0.0.1

QuestHeim/QuestHeim.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HeaveHo.Shared;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using QuestHeim.Shared;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI.")]
[assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HeaveHo.Shared
{
	internal sealed class ServerSettings : IDisposable
	{
		public enum ServerMod
		{
			NotConnected,
			Checking,
			Installed,
			Missing
		}

		private sealed class ConfigurationManagerAttributes
		{
			public bool? ReadOnly;
		}

		private sealed class SyncedText
		{
			public string Key;

			public Func<string> Own;

			public Action<string> Use;

			public string Given;

			public bool FromServer;

			public bool Pending;

			public byte[] Packed;

			public bool TooBig;

			public MemoryStream Incoming;

			public int NextPart;
		}

		private sealed class Sending
		{
			public ZRpc Rpc;

			public string Key;

			public byte[] Data;

			public int Next;

			public int Parts;
		}

		private const int Protocol = 2;

		private const float ChangeSendGap = 0.5f;

		private const float AnswerWait = 20f;

		private const float FileWait = 1f;

		private const int PartSize = 65536;

		private const int CompressFrom = 1024;

		private const int WarnBytes = 262144;

		private const int MaxBytes = 8388608;

		private const int PaceBelow = 10240;

		private readonly string _send;

		private readonly string _ask;

		private readonly string _dAsk;

		private readonly string _dHello;

		private readonly string _dSend;

		private readonly string _edit;

		private readonly string _dTextAsk;

		private readonly string _dText;

		private readonly string _dAgain;

		private readonly ConfigFile _config;

		private readonly ManualLogSource _log;

		private readonly string _name;

		private readonly List<ConfigEntryBase> _entries = new List<ConfigEntryBase>();

		private readonly Dictionary<ConfigEntryBase, object> _own = new Dictionary<ConfigEntryBase, object>();

		private readonly Dictionary<ConfigEntryBase, object> _server = new Dictionary<ConfigEntryBase, object>();

		private readonly List<GameObject> _guardedPieces = new List<GameObject>();

		private readonly List<Recipe> _guardedRecipes = new List<Recipe>();

		private ZRoutedRpc _registeredOn;

		private bool _applying;

		private bool _reloading;

		private bool _changePending;

		private float _nextChangeSend;

		private float _askedAt = -1f;

		private int _serverProtocol;

		private bool _directSeen;

		private readonly HashSet<ZRpc> _directPeers = new HashSet<ZRpc>();

		private readonly List<SyncedText> _texts = new List<SyncedText>();

		private readonly Dictionary<ZRpc, HashSet<string>> _textPeers = new Dictionary<ZRpc, HashSet<string>>();

		private bool _textPending;

		private float _nextTextSend;

		private bool _readOnlyShown;

		private float _nextAdminCheck;

		private FileSystemWatcher _watcher;

		private volatile bool _fileTouched;

		private float _reloadAt = -1f;

		private string _fileText;

		private ZRpc _adminConnection;

		private readonly List<Sending> _outbox = new List<Sending>();

		private static readonly MethodInfo UpdateAvailablePieces = AccessTools.Method(typeof(Player), "UpdateAvailablePiecesList", (Type[])null, (Type[])null);

		private static readonly List<ServerSettings> Live = new List<ServerSettings>();

		private static Harmony _saveHarmony;

		public string Version { get; }

		public bool FromServer => _server.Count > 0;

		public ServerMod Server { get; private set; } = ServerMod.NotConnected;

		public string ServerVersion { get; private set; }

		public bool CanEditServer => IsServer || (_adminConnection != null && Server == ServerMod.Installed && _serverProtocol >= 1 && IsServerConnection(_adminConnection));

		private static bool IsServer => (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();

		public event Action ServerChanged;

		public ServerSettings(string guid, ConfigFile config, ManualLogSource log)
		{
			_send = guid + ".settings";
			_ask = guid + ".settings.ask";
			_dAsk = guid + ".settings.d.ask";
			_dHello = guid + ".settings.d.hello";
			_dSend = guid + ".settings.d.values";
			_edit = guid + ".settings.d.edit";
			_dTextAsk = guid + ".settings.d.textask";
			_dAgain = guid + ".settings.d.again";
			_dText = guid + ".settings.d.text";
			_config = config;
			_log = log;
			BepInPlugin val = PluginOf(guid);
			_name = ((val != null) ? val.Name : null) ?? guid;
			Version = ((val == null) ? null : val.Version?.ToString()) ?? "";
			Live.Add(this);
			if (Live.Count == 1)
			{
				PatchSave();
			}
			WatchFile();
		}

		public ConfigEntry<T> Add<T>(ConfigEntry<T> entry)
		{
			if (entry == null || _entries.Contains((ConfigEntryBase)(object)entry))
			{
				return entry;
			}
			_entries.Add((ConfigEntryBase)(object)entry);
			entry.SettingChanged += OnChanged;
			GiveManagerAttributes((ConfigEntryBase)(object)entry);
			return entry;
		}

		private static void GiveManagerAttributes(ConfigEntryBase entry)
		{
			ConfigDescription description = entry.Description;
			if (description == null || description == ConfigDescription.Empty || (description.Tags ?? new object[0]).Any(IsManagerAttributes))
			{
				return;
			}
			try
			{
				object[] value = new object[1]
				{
					new ConfigurationManagerAttributes()
				}.Concat(description.Tags ?? new object[0]).ToArray();
				AccessTools.Field(typeof(ConfigDescription), "<Tags>k__BackingField")?.SetValue(description, value);
			}
			catch (Exception)
			{
			}
		}

		public ConfigEntry<T> Bind<T>(string section, string key, T value, string description, AcceptableValueBase range = null, params object[] tags)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			if (!description.Contains("[Synced with Server]"))
			{
				description += " [Synced with Server]";
			}
			return Add<T>(_config.Bind<T>(section, key, value, new ConfigDescription(description, range, tags ?? new object[0])));
		}

		public void NeedsServer(GameObject piecePrefab)
		{
			if (!((Object)(object)piecePrefab == (Object)null))
			{
				_guardedPieces.RemoveAll((GameObject p) => (Object)(object)p == (Object)null || ((Object)p).name == ((Object)piecePrefab).name);
				_guardedPieces.Add(piecePrefab);
				Guard();
			}
		}

		public void NeedsServer(Recipe recipe)
		{
			if (!((Object)(object)recipe == (Object)null))
			{
				_guardedRecipes.RemoveAll((Recipe r) => (Object)(object)r == (Object)null || (Object)(object)r == (Object)(object)recipe || (!string.IsNullOrEmpty(((Object)recipe).name) && ((Object)r).name == ((Object)recipe).name));
				_guardedRecipes.Add(recipe);
				Guard();
			}
		}

		public void AddText(string key, Func<string> own, Action<string> use)
		{
			if (string.IsNullOrEmpty(key) || own == null || use == null)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogError((object)"AddText needs a key, own and use");
				}
				return;
			}
			_texts.RemoveAll((SyncedText x) => x.Key == key);
			SyncedText syncedText = new SyncedText
			{
				Key = key,
				Own = own,
				Use = use
			};
			_texts.Add(syncedText);
			Give(syncedText, OwnText(syncedText));
			ZRpc val = ((!((Object)(object)ZNet.instance != (Object)null) || IsServer) ? null : ZNet.instance.GetServerPeer()?.m_rpc);
			if (_registeredOn != null && val != null)
			{
				val.Register<ZPackage>(_dText, (Action<ZRpc, ZPackage>)OnText);
				AskTexts(val);
			}
		}

		public void TextChanged(string key)
		{
			SyncedText syncedText = _texts.FirstOrDefault((SyncedText x) => x.Key == key);
			if (syncedText != null)
			{
				syncedText.Packed = null;
				if (!syncedText.FromServer)
				{
					Give(syncedText, OwnText(syncedText));
				}
				if (IsServer)
				{
					syncedText.Pending = true;
					_textPending = true;
				}
			}
		}

		public bool TextFromServer(string key)
		{
			return _texts.Any((SyncedText x) => x.Key == key && x.FromServer);
		}

		public void Update()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != _registeredOn)
			{
				Unregister();
				Restore();
				RestoreTexts(give: true);
				_changePending = false;
				_textPending = false;
				SetServer(ServerMod.NotConnected, null, 0);
				if (instance != null)
				{
					Register(instance);
				}
			}
			float unscaledTime = Time.unscaledTime;
			if (_changePending && unscaledTime >= _nextChangeSend)
			{
				SendChange();
			}
			if (_textPending && unscaledTime >= _nextTextSend)
			{
				SendTexts();
			}
			if (_outbox.Count > 0)
			{
				Pump();
			}
			if (Server == ServerMod.Checking && _askedAt >= 0f && unscaledTime - _askedAt > 20f)
			{
				if (_guardedPieces.Count + _guardedRecipes.Count > 0)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)("The server didn't answer: " + _name + " isn't installed there (or its copy is older than 2026-10-11), so its pieces and items can't be made on this server."));
					}
				}
				else
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogInfo((object)("The server didn't answer: " + _name + " isn't installed there, or its copy is older" + (FromServer ? " (its settings apply)." : " (your own settings apply).")));
					}
				}
				SetServer(ServerMod.Missing, null, 0);
			}
			if (_fileTouched)
			{
				_fileTouched = false;
				_reloadAt = unscaledTime + 1f;
			}
			if (_reloadAt >= 0f && unscaledTime >= _reloadAt)
			{
				_reloadAt = -1f;
				ReloadFile();
			}
			if (FromServer && unscaledTime >= _nextAdminCheck)
			{
				_nextAdminCheck = unscaledTime + 1f;
				ShowReadOnly();
			}
		}

		public void Dispose()
		{
			Unregister();
			Restore();
			RestoreTexts(give: false);
			SetServer(ServerMod.NotConnected, null, 0);
			foreach (ConfigEntryBase entry in _entries)
			{
				Unsubscribe(entry);
			}
			_entries.Clear();
			_texts.Clear();
			try
			{
				_watcher?.Dispose();
			}
			catch (Exception)
			{
			}
			_watcher = null;
			Live.Remove(this);
			if (Live.Count == 0)
			{
				UnpatchSave();
			}
		}

		private void Register(ZRoutedRpc rpc)
		{
			Forget(rpc, _send);
			Forget(rpc, _ask);
			rpc.Register<ZPackage>(_send, (Action<long, ZPackage>)OnRoutedValues);
			rpc.Register(_ask, (Action<long>)OnRoutedAsk);
			rpc.m_onNewPeer = (Action<long>)Delegate.Combine(rpc.m_onNewPeer, new Action<long>(OnNewPeer));
			_registeredOn = rpc;
			if (IsServer)
			{
				foreach (ZNetPeer peer in ZNet.instance.GetPeers())
				{
					ListenTo(peer);
					if (peer != null)
					{
						ZRpc rpc2 = peer.m_rpc;
						if (rpc2 != null)
						{
							rpc2.Invoke(_dAgain, Array.Empty<object>());
						}
					}
				}
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				SetServer(ServerMod.Checking, null, 0);
				if (ZNet.instance.GetServerPeer() != null)
				{
					AskServer();
				}
			}
		}

		private void Unregister()
		{
			if (_registeredOn == null)
			{
				return;
			}
			ZRoutedRpc registeredOn = _registeredOn;
			registeredOn.m_onNewPeer = (Action<long>)Delegate.Remove(registeredOn.m_onNewPeer, new Action<long>(OnNewPeer));
			Forget(_registeredOn, _send);
			Forget(_registeredOn, _ask);
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				foreach (ZNetPeer peer in ZNet.instance.GetPeers())
				{
					if (peer?.m_rpc != null)
					{
						string[] array = new string[7] { _dAsk, _dHello, _dSend, _edit, _dTextAsk, _dText, _dAgain };
						foreach (string name in array)
						{
							ForgetDirect(peer.m_rpc, name);
						}
					}
				}
			}
			_directPeers.Clear();
			_textPeers.Clear();
			_outbox.Clear();
			_directSeen = false;
			_registeredOn = null;
		}

		private static void Forget(ZRoutedRpc rpc, string name)
		{
			if (AccessTools.Field(typeof(ZRoutedRpc), "m_functions")?.GetValue(rpc) is IDictionary dictionary)
			{
				dictionary.Remove(StringExtensionMethods.GetStableHashCode(name));
			}
		}

		private static void ForgetDirect(ZRpc rpc, string name)
		{
			if (AccessTools.Field(typeof(ZRpc), "m_functions")?.GetValue(rpc) is IDictionary dictionary)
			{
				dictionary.Remove(StringExtensionMethods.GetStableHashCode(name));
			}
		}

		private void OnNewPeer(long uid)
		{
			if (IsServer)
			{
				ZNetPeer peer = ZNet.instance.GetPeer(uid);
				ListenTo(peer);
				if (peer?.m_rpc != null)
				{
					SendDirect(peer.m_rpc);
				}
				SendRouted(uid);
			}
			else if ((Object)(object)ZNet.instance != (Object)null)
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null && serverPeer.m_uid == uid)
				{
					AskServer();
				}
			}
		}

		private void ListenTo(ZNetPeer peer)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			if (peer?.m_rpc != null)
			{
				peer.m_rpc.Register(_dAsk, new Method(OnDirectAsk));
				peer.m_rpc.Register<ZPackage>(_edit, (Action<ZRpc, ZPackage>)OnEdit);
				peer.m_rpc.Register<ZPackage>(_dTextAsk, (Action<ZRpc, ZPackage>)OnTextAsk);
			}
		}

		private void OnDirectAsk(ZRpc rpc)
		{
			if (IsServer)
			{
				_directPeers.Add(rpc);
				SendDirect(rpc);
			}
		}

		private void OnRoutedAsk(long sender)
		{
			if (IsServer)
			{
				SendRouted(sender);
			}
		}

		private void SendDirect(ZRpc rpc)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(2);
			val.Write(Version);
			ISocket socket = rpc.GetSocket();
			string text = ((socket != null) ? socket.GetHostName() : null);
			val.Write(!string.IsNullOrEmpty(text) && ZNet.instance.IsAdmin(text));
			rpc.Invoke(_dHello, new object[1] { val });
			if (_entries.Count > 0)
			{
				rpc.Invoke(_dSend, new object[1] { Values() });
			}
		}

		private void SendRouted(long target)
		{
			if (ZRoutedRpc.instance != null && _entries.Count > 0)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(target, _send, new object[1] { Values() });
			}
		}

		private ZPackage Values()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(_entries.Count);
			foreach (ConfigEntryBase entry in _entries)
			{
				val.Write(entry.Definition.Section);
				val.Write(entry.Definition.Key);
				val.Write(TomlTypeConverter.ConvertToString(entry.BoxedValue, entry.SettingType));
			}
			return val;
		}

		private void AskServer()
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Expected O, but got Unknown
			ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null);
			if (val?.m_rpc == null)
			{
				return;
			}
			_askedAt = Time.unscaledTime;
			val.m_rpc.Register<ZPackage>(_dHello, (Action<ZRpc, ZPackage>)OnDirectHello);
			val.m_rpc.Register<ZPackage>(_dSend, (Action<ZRpc, ZPackage>)OnDirectValues);
			val.m_rpc.Register<ZPackage>(_dText, (Action<ZRpc, ZPackage>)OnText);
			val.m_rpc.Register(_dAgain, new Method(OnAskAgain));
			val.m_rpc.Invoke(_dAsk, Array.Empty<object>());
			AskTexts(val.m_rpc);
			try
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC(val.m_uid, _ask, Array.Empty<object>());
				}
			}
			catch (Exception)
			{
			}
		}

		private static bool IsServerConnection(ZRpc rpc)
		{
			int result;
			if (!IsServer)
			{
				ZNet instance = ZNet.instance;
				result = ((((instance == null) ? null : instance.GetServerPeer()?.m_rpc) == rpc) ? 1 : 0);
			}
			else
			{
				result = 0;
			}
			return (byte)result != 0;
		}

		private void OnAskAgain(ZRpc rpc)
		{
			if (IsServerConnection(rpc))
			{
				rpc.Invoke(_dAsk, Array.Empty<object>());
				AskTexts(rpc);
			}
		}

		private void OnDirectHello(ZRpc rpc, ZPackage pkg)
		{
			if (!IsServerConnection(rpc))
			{
				return;
			}
			try
			{
				int protocol = pkg.ReadInt();
				string text = pkg.ReadString();
				_adminConnection = ((pkg.GetPos() < pkg.Size() && pkg.ReadBool()) ? rpc : null);
				if (text != Version && text != ServerVersion)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)("The server has " + _name + " " + text + ", you have " + Version + ": some things may not match. Use the same version as the server."));
					}
				}
				_directSeen = true;
				SetServer(ServerMod.Installed, text, protocol);
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Could not read the server's hello: " + ex.Message));
				}
			}
		}

		private void OnDirectValues(ZRpc rpc, ZPackage pkg)
		{
			if (IsServerConnection(rpc))
			{
				TakeValues(pkg);
			}
		}

		private void OnRoutedValues(long sender, ZPackage pkg)
		{
			if (!_directSeen && !IsServer)
			{
				ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null);
				if (val != null && val.m_uid == sender)
				{
					TakeValues(pkg);
				}
			}
		}

		private void TakeValues(ZPackage pkg)
		{
			try
			{
				int num = pkg.ReadInt();
				List<string> list = new List<string>();
				for (int i = 0; i < num; i++)
				{
					string section = pkg.ReadString();
					string key = pkg.ReadString();
					string text = pkg.ReadString();
					ConfigEntryBase val = ((IEnumerable<ConfigEntryBase>)_entries).FirstOrDefault((Func<ConfigEntryBase, bool>)((ConfigEntryBase x) => x.Definition.Section == section && x.Definition.Key == key));
					if (val != null)
					{
						object obj;
						try
						{
							obj = TomlTypeConverter.ConvertToValue(text, val.SettingType);
						}
						catch (Exception)
						{
							continue;
						}
						if (!_own.ContainsKey(val))
						{
							_own[val] = val.BoxedValue;
						}
						_server[val] = obj;
						if (!object.Equals(val.BoxedValue, obj))
						{
							Apply(val, obj);
							list.Add(section + "/" + key + " = " + text);
						}
					}
				}
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)($"Settings from the server apply while you play here ({_server.Count})" + ((list.Count > 0) ? (": " + string.Join(", ", list.ToArray())) : "")));
				}
				ShowReadOnly();
			}
			catch (Exception ex2)
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Could not read the server's settings: " + ex2.Message));
				}
			}
		}

		private void Apply(ConfigEntryBase e, object value)
		{
			bool saveOnConfigSet = _config.SaveOnConfigSet;
			_applying = true;
			try
			{
				_config.SaveOnConfigSet = false;
				e.BoxedValue = value;
			}
			finally
			{
				_config.SaveOnConfigSet = saveOnConfigSet;
				_applying = false;
			}
		}

		private void Restore()
		{
			if (_server.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<ConfigEntryBase, object> item in _own)
			{
				Apply(item.Key, item.Value);
			}
			_own.Clear();
			_server.Clear();
			ShowReadOnly();
			ManualLogSource log = _log;
			if (log != null)
			{
				log.LogInfo((object)"Your own settings apply again");
			}
		}

		private void OnChanged(object sender, EventArgs args)
		{
			if (_applying)
			{
				return;
			}
			ConfigEntryBase val = (ConfigEntryBase)((sender is ConfigEntryBase) ? sender : null);
			if (val == null)
			{
				return;
			}
			if (_server.TryGetValue(val, out var value))
			{
				if (!_reloading && CanEditServer)
				{
					SendEdit(val);
				}
				else
				{
					_own[val] = val.BoxedValue;
				}
				Apply(val, value);
			}
			else if (IsServer)
			{
				_changePending = true;
				if (Time.unscaledTime >= _nextChangeSend)
				{
					SendChange();
				}
			}
		}

		private void SendChange()
		{
			_changePending = false;
			_nextChangeSend = Time.unscaledTime + 0.5f;
			if (!IsServer)
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer?.m_rpc == null)
				{
					continue;
				}
				if (_directPeers.Contains(peer.m_rpc))
				{
					if (_entries.Count > 0)
					{
						peer.m_rpc.Invoke(_dSend, new object[1] { Values() });
					}
				}
				else
				{
					SendRouted(peer.m_uid);
				}
			}
		}

		private void SendEdit(ConfigEntryBase e)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			ZNet instance = ZNet.instance;
			ZNetPeer val = ((instance != null) ? instance.GetServerPeer() : null);
			if (val?.m_rpc != null)
			{
				ZPackage val2 = new ZPackage();
				val2.Write(e.Definition.Section);
				val2.Write(e.Definition.Key);
				val2.Write(TomlTypeConverter.ConvertToString(e.BoxedValue, e.SettingType));
				val.m_rpc.Invoke(_edit, new object[1] { val2 });
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)("Asked the server to set " + e.Definition.Section + "/" + e.Definition.Key));
				}
			}
		}

		private void OnEdit(ZRpc rpc, ZPackage pkg)
		{
			try
			{
				if (!IsServer)
				{
					return;
				}
				ISocket socket = rpc.GetSocket();
				string text = ((socket != null) ? socket.GetHostName() : null) ?? "";
				ZNetPeer val = ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p.m_rpc == rpc));
				string section = pkg.ReadString();
				string key = pkg.ReadString();
				string text2 = pkg.ReadString();
				if (!ZNet.instance.IsAdmin(text))
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)((val?.m_playerName ?? text) + " tried to change " + section + "/" + key + " but isn't on the admin list"));
					}
					if (_entries.Count > 0)
					{
						rpc.Invoke(_dSend, new object[1] { Values() });
					}
					return;
				}
				ConfigEntryBase val2 = ((IEnumerable<ConfigEntryBase>)_entries).FirstOrDefault((Func<ConfigEntryBase, bool>)((ConfigEntryBase x) => x.Definition.Section == section && x.Definition.Key == key));
				if (val2 != null)
				{
					val2.BoxedValue = TomlTypeConverter.ConvertToValue(text2, val2.SettingType);
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogInfo((object)("Admin " + (val?.m_playerName ?? text) + " set " + section + "/" + key + " = " + text2));
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = _log;
				if (log3 != null)
				{
					log3.LogWarning((object)("Could not take an admin's change: " + ex.Message));
				}
			}
		}

		private void ShowReadOnly()
		{
			bool flag = FromServer && !CanEditServer;
			if (flag == _readOnlyShown)
			{
				return;
			}
			_readOnlyShown = flag;
			foreach (ConfigEntryBase entry in _entries)
			{
				ConfigDescription description = entry.Description;
				object[] array = ((description != null) ? description.Tags : null) ?? new object[0];
				foreach (object obj in array)
				{
					if (IsManagerAttributes(obj))
					{
						try
						{
							AccessTools.Field(obj.GetType(), "ReadOnly")?.SetValue(obj, flag ? ((object)true) : null);
						}
						catch (Exception)
						{
						}
					}
				}
			}
		}

		private static bool IsManagerAttributes(object tag)
		{
			return tag != null && tag.GetType().Name == "ConfigurationManagerAttributes";
		}

		private string OwnText(SyncedText t)
		{
			try
			{
				return t.Own() ?? "";
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("Could not read " + t.Key + ": " + ex.Message));
				}
				return t.Given ?? "";
			}
		}

		private void Give(SyncedText t, string text)
		{
			if (t.Given != null && t.Given == text)
			{
				return;
			}
			t.Given = text;
			try
			{
				t.Use(text);
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("Could not use " + t.Key + ": " + ex));
				}
			}
		}

		private void RestoreTexts(bool give)
		{
			foreach (SyncedText text in _texts)
			{
				text.Incoming = null;
				if (!text.FromServer)
				{
					continue;
				}
				text.FromServer = false;
				if (give)
				{
					Give(text, OwnText(text));
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogInfo((object)("Your own " + text.Key + " applies again"));
					}
				}
			}
		}

		private void AskTexts(ZRpc server)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			if (_texts.Count == 0 || server == null)
			{
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(_texts.Count);
			foreach (SyncedText text in _texts)
			{
				val.Write(text.Key);
			}
			server.Invoke(_dTextAsk, new object[1] { val });
		}

		private void OnTextAsk(ZRpc rpc, ZPackage pkg)
		{
			try
			{
				if (!IsServer)
				{
					return;
				}
				int num = pkg.ReadInt();
				HashSet<string> hashSet = new HashSet<string>();
				for (int i = 0; i < num && i < 1000; i++)
				{
					string key = pkg.ReadString();
					if (_texts.Any((SyncedText x) => x.Key == key))
					{
						hashSet.Add(key);
					}
				}
				if (_textPeers.TryGetValue(rpc, out var value))
				{
					hashSet.UnionWith(value);
				}
				foreach (ZRpc item in _textPeers.Keys.Where((ZRpc r) => !r.IsConnected()).ToList())
				{
					_textPeers.Remove(item);
				}
				_textPeers[rpc] = hashSet;
				foreach (SyncedText text in _texts)
				{
					if (hashSet.Contains(text.Key))
					{
						SendText(rpc, text);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("Could not answer a player's ask for server data: " + ex.Message));
				}
			}
		}

		private void SendTexts()
		{
			_textPending = false;
			_nextTextSend = Time.unscaledTime + 0.5f;
			if (!IsServer)
			{
				return;
			}
			foreach (SyncedText text in _texts)
			{
				if (!text.Pending)
				{
					continue;
				}
				text.Pending = false;
				foreach (ZNetPeer peer in ZNet.instance.GetPeers())
				{
					if (peer?.m_rpc != null && _textPeers.TryGetValue(peer.m_rpc, out var value) && value.Contains(text.Key))
					{
						SendText(peer.m_rpc, text);
					}
				}
			}
		}

		private void SendText(ZRpc rpc, SyncedText t)
		{
			byte[] array = Packed(t);
			if (array != null)
			{
				_outbox.RemoveAll((Sending s) => s.Rpc == rpc && s.Key == t.Key);
				_outbox.Add(new Sending
				{
					Rpc = rpc,
					Key = t.Key,
					Data = array,
					Parts = Math.Max(1, (array.Length + 65536 - 1) / 65536)
				});
				Pump();
			}
		}

		private void Pump()
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			for (int num = _outbox.Count - 1; num >= 0; num--)
			{
				Sending sending = _outbox[num];
				try
				{
					while (sending.Next < sending.Parts && sending.Rpc.IsConnected() && sending.Rpc.GetSocket().GetSendQueueSize() < 10240)
					{
						int num2 = sending.Next * 65536;
						int num3 = Math.Min(65536, sending.Data.Length - num2);
						byte[] array = new byte[num3];
						Buffer.BlockCopy(sending.Data, num2, array, 0, num3);
						ZPackage val = new ZPackage();
						val.Write(sending.Key);
						val.Write(sending.Next);
						val.Write(sending.Parts);
						val.Write(array);
						sending.Rpc.Invoke(_dText, new object[1] { val });
						sending.Next++;
					}
					if (sending.Next >= sending.Parts || !sending.Rpc.IsConnected())
					{
						_outbox.RemoveAt(num);
					}
				}
				catch (Exception ex)
				{
					_outbox.RemoveAt(num);
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)("Could not send " + sending.Key + " to a player: " + ex.Message));
					}
				}
			}
		}

		private byte[] Packed(SyncedText t)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			if (t.Packed == null)
			{
				string text = OwnText(t);
				ZPackage val = new ZPackage();
				val.Write(text);
				ZPackage val2 = new ZPackage();
				bool flag = text.Length > 1024;
				val2.Write(flag);
				if (flag)
				{
					val2.WriteCompressed(val);
				}
				else
				{
					val2.Write(val);
				}
				t.Packed = val2.GetArray();
				t.TooBig = t.Packed.Length > 8388608;
				string arg = $"{t.Packed.Length / 1024} KB" + (flag ? $" gzipped ({text.Length / 1024} KB of text)" : "");
				if (t.TooBig)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogError((object)$"{t.Key} is too big to send to players ({arg}, at most {8} MB): they keep their own.");
					}
				}
				else if (t.Packed.Length > 262144)
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogWarning((object)$"{t.Key} is big to send to every player who joins ({arg}): it takes about {(float)t.Packed.Length / 153600f:0} s of their connection, and their world loads slower meanwhile.");
					}
				}
			}
			return t.TooBig ? null : t.Packed;
		}

		private void OnText(ZRpc rpc, ZPackage pkg)
		{
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			if (!IsServerConnection(rpc))
			{
				return;
			}
			SyncedText syncedText = null;
			try
			{
				string key = pkg.ReadString();
				int num = pkg.ReadInt();
				int num2 = pkg.ReadInt();
				byte[] array = pkg.ReadByteArray();
				syncedText = _texts.FirstOrDefault((SyncedText x) => x.Key == key);
				if (syncedText == null)
				{
					return;
				}
				if (num2 < 1 || num2 > 129 || num < 0 || num >= num2)
				{
					throw new InvalidDataException($"part {num} of {num2}");
				}
				if (num == 0)
				{
					syncedText.Incoming = new MemoryStream();
					syncedText.NextPart = 0;
				}
				if (syncedText.Incoming == null || num != syncedText.NextPart)
				{
					throw new InvalidDataException($"part {num} came out of order");
				}
				syncedText.Incoming.Write(array, 0, array.Length);
				syncedText.NextPart = num + 1;
				if (num >= num2 - 1)
				{
					ZPackage val = new ZPackage(syncedText.Incoming.ToArray());
					syncedText.Incoming = null;
					ZPackage val2 = (val.ReadBool() ? val.ReadCompressedPackage() : val.ReadPackage());
					string text = val2.ReadString();
					syncedText.FromServer = true;
					Give(syncedText, text);
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogInfo((object)$"The server's {key} applies while you play here ({text.Length / 1024} KB)");
					}
				}
			}
			catch (Exception ex)
			{
				if (syncedText != null)
				{
					syncedText.Incoming = null;
				}
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Could not read the server's data" + ((syncedText != null) ? (" for " + syncedText.Key) : "") + ": " + ex.Message));
				}
			}
		}

		private void SetServer(ServerMod state, string version, int protocol)
		{
			if (state != ServerMod.Checking)
			{
				_askedAt = -1f;
			}
			if (state != ServerMod.Installed)
			{
				_adminConnection = null;
			}
			bool flag = state != Server || version != ServerVersion;
			Server = state;
			ServerVersion = version;
			_serverProtocol = protocol;
			if (!flag)
			{
				return;
			}
			Guard();
			try
			{
				this.ServerChanged?.Invoke();
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("ServerChanged: " + ex));
				}
			}
		}

		private void Guard()
		{
			bool flag = Server == ServerMod.NotConnected || Server == ServerMod.Installed;
			bool flag2 = false;
			foreach (GameObject guardedPiece in _guardedPieces)
			{
				Piece val = (((Object)(object)guardedPiece != (Object)null) ? guardedPiece.GetComponent<Piece>() : null);
				if ((Object)(object)val != (Object)null && val.m_enabled != flag)
				{
					val.m_enabled = flag;
					flag2 = true;
				}
			}
			foreach (Recipe guardedRecipe in _guardedRecipes)
			{
				if ((Object)(object)guardedRecipe != (Object)null && guardedRecipe.m_enabled != flag)
				{
					guardedRecipe.m_enabled = flag;
					flag2 = true;
				}
			}
			if (flag2 && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				try
				{
					UpdateAvailablePieces?.Invoke(Player.m_localPlayer, null);
				}
				catch (Exception)
				{
				}
			}
		}

		private void WatchFile()
		{
			try
			{
				string configFilePath = _config.ConfigFilePath;
				_watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath))
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite)
				};
				FileSystemEventHandler value = delegate
				{
					_fileTouched = true;
				};
				_watcher.Changed += value;
				_watcher.Created += value;
				_watcher.Renamed += delegate
				{
					_fileTouched = true;
				};
				_watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("Edits to the config file apply after a restart (can't watch it: " + ex.Message + ")"));
				}
			}
		}

		private void ReloadFile()
		{
			string text = ReadFile(_config.ConfigFilePath);
			if (text == null)
			{
				if (File.Exists(_config.ConfigFilePath))
				{
					_reloadAt = Time.unscaledTime + 1f;
				}
			}
			else
			{
				if (text == _fileText)
				{
					return;
				}
				_fileText = text;
				bool saveOnConfigSet = _config.SaveOnConfigSet;
				_reloading = true;
				try
				{
					_config.SaveOnConfigSet = false;
					_config.Reload();
				}
				catch (Exception ex)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)("Could not read the edited config file (check the line you changed): " + ex.Message));
					}
				}
				finally
				{
					_config.SaveOnConfigSet = saveOnConfigSet;
					_reloading = false;
				}
			}
		}

		private void PatchSave()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0069: Expected O, but got Unknown
			try
			{
				_saveHarmony = new Harmony(_send + ".save");
				_saveHarmony.Patch((MethodBase)AccessTools.Method(typeof(ConfigFile), "Save", (Type[])null, (Type[])null), new HarmonyMethod(typeof(ServerSettings), "BeforeSave", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(ServerSettings), "AfterSave", (Type[])null), (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)("Could not keep the server's values out of your config file: " + ex.Message));
				}
			}
		}

		private static void UnpatchSave()
		{
			try
			{
				Harmony saveHarmony = _saveHarmony;
				if (saveHarmony != null)
				{
					saveHarmony.UnpatchSelf();
				}
			}
			catch (Exception)
			{
			}
			_saveHarmony = null;
		}

		private static void BeforeSave(ConfigFile __instance)
		{
			foreach (ServerSettings item in Live)
			{
				if (item._config != __instance)
				{
					continue;
				}
				foreach (KeyValuePair<ConfigEntryBase, object> item2 in item._own)
				{
					SetQuietly(item2.Key, item2.Value);
				}
			}
		}

		private static Exception AfterSave(ConfigFile __instance, Exception __exception)
		{
			foreach (ServerSettings item in Live)
			{
				if (item._config != __instance)
				{
					continue;
				}
				foreach (KeyValuePair<ConfigEntryBase, object> item2 in item._server)
				{
					SetQuietly(item2.Key, item2.Value);
				}
				item._fileText = ReadFile(__instance.ConfigFilePath);
			}
			return __exception;
		}

		private static string ReadFile(string path)
		{
			try
			{
				return File.Exists(path) ? File.ReadAllText(path) : null;
			}
			catch (Exception)
			{
				return null;
			}
		}

		private static void SetQuietly(ConfigEntryBase e, object value)
		{
			AccessTools.Field(((object)e).GetType(), "_typedValue")?.SetValue(e, value);
		}

		private static BepInPlugin PluginOf(string guid)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			Type[] array;
			try
			{
				array = typeof(ServerSettings).Assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				array = ex.Types.Where((Type t) => t != null).ToArray();
			}
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				object[] customAttributes = type.GetCustomAttributes(typeof(BepInPlugin), inherit: false);
				for (int num2 = 0; num2 < customAttributes.Length; num2++)
				{
					BepInPlugin val = (BepInPlugin)customAttributes[num2];
					if (val.GUID == guid || guid.StartsWith(val.GUID + "."))
					{
						return val;
					}
				}
			}
			return null;
		}

		private void Unsubscribe(ConfigEntryBase e)
		{
			((object)e).GetType().GetEvent("SettingChanged")?.RemoveEventHandler(e, new EventHandler(OnChanged));
		}
	}
}
namespace QuestHeim
{
	internal static class Board
	{
		[HarmonyPatch(typeof(Piece), "DropResources")]
		private static class Piece_DropResources
		{
			private static bool Prefix(Piece __instance)
			{
				return !IsBoard(((Component)__instance).gameObject);
			}
		}

		[HarmonyPatch(typeof(Player), "CheckCanRemovePiece", new Type[] { typeof(Piece) })]
		private static class Player_CheckCanRemovePiece
		{
			private static bool Prefix(Piece __0, ref bool __result)
			{
				if ((Object)(object)__0 == (Object)null || !IsBoard(((Component)__0).gameObject) || CanBuildOrRemove)
				{
					return true;
				}
				__result = false;
				return false;
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetScene_Awake
		{
			private static void Postfix(ZNetScene __instance)
			{
				try
				{
					Register(__instance);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Registering the QuestHeim board failed: " + ex));
				}
			}
		}

		public const string PrefabName = "marlthon_questheim_board";

		public const int Slots = 10;

		private static GameObject _prefab;

		private static GameObject[] _effects = Array.Empty<GameObject>();

		private static PieceTable _table;

		private static bool _prepared;

		private static readonly FieldRef<ZNetScene, Dictionary<int, GameObject>> NamedPrefabs = AccessTools.FieldRefAccess<ZNetScene, Dictionary<int, GameObject>>("m_namedPrefabs");

		internal static bool CanBuildOrRemove => Plugin.Synced?.CanEditServer ?? false;

		internal static bool IsBoard(GameObject go)
		{
			return (Object)(object)go != (Object)null && Utils.GetPrefabName(go) == "marlthon_questheim_board";
		}

		internal static void Load()
		{
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = typeof(Board).Assembly.GetManifestResourceStream("QuestHeim.assets.mar_questheim");
			if (stream == null)
			{
				throw new InvalidOperationException("The QuestHeim bundle is missing.");
			}
			using BinaryReader binaryReader = new BinaryReader(stream);
			AssetBundle val = AssetBundle.LoadFromMemory(binaryReader.ReadBytes((int)stream.Length));
			GameObject boardAsset = ((val != null) ? val.LoadAsset<GameObject>("QH_NoticeBoard") : null);
			GameObject val2 = ((val != null) ? val.LoadAsset<GameObject>("QH_Chest") : null);
			if ((Object)(object)val != (Object)null)
			{
				string[] array = new string[4] { "sfx_build_hammer_wood_questheim", "sfx_wood_destroyed_questheim", "vfx_Place_wood_wall_questheim", "vfx_SawDust_questheim" };
				List<GameObject> list = new List<GameObject>();
				string[] array2 = array;
				foreach (string text in array2)
				{
					GameObject val3 = val.LoadAsset<GameObject>(text);
					if ((Object)(object)val3 != (Object)null)
					{
						list.Add(val3);
					}
					else
					{
						Plugin.Log.LogWarning((object)("Effect prefab '" + text + "' is missing from mar_questheim."));
					}
				}
				_effects = list.ToArray();
			}
			if (val != null)
			{
				val.Unload(false);
			}
			if ((Object)(object)boardAsset == (Object)null || (Object)(object)val2 == (Object)null || !Object.op_Implicit((Object)(object)boardAsset.GetComponent<Piece>()) || !Object.op_Implicit((Object)(object)boardAsset.GetComponent<ZNetView>()) || !Object.op_Implicit((Object)(object)val2.GetComponent<Container>()) || !Object.op_Implicit((Object)(object)boardAsset.transform.Find("chest_anchor")) || Enumerable.Range(0, 10).Any((int num) => !Object.op_Implicit((Object)(object)boardAsset.transform.Find("notice_" + num))))
			{
				throw new InvalidOperationException("The QuestHeim bundle is missing a prefab, component or anchor.");
			}
			GameObject val4 = new GameObject("QuestHeim_prefabs");
			val4.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)val4);
			_prefab = Object.Instantiate<GameObject>(boardAsset, val4.transform);
			((Object)_prefab).name = "marlthon_questheim_board";
			_prefab.GetComponent<Piece>().m_name = "$questheim_board";
			_prefab.GetComponent<Piece>().m_description = "$questheim_board_desc";
			_prefab.AddComponent<BoardFace>();
			GameObject val5 = Object.Instantiate<GameObject>(val2, _prefab.transform.Find("chest_anchor"), false);
			val5.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
			Object.DestroyImmediate((Object)(object)val5.GetComponent<Piece>());
			Object.DestroyImmediate((Object)(object)val5.GetComponent<WearNTear>());
			Object.DestroyImmediate((Object)(object)val5.GetComponent<ZNetView>());
			Container component = val5.GetComponent<Container>();
			component.m_rootObjectOverride = _prefab.GetComponent<ZNetView>();
			component.m_name = "$questheim_box";
			Piece component2 = _prefab.GetComponent<Piece>();
			component2.m_category = (PieceCategory)4;
			component2.m_usage = (UsageTagFlags)128;
			Plugin.Synced.NeedsServer(_prefab);
		}

		internal static void Register(ZNetScene scene)
		{
			if ((Object)(object)scene == (Object)null || (Object)(object)_prefab == (Object)null)
			{
				return;
			}
			GameObject[] effects = _effects;
			foreach (GameObject prefab in effects)
			{
				RegisterPrefab(scene, prefab);
			}
			PrepareNotices(scene);
			ApplyConstructionSettings();
			RegisterPrefab(scene, _prefab);
			GameObject prefab2 = scene.GetPrefab("Hammer");
			PieceTable val = ((!((Object)(object)prefab2 != (Object)null)) ? null : prefab2.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_buildPieces);
			if (!((Object)(object)val == (Object)null))
			{
				_table = val;
				val.m_pieces.RemoveAll((GameObject val2) => (Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)_prefab && ((Object)val2).name == "marlthon_questheim_board");
				if (!val.m_pieces.Contains(_prefab))
				{
					val.m_pieces.Add(_prefab);
				}
			}
		}

		private static void RegisterPrefab(ZNetScene scene, GameObject prefab)
		{
			List<GameObject> list = (((Object)(object)prefab.GetComponent<ZNetView>() != (Object)null) ? scene.m_prefabs : scene.m_nonNetViewPrefabs);
			list.RemoveAll((GameObject existing) => (Object)(object)existing != (Object)null && (Object)(object)existing != (Object)(object)prefab && ((Object)existing).name == ((Object)prefab).name);
			if (!list.Contains(prefab))
			{
				list.Add(prefab);
			}
			NamedPrefabs.Invoke(scene)[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = prefab;
		}

		internal static void ApplyConstructionSettings()
		{
			if (!((Object)(object)_prefab == (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null))
			{
				Piece component = _prefab.GetComponent<Piece>();
				if (!Costs.Apply(component, Plugin.BoardCost.Value, Plugin.Log) && (component.m_resources == null || component.m_resources.Length == 0))
				{
					Costs.Apply(component, ((ConfigEntryBase)Plugin.BoardCost).DefaultValue as string, Plugin.Log);
				}
				if (!Costs.ApplyStation(component, Plugin.BoardStation.Value, Plugin.Log) && (Object)(object)component.m_craftingStation == (Object)null)
				{
					Costs.ApplyStation(component, ((ConfigEntryBase)Plugin.BoardStation).DefaultValue as string, Plugin.Log);
				}
			}
		}

		internal static void Unload()
		{
			if ((Object)(object)_table != (Object)null)
			{
				_table.m_pieces.Remove(_prefab);
			}
		}

		private static void PrepareNotices(ZNetScene scene)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_prefab == (Object)null) && !_prepared)
			{
				Material iron = MaterialOf(scene, "Iron") ?? MaterialOf(scene, "Bronze");
				AddNotices(_prefab.transform, scene, iron);
				Transform val = _prefab.transform.Find("chest_anchor");
				GameObject val2 = new GameObject("questheim_pouch");
				val2.transform.SetParent(val, false);
				val2.transform.localPosition = new Vector3(0f, 0.25f, -0.2f);
				PouchModel.Make(val2.transform, scene);
				val2.SetActive(false);
				_prefab.AddComponent<NoticeBoard>();
				_prepared = true;
			}
		}

		private static void AddNotices(Transform board, ZNetScene scene, Material iron)
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Expected O, but got Unknown
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			Mesh sharedMesh = QuadMesh();
			BoxCollider component = ((Component)board).GetComponent<BoxCollider>();
			Vector3 localPosition2 = default(Vector3);
			for (int i = 0; i < 10; i++)
			{
				Transform val = board.Find("notice_" + i);
				BoxCollider val2 = ((Component)val).gameObject.AddComponent<BoxCollider>();
				float num = -0.015f;
				if ((Object)(object)component != (Object)null)
				{
					Vector3 localPosition = val.localPosition;
					localPosition.z = component.center.z - component.size.z * 0.5f;
					float z = val.InverseTransformPoint(board.TransformPoint(localPosition)).z;
					num = Mathf.Min(num, z - 0.01f);
				}
				val2.center = new Vector3(0f, 0f, (num + 0.005f) * 0.5f);
				val2.size = new Vector3(0.33f, 0.42f, 0.005f - num);
				((Component)val).gameObject.AddComponent<NoticePaper>();
				GameObject val3 = new GameObject("paper");
				val3.transform.SetParent(val, false);
				val3.transform.localScale = new Vector3(0.33f, 0.42f, 1f);
				val3.AddComponent<MeshFilter>().sharedMesh = sharedMesh;
				((Renderer)val3.AddComponent<MeshRenderer>()).sharedMaterial = Paper.For(i switch
				{
					0 => Kind.Trophy, 
					2 => Kind.Kill, 
					_ => (Kind)(i % 4), 
				}, scene);
				((Vector3)(ref localPosition2))..ctor(0f, 0.16499999f, 0f);
				GameObject val4 = new GameObject("pin_nail");
				val4.transform.SetParent(val, false);
				if ((Object)(object)iron != (Object)null)
				{
					GameObject val5 = GameObject.CreatePrimitive((PrimitiveType)2);
					Object.DestroyImmediate((Object)(object)val5.GetComponent<Collider>());
					((Object)val5).name = "nail";
					val5.transform.SetParent(val4.transform, false);
					val5.transform.localPosition = localPosition2;
					val5.transform.localRotation = Quaternion.Euler(90f, 0f, 0f);
					val5.transform.localScale = new Vector3(0.022f, 0.012f, 0.022f);
					((Renderer)val5.GetComponent<MeshRenderer>()).sharedMaterial = iron;
				}
				((Component)val).gameObject.SetActive(false);
			}
		}

		private static Mesh QuadMesh()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: 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)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			Mesh val = new Mesh
			{
				name = "questheim_quad"
			};
			val.vertices = (Vector3[])(object)new Vector3[4]
			{
				new Vector3(-0.5f, -0.5f, 0f),
				new Vector3(0.5f, -0.5f, 0f),
				new Vector3(-0.5f, 0.5f, 0f),
				new Vector3(0.5f, 0.5f, 0f)
			};
			val.uv = (Vector2[])(object)new Vector2[4]
			{
				new Vector2(0f, 0f),
				new Vector2(1f, 0f),
				new Vector2(0f, 1f),
				new Vector2(1f, 1f)
			};
			val.triangles = new int[6] { 0, 2, 1, 2, 3, 1 };
			val.RecalculateNormals();
			val.RecalculateTangents();
			val.RecalculateBounds();
			return val;
		}

		private static Material MaterialOf(ZNetScene scene, string itemPrefab)
		{
			GameObject prefab = scene.GetPrefab(itemPrefab);
			MeshRenderer val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponentInChildren<MeshRenderer>(true) : null);
			return ((Object)(object)val != (Object)null) ? ((Renderer)val).sharedMaterial : null;
		}
	}
	internal static class Costs
	{
		public const string Help = "Item prefab names and amounts, separated by commas, like FineWood:12, BronzeNails:30.";

		public const string StationHelp = "The crafting station's prefab name (piece_workbench, forge, piece_stonecutter, piece_artisanstation, blackforge, piece_magetable), or none.";

		public static Requirement[] Parse(string text, List<string> problems)
		{
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Expected O, but got Unknown
			problems = problems ?? new List<string>();
			int count = problems.Count;
			List<Requirement> list = new List<Requirement>();
			string[] array = (text ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				if (text3.Length == 0)
				{
					continue;
				}
				string[] array2 = (from b in text3.Split(new char[1] { ':' })
					select b.Trim()).ToArray();
				if (array2.Length < 2 || array2.Length > 3 || !Whole(array2[1], out var value) || value < 1)
				{
					problems.Add("\"" + text3 + "\" isn't Name:amount");
					continue;
				}
				bool result = true;
				if (array2.Length == 3 && !bool.TryParse(array2[2], out result))
				{
					problems.Add("\"" + text3 + "\": the last part isn't true or false");
					continue;
				}
				ItemDrop item = FindItem(array2[0]);
				if ((Object)(object)item == (Object)null)
				{
					problems.Add("no item called \"" + array2[0] + "\"");
					continue;
				}
				if (list.Any((Requirement r) => (Object)(object)r.m_resItem == (Object)(object)item))
				{
					problems.Add(array2[0] + " is listed twice");
					continue;
				}
				list.Add(new Requirement
				{
					m_resItem = item,
					m_amount = value,
					m_recover = result
				});
			}
			if (list.Count == 0 && problems.Count == count)
			{
				problems.Add("it's empty (nothing would be free: write at least one item)");
			}
			return (problems.Count == count) ? list.ToArray() : null;
		}

		private static bool Whole(string text, out int value)
		{
			return int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out value);
		}

		public static ItemDrop FindItem(string name)
		{
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(name) : null);
			if ((Object)(object)val == (Object)null && (Object)(object)ObjectDB.instance != (Object)null)
			{
				val = ObjectDB.instance.GetItemPrefab(name);
			}
			return ((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null;
		}

		public static CraftingStation FindStation(string name, out bool found)
		{
			name = (name ?? "").Trim();
			found = true;
			if (name.Length == 0 || name.Equals("none", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(name) : null);
			CraftingStation val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<CraftingStation>() : null);
			found = (Object)(object)val2 != (Object)null;
			return val2;
		}

		public static bool Apply(Piece piece, string text, ManualLogSource log)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return false;
			}
			List<string> problems = new List<string>();
			Requirement[] array = Parse(text, problems);
			Report(log, ((Object)piece).name, problems);
			if (array == null)
			{
				return false;
			}
			piece.m_resources = array;
			return true;
		}

		public static bool ApplyStation(Piece piece, string text, ManualLogSource log)
		{
			if ((Object)(object)piece == (Object)null)
			{
				return false;
			}
			bool found;
			CraftingStation craftingStation = FindStation((text ?? "").Split(new char[1] { ':' })[0], out found);
			if (!found)
			{
				if (log != null)
				{
					log.LogWarning((object)(((Object)piece).name + ": no crafting station called \"" + text + "\", it keeps its station"));
				}
				return false;
			}
			piece.m_craftingStation = craftingStation;
			return true;
		}

		private static void Report(ManualLogSource log, string what, List<string> problems)
		{
			if (problems.Count > 0 && log != null)
			{
				log.LogWarning((object)(what + "'s cost setting has a mistake, so it keeps its last cost: " + string.Join("; ", problems.ToArray())));
			}
		}
	}
	internal enum NoticeRemoval
	{
		PerPlayer,
		Everyone
	}
	internal sealed class NoticeBoard : MonoBehaviour
	{
		private sealed class SharedClaim
		{
			public NoticeBoard Board;

			public Player Player;

			public Contract Offer;

			public string Token;

			public long Owner;

			public float Expires;
		}

		public const int Slots = 10;

		private const string KeyNotices = "QuestHeim.notices";

		private const string KeyDay = "QuestHeim.day";

		private const string KeyFirst = "QuestHeim.first";

		private const string KeyCustomRevision = "QuestHeim.customRevision";

		private const string ClaimRpc = "QuestHeim_ClaimSharedNotice";

		private const string ClaimResultRpc = "QuestHeim_SharedNoticeResult";

		private static SharedClaim _pendingClaim;

		private ZNetView _view;

		private GameObject[] _slots;

		private MeshRenderer[] _papers;

		private GameObject _pouch;

		private Container _box;

		private string _shown;

		private readonly Contract[] _sharedOffers = new Contract[10];

		private readonly Contract[] _personalOffers = new Contract[10];

		private int _personalTier = -1;

		private Player _shownPlayer;

		private int _shownDay = -1;

		private int _claimedSlots;

		private bool _shownPersonal;

		private float _nextShow;

		private float _nextPost;

		internal static readonly List<NoticeBoard> Loaded = new List<NoticeBoard>();

		private unsafe string ClaimKey
		{
			get
			{
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				string text = ZNet.instance.GetWorldUID().ToString();
				ZDOID uid = _view.GetZDO().m_uid;
				return "QuestHeim.claims." + text + "." + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
			}
		}

		private void Awake()
		{
			_view = ((Component)this).GetComponent<ZNetView>();
			_slots = (GameObject[])(object)new GameObject[10];
			_papers = (MeshRenderer[])(object)new MeshRenderer[10];
			for (int i = 0; i < 10; i++)
			{
				Transform val = ((Component)this).transform.Find("notice_" + i);
				_slots[i] = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null);
				MeshRenderer[] papers = _papers;
				int num = i;
				object obj;
				if (!((Object)(object)val != (Object)null))
				{
					obj = null;
				}
				else
				{
					Transform obj2 = val.Find("paper");
					obj = ((obj2 != null) ? ((Component)obj2).GetComponent<MeshRenderer>() : null);
				}
				papers[num] = (MeshRenderer)obj;
			}
			Transform obj3 = ((Component)this).transform.Find("chest_anchor/questheim_pouch");
			_pouch = ((obj3 != null) ? ((Component)obj3).gameObject : null);
			_box = ((Component)this).GetComponentInChildren<Container>(true);
			if (!((Object)(object)_view == (Object)null) && _view.IsValid())
			{
				Loaded.Add(this);
				_view.Register<int, string, string>("QuestHeim_ClaimSharedNotice", (Action<long, int, string, string>)ClaimSharedNotice);
				_view.Register<string, bool>("QuestHeim_SharedNoticeResult", (Action<long, string, bool>)ReceiveSharedNotice);
				Show(force: true);
			}
		}

		private void OnDestroy()
		{
			Loaded.Remove(this);
			if ((Object)(object)_pendingClaim?.Board == (Object)(object)this)
			{
				_pendingClaim = null;
			}
		}

		private void Update()
		{
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return;
			}
			float time = Time.time;
			if (time >= _nextPost && _view.IsOwner())
			{
				_nextPost = time + 2f;
				try
				{
					Post();
					ClearPouch();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Posting notices: " + ex.Message));
				}
			}
			if (time >= _nextShow)
			{
				_nextShow = time + 0.5f;
				Show(force: false);
			}
		}

		private string[] Read()
		{
			string[] array = new string[10];
			string[] array2 = (_view.GetZDO().GetString("QuestHeim.notices", "") ?? "").Split(new char[1] { ';' });
			for (int i = 0; i < 10; i++)
			{
				array[i] = ((i < array2.Length) ? array2[i] : "");
			}
			return array;
		}

		private void Write(string[] slots)
		{
			_view.GetZDO().Set("QuestHeim.notices", string.Join(";", slots));
		}

		public int Count()
		{
			return _sharedOffers.Where((Contract c, int slot) => c != null && (_claimedSlots & (1 << slot)) == 0).Count();
		}

		public Contract ContractAt(int slot)
		{
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || slot < 0 || slot >= 10 || (_claimedSlots & (1 << slot)) != 0)
			{
				return null;
			}
			Contract contract = _sharedOffers[slot];
			Player localPlayer = Player.m_localPlayer;
			if (contract == null || (Object)(object)localPlayer == (Object)null || Plugin.Progression.Value == BossProgression.Global)
			{
				return contract;
			}
			int num = Contracts.PlayerTier(localPlayer);
			if (num != _personalTier)
			{
				Array.Clear(_personalOffers, 0, 10);
				_personalTier = num;
			}
			return _personalOffers[slot] ?? (_personalOffers[slot] = Contracts.Make(new Random(StringExtensionMethods.GetStableHashCode(contract.Id)), -1, num));
		}

		public static void RedrawAll()
		{
			foreach (NoticeBoard item in Loaded)
			{
				if ((Object)(object)item != (Object)null && (Object)(object)item._view != (Object)null && item._view.IsValid())
				{
					item.Show(force: true);
				}
			}
		}

		internal static void CustomQuestsChanged()
		{
			foreach (NoticeBoard item in Loaded)
			{
				if ((Object)(object)item != (Object)null)
				{
					Array.Clear(item._personalOffers, 0, 10);
				}
			}
		}

		private void Show(bool force)
		{
			if ((Object)(object)_pouch != (Object)null)
			{
				_pouch.SetActive(!string.IsNullOrEmpty(_view.GetZDO().GetString("QuestHeim.pouch", "")));
			}
			string text = _view.GetZDO().GetString("QuestHeim.notices", "");
			int num = _view.GetZDO().GetInt("QuestHeim.day", -1);
			Player localPlayer = Player.m_localPlayer;
			bool flag = Plugin.Progression.Value == BossProgression.PerPlayer;
			bool flag2 = text != _shown;
			bool flag3 = (Object)(object)_shownPlayer != (Object)(object)localPlayer || _shownDay != num;
			if (!force && !flag2 && !flag3 && flag == _shownPersonal)
			{
				return;
			}
			if (flag2)
			{
				string[] array = Read();
				for (int i = 0; i < 10; i++)
				{
					_sharedOffers[i] = Contract.Read(array[i]);
				}
				Array.Clear(_personalOffers, 0, 10);
				_shown = text;
			}
			if (flag3)
			{
				_claimedSlots = 0;
				string text2 = default(string);
				if ((Object)(object)localPlayer != (Object)null && localPlayer.TryGetUniqueKeyValue(ClaimKey, ref text2))
				{
					string[] array2 = text2.Split(new char[1] { ':' });
					if (array2.Length == 2 && int.TryParse(array2[0], out var result) && result == num)
					{
						int.TryParse(array2[1], out _claimedSlots);
					}
				}
				_shownPlayer = localPlayer;
				_shownDay = num;
			}
			_shownPersonal = flag;
			for (int j = 0; j < 10; j++)
			{
				Contract contract = _sharedOffers[j];
				bool flag4 = contract != null && (_claimedSlots & (1 << j)) == 0;
				if ((Object)(object)_slots[j] != (Object)null)
				{
					_slots[j].SetActive(flag4);
				}
				if (flag4 && !((Object)(object)_papers[j] == (Object)null))
				{
					Material val = ((!flag && Plugin.Illustrated.Value) ? Sketch.For(contract, ZNetScene.instance, Plugin.NoticeHeading.Value) : null);
					val = val ?? Paper.For(flag ? Kind.Gather : contract.Kind, ZNetScene.instance);
					if ((Object)(object)val != (Object)null)
					{
						((Renderer)_papers[j]).sharedMaterial = val;
					}
				}
			}
		}

		private void ClearPouch()
		{
			string text = _view.GetZDO().GetString("QuestHeim.pouch", "");
			if (!string.IsNullOrEmpty(text) && !((Object)(object)_box == (Object)null))
			{
				string[] names = text.Split(new char[1] { ',' });
				if (!_box.GetInventory().GetAllItems().Any((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && names.Contains(((Object)i.m_dropPrefab).name)))
				{
					_view.GetZDO().Set("QuestHeim.pouch", "");
				}
			}
		}

		private void Post()
		{
			if ((Object)(object)EnvMan.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || !HasCurrentQuestConfig())
			{
				return;
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			int num = EnvMan.instance.GetDay(timeSeconds);
			if (timeSeconds < EnvMan.instance.GetMorningStartSec(num))
			{
				num--;
			}
			TakeDownOrphans();
			int num2 = _view.GetZDO().GetInt("QuestHeim.day", -1);
			if (num2 == num)
			{
				RefreshCustomOffers();
				return;
			}
			if (num2 < 0 || num < num2)
			{
				_view.GetZDO().Set("QuestHeim.day", num);
				if (num2 < 0)
				{
					_view.GetZDO().Set("QuestHeim.first", 1);
				}
				return;
			}
			Random random = new Random();
			int num4;
			if (_view.GetZDO().GetInt("QuestHeim.first", 0) == 1)
			{
				int value = Plugin.FirstDawnLeast.Value;
				int num3 = Math.Max(value, Plugin.FirstDawnMost.Value);
				num4 = random.Next(value, num3 + 1);
				_view.GetZDO().Set("QuestHeim.first", 0);
			}
			else
			{
				num4 = Read().Count((string s) => !string.IsNullOrEmpty(s));
				for (int num5 = 0; num5 < Math.Min(num - num2, 5); num5++)
				{
					num4 += DawnCount(random);
				}
			}
			string[] slots = new string[10];
			Fill(slots, num4, random);
			Write(slots);
			_view.GetZDO().Set("QuestHeim.day", num);
			_view.GetZDO().Set("QuestHeim.customRevision", CustomQuests.Revision);
		}

		private static bool HasCurrentQuestConfig()
		{
			return ZNet.instance.IsServer() || Plugin.Synced.TextFromServer("custom-quests");
		}

		private void RefreshCustomOffers()
		{
			ZDO zDO = _view.GetZDO();
			int revision = CustomQuests.Revision;
			int num = zDO.GetInt("QuestHeim.customRevision", int.MinValue);
			if (num == revision)
			{
				return;
			}
			if (num != int.MinValue)
			{
				string[] array = Read();
				Random rnd = new Random();
				for (int i = 0; i < 10; i++)
				{
					if (!string.IsNullOrEmpty(array[i]))
					{
						array[i] = Contracts.Make(rnd)?.Write() ?? array[i];
					}
				}
				Write(array);
			}
			zDO.Set("QuestHeim.customRevision", revision);
		}

		public int PostNow(int count, IList<Kind> kinds, bool clear)
		{
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner())
			{
				return 0;
			}
			string[] slots = (clear ? new string[10] : Read());
			HashSet<int> hashSet = new HashSet<int>(from i in Enumerable.Range(0, 10)
				where !string.IsNullOrEmpty(slots[i])
				select i);
			Random rnd = new Random();
			int num = Fill(slots, count, rnd);
			int num2 = 0;
			for (int num3 = 0; num3 < 10; num3++)
			{
				if (hashSet.Contains(num3))
				{
					continue;
				}
				Contract contract = Contract.Read(slots[num3]);
				if (contract == null)
				{
					continue;
				}
				if (num2 < kinds.Count)
				{
					Kind kind = kinds[num2++];
					for (int num4 = 0; num4 < 200; num4++)
					{
						if (contract.Kind == kind)
						{
							break;
						}
						Contract contract2 = Contracts.Make(rnd);
						if (contract2 != null)
						{
							contract = contract2;
						}
					}
				}
				slots[num3] = contract.Write();
			}
			if (clear)
			{
				Write(slots);
				return num;
			}
			if (num > 0)
			{
				Write(slots);
			}
			return num;
		}

		private void TakeDownOrphans()
		{
			string[] array = Read();
			bool flag = false;
			for (int i = 0; i < array.Length; i++)
			{
				Contract contract = Contract.Read(array[i]);
				if (contract != null && !Contracts.Known(contract.Target))
				{
					array[i] = "";
					flag = true;
				}
			}
			if (flag)
			{
				Write(array);
			}
		}

		private static int DawnCount(Random rnd)
		{
			int num = 1;
			string[] array = (Plugin.MoreChances.Value ?? "").Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (!float.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || rnd.NextDouble() * 100.0 >= (double)result)
				{
					break;
				}
				num++;
			}
			return num;
		}

		private static int Fill(string[] slots, int count, Random rnd)
		{
			int num = 0;
			for (int i = 0; i < count; i++)
			{
				if (slots.Count((string x) => !string.IsNullOrEmpty(x)) >= Plugin.MaxNotices.Value)
				{
					break;
				}
				int[] array = (from num2 in Enumerable.Range(0, 10)
					where string.IsNullOrEmpty(slots[num2])
					select num2).ToArray();
				if (array.Length == 0)
				{
					break;
				}
				Contract contract = Contracts.Make(rnd);
				if (contract == null)
				{
					break;
				}
				slots[array[rnd.Next(array.Length)]] = contract.Write();
				num++;
			}
			return num;
		}

		public void RequestTake(int slot)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)_view == (Object)null || !_view.IsValid() || (_pendingClaim != null && Time.unscaledTime < _pendingClaim.Expires))
			{
				return;
			}
			_pendingClaim = null;
			Show(force: false);
			Contract contract = ContractAt(slot);
			if (contract == null)
			{
				return;
			}
			if (!QuestProgress.CanTake(localPlayer))
			{
				((Character)localPlayer).Message((MessageType)2, Translations.T("questheim_too_many", Plugin.MaxActiveQuests.Value), 0, (Sprite)null, false);
			}
			else if ((Object)(object)NoticeItem.Prefab == (Object)null || !((Humanoid)localPlayer).GetInventory().HaveEmptySlot())
			{
				((Character)localPlayer).Message((MessageType)2, Translations.T("questheim_bag_full"), 0, (Sprite)null, false);
			}
			else if (Plugin.RemoveNotice.Value == NoticeRemoval.Everyone)
			{
				long owner = _view.GetZDO().GetOwner();
				if (owner != 0)
				{
					SharedClaim sharedClaim = (_pendingClaim = new SharedClaim
					{
						Board = this,
						Player = localPlayer,
						Offer = contract,
						Owner = owner,
						Token = Guid.NewGuid().ToString("N"),
						Expires = Time.unscaledTime + 8f
					});
					_view.InvokeRPC(owner, "QuestHeim_ClaimSharedNotice", new object[3]
					{
						slot,
						_sharedOffers[slot].Id,
						sharedClaim.Token
					});
				}
			}
			else if (!QuestProgress.Accept(localPlayer, contract))
			{
				((Character)localPlayer).Message((MessageType)2, Translations.T("questheim_bag_full"), 0, (Sprite)null, false);
			}
			else
			{
				_claimedSlots |= 1 << slot;
				localPlayer.AddUniqueKeyValue(ClaimKey, _shownDay.ToString(CultureInfo.InvariantCulture) + ":" + _claimedSlots.ToString(CultureInfo.InvariantCulture));
				Show(force: true);
				((Character)localPlayer).Message((MessageType)1, Translations.T("questheim_took", contract.Line()), 0, (Sprite)null, false);
			}
		}

		private void ClaimSharedNotice(long sender, int slot, string offerId, string token)
		{
			if (!_view.IsValid() || !_view.IsOwner())
			{
				return;
			}
			bool flag = false;
			if (Plugin.RemoveNotice.Value == NoticeRemoval.Everyone && slot >= 0 && slot < 10)
			{
				string[] array = Read();
				Contract contract = Contract.Read(array[slot]);
				if (contract != null && contract.Id == offerId)
				{
					array[slot] = "";
					Write(array);
					flag = true;
				}
			}
			_view.InvokeRPC(sender, "QuestHeim_SharedNoticeResult", new object[2] { token, flag });
		}

		private void ReceiveSharedNotice(long sender, string token, bool granted)
		{
			SharedClaim pendingClaim = _pendingClaim;
			if (pendingClaim == null || (Object)(object)pendingClaim.Board != (Object)(object)this || pendingClaim.Owner != sender || pendingClaim.Token != token)
			{
				return;
			}
			_pendingClaim = null;
			if (!((Object)(object)pendingClaim.Player == (Object)null) && !((Object)(object)pendingClaim.Player != (Object)(object)Player.m_localPlayer))
			{
				if (!granted)
				{
					((Character)pendingClaim.Player).Message((MessageType)2, Translations.T("questheim_gone"), 0, (Sprite)null, false);
				}
				else if (QuestProgress.Accept(pendingClaim.Player, pendingClaim.Offer, dropIfFull: true))
				{
					((Character)pendingClaim.Player).Message((MessageType)1, Translations.T("questheim_took", pendingClaim.Offer.Line()), 0, (Sprite)null, false);
				}
				else
				{
					((Character)pendingClaim.Player).Message((MessageType)2, Translations.T("questheim_bag_full"), 0, (Sprite)null, false);
				}
			}
		}
	}
	internal sealed class NoticePaper : MonoBehaviour, Hoverable, Interactable
	{
		private int _slot = -1;

		private NoticeBoard _board;

		private void Awake()
		{
			int.TryParse(((Object)this).name.Substring(((Object)this).name.LastIndexOf('_') + 1), out _slot);
			_board = ((Component)this).GetComponentInParent<NoticeBoard>();
		}

		public string GetHoverText()
		{
			Contract contract = (((Object)(object)_board != (Object)null) ? _board.ContractAt(_slot) : null);
			if (contract == null)
			{
				return "";
			}
			return Localization.instance.Localize(Contracts.Describe(contract) + "\n[<color=yellow><b>$KEY_Use</b></color>] " + Translations.T("questheim_take"));
		}

		public string GetHoverName()
		{
			return Translations.T("questheim_notice_name");
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (hold || (Object)(object)_board == (Object)null || (Object)(object)user != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			_board.RequestTake(_slot);
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}
	}
	internal sealed class BoardFace : MonoBehaviour, Hoverable, Interactable
	{
		public string GetHoverText()
		{
			NoticeBoard componentInParent = ((Component)this).GetComponentInParent<NoticeBoard>();
			string text = (((Object)(object)componentInParent != (Object)null && componentInParent.Count() == 0) ? Translations.T("questheim_board_empty") : Translations.T("questheim_board_hint"));
			string text2 = ((!ZInput.IsNonClassicFunctionality() || !ZInput.IsGamepadActive()) ? "$KEY_AltPlace" : "$KEY_AltKeys");
			return Localization.instance.Localize("$questheim_board\n<size=80%><color=#bdb7a9>" + text + "</color></size>\n[<color=yellow><b>" + text2 + " + $KEY_Use</b></color>] " + Translations.T("questheim_abandon"));
		}

		public string GetHoverName()
		{
			return Localization.instance.Localize("$questheim_board");
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			int result;
			if (!hold && alt)
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				if (val != null && (Object)(object)val == (Object)(object)Player.m_localPlayer)
				{
					result = (QuestProgress.AbandonOldest(val) ? 1 : 0);
					goto IL_0028;
				}
			}
			result = 0;
			goto IL_0028;
			IL_0028:
			return (byte)result != 0;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}
	}
	internal static class NoticeItem
	{
		[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(bool),
			typeof(float),
			typeof(int),
			typeof(bool)
		})]
		private static class ItemData_GetTooltip
		{
			private static void Postfix(ItemData item, ref string __result)
			{
				try
				{
					Contract contract = ContractOf(item);
					if (contract != null)
					{
						__result = Contracts.Describe(contract) + "\n" + Status(item, contract) + "\n\n" + __result;
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Notice tooltip: " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(ItemData), "GetIcon")]
		private static class ItemData_GetIcon
		{
			private static void Postfix(ItemData __instance, ref Sprite __result)
			{
				if (Is(__instance))
				{
					Sprite[] icons = __instance.m_shared.m_icons;
					Contract contract = ContractOf(__instance);
					if (contract != null && icons != null && icons.Length >= 8)
					{
						bool flag = IsDone(__instance);
						Sprite val = (Plugin.Illustrated.Value ? Sketch.IconFor(contract, ZNetScene.instance, Plugin.NoticeHeading.Value, flag) : null);
						__result = val ?? icons[(int)(contract.Kind + (flag ? 4 : 0))] ?? __result;
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetScene_Awake
		{
			private static void Postfix(ZNetScene __instance)
			{
				Guarded(delegate
				{
					Register(__instance, ObjectDB.instance);
				});
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDB_Awake
		{
			private static void Postfix(ObjectDB __instance)
			{
				Guarded(delegate
				{
					Register(ZNetScene.instance, __instance);
				});
			}
		}

		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static class ObjectDB_CopyOtherDB
		{
			private static void Postfix(ObjectDB __instance)
			{
				Guarded(delegate
				{
					Register(ZNetScene.instance, __instance);
				});
			}
		}

		public const string PrefabName = "marlthon_questheim_notice";

		public const string ItemName = "$questheim_notice";

		private const string KeyContract = "QuestHeim.contract";

		internal static GameObject Prefab;

		private static GameObject _holder;

		private const int Kinds = 4;

		private const string KeyDone = "QuestHeim.done";

		private static readonly FieldRef<InventoryGui, Container> OpenChest = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

		public static void Register(ZNetScene scene, ObjectDB db)
		{
			if (!((Object)(object)scene == (Object)null) || !((Object)(object)db == (Object)null))
			{
				scene = (((Object)(object)scene != (Object)null) ? scene : ZNetScene.instance);
				if ((Object)(object)Prefab == (Object)null && (Object)(object)scene != (Object)null)
				{
					Prefab = Build(scene);
				}
				if (!((Object)(object)Prefab == (Object)null))
				{
					Prefab.GetComponent<ItemDrop>().m_itemData.m_dropPrefab = Prefab;
					ItemReload.Replace(scene, db, (IEnumerable<GameObject>)(object)new GameObject[1] { Prefab });
				}
			}
		}

		public static void Forget()
		{
			Prefab = null;
			_holder = null;
		}

		public static void MarkDone(ItemData item, Contract c)
		{
			item.m_customData["QuestHeim.done"] = "1";
		}

		private static bool IsDone(ItemData item)
		{
			string value;
			return item.m_customData.TryGetValue("QuestHeim.done", out value) && value == "1";
		}

		public static bool Is(ItemData item)
		{
			return item != null && item.m_shared.m_name == "$questheim_notice";
		}

		public static Contract ContractOf(ItemData item)
		{
			string value;
			return (Is(item) && item.m_customData.TryGetValue("QuestHeim.contract", out value)) ? Contract.Read(value) : null;
		}

		public static bool Give(Player player, Contract contract, bool dropIfFull)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Prefab == (Object)null || (Object)(object)player == (Object)null)
			{
				return false;
			}
			ItemData val = Prefab.GetComponent<ItemDrop>().m_itemData.Clone();
			val.m_dropPrefab = Prefab;
			val.m_stack = 1;
			val.m_customData["QuestHeim.contract"] = contract.Write();
			if (((Humanoid)player).GetInventory().AddItem(val))
			{
				return true;
			}
			if (!dropIfFull)
			{
				return false;
			}
			Transform transform = ((Component)player).transform;
			if ((Object)(object)ItemDrop.DropItem(val, 1, transform.position + transform.forward * 0.6f + Vector3.up, transform.rotation) == (Object)null)
			{
				return false;
			}
			((Character)player).Message((MessageType)2, Translations.T("questheim_dropped"), 0, (Sprite)null, false);
			return true;
		}

		private static GameObject Build(ZNetScene scene)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab = scene.GetPrefab("LeatherScraps");
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogWarning((object)"The game has no leather scraps to copy: no notices this session");
				return null;
			}
			_holder = new GameObject("QuestHeim_items");
			_holder.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)_holder);
			GameObject val = Object.Instantiate<GameObject>(prefab, _holder.transform);
			((Object)val).name = "marlthon_questheim_notice";
			ItemDrop component = val.GetComponent<ItemDrop>();
			SharedData shared = component.m_itemData.m_shared;
			shared.m_name = "$questheim_notice";
			shared.m_description = "$questheim_notice_desc";
			shared.m_itemType = (ItemType)16;
			shared.m_maxStackSize = 1;
			shared.m_autoStack = false;
			shared.m_value = 0;
			shared.m_weight = 0.1f;
			shared.m_teleportable = true;
			try
			{
				Dress(val, scene);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Notice look: " + ex.Message));
			}
			Sprite[] array = (Sprite[])(object)new Sprite[8];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = Paper.IconFor((Kind)(i % 4), scene, i >= 4);
			}
			if (array.All((Sprite val2) => (Object)(object)val2 != (Object)null))
			{
				shared.m_icons = array;
			}
			component.m_itemData.m_dropPrefab = val;
			return val;
		}

		private static void Dress(GameObject go, ZNetScene scene)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				val.enabled = false;
			}
			Material val2 = Paper.For(Kind.Kill, scene);
			if (!((Object)(object)val2 == (Object)null))
			{
				GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)3);
				Object.DestroyImmediate((Object)(object)val3.GetComponent<Collider>());
				((Object)val3).name = "questheim_sheet";
				val3.transform.SetParent(go.transform, false);
				val3.transform.localScale = new Vector3(0.33f, 0.004f, 0.42f);
				((Renderer)val3.GetComponent<MeshRenderer>()).sharedMaterial = val2;
			}
		}

		private static string Status(ItemData item, Contract c)
		{
			if (c.Kind == Kind.Kill)
			{
				int num = Kills.Progress(item);
				return (num >= c.Count) ? ("<color=#99e68c>" + Translations.T("questheim_status_done") + "</color>") : Translations.T("questheim_status_kills", num, c.Count);
			}
			Player localPlayer = Player.m_localPlayer;
			Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null);
			object obj;
			if (!((Object)(object)InventoryGui.instance != (Object)null))
			{
				obj = null;
			}
			else
			{
				Container obj2 = OpenChest.Invoke(InventoryGui.instance);
				obj = ((obj2 != null) ? obj2.GetInventory() : null);
			}
			Inventory val2 = (Inventory)obj;
			string text;
			if (val != null && val.ContainsItem(item))
			{
				text = Translations.T("questheim_status_have", HandIn.CountOf(val, c.Target), c.Count);
			}
			else
			{
				if (val2 == null || !val2.ContainsItem(item))
				{
					return Translations.T("questheim_status_needs", c.Count);
				}
				text = Translations.T("questheim_status_box", HandIn.CountOf(val2, c.Target), c.Count);
			}
			int num2 = HandIn.CountOf((val != null && val.ContainsItem(item)) ? val : val2, c.Target);
			return (num2 >= c.Count) ? ("<color=#99e68c>" + text + "</color>") : text;
		}

		private static void Guarded(Action register)
		{
			try
			{
				register();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Registering the notice failed: " + ex));
			}
		}
	}
	internal static class Commands
	{
		private const string Owner = "QuestHeim";

		private static readonly string[] ToolGuids = new string[2] { "com.quad.claudetools", "com.dhack.claudetools" };

		private const string Usage = "questheim boards [radius]: the QuestHeim boards near the player, which way they face, their notices and materials | questheim names: prefab names the contract lists use that this game doesn't have, and the world's stage | questheim found: what discovery adds beyond the built-in lists (other mods' creatures, things, food, metals) | questheim spawns <creature>: its entries in the world's spawn lists | questheim sample [count] [stage]: notices as a board would write them, nothing posted | questheim sketch <kind> <prefab> <count> [runes|bounty|none]: a notice's illustrated paper and bag icon as PNGs in claude/shots | questheim post [count] [clear] [kill|gather|trophy|provisions ...]: put notices up (clear: take the others down first; kinds in place order) on the nearest board now (single player, host or devcommands)";

		private static BaseUnityPlugin _registeredWith;

		private static float _nextLook;

		internal static void Update()
		{
			if (Time.unscaledTime < _nextLook)
			{
				return;
			}
			_nextLook = Time.unscaledTime + 2f;
			BaseUnityPlugin val = null;
			string[] toolGuids = ToolGuids;
			foreach (string key in toolGuids)
			{
				if (Chainloader.PluginInfos.TryGetValue(key, out var value) && (Object)(object)value.Instance != (Object)null)
				{
					val = value.Instance;
					break;
				}
			}
			if ((Object)(object)val == (Object)null || val == _registeredWith)
			{
				return;
			}
			_registeredWith = val;
			try
			{
				MethodInfo method = ((object)val).GetType().GetMethod("RegisterCommand", BindingFlags.Static | BindingFlags.Public);
				if (!(method == null))
				{
					Type type = method.GetParameters()[3].ParameterType.GetGenericArguments()[1].GetGenericArguments()[0];
					MethodInfo method2 = type.GetMethod("Parse", new Type[1] { typeof(string) });
					object obj = typeof(Commands).GetMethod("Wrap", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type).Invoke(null, new object[1] { method2 });
					method.Invoke(null, new object[4] { "QuestHeim", "questheim", "questheim boards [radius]: the QuestHeim boards near the player, which way they face, their notices and materials | questheim names: prefab names the contract lists use that this game doesn't have, and the world's stage | questheim found: what discovery adds beyond the built-in lists (other mods' creatures, things, food, metals) | questheim spawns <creature>: its entries in the world's spawn lists | questheim sample [count] [stage]: notices as a board would write them, nothing posted | questheim sketch <kind> <prefab> <count> [runes|bounty|none]: a notice's illustrated paper and bag icon as PNGs in claude/shots | questheim post [count] [clear] [kill|gather|trophy|provisions ...]: put notices up (clear: take the others down first; kinds in place order) on the nearest board now (single player, host or devcommands)", obj });
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't add the 'questheim' command to Claude Tools (" + ex.Message + ")."));
			}
		}

		private static Func<string[], Action<T>, Action<string>, IEnumerator> Wrap<T>(MethodInfo parse)
		{
			Func<string, T> toJson = (Func<string, T>)Delegate.CreateDelegate(typeof(Func<string, T>), parse);
			return (string[] args, Action<T> output, Action<string> error) => Run(args, delegate(object o)
			{
				output(toJson(JsonConvert.SerializeObject(o)));
			}, error);
		}

		internal static void Unregister()
		{
			try
			{
				((object)_registeredWith)?.GetType().GetMethod("UnregisterAll", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, new object[1] { "QuestHeim" });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't remove the 'questheim' command (" + ex.Message + ")."));
			}
			_registeredWith = null;
		}

		private static IEnumerator Run(string[] args, Action<object> output, Action<string> error)
		{
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0536: Unknown result type (might be due to invalid IL or missing references)
			//IL_0540: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Expected I4, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0dd7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "");
				if (text == "spawns" && args.Length > 2)
				{
					List<string> list = new List<string>();
					List<SpawnSystemList> list2 = new List<SpawnSystemList>();
					GameObject prefab = ZNetScene.instance.GetPrefab("_ZoneCtrl");
					SpawnSystem val = ((prefab != null) ? prefab.GetComponent<SpawnSystem>() : null);
					if ((Object)(object)val != (Object)null)
					{
						list2.AddRange(val.m_spawnLists);
					}
					foreach (SpawnSystemList item in list2)
					{
						foreach (SpawnData spawner in item.m_spawners)
						{
							if ((Object)(object)spawner.m_prefab != (Object)null && ((Object)spawner.m_prefab).name == args[2])
							{
								list.Add($"{((Object)item).name}: biome={spawner.m_biome} ({(int)spawner.m_biome}) area={spawner.m_biomeArea} enabled={spawner.m_enabled} key='{spawner.m_requiredGlobalKey}' event='{spawner.m_requiredPersistentEvent}' night={spawner.m_spawnAtNight}/{spawner.m_spawnAtDay}");
							}
						}
					}
					output(new Dictionary<string, object> { { "spawns", list } });
					return null;
				}
				if (text == "post")
				{
					if ((!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()) && (!((Object)(object)Console.instance != (Object)null) || !((Terminal)Console.instance).IsCheatsEnabled()))
					{
						error("questheim post changes the world: single player, the host, or with devcommands.");
						return null;
					}
					Player me = Player.m_localPlayer;
					if ((Object)(object)me == (Object)null)
					{
						error("Not in a world.");
						return null;
					}
					NoticeBoard noticeBoard = (from b in NoticeBoard.Loaded
						where (Object)(object)b != (Object)null
						orderby Vector3.Distance(((Component)b).transform.position, ((Component)me).transform.position)
						select b).FirstOrDefault();
					if ((Object)(object)noticeBoard == (Object)null || Vector3.Distance(((Component)noticeBoard).transform.position, ((Component)me).transform.position) > 20f)
					{
						error("No QuestHeim board within 20 m.");
						return null;
					}
					int result;
					int count = ((args.Length <= 2 || !int.TryParse(args[2], out result)) ? 1 : result);
					List<Kind> list3 = new List<Kind>();
					bool clear = false;
					for (int num = 3; num < args.Length; num++)
					{
						Kind result2;
						if (args[num].Equals("clear", StringComparison.OrdinalIgnoreCase))
						{
							clear = true;
						}
						else if (Enum.TryParse<Kind>(args[num], ignoreCase: true, out result2))
						{
							list3.Add(result2);
						}
					}
					output(new Dictionary<string, object> { 
					{
						"posted",
						noticeBoard.PostNow(count, list3, clear)
					} });
					return null;
				}
				if (text == "sketch" && args.Length > 4)
				{
					if (!Enum.TryParse<Kind>(args[2], ignoreCase: true, out var result3))
					{
						error("Kind: Kill, Gather, Trophy or Provisions.");
						return null;
					}
					int result4;
					Contract contract = new Contract
					{
						Kind = result3,
						Target = args[3],
						Count = ((!int.TryParse(args[4], out result4)) ? 1 : result4),
						Id = "sketch"
					};
					Heading result5;
					Heading heading = ((args.Length > 5 && Enum.TryParse<Heading>(args[5], ignoreCase: true, out result5)) ? result5 : Plugin.NoticeHeading.Value);
					Material val2 = Sketch.For(contract, ZNetScene.instance, heading);
					Sprite val3 = Sketch.IconFor(contract, ZNetScene.instance, heading, done: false);
					if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null)
					{
						error("No picture of " + args[3] + ".");
						return null;
					}
					string path = Path.Combine(Paths.BepInExRootPath, "claude", "shots");
					string text2 = Path.Combine(path, $"questheim_sketch_{result3}_{args[3]}_{contract.Count}_{heading}.png");
					string text3 = Path.Combine(path, $"questheim_icon_{result3}_{args[3]}_{heading}.png");
					File.WriteAllBytes(text2, ImageConversion.EncodeToPNG((Texture2D)val2.mainTexture));
					File.WriteAllBytes(text3, ImageConversion.EncodeToPNG(val3.texture));
					output(new Dictionary<string, object>
					{
						{ "paper", text2 },
						{ "icon", text3 },
						{
							"picture",
							Sketch.PictureInfo(contract, ZNetScene.instance)
						}
					});
					return null;
				}
				switch (text)
				{
				case "trophies":
				{
					List<string> list5 = new List<string>();
					List<Entry> list6 = ContractData.AllEntries().ToList();
					foreach (Entry item2 in list6.Where((Entry e) => e.Kind == Kind.Trophy))
					{
						float chance;
						Entry entry = Contracts.TrophySource(item2.Prefab, list6, out chance);
						if (entry == null)
						{
							list5.Add($"t{item2.Tier} {item2.Prefab}: no creature found, priced @{item2.Value} x{item2.Count}");
							continue;
						}
						float num4 = entry.Value / Mathf.Max(0.01f, chance);
						int num5 = Math.Max(1, Mathf.RoundToInt((float)entry.Count * chance));
						list5.Add($"t{item2.Tier} {item2.Prefab} | {entry.Prefab} @{entry.Value} x{entry.Count} drops it {chance:P0} | " + $"usual x{num5}, each worth {num4:0}, notice for x{num5} pays {(float)num5 * num4 * Contracts.Premium(Kind.Trophy):0}");
					}
					output(new Dictionary<string, object> { { "trophies", list5 } });
					return null;
				}
				case "gathers":
				{
					List<string> list8 = new List<string>();
					List<Entry> list9 = ContractData.AllEntries().ToList();
					Dictionary<string, List<Gathering.Source>> dictionary = Gathering.Sources();
					foreach (Entry g in from e in list9
						where e.Kind == Kind.Gather
						orderby e.Tier
						select e)
					{
						List<Gathering.Source> value;
						string text4 = (dictionary.TryGetValue(g.Prefab, out value) ? string.Join(", ", from x in value.OrderByDescending((Gathering.Source x) => x.Each).Take(3)
							select $"{x.Prefab}[{x.Way}] {x.Each:0.##}") : "no source found");
						bool flag = ContractData.Entries.Any((Entry e) => e.Kind == Kind.Gather && e.Prefab == g.Prefab);
						Gathering.Price(g, list9, out var usual, out var each);
						float cost;
						int most;
						string text5 = (Gathering.Easiest(g, list9, out cost, out most) ? $"one takes {cost:0.#}, most x{most}" : "no way found");
						list8.Add(string.Format("t{0} {1}{2} | was x{3} @{4} = {5:0} | {6} | ", g.Tier, g.Prefab, flag ? "" : " (found)", g.Count, g.Value, (float)g.Count * g.Value, text5) + $"now x{usual} @{each:0.#} = {(float)usual * each:0} | {text4}");
					}
					output(new Dictionary<string, object> { { "gathers", list8 } });
					return null;
				}
				case "sample":
				{
					int result7;
					int num2 = ((args.Length > 2 && int.TryParse(args[2], out result7)) ? result7 : 10);
					int result8;
					int forTier = ((args.Length > 3 && int.TryParse(args[3], out result8)) ? result8 : (-1));
					Random rnd = new Random();
					List<string> list4 = new List<string>();
					for (int num3 = 0; num3 < num2; num3++)
					{
						Contract contract2 = Contracts.Make(rnd, forTier);
						if (contract2 != null)
						{
							list4.Add($"t{contract2.Tier} {contract2.Kind} {contract2.Target} x{contract2.Count} -> {contract2.Reward}");
						}
					}
					output(new Dictionary<string, object> { { "sample", list4 } });
					return null;
				}
				case "found":
				{
					Discovery.Forget();
					List<Entry> list7 = ContractData.AllEntries().ToList();
					output(new Dictionary<string, object>
					{
						{
							"found",
							(from e in Discovery.Found
								orderby e.Tier, e.Kind
								select $"{e.Kind} t{e.Tier} {e.Prefab} x{e.Count} @{e.Value} ({Contracts.DisplayName(e.Prefab)})").ToList()
						},
						{
							"foundRewards",
							Discovery.FoundPay.Select((PayEntry p) => $"t{p.Tier} {p.Prefab} @{p.Value} ({Contracts.DisplayName(p.Prefab)})").ToList()
						},
						{
							"arrows",
							(from a in Discovery.Ammo
								orderby a.Tier
								select $"t{a.Tier} {a.Prefab} @{a.Value} ({Contracts.DisplayName(a.Prefab)})").ToList()
						},
						{
							"valuables",
							Discovery.Valuables.Select((PayEntry v) => $"{v.Prefab} @{v.Value} ({Contracts.DisplayName(v.Prefab)})").ToList()
						},
						{
							"creaturesSkipped",
							Discovery.Skipped()
						},
						{
							"builtIn",
							ContractData.Entries.Count
						},
						{ "total", list7.Count }
					});
					return null;
				}
				case "names":
					output(new Dictionary<string, object>
					{
						{
							"unknown",
							Contracts.Unknown()
						},
						{
							"worldTier",
							Contracts.WorldTier()
						}
					});
					return null;
				default:
				{
					if (text != "boards")
					{
						output(new Dictionary<string, object> { { "usage", "questheim boards [radius]: the QuestHeim boards near the player, which way they face, their notices and materials | questheim names: prefab names the contract lists use that this game doesn't have, and the world's stage | questheim found: what discovery adds beyond the built-in lists (other mods' creatures, things, food, metals) | questheim spawns <creature>: its entries in the world's spawn lists | questheim sample [count] [stage]: notices as a board would write them, nothing posted | questheim sketch <kind> <prefab> <count> [runes|bounty|none]: a notice's illustrated paper and bag icon as PNGs in claude/shots | questheim post [count] [clear] [kill|gather|trophy|provisions ...]: put notices up (clear: take the others down first; kinds in place order) on the nearest board now (single player, host or devcommands)" } });
						return null;
					}
					Player localPlayer = Player.m_localPlayer;
					if ((Object)(object)localPlayer == (Object)null)
					{
						error("Not in a world.");
						return null;
					}
					float result6;
					float radius = ((args.Length > 2 && float.TryParse(args[2], out result6)) ? result6 : 30f);
					output(new Dictionary<string, object> { 
					{
						"boards",
						Boards(((Component)localPlayer).transform.position, radius)
					} });
					break;
				}
				}
			}
			catch (Exception ex)
			{
				error(ex.Message);
			}
			return null;
		}

		private static List<object> Boards(Vector3 from, float radius)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			List<object> list = new List<object>();
			int stableHashCode = StringExtensionMethods.GetStableHashCode("marlthon_questheim_board");
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if ((Object)(object)val == (Object)null || val.GetZDO() == null || val.GetZDO().GetPrefab() != stableHashCode)
				{
					continue;
				}
				Transform transform = ((Component)val).transform;
				if (!(Vector3.Distance(transform.position, from) > radius))
				{
					Transform val2 = transform.Find("questheim_look");
					object obj;
					if (!((Object)(object)val2 != (Object)null))
					{
						obj = null;
					}
					else
					{
						Transform obj2 = val2.Find("plank");
						obj = ((obj2 != null) ? ((Component)obj2).GetComponent<MeshRenderer>() : null);
					}
					MeshRenderer r = (MeshRenderer)obj;
					object obj3;
					if (!((Object)(object)val2 != (Object)null))
					{
						obj3 = null;