Decompiled source of Renegades v0.5.0

plugins/Renegades.dll

Decompiled 10 hours ago
using System;
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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Renegades.Core;
using Sansara.Admin;
using Sansara.Clock;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Renegades")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.4.2.0")]
[assembly: AssemblyInformationalVersion("0.4.2+1f5af9d283e5f33fee05a8443556f28c1bea0429")]
[assembly: AssemblyProduct("Renegades")]
[assembly: AssemblyTitle("Renegades")]
[assembly: AssemblyVersion("0.4.2.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 Sansara.Clock
{
	public static class RealClock
	{
		private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

		public const double WorldClockLimit = 1000000000.0;

		public static double Now => (DateTime.UtcNow - Epoch).TotalSeconds;

		public static bool IsWorldTime(double t)
		{
			if (t > 0.0)
			{
				return t < 1000000000.0;
			}
			return false;
		}

		public static double FromWorld(double t, double worldNow, double realNow)
		{
			if (!IsWorldTime(t))
			{
				return t;
			}
			return realNow - (worldNow - t);
		}
	}
}
namespace Sansara.Admin
{
	internal static class AdminChannel
	{
		private const float ScanSeconds = 1f;

		private const float DenialLogSeconds = 30f;

		private static ManualLogSource _log;

		private static bool _owner;

		private static bool _announced;

		private static float _nextScan;

		private static readonly HashSet<ZRpc> Registered = new HashSet<ZRpc>();

		private static readonly Dictionary<ZRpc, float> LastDenial = new Dictionary<ZRpc, float>();

		public static void Register(string module, string[] verbs, Func<string, string, string, string> handler, ManualLogSource log)
		{
			_log = log;
			if (AdminRegistry.Shared.Register(module, verbs, handler))
			{
				log.LogWarning((object)("Админ-канал: модуль " + module + " зарегистрирован повторно — обработчик заменён."));
			}
			if (AdminRegistry.Shared.ClaimOwner(typeof(AdminChannel).Assembly.GetName().Name))
			{
				_owner = true;
			}
		}

		public static void Tick()
		{
			if (!_owner || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || Time.realtimeSinceStartup < _nextScan)
			{
				return;
			}
			_nextScan = Time.realtimeSinceStartup + 1f;
			try
			{
				Scan();
			}
			catch (Exception ex)
			{
				_log.LogError((object)("Админ-канал: ошибка: " + ex));
			}
		}

		public static void CheckFile(string guid, string logPrefix, Func<string, string, string, string> handler)
		{
			string path = Path.Combine(Paths.ConfigPath, guid + ".command");
			if (!File.Exists(path))
			{
				return;
			}
			string text;
			try
			{
				text = File.ReadAllText(path).Trim();
				File.Delete(path);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)(logPrefix + ": файл команды не прочитан: " + ex.Message));
				return;
			}
			_log.LogInfo((object)(logPrefix + ": команда «" + AdminProtocol.ForLog(text) + "»."));
			try
			{
				_log.LogInfo((object)(logPrefix + ": " + handler(text, "файл команд", "").Replace("\n", "; ")));
			}
			catch (Exception ex2)
			{
				_log.LogError((object)(logPrefix + ": ошибка команды: " + ex2));
			}
		}

		private static void Scan()
		{
			if (!_announced)
			{
				_announced = true;
				_log.LogInfo((object)("Админ-канал: владелец " + AdminRegistry.Shared.Owner + ", модули: " + string.Join(", ", AdminRegistry.Shared.Modules().ToArray()) + "."));
			}
			HashSet<ZRpc> live = new HashSet<ZRpc>();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && peer.m_rpc != null)
				{
					live.Add(peer.m_rpc);
					if (Registered.Add(peer.m_rpc))
					{
						peer.m_rpc.Register<ZPackage>("Sansara_Admin", (Action<ZRpc, ZPackage>)OnRequest);
					}
				}
			}
			Registered.RemoveWhere((ZRpc r) => !live.Contains(r));
			foreach (ZRpc item in new List<ZRpc>(LastDenial.Keys))
			{
				if (!live.Contains(item))
				{
					LastDenial.Remove(item);
				}
			}
		}

		private static void OnRequest(ZRpc rpc, ZPackage pkg)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			int num = 0;
			AdminReply adminReply;
			try
			{
				int protocol = pkg.ReadInt();
				num = pkg.ReadInt();
				string module = pkg.ReadString();
				string line = pkg.ReadString();
				adminReply = Handle(rpc, protocol, module, line);
			}
			catch (Exception ex)
			{
				ZNetPeer val = ZNet.instance.GetPeers().Find((ZNetPeer p) => p.m_rpc == rpc);
				if (val == null || !val.IsReady())
				{
					return;
				}
				LogThrottled(rpc, "Админ-канал: неверный запрос от " + AdminProtocol.ForLog(val.m_playerName) + ": " + AdminProtocol.ForLog(ex.Message));
				adminReply = new AdminReply(4, "неверный запрос");
			}
			if (adminReply != null)
			{
				ZPackage val2 = new ZPackage();
				val2.Write(1);
				val2.Write(num);
				val2.Write(adminReply.Code);
				val2.Write(AdminProtocol.Truncate(adminReply.Text));
				rpc.Invoke("Sansara_AdminReply", new object[1] { val2 });
			}
		}

		private static void LogThrottled(ZRpc rpc, string message)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (!LastDenial.TryGetValue(rpc, out var value) || realtimeSinceStartup - value >= 30f)
			{
				LastDenial[rpc] = realtimeSinceStartup;
				_log.LogWarning((object)message);
			}
		}

		private static AdminReply Handle(ZRpc rpc, int protocol, string module, string line)
		{
			ZNetPeer val = ZNet.instance.GetPeers().Find((ZNetPeer p) => p.m_rpc == rpc);
			if (val == null || !val.IsReady())
			{
				return null;
			}
			AdminReply adminReply = AdminProtocol.Check(protocol, module, line);
			if (adminReply != null)
			{
				return adminReply;
			}
			string text = AdminProtocol.ForLog(val.m_playerName);
			string text2 = AdminProtocol.ForLog(val.m_socket.GetHostName());
			string text3 = AdminProtocol.ForLog(AdminProtocol.Shown(module, line));
			bool flag = ((ZDOID)(ref val.m_characterID)).IsNone();
			if (flag || !ZNet.instance.IsAdmin(val.m_socket.GetHostName()))
			{
				LogThrottled(rpc, "Админ-канал: отказано " + text + " (" + text2 + "): " + text3);
				return new AdminReply(1, flag ? "отказано: персонаж не в мире (ещё не вошёл или погиб)" : "отказано: нужны права администратора");
			}
			Exception error;
			AdminReply adminReply2 = AdminRegistry.Shared.Dispatch(module, line, val.m_playerName, val.m_socket.GetHostName(), out error);
			if (error != null)
			{
				_log.LogError((object)("Админ-канал: " + text + " (" + text2 + ") → " + text3 + ": " + AdminProtocol.ForLog(error.Message) + Environment.NewLine + error.StackTrace));
			}
			else
			{
				_log.LogInfo((object)("Админ-канал: " + text + " (" + text2 + ") → " + text3 + ": " + AdminProtocol.ForLog(AdminProtocol.FirstLine(adminReply2.Text))));
			}
			return adminReply2;
		}
	}
	public sealed class AdminCommand
	{
		public string Verb = "";

		public readonly List<string> Args = new List<string>();

		public string Argument => string.Join(" ", Args);

		public static AdminCommand Parse(string text)
		{
			AdminCommand adminCommand = new AdminCommand();
			List<string> list = Tokens(text);
			if (list.Count == 0)
			{
				return adminCommand;
			}
			adminCommand.Verb = list[0].ToLowerInvariant();
			adminCommand.Args.AddRange(list.GetRange(1, list.Count - 1));
			return adminCommand;
		}

		public static List<string> Tokens(string text)
		{
			List<string> list = new List<string>();
			StringBuilder stringBuilder = new StringBuilder();
			bool flag = false;
			bool flag2 = false;
			string text2 = text ?? "";
			foreach (char c in text2)
			{
				if (c == '"')
				{
					flag = !flag;
					flag2 = true;
				}
				else if (!flag && char.IsWhiteSpace(c))
				{
					if (flag2)
					{
						list.Add(stringBuilder.ToString());
						stringBuilder.Clear();
						flag2 = false;
					}
				}
				else
				{
					stringBuilder.Append(c);
					flag2 = true;
				}
			}
			if (flag2)
			{
				list.Add(stringBuilder.ToString());
			}
			return list;
		}

		public static bool TryId(string argument, out string id)
		{
			string text = (argument ?? "").Trim();
			id = ((text.Length > 1 && text[0] == '@') ? text.Substring(1).Trim() : null);
			if (id == "")
			{
				id = null;
			}
			return id != null;
		}
	}
	public sealed class AdminReply
	{
		public readonly int Code;

		public readonly string Text;

		public AdminReply(int code, string text)
		{
			Code = code;
			Text = text ?? "";
		}
	}
	public static class AdminProtocol
	{
		public const int Version = 1;

		public const string RequestRpc = "Sansara_Admin";

		public const string ReplyRpc = "Sansara_AdminReply";

		public const int MaxLine = 256;

		public const int MaxReply = 4000;

		public const int Done = 0;

		public const int Denied = 1;

		public const int NoModule = 2;

		public const int Failed = 3;

		public const int Invalid = 4;

		public const int MaxLogText = 300;

		public static string Truncate(string text)
		{
			string text2 = text ?? "";
			if (text2.Length > 4000)
			{
				return text2.Substring(0, 3999) + "…";
			}
			return text2;
		}

		public static AdminReply Check(int protocol, string module, string line)
		{
			if (protocol != 1)
			{
				return new AdminReply(4, $"неверный запрос: обновите Sansara Admin (протокол сервера {1})");
			}
			if (module == null || line == null)
			{
				return new AdminReply(4, "неверный запрос");
			}
			if (Shown(module, line).Length > 256)
			{
				return new AdminReply(4, $"строка длиннее {256} символов");
			}
			return null;
		}

		public static string Shown(string module, string line)
		{
			string text = (module ?? "").Trim();
			if (!(text == ""))
			{
				return (text + " " + (line ?? "").Trim()).Trim();
			}
			return "help";
		}

		public static string ForLog(string s)
		{
			char[] array = (s ?? "").ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				if (char.IsControl(array[i]))
				{
					array[i] = ' ';
				}
			}
			if (array.Length > 300)
			{
				return new string(array, 0, 300);
			}
			return new string(array);
		}

		public static string FirstLine(string text)
		{
			string text2 = (text ?? "").Replace("\r", "");
			int num = text2.IndexOf('\n');
			if (num >= 0)
			{
				return text2.Substring(0, num);
			}
			return text2;
		}

		public static string HelpLine(string module, IEnumerable<string> verbs)
		{
			return module + ": " + string.Join(", ", verbs);
		}

		public static Dictionary<string, string[]> ParseHelp(string text)
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.Ordinal);
			string[] array = (text ?? "").Replace("\r", "").Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				int num = text2.IndexOf(':');
				if (num <= 0)
				{
					continue;
				}
				string text3 = text2.Substring(0, num).Trim();
				if (IsName(text3))
				{
					dictionary[text3] = (from v in text2.Substring(num + 1).Split(new char[1] { ',' })
						select v.Trim()).Where(IsName).ToArray();
				}
			}
			return dictionary;
		}

		public static bool IsName(string s)
		{
			if (!string.IsNullOrEmpty(s))
			{
				return s.All((char c) => c >= 'a' && c <= 'z');
			}
			return false;
		}

		public static string Unknown(string verb, IEnumerable<string> verbs)
		{
			return (string.IsNullOrEmpty(verb) ? "укажите действие" : ("неизвестное действие «" + verb + "»")) + "; есть: " + string.Join(", ", verbs);
		}
	}
	public sealed class AdminRegistry
	{
		public const string SharedKey = "sansara.Admin.v1";

		public static readonly AdminRegistry Shared = new AdminRegistry("sansara.Admin.v1");

		private readonly string _key;

		private Dictionary<string, Tuple<string[], Func<string, string, string, string>>> Table
		{
			get
			{
				object data = AppDomain.CurrentDomain.GetData(_key);
				if (data == null)
				{
					Dictionary<string, Tuple<string[], Func<string, string, string, string>>> dictionary = new Dictionary<string, Tuple<string[], Func<string, string, string, string>>>(StringComparer.Ordinal);
					AppDomain.CurrentDomain.SetData(_key, dictionary);
					return dictionary;
				}
				return (data as Dictionary<string, Tuple<string[], Func<string, string, string, string>>>) ?? throw new InvalidOperationException("данные " + _key + " другого формата");
			}
		}

		public string Owner => (AppDomain.CurrentDomain.GetData(_key + ".owner") as string) ?? "";

		public AdminRegistry(string key)
		{
			_key = key;
		}

		public bool Register(string module, string[] verbs, Func<string, string, string, string> handler)
		{
			string text = (module ?? "").Trim().ToLowerInvariant();
			if (!AdminProtocol.IsName(text))
			{
				throw new ArgumentException("имя модуля — латинские буквы: " + module);
			}
			string[] array = verbs ?? new string[0];
			foreach (string text2 in array)
			{
				if (!AdminProtocol.IsName(text2))
				{
					throw new ArgumentException("глагол — латинские буквы: " + text2);
				}
			}
			if (handler == null)
			{
				throw new ArgumentNullException("handler");
			}
			Dictionary<string, Tuple<string[], Func<string, string, string, string>>> table = Table;
			bool result = table.ContainsKey(text);
			table[text] = Tuple.Create(verbs ?? new string[0], handler);
			return result;
		}

		public bool ClaimOwner(string assembly)
		{
			if (string.IsNullOrEmpty(assembly))
			{
				throw new ArgumentException("пустое имя сборки", "assembly");
			}
			string name = _key + ".owner";
			if (!(AppDomain.CurrentDomain.GetData(name) is string text))
			{
				AppDomain.CurrentDomain.SetData(name, assembly);
				return true;
			}
			return text == assembly;
		}

		public List<string> Modules()
		{
			return Table.Keys.OrderBy<string, string>((string k) => k, StringComparer.Ordinal).ToList();
		}

		public string HelpText()
		{
			Dictionary<string, Tuple<string[], Func<string, string, string, string>>> table = Table;
			if (table.Count != 0)
			{
				return string.Join("\n", from k in Modules()
					select AdminProtocol.HelpLine(k, table[k].Item1));
			}
			return "на сервере нет модулей Sansara Admin";
		}

		public AdminReply Dispatch(string module, string line, string adminName, string adminId, out Exception error)
		{
			error = null;
			try
			{
				string text = (module ?? "").Trim().ToLowerInvariant();
				if (text == "")
				{
					return new AdminReply(0, AdminProtocol.Truncate(HelpText()));
				}
				Dictionary<string, Tuple<string[], Func<string, string, string, string>>> table = Table;
				if (!table.TryGetValue(text, out var value))
				{
					return new AdminReply(2, "модуля " + text + " нет на сервере; есть: " + ((table.Count == 0) ? "ничего" : string.Join(", ", Modules())));
				}
				string text2 = value.Item2((line ?? "").Trim(), adminName ?? "", adminId ?? "");
				return new AdminReply(0, AdminProtocol.Truncate(string.IsNullOrEmpty(text2) ? "готово" : text2));
			}
			catch (Exception ex)
			{
				return new AdminReply(3, AdminProtocol.Truncate("ошибка: " + (error = ex).Message));
			}
		}
	}
	public static class AdminUi
	{
		public const string Verb = "ui";

		public const string Head = "ui=1";

		public const string NoteKey = "note";

		public const string MoreKey = "more";

		private const int Reserve = 16;

		public static string Field(object value)
		{
			char[] array = ((!(value is bool)) ? (Convert.ToString(value, CultureInfo.InvariantCulture) ?? "") : (((bool)value) ? "1" : "0")).ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				if (array[i] == ';')
				{
					array[i] = ',';
				}
				else if (char.IsControl(array[i]))
				{
					array[i] = ' ';
				}
			}
			return new string(array).Trim();
		}

		public static string Row(string key, params object[] fields)
		{
			if (!AdminProtocol.IsName(key))
			{
				throw new ArgumentException("ключ строки ui — латинские буквы в нижнем регистре: " + key);
			}
			return key + "=" + string.Join(";", (fields ?? new object[0]).Select(Field));
		}

		public static string Note(string text)
		{
			return Text(new string[1] { Row("note", text) });
		}

		public static string Text(IEnumerable<string> rows)
		{
			List<string> list = (rows ?? Enumerable.Empty<string>()).ToList();
			StringBuilder stringBuilder = new StringBuilder("ui=1");
			for (int i = 0; i < list.Count; i++)
			{
				if (stringBuilder.Length + 1 + list[i].Length > 3984)
				{
					stringBuilder.Append('\n').Append("more").Append('=')
						.Append((list.Count - i).ToString(CultureInfo.InvariantCulture));
					break;
				}
				stringBuilder.Append('\n').Append(list[i]);
			}
			return stringBuilder.ToString();
		}
	}
}
namespace Renegades
{
	internal static class CampConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> IntervalHours;

		internal static ConfigEntry<int> MaxActive;

		internal static ConfigEntry<float> FirstDelayMinutes;

		internal static ConfigEntry<float> MinDistance;

		internal static ConfigEntry<float> MaxDistance;

		internal static ConfigEntry<float> MinPlayerDistance;

		internal static ConfigEntry<float> Spacing;

		internal static ConfigEntry<float> MaxRelief;

		internal static ConfigEntry<float> MaxReliefRelaxed;

		internal static ConfigEntry<float> SearchWiden;

		internal static ConfigEntry<float> RevealDistance;

		internal static ConfigEntry<float> PingSeconds;

		internal static ConfigEntry<float> LifetimeHours;

		internal static ConfigEntry<float> CleanupMinutes;

		internal static ConfigEntry<int> GarrisonMin;

		internal static ConfigEntry<int> GarrisonMax;

		internal static ConfigEntry<int> LeaderStars;

		internal static ConfigEntry<int> MemberStars;

		internal static ConfigEntry<int> Mages;

		internal static ConfigEntry<float> LeaderMageChance;

		internal static ConfigEntry<string> ChestPrefab;

		internal static ConfigEntry<string> ChestTokens;

		internal static ConfigEntry<int> RewardCount;

		internal static readonly ConfigEntry<string>[] RewardPool = new ConfigEntry<string>[8];

		internal const string DefaultChest = "piece_chest_wood";

		internal const string DefaultChestTokens = "2-3,3-4,4-6,5-8,6-10,8-12,10-15";

		private static ConfigEntry<float> F(ConfigFile c, string key, float value, float min, float max, string description)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			return c.Bind<float>("Camps", key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
		}

		private static ConfigEntry<int> I(ConfigFile c, string key, int value, int min, int max, string description)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			return c.Bind<int>("Camps", key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(min, max), Array.Empty<object>()));
		}

		internal static void Bind(ConfigFile c)
		{
			Enabled = c.Bind<bool>("Camps", "Enabled", true, "Сервер сам ставит лагеря отступников по расписанию. false — новых нет, стоящие доживают свой срок и убираются. Команда camp start работает и при false.");
			IntervalHours = F(c, "IntervalHours", 6f, 0.05f, 168f, "Часов реального времени между постановками лагерей (от старта предыдущего).");
			MaxActive = I(c, "MaxActive", 2, 0, 10, "Сколько лагерей может стоять одновременно (вместе с разбитыми, ждущими уборки). 0 — расписание не ставит.");
			FirstDelayMinutes = F(c, "FirstDelayMinutes", 10f, 0f, 1440f, "Первый лагерь после запуска сервера — не раньше чем через столько минут (и при игроке на поверхности).");
			MinDistance = F(c, "MinDistance", 400f, 100f, 5000f, "Ближняя граница кольца вокруг случайного игрока на поверхности, м.");
			MaxDistance = F(c, "MaxDistance", 1200f, 150f, 8000f, "Дальняя граница кольца, м.");
			MinPlayerDistance = F(c, "MinPlayerDistance", 150f, 0f, 2000f, "Ни одного игрока ближе к месту лагеря при постановке, м: лагерь не возникает на глазах.");
			Spacing = F(c, "Spacing", 500f, 0f, 5000f, "Не ближе к другому лагерю отступников, м.");
			MaxRelief = F(c, "MaxRelief", 3f, 0.5f, 20f, "Наибольший перепад высот генератора в круге лагеря (12 м), м. Рельеф не выравнивается.");
			MaxReliefRelaxed = F(c, "MaxReliefRelaxed", 6f, 0.5f, 20f, "0.4.1: если при Camps.MaxRelief места нет, поиск пробует перепад ×1,5 и ×2, но не больше этого, м. Не больше MaxRelief — смягчения нет. Детали лагеря ставятся по высоте земли в своих точках: на перепаде 4–6 м частокол и навесы стоят ступенями.");
			SearchWiden = F(c, "SearchWiden", 2f, 1f, 4f, "0.4.1: если в кольце MinDistance…MaxDistance места нет, поиск расширяет его до MaxDistance × это. 1 — не расширять.");
			RevealDistance = F(c, "RevealDistance", 250f, 30f, 2000f, "Ближе этого игрок получает сообщение (один раз) и метку на карте (пинг, повторяется); лагерь убирается, только когда ближе никого нет, м.");
			PingSeconds = F(c, "PingSeconds", 5f, 1f, 60f, "Повтор пинга лагеря игрокам рядом, с.");
			LifetimeHours = F(c, "LifetimeHours", 24f, 0.05f, 336f, "Срок лагеря от постановки, часов реального времени.");
			CleanupMinutes = F(c, "CleanupMinutes", 30f, 0f, 1440f, "Сколько минут разбитый лагерь (с сундуком) стоит до уборки.");
			GarrisonMin = I(c, "GarrisonMin", 4, 1, 10, "Наименьший гарнизон без вожака.");
			GarrisonMax = I(c, "GarrisonMax", 6, 1, 10, "Наибольший гарнизон без вожака.");
			LeaderStars = I(c, "LeaderStars", 1, 0, 2, "Звёзды вожака лагеря.");
			MemberStars = I(c, "MemberStars", 0, 0, 2, "Звёзды рядовых лагеря.");
			Mages = I(c, "Mages", 1, 0, 3, "0.5.0: в лагерях тиров 6–7 столько рядовых (0–3; сначала без роли) — маги; школа случайная, при двух и больше — не все одной школы и не больше одного мага поддержки. На тирах 1–5 не действует. Баланс: маг держит дистанцию и бьёт по площади, поддержка ставит барьер на раненых своих — лагерь труднее взять наскоком.");
			LeaderMageChance = F(c, "LeaderMageChance", 0.25f, 0f, 1f, "0.5.0: вероятность, что вожак лагеря тиров 6–7 — маг (огонь или лёд, не поддержка), с прежними множителями вожака и аурой. 0 — никогда, 1 — всегда.");
			ChestPrefab = c.Bind<string>("Camps", "ChestPrefab", "piece_chest_wood", "Сундук, который появляется у костра разбитого лагеря (игровая деталь с Container). Неизвестный — piece_chest_wood и предупреждение.");
			ChestTokens = c.Bind<string>("Camps", "ChestTokens", "2-3,3-4,4-6,5-8,6-10,8-12,10-15", "Жетоны клана в сундуке: семь пар «мин-макс» по тирам 1–7 (0–50; 0-0 — нет). Неверная строка — значения по умолчанию и предупреждение.");
			RewardCount = I(c, "RewardCount", 1, 0, 5, "Сколько наград Event Rewards получает каждый ударивший член лагеря игрок. 0 — наград нет.");
			for (int i = 1; i <= 7; i++)
			{
				RewardPool[i] = c.Bind<string>("Tier" + i.ToString(CultureInfo.InvariantCulture), "RewardPool", "", $"Награды Event Rewards за лагерь тира {i} ({RenegadeTiers.Get(i).Biome}): id через запятую, одна случайная на награду. " + "Пусто — наград нет. По правилу N и N−1 (решение 0042): награды этого и предыдущего биома.");
			}
		}

		internal static CampNumbers Numbers()
		{
			return new CampNumbers
			{
				Enabled = Enabled.Value,
				IntervalHours = IntervalHours.Value,
				MaxActive = MaxActive.Value,
				FirstDelayMinutes = FirstDelayMinutes.Value,
				LifetimeHours = LifetimeHours.Value,
				CleanupMinutes = CleanupMinutes.Value,
				GarrisonMin = GarrisonMin.Value,
				GarrisonMax = GarrisonMax.Value,
				LeaderStars = LeaderStars.Value,
				MemberStars = MemberStars.Value,
				Mages = Mages.Value,
				LeaderMageChance = LeaderMageChance.Value
			};
		}

		private static string V(float v)
		{
			return v.ToString("0.###", CultureInfo.InvariantCulture);
		}

		internal static IEnumerable<string> Lines()
		{
			yield return $"Camps.Enabled = {Enabled.Value}";
			yield return "Camps.IntervalHours = " + V(IntervalHours.Value);
			yield return $"Camps.MaxActive = {MaxActive.Value}";
			yield return "Camps.FirstDelayMinutes = " + V(FirstDelayMinutes.Value);
			yield return "Camps.MinDistance = " + V(MinDistance.Value);
			yield return "Camps.MaxDistance = " + V(MaxDistance.Value);
			yield return "Camps.MinPlayerDistance = " + V(MinPlayerDistance.Value);
			yield return "Camps.Spacing = " + V(Spacing.Value);
			yield return "Camps.MaxRelief = " + V(MaxRelief.Value);
			yield return "Camps.MaxReliefRelaxed = " + V(MaxReliefRelaxed.Value);
			yield return "Camps.SearchWiden = " + V(SearchWiden.Value);
			yield return "Camps.RevealDistance = " + V(RevealDistance.Value);
			yield return "Camps.PingSeconds = " + V(PingSeconds.Value);
			yield return "Camps.LifetimeHours = " + V(LifetimeHours.Value);
			yield return "Camps.CleanupMinutes = " + V(CleanupMinutes.Value);
			yield return $"Camps.GarrisonMin = {GarrisonMin.Value}";
			yield return $"Camps.GarrisonMax = {GarrisonMax.Value}";
			yield return $"Camps.LeaderStars = {LeaderStars.Value}";
			yield return $"Camps.MemberStars = {MemberStars.Value}";
			yield return $"Camps.Mages = {Mages.Value}";
			yield return "Camps.LeaderMageChance = " + V(LeaderMageChance.Value);
			yield return "Camps.ChestPrefab = " + ChestPrefab.Value;
			yield return "Camps.ChestTokens = «" + ChestTokens.Value + "»";
			yield return $"Camps.RewardCount = {RewardCount.Value}";
			for (int t = 1; t <= 7; t++)
			{
				yield return $"Tier{t}.RewardPool = «{RewardPool[t].Value}»";
			}
		}
	}
	internal sealed class CampHost : ICampHost
	{
		private sealed class Pending
		{
			public Vector3 Center;

			public int Tier;

			public int Seed;

			public List<Vector2s> Zones;

			public float Since;

			public bool Scheduled;

			public bool Here;

			public long AdminPeer;

			public string Where = "";

			public bool ForceMage;
		}

		internal enum SearchKind
		{
			Schedule,
			Start,
			Probe,
			OutcastSchedule,
			OutcastStart,
			OutcastProbe
		}

		internal sealed class SiteRules
		{
			public float PlayerDistance;

			public float ThingsRadius;

			public float LocationRadius;
		}

		private sealed class SearchJob
		{
			public SearchKind Kind;

			public CampSiteSearch Search;

			public CampSiteWorld World;

			public List<CampWorld.PlayerInfo> Players;

			public List<CampZone> Hunt;

			public HashSet<CampZone> Events;

			public SiteRules Rules;

			public Action<CampSiteSearch, string> Done;

			public long AdminPeer;

			public bool ForceMage;

			public double Ms;

			public int Frames;
		}

		internal static readonly CampHost Instance = new CampHost();

		private const double SearchFrameMs = 3.0;

		private const int SearchChunk = 2;

		private const float HeavySeconds = 10f;

		private const float GenerateTimeout = 60f;

		private const float RecountDelay = 10f;

		private const float TempleKeepOff = 300f;

		private const float HereDistance = 60f;

		private CampState _state;

		private string _world;

		private string _statePath;

		private string _busyPath;

		private string _busyText;

		private double _serverStartedAt;

		private float _loadedAt;

		private bool _recounted;

		private float _nextTick;

		private float _nextHeavy;

		private SearchJob _job;

		private string _lastWait = "";

		private Pending _pending;

		private readonly Random _rng = new Random();

		private readonly Dictionary<string, float> _nextPing = new Dictionary<string, float>();

		private readonly Dictionary<string, List<ZDOID>> _members = new Dictionary<string, List<ZDOID>>(StringComparer.Ordinal);

		private readonly HashSet<string> _logged = new HashSet<string>(StringComparer.Ordinal);

		private readonly HashSet<string> _waitLogged = new HashSet<string>(StringComparer.Ordinal);

		private readonly Dictionary<string, GameObject> _pieces = new Dictionary<string, GameObject>(StringComparer.Ordinal);

		private MethodInfo _enqueue;

		private bool _enqueueChecked;

		internal static bool Ready => Instance._state != null;

		internal int Count => _state?.Camps.Count ?? 0;

		internal List<Vector3> Centers()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			if (_state != null)
			{
				list.AddRange(_state.Camps.Select(Center));
			}
			if (_pending != null)
			{
				list.Add(_pending.Center);
			}
			return list;
		}

		internal void Tick()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || WorldGenerator.instance == null || ZRoutedRpc.instance == null)
			{
				return;
			}
			try
			{
				if (!EnsureLoaded())
				{
					return;
				}
			}
			catch (Exception e)
			{
				LogOnce("load", e);
				return;
			}
			Guard("search", Work);
			if (Time.time < _nextTick)
			{
				return;
			}
			_nextTick = Time.time + 1f;
			double now = RealClock.Now;
			Guard("pending", delegate
			{
				TickPending(now);
			});
			if (!_recounted)
			{
				if (Time.time - _loadedAt < 10f)
				{
					return;
				}
				Guard("recount", Recount);
				_recounted = true;
			}
			foreach (Camp camp in _state.Camps.ToList())
			{
				Guard("camp", delegate
				{
					TickCamp(camp, now);
				});
			}
			if (Time.time >= _nextHeavy)
			{
				_nextHeavy = Time.time + 10f;
				Guard("schedule", delegate
				{
					Schedule(now);
				});
			}
			Guard("busy", WriteBusy);
		}

		private void Guard(string where, Action action)
		{
			try
			{
				action();
			}
			catch (Exception e)
			{
				LogOnce(where, e);
				if (where == "pending")
				{
					_pending = null;
				}
				if (where == "search")
				{
					_job = null;
				}
			}
		}

		private void LogOnce(string where, Exception e)
		{
			if (_logged.Add(where + "|" + e.GetType().Name + "|" + e.Message))
			{
				Plugin.Log.LogError((object)$"Отступники: лагеря ({where}): {e}");
			}
		}

		private bool EnsureLoaded()
		{
			string worldName = ZNet.instance.GetWorldName();
			if (string.IsNullOrEmpty(worldName))
			{
				return false;
			}
			if (_state != null && _world == worldName)
			{
				return true;
			}
			_world = worldName;
			CampWorld.Reset();
			string text = Path.Combine(Paths.ConfigPath, "sansara.Renegades");
			Directory.CreateDirectory(text);
			string text2 = CampWorld.WorldFileName();
			_statePath = Path.Combine(text, text2 + ".camps");
			_busyPath = Path.Combine(text, text2 + ".busy.txt");
			CampState campState = (File.Exists(_statePath) ? CampState.Parse(File.ReadAllText(_statePath)) : new CampState());
			double now = RealClock.Now;
			campState.NextAt = CampPlanner.InitialNext(campState.NextAt, now, CampConfig.Numbers());
			_state = campState;
			_serverStartedAt = now;
			_loadedAt = Time.time;
			_recounted = false;
			_pending = null;
			_job = null;
			_busyText = null;
			_nextPing.Clear();
			_members.Clear();
			_waitLogged.Clear();
			_pieces.Clear();
			RenegadeHost.ListenDestroyed();
			Save();
			Plugin.Log.LogInfo((object)($"Отступники: лагерей в файле {_state.Camps.Count}" + ((_state.Camps.Count > 0) ? (": " + string.Join(", ", _state.Camps.Select((Camp c) => string.Format("{0} (тир {1}{2})", c.Id, c.Tier, c.Cleared ? ", разбит" : "")))) : "") + string.Format("; расписание {0}, первый не раньше чем через {1:0.#} мин.", CampConfig.Enabled.Value ? "включено" : "выключено", CampConfig.FirstDelayMinutes.Value)));
			return true;
		}

		private void Save()
		{
			try
			{
				File.WriteAllText(_statePath, _state.Serialize());
			}
			catch (Exception e)
			{
				LogOnce("save", e);
			}
		}

		private void Recount()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			bool flag = false;
			foreach (Camp camp in _state.Camps)
			{
				int count = Members(camp).Count;
				int count2 = CampWorld.CampObjects(Center(camp), camp.Id).Count;
				string text = $"Отступники: лагерь {camp.Id} после запуска — живых {count} из {camp.Total}, погибло {camp.Killed}, деталей {count2}";
				if (!camp.Cleared && count < camp.Expected)
				{
					int num = camp.Expected - count;
					camp.Lost += num;
					flag = true;
					text += $"; пропало без боя {num}";
				}
				Plugin.Log.LogInfo((object)(text + "."));
			}
			if (flag)
			{
				Save();
			}
		}

		private static Vector3 Center(Camp camp)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(camp.X, camp.Y, camp.Z);
		}

		private List<ZDO> Members(Camp camp)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if (!_members.TryGetValue(camp.Id, out var value))
			{
				value = (from z in RenegadeHost.Instance.CampMembers(camp.Id)
					select z.m_uid).ToList();
				_members[camp.Id] = value;
			}
			List<ZDO> list = new List<ZDO>();
			foreach (ZDOID item in value)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(item);
				if (zDO != null && !RenegadeHost.OwnDestroyed.Contains(item) && zDO.GetString("sansara.Renegades.camp", "") == camp.Id)
				{
					list.Add(zDO);
				}
			}
			return list;
		}

		private void TickCamp(Camp camp, double now)
		{
			List<ZDO> list = Members(camp);
			if (!camp.Cleared && list.Count < camp.Expected)
			{
				int num = camp.Expected - list.Count;
				camp.Lost += num;
				Save();
				Plugin.Log.LogInfo((object)$"Отступники: лагерь {camp.Id} — членов убрано не в бою: {num} (renegade clear?); такой лагерь уйдёт без сундука.");
			}
			Marks(camp, list);
			List<CampWorld.PlayerInfo> list2 = (from p in CampWorld.Players()
				where Utils.DistanceXZ(p.Pos, Center(camp)) <= CampConfig.RevealDistance.Value && CampWorld.OnSurface(p.Pos)
				select p).ToList();
			if (!camp.Cleared)
			{
				Reveal(camp, list2);
			}
			if (camp.Cleared && !camp.ChestPlaced)
			{
				PlaceChest(camp);
			}
			switch (CampPlanner.Step(camp, now, list.Count, list2.Count > 0, CampConfig.Numbers()))
			{
			case CampStep.Clear:
				Take(camp, now);
				break;
			case CampStep.Remove:
				Remove(camp, camp.Cleared ? "разбит, время уборки" : ((list.Count == 0) ? "гарнизон пропал" : "вышел срок"));
				break;
			case CampStep.WaitPlayers:
				if (_waitLogged.Add(camp.Id))
				{
					Plugin.Log.LogInfo((object)$"Отступники: лагерь {camp.Id} пора убрать, но рядом игроки ({list2.Count}) — ждёт.");
				}
				break;
			}
		}

		private void Marks(Camp camp, IList<ZDO> members)
		{
			if (members.Count == 0)
			{
				return;
			}
			bool flag = false;
			foreach (CampWorld.PlayerInfo item in CampWorld.Players())
			{
				if (item.PlayerId != 0L && !(item.Name == ""))
				{
					int s_attackers = ZDOVars.s_attackers;
					string key = s_attackers + item.Name;
					if (members.Any((ZDO z) => z.GetBool(key, false)) && camp.AddPlayer(item.PlayerId, item.Name))
					{
						flag = true;
						Plugin.Log.LogInfo((object)$"Отступники: лагерь {camp.Id} — игрок {item.PlayerId} ({item.Name}) ранил его члена.");
					}
				}
			}
			if (flag)
			{
				Save();
			}
		}

		private void Reveal(Camp camp, List<CampWorld.PlayerInfo> near)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			if (near.Count == 0)
			{
				return;
			}
			bool flag = false;
			foreach (CampWorld.PlayerInfo item in near)
			{
				if (item.PlayerId != 0L && camp.Seen.Add(item.PlayerId))
				{
					flag = true;
					CampWorld.Message(item.Peer, CampTexts.Revealed(camp.Tier, camp.Leader));
					Plugin.Log.LogInfo((object)$"Отступники: игрок {item.Name} ({item.PlayerId}) нашёл лагерь {camp.Id}, в {Utils.DistanceXZ(item.Pos, Center(camp)):0} м.");
				}
			}
			if (flag)
			{
				Save();
			}
			if (_nextPing.TryGetValue(camp.Id, out var value) && !(Time.time >= value))
			{
				return;
			}
			_nextPing[camp.Id] = Time.time + Mathf.Max(1f, CampConfig.PingSeconds.Value);
			foreach (CampWorld.PlayerInfo item2 in near)
			{
				CampWorld.Ping(item2.Peer, Center(camp), "Лагерь отступников");
			}
		}

		internal void OnDestroyed(ZDO zdo, bool own)
		{
			try
			{
				if (_state == null)
				{
					return;
				}
				string text = zdo.GetString("sansara.Renegades.camp", "");
				if (!(text == "") && !(text == "admin") && !(zdo.GetString("sansara.Renegades.campPiece", "") != ""))
				{
					Camp camp = _state.Find(text);
					if (camp != null && !camp.Cleared && camp.Expected > 0 && !own)
					{
						Marks(camp, new List<ZDO> { zdo });
						camp.Killed++;
						Plugin.Log.LogInfo((object)("Отступники: лагерь " + camp.Id + " — погиб " + ((zdo.GetInt("sansara.Renegades.leader", 0) == 1) ? "вожак" : "член") + " " + $"({camp.Killed} из {camp.Total}, ударивших игроков {zdo.GetInt(ZDOVars.s_attackers, 0)})."));
						Save();
					}
				}
			}
			catch (Exception e)
			{
				LogOnce("destroyed", e);
			}
		}

		private void Take(Camp camp, double now)
		{
			camp.Cleared = true;
			camp.ClearedAt = now;
			Save();
			PlaceChest(camp);
			List<long> list = GiveRewards(camp);
			CampWorld.MessageAll(CampTexts.Taken(camp.Tier, camp.Players.Select((CampPlayer p) => p.Name)));
			Plugin.Log.LogInfo((object)($"Отступники: лагерь {camp.Id} разбит — погибло {camp.Killed} из {camp.Total}; ударившие: " + ((camp.Players.Count == 0) ? "никто" : string.Join(", ", camp.Players.Select((CampPlayer p) => $"{p.Name} ({p.Id})"))) + "; " + string.Format("награждены: {0}; уберётся через {1:0.#} мин.", (list.Count == 0) ? "никто" : string.Join(", ", list), CampConfig.CleanupMinutes.Value)));
		}

		private void PlaceChest(Camp camp)
		{
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			string text = (CampConfig.ChestPrefab.Value ?? "").Trim();
			GameObject prefab = ZNetScene.instance.GetPrefab(text);
			if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<Container>() == (Object)null || (Object)(object)prefab.GetComponent<ZNetView>() == (Object)null)
			{
				if (_logged.Add("chestprefab|" + text))
				{
					Plugin.Log.LogWarning((object)("Отступники: Camps.ChestPrefab «" + text + "» — не сундук игры; взят piece_chest_wood."));
				}
				prefab = ZNetScene.instance.GetPrefab("piece_chest_wood");
			}
			camp.ChestPlaced = true;
			Save();
			if ((Object)(object)prefab == (Object)null)
			{
				Plugin.Log.LogError((object)("Отступники: сундука piece_chest_wood нет в игре — лагерь " + camp.Id + " без сундука."));
				return;
			}
			CampPiece campPiece = CampLayout.Build(camp.Seed).FirstOrDefault((CampPiece p) => p.Kind == CampPieceKind.Chest) ?? new CampPiece
			{
				X = 2f
			};
			float num = camp.X + campPiece.X;
			float num2 = camp.Z + campPiece.Z;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(num, CampWorld.Height(num, num2), num2);
			List<string> list = new List<string>();
			TokenTable table = TokenTable.Parse(CampConfig.ChestTokens.Value, list);
			foreach (string item in list)
			{
				if (_logged.Add("chesttokens|" + item))
				{
					Plugin.Log.LogWarning((object)("Отступники: Camps.ChestTokens: " + item + "."));
				}
			}
			int amount = CampPlanner.ChestTokens(table, camp.Tier, _rng);
			CampWorld.CreateChest(prefab, val, Quaternion.Euler(0f, campPiece.Yaw, 0f), camp.Id, "SansaraClanToken", amount, out var added);
			Plugin.Log.LogInfo((object)$"Отступники: лагерь {camp.Id} — сундук {((Object)prefab).name} у костра ({val.x:0}, {val.y:0}, {val.z:0}), жетонов {added}.");
		}

		private List<long> GiveRewards(Camp camp)
		{
			List<long> list = new List<long>();
			int value = CampConfig.RewardCount.Value;
			string[] array = CampPlanner.Pool(CampConfig.RewardPool[camp.Tier].Value);
			if (value <= 0 || array.Length == 0 || camp.Players.Count == 0)
			{
				Plugin.Log.LogInfo((object)("Отступники: лагерь " + camp.Id + " — наград нет (" + ((value <= 0) ? "Camps.RewardCount = 0" : ((array.Length == 0) ? $"Tier{camp.Tier}.RewardPool пуст" : "никто не ударил")) + ")."));
				return list;
			}
			MethodInfo methodInfo = Enqueue();
			if (methodInfo == null)
			{
				Plugin.Log.LogError((object)("Отступники: лагерь " + camp.Id + " — награды не выданы: нет Event Rewards (ExternalRewards.Enqueue)."));
				return list;
			}
			List<CampWorld.PlayerInfo> source = CampWorld.Players();
			foreach (CampPlayer player in camp.Players)
			{
				bool flag = false;
				for (int i = 1; i <= value; i++)
				{
					string text = array[_rng.Next(array.Length)];
					string text2 = CampPlanner.RewardReason(camp.Id, i);
					try
					{
						string text3 = (string)methodInfo.Invoke(null, new object[3] { player.Id, text, text2 });
						if (text3 != null)
						{
							Plugin.Log.LogError((object)$"Отступники: награда {text} игроку {player.Id} не выдана: {text3}.");
							continue;
						}
						flag = true;
						Plugin.Log.LogInfo((object)$"Отступники: игроку {player.Id} ({player.Name}) поставлена награда {text} ({text2}).");
					}
					catch (Exception arg)
					{
						Plugin.Log.LogError((object)$"Отступники: награда {text} игроку {player.Id}: {arg}");
					}
				}
				if (flag)
				{
					list.Add(player.Id);
					continue;
				}
				CampWorld.PlayerInfo playerInfo = source.FirstOrDefault((CampWorld.PlayerInfo o) => o.PlayerId == player.Id);
				if (playerInfo.Peer != null)
				{
					CampWorld.Message(playerInfo.Peer, CampTexts.NoReward);
				}
			}
			return list;
		}

		private MethodInfo Enqueue()
		{
			if (!_enqueueChecked)
			{
				_enqueueChecked = true;
				Type type = AccessTools.TypeByName("EventRewards.ExternalRewards");
				_enqueue = ((type == null) ? null : AccessTools.Method(type, "Enqueue", new Type[3]
				{
					typeof(long),
					typeof(string),
					typeof(string)
				}, (Type[])null));
			}
			return _enqueue;
		}

		private int Remove(Camp camp, string why)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			int num = RenegadeHost.Instance.ClearCamp(camp.Id);
			int num2 = 0;
			foreach (ZDO item in CampWorld.CampObjects(Center(camp), camp.Id))
			{
				if (!RenegadeHost.OwnDestroyed.Contains(item.m_uid))
				{
					CampWorld.Destroy(item);
					num2++;
				}
			}
			_state.Camps.Remove(camp);
			_members.Remove(camp.Id);
			_nextPing.Remove(camp.Id);
			_waitLogged.Remove(camp.Id);
			Save();
			WriteBusy();
			Plugin.Log.LogInfo((object)$"Отступники: лагерь {camp.Id} убран ({why}) — существ {num}, объектов {num2}.");
			return num + num2;
		}

		private void Schedule(double now)
		{
			if (_pending == null && _job == null)
			{
				List<CampWorld.PlayerInfo> list = CampWorld.Players();
				List<CampWorld.PlayerInfo> list2 = list.Where((CampWorld.PlayerInfo p) => p.PlayerId != 0L && CampWorld.OnSurface(p.Pos) && !CampWorld.NearTrial(p.Pos)).ToList();
				if (!CampPlanner.ShouldStart(now, _state, CampConfig.Numbers(), Plugin.Enabled.Value, _serverStartedAt, list2.Count, out var reason))
				{
					_lastWait = reason;
					return;
				}
				StartSearch(SearchKind.Schedule, list2.Select(SiteCenter).ToList(), list, OpenTier(), 0, 0L);
				_lastWait = "ищет место";
			}
		}

		internal static int OpenTier()
		{
			return CampPlanner.OpenTier(new HashSet<string>(ZoneSystem.instance.GetGlobalKeys()));
		}

		internal static CampSiteCenter SiteCenter(CampWorld.PlayerInfo p)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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)
			return new CampSiteCenter
			{
				Name = ((p.Name == "") ? ("игрока " + p.PlayerId) : p.Name),
				X = p.Pos.x,
				Z = p.Pos.z,
				Biome = CampWorld.BiomeAt(p.Pos.x, p.Pos.z)
			};
		}

		private void StartSearch(SearchKind kind, List<CampSiteCenter> centers, List<CampWorld.PlayerInfo> all, int open, int forcedTier, long adminPeer)
		{
			float value = CampConfig.MinDistance.Value;
			CampSiteSettings settings = new CampSiteSettings
			{
				MinDistance = value,
				MaxDistance = Mathf.Max(value + 50f, CampConfig.MaxDistance.Value),
				Widen = CampConfig.SearchWiden.Value,
				MaxRelief = CampConfig.MaxRelief.Value,
				MaxReliefRelaxed = CampConfig.MaxReliefRelaxed.Value,
				OpenTier = open,
				ForcedTier = forcedTier
			};
			SiteRules rules = new SiteRules
			{
				PlayerDistance = CampConfig.MinPlayerDistance.Value,
				ThingsRadius = 12f,
				LocationRadius = 12f
			};
			Begin(kind, centers, all, settings, rules, adminPeer, null);
		}

		private void Begin(SearchKind kind, List<CampSiteCenter> centers, List<CampWorld.PlayerInfo> all, CampSiteSettings settings, SiteRules rules, long adminPeer, Action<CampSiteSearch, string> done)
		{
			HashSet<CampZone> hashSet = new HashSet<CampZone>(PirateHost.Instance.BusyZones());
			if (!IsOutcast(kind))
			{
				hashSet.UnionWith(OutcastHost.Instance.BusyZones());
			}
			SearchJob job = new SearchJob
			{
				Kind = kind,
				Search = new CampSiteSearch(centers, settings, _rng.Next()),
				Players = all,
				Hunt = CampWorld.BountyHuntBusy(),
				Events = hashSet,
				Rules = rules,
				Done = done,
				AdminPeer = adminPeer
			};
			job.World = new CampSiteWorld(CampWorld.BiomeAt, CampWorld.Height, CampWorld.WaterLevel, (float x, float z) => Hard(x, z, job));
			_job = job;
		}

		private static bool IsOutcast(SearchKind kind)
		{
			if (kind != SearchKind.OutcastSchedule && kind != SearchKind.OutcastStart)
			{
				return kind == SearchKind.OutcastProbe;
			}
			return true;
		}

		internal string Busy()
		{
			if (_job != null)
			{
				return KindName(_job.Kind) + ": " + _job.Search.Summary;
			}
			return "";
		}

		internal bool SearchOutcast(SearchKind kind, List<CampSiteCenter> centers, List<CampWorld.PlayerInfo> all, CampSiteSettings settings, float playerDistance, long adminPeer, Action<CampSiteSearch, string> done)
		{
			if (_state == null || _job != null || !IsOutcast(kind) || done == null)
			{
				return false;
			}
			SiteRules rules = new SiteRules
			{
				PlayerDistance = playerDistance,
				ThingsRadius = 12f,
				LocationRadius = 6f
			};
			Begin(kind, centers, all, settings, rules, adminPeer, done);
			return true;
		}

		private void Work()
		{
			SearchJob job = _job;
			if (job != null)
			{
				Stopwatch stopwatch = Stopwatch.StartNew();
				while (job.Search.State == CampSiteState.Running && stopwatch.Elapsed.TotalMilliseconds < 3.0)
				{
					job.Search.Run(job.World, 2);
				}
				job.Ms += stopwatch.Elapsed.TotalMilliseconds;
				job.Frames++;
				if (job.Search.State != CampSiteState.Running)
				{
					_job = null;
					Finish(job, RealClock.Now);
				}
			}
		}

		private void Finish(SearchJob job, double now)
		{
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			CampSiteSearch search = job.Search;
			string text = string.Format("{0}; {1} мс за {2} кадров, вызовов генератора {3}", search.Details, job.Ms.ToString("0", CultureInfo.InvariantCulture), job.Frames, job.World.Samples);
			if (job.Done != null)
			{
				job.Done(search, text);
				return;
			}
			bool flag = search.State == CampSiteState.Found;
			Vector3 center = default(Vector3);
			((Vector3)(ref center))..ctor(search.X, search.Height, search.Z);
			string text2 = (flag ? $"в {search.Distance:0} м от {search.Center.Name}, шаг {search.StageIndex + 1}" : "");
			switch (job.Kind)
			{
			case SearchKind.Probe:
				Plugin.Log.LogInfo((object)("Отступники: camp probe — " + text + "."));
				TellPeer(job.AdminPeer, "Пробный поиск места: " + search.Summary + ".");
				return;
			case SearchKind.Start:
				Plugin.Log.LogInfo((object)("Отступники: camp start — " + text + "."));
				if (!flag)
				{
					TellPeer(job.AdminPeer, "Лагерь не поставлен: " + search.Summary + " (подробно — журнал сервера).");
					return;
				}
				if (_pending != null)
				{
					TellPeer(job.AdminPeer, "Лагерь не поставлен: другой уже ставится.");
					return;
				}
				StartPending(center, search.Tier, scheduled: false, job.AdminPeer, text2 + " (по команде)", here: false, job.ForceMage);
				TellPeer(job.AdminPeer, $"Лагерь ставится в ({search.X:0}, {search.Z:0}), {RenegadeAdmin.TierName(search.Tier)}.");
				return;
			}
			if (!flag)
			{
				_state.NextAt = now + 300.0;
				Save();
				_lastWait = "места не нашлось (" + search.Summary + "); снова через " + CampPlanner.Minutes(300.0);
				Plugin.Log.LogInfo((object)("Отступники: лагерь — места не нашлось; снова через " + CampPlanner.Minutes(300.0) + ". " + text + "."));
			}
			else
			{
				_lastWait = "";
				if (_pending == null)
				{
					Plugin.Log.LogInfo((object)("Отступники: место лагеря по расписанию — " + text + "."));
					StartPending(center, search.Tier, scheduled: true, 0L, text2);
				}
			}
		}

		private string Hard(float x, float z, SearchJob job)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 p = new Vector3(x, 0f, z);
			if (Utils.LengthXZ(p) > 9700f)
			{
				return "край мира";
			}
			Vector3? val = CampWorld.StartTemple();
			if (val.HasValue && Utils.DistanceXZ(val.Value, p) < 300f)
			{
				return "стартовый алтарь";
			}
			float spacing = CampConfig.Spacing.Value;
			if (_state.Camps.Any((Camp c) => Utils.DistanceXZ(Center(c), p) < spacing) || (_pending != null && Utils.DistanceXZ(_pending.Center, p) < spacing))
			{
				return "другой лагерь";
			}
			if (job.Players.Any((CampWorld.PlayerInfo o) => Utils.DistanceXZ(o.Pos, p) < job.Rules.PlayerDistance))
			{
				return "игроки рядом";
			}
			CampZone zone = CampPlanner.ZoneOf(p.x, p.z);
			if (CampPlanner.NearZones(zone, job.Hunt, 1))
			{
				return "зоны Охоты";
			}
			if (NearEvent(zone, job.Events))
			{
				return "другое событие отступников";
			}
			if (CampWorld.NearTrial(p))
			{
				return "испытание Mine Portal";
			}
			if (CampWorld.NearLocation(p, job.Rules.LocationRadius))
			{
				return "локация";
			}
			return CampWorld.PlayerThings(p, job.Rules.ThingsRadius, PlaceMode.Schedule);
		}

		private static bool NearEvent(CampZone zone, HashSet<CampZone> events)
		{
			if (events == null || events.Count == 0)
			{
				return false;
			}
			for (int i = -1; i <= 1; i++)
			{
				for (int j = -1; j <= 1; j++)
				{
					if (events.Contains(new CampZone(zone.X + i, zone.Y + j)))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static string KindName(SearchKind kind)
		{
			return kind switch
			{
				SearchKind.Schedule => "лагерь по расписанию", 
				SearchKind.Start => "camp start", 
				SearchKind.Probe => "camp probe", 
				SearchKind.OutcastSchedule => "изгой по расписанию", 
				SearchKind.OutcastStart => "outcast start", 
				_ => "outcast probe", 
			};
		}

		internal static void TellPeer(long uid, string text)
		{
			ZNetPeer val = ((uid == 0L) ? null : ZNet.instance.GetPeer(uid));
			if (val != null)
			{
				CampWorld.Message(val, text);
			}
		}

		private void StartPending(Vector3 center, int tier, bool scheduled, long adminPeer, string where, bool here = false, bool forceMage = false)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			_pending = new Pending
			{
				ForceMage = forceMage,
				Center = center,
				Tier = tier,
				Seed = _rng.Next(1, int.MaxValue),
				Zones = CampWorld.ZonesAround(center, 14f),
				Since = Time.time,
				Scheduled = scheduled,
				Here = here,
				AdminPeer = adminPeer,
				Where = (where ?? "")
			};
			Plugin.Log.LogInfo((object)$"Отступники: место лагеря ({center.x:0}, {center.z:0}), тир {tier}, {where}; генерирую зоны ({_pending.Zones.Count}).");
		}

		private void TickPending(double now)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			Pending pending = _pending;
			if (pending == null)
			{
				return;
			}
			List<Vector2s> list = pending.Zones.Where((Vector2s z) => !CampWorld.IsZoneGenerated(z)).ToList();
			foreach (Vector2s item in list)
			{
				CampWorld.GenerateZone(item);
			}
			if (list.Any((Vector2s z) => !CampWorld.IsZoneGenerated(z)))
			{
				if (Time.time - pending.Since > 60f)
				{
					Abort(pending, now, $"зоны не сгенерировались за {60f:0} с");
				}
			}
			else
			{
				_pending = null;
				Build(pending, now);
			}
		}

		private void Abort(Pending p, double now, string why)
		{
			_pending = null;
			if (p.Scheduled)
			{
				_state.NextAt = now + 300.0;
				Save();
			}
			Plugin.Log.LogWarning((object)$"Отступники: лагерь в ({p.Center.x:0}, {p.Center.z:0}) не поставлен — {why}.");
			Tell(p, "Лагерь не поставлен: " + why + ".");
		}

		private void Tell(Pending p, string text)
		{
			ZNetPeer val = ((p.AdminPeer == 0L) ? null : ZNet.instance.GetPeer(p.AdminPeer));
			if (val != null)
			{
				CampWorld.Message(val, text);
			}
		}

		private void Build(Pending p, double now)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0333: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Unknown result type (might be due to invalid IL or missing references)
			//IL_0367: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
			string text = CampWorld.PlayerThings(p.Center, 12f, (!p.Here) ? PlaceMode.Schedule : PlaceMode.Here);
			if (text != null)
			{
				Abort(p, now, "рядом " + text);
				return;
			}
			if (p.Scheduled && CampWorld.Players().Any((CampWorld.PlayerInfo o) => Utils.DistanceXZ(o.Pos, p.Center) < CampConfig.MinPlayerDistance.Value))
			{
				Abort(p, now, "подошли игроки");
				return;
			}
			double num = CampPlanner.UniqueStart(now, _state.LastStart);
			string text2 = CampPlanner.IdOf(num);
			_state.LastStart = num;
			int num2 = CampWorld.ClearVegetation(p.Center, 13f);
			List<CampPiece> source = CampLayout.Build(p.Seed);
			int num3 = 0;
			int num4 = 0;
			Vector3 pos = default(Vector3);
			foreach (CampPiece item in source.Where((CampPiece i) => i.Kind == CampPieceKind.Fire || i.Kind == CampPieceKind.Piece))
			{
				GameObject val = PiecePrefab(item.Prefab);
				if ((Object)(object)val == (Object)null)
				{
					num4++;
					continue;
				}
				float num5 = p.Center.x + item.X;
				float num6 = p.Center.z + item.Z;
				((Vector3)(ref pos))..ctor(num5, CampWorld.Height(num5, num6) + item.Lift, num6);
				CampWorld.CreatePiece(val, pos, Quaternion.Euler(0f, item.Yaw, 0f), text2, (item.Kind == CampPieceKind.Fire) ? "fire" : "piece");
				num3++;
			}
			List<GarrisonSlot> list = CampPlanner.Garrison(new Random(p.Seed), CampConfig.Numbers(), p.Tier, p.ForceMage);
			CampPiece campPiece = source.First((CampPiece i) => i.Kind == CampPieceKind.LeaderPost);
			List<CampPiece> list2 = source.Where((CampPiece i) => i.Kind == CampPieceKind.Post).ToList();
			int num7 = 0;
			int num8 = 0;
			string text3 = "";
			List<ZDOID> list3 = new List<ZDOID>();
			_members[text2] = list3;
			Vector3 val2 = default(Vector3);
			foreach (GarrisonSlot item2 in list)
			{
				CampPiece campPiece2 = (item2.Leader ? campPiece : list2[num8++ % list2.Count]);
				float num9 = p.Center.x + campPiece2.X;
				float num10 = p.Center.z + campPiece2.Z;
				((Vector3)(ref val2))..ctor(num9, CampWorld.Height(num9, num10) + 0.5f, num10);
				Quaternion rotation = Quaternion.Euler(0f, CampLayout.YawToward(0f - campPiece2.X, 0f - campPiece2.Z), 0f);
				string error;
				ZDO val3 = RenegadeHost.Instance.SpawnAt(val2, rotation, p.Tier, item2.Level, item2.Role, item2.Leader, text2, (string)null, (Vector3?)new Vector3(num9, val2.y - 0.5f, num10), (string)null, outcast: false, item2.School, out error);
				if (val3 == null)
				{
					Plugin.Log.LogWarning((object)("Отступники: лагерь " + text2 + " — отступник не создан: " + error + "."));
				}
				else
				{
					num7++;
					list3.Add(val3.m_uid);
					if (item2.Leader)
					{
						text3 = val3.GetString(ZDOVars.s_overrideHoverName, "");
					}
				}
			}
			Camp camp = new Camp
			{
				Id = text2,
				StartedAt = num,
				Tier = p.Tier,
				X = p.Center.x,
				Y = CampWorld.Height(p.Center.x, p.Center.z),
				Z = p.Center.z,
				Seed = p.Seed,
				Leader = text3,
				Total = num7,
				ExpiresAt = num + (double)Mathf.Max(0.05f, CampConfig.LifetimeHours.Value) * 3600.0,
				Mages = CampPlanner.Mages(list)
			};
			if (num7 == 0)
			{
				_state.Camps.Add(camp);
				Remove(camp, "гарнизон не создан");
				Abort(p, now, "гарнизон не создан (см. журнал)");
				return;
			}
			_state.Camps.Add(camp);
			if (p.Scheduled)
			{
				_state.NextAt = now + (double)Mathf.Max(0.05f, CampConfig.IntervalHours.Value) * 3600.0;
			}
			Save();
			WriteBusy();
			string text4 = $"щитоносцев {list.Count((GarrisonSlot s) => s.Role == RenegadeRole.Shield)}, берсерков {list.Count((GarrisonSlot s) => s.Role == RenegadeRole.Berserk)}" + (camp.Mages.Any ? (", " + camp.Mages.Describe("вожак")) : "");
			Plugin.Log.LogInfo((object)($"Отступники: лагерь {text2} поставлен — тир {p.Tier}, ({p.Center.x:0}, {camp.Y:0}, {p.Center.z:0}), {p.Where}; " + string.Format("вожак {0}, отступников {1} ({2}, с вожаком); деталей {3}", (text3 == "") ? "?" : text3, num7, text4, num3) + string.Format("{0}, расчищено растительности {1}; срок {2:0.##} ч", (num4 > 0) ? $", пропущено {num4}" : "", num2, CampConfig.LifetimeHours.Value) + (p.Scheduled ? $"; следующий через {CampConfig.IntervalHours.Value:0.##} ч" : " (по команде)") + "."));
			Tell(p, $"Лагерь отступников {text2} поставлен: тир {p.Tier}, ({p.Center.x:0}, {p.Center.z:0}), вожак {text3}" + (camp.Mages.Any ? ("; " + camp.Mages.Describe("вожак")) : (p.ForceMage ? "; магов нет (тир без магов)" : "")) + ".");
		}

		private GameObject PiecePrefab(string name)
		{
			if (_pieces.TryGetValue(name, out var value))
			{
				return value;
			}
			GameObject val = ZNetScene.instance.GetPrefab(name);
			if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<ZNetView>() == (Object)null || (Object)(object)val.GetComponent<Piece>() == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Отступники: детали лагеря " + name + " нет в игре (или это не деталь) — пропускается."));
				val = null;
			}
			_pieces[name] = val;
			return val;
		}

		private void WriteBusy()
		{
			if (_busyPath == null || _state == null)
			{
				return;
			}
			List<CampZone> list = new List<CampZone>();
			foreach (Camp camp in _state.Camps)
			{
				list.AddRange(CampPlanner.BusyZones(camp.X, camp.Z, 12.0));
			}
			if (_pending != null)
			{
				list.AddRange(CampPlanner.BusyZones(_pending.Center.x, _pending.Center.z, 12.0));
			}
			list.AddRange(OutcastHost.Instance.BusyZones());
			list.AddRange(PirateHost.Instance.BusyZones());
			string text = CampPlanner.BusyText(list);
			if (text == _busyText)
			{
				return;
			}
			try
			{
				File.WriteAllText(_busyPath, text);
				_busyText = text;
			}
			catch (Exception e)
			{
				LogOnce("busy", e);
			}
		}

		public string Status()
		{
			CampPlanner.ShouldStart(RealClock.Now, playersOnSurface: (from p in CampWorld.Players()
				where p.PlayerId != 0L && CampWorld.OnSurface(p.Pos)
				select p).ToList().Count, state: _state, n: CampConfig.Numbers(), modEnabled: Plugin.Enabled.Value, serverStartedAt: _serverStartedAt, reason: out var reason);
			string text = ((_pending != null) ? "лагерь ставится (генерируются зоны)" : ((_job != null) ? ("ищет место (" + KindName(_job.Kind) + "): " + _job.Search.Summary) : (reason ?? ((_lastWait != "") ? _lastWait : "следующий — при ближайшей проверке"))));
			int num = _state.Camps.Count((Camp c) => c.Cleared);
			return $"лагерей {_state.Camps.Count} (разбитых {num}), открытый тир {OpenTier()}; " + "в лагерях " + MageRoster.Summary(_state.Camps.Select((Camp c) => c.Mages)) + "; расписание: " + text + ".\n" + string.Join(", ", CampConfig.Lines());
		}

		public IReadOnlyList<CampInfo> Camps()
		{
			double now = RealClock.Now;
			CampNumbers numbers = CampConfig.Numbers();
			return _state.Camps.Select((Camp c) => new CampInfo
			{
				Id = c.Id,
				Tier = c.Tier,
				X = c.X,
				Z = c.Z,
				Leader = c.Leader,
				Alive = Members(c).Count,
				Total = c.Total,
				AgeSeconds = now - c.StartedAt,
				Cleared = c.Cleared,
				LeftSeconds = (c.Cleared ? (c.ClearedAt + (double)numbers.CleanupMinutes * 60.0) : c.ExpiresAt) - now,
				Mages = c.Mages,
				MagesAlive = (c.Mages.Any ? Members(c).Count((ZDO z) => z.GetInt("sansara.Renegades.role", 0) == 4) : (-1))
			}).ToList();
		}

		public string Start(CampStartRequest request, string adminId)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: 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_010a: 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_0116: 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_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled.Value)
			{
				return "отступники выключены (General.Enabled = false)";
			}
			if (_pending != null)
			{
				return "лагерь уже ставится (генерируются зоны) — подождите несколько секунд";
			}
			if (_job != null && !request.Here)
			{
				return "место уже ищется (" + KindName(_job.Kind) + ": " + _job.Search.Summary + ") — подождите несколько секунд";
			}
			List<CampWorld.PlayerInfo> list = CampWorld.Players();
			CampWorld.PlayerInfo playerInfo = list.FirstOrDefault(delegate(CampWorld.PlayerInfo p)
			{
				if (!string.IsNullOrEmpty(adminId))
				{
					ISocket socket = p.Peer.m_socket;
					return ((socket != null) ? socket.GetHostName() : null) == adminId;
				}
				return false;
			});
			if (request.Here)
			{
				if (playerInfo.Peer == null)
				{
					return "вашего персонажа нет в мире (команда из файла?): here ставит лагерь перед администратором";
				}
				Vector3 val = ZDOMan.instance.GetZDO(playerInfo.Peer.m_characterID).GetRotation() * Vector3.forward;
				val.y = 0f;
				val = ((((Vector3)(ref val)).sqrMagnitude < 1E-06f) ? Vector3.forward : ((Vector3)(ref val)).normalized);
				Vector3 val2 = playerInfo.Pos + val * 60f;
				if (!CampWorld.OnSurface(playerInfo.Pos))
				{
					return "вы не на поверхности мира";
				}
				float num = CampWorld.Height(val2.x, val2.z);
				if (num < CampWorld.WaterLevel + 0.5f)
				{
					return $"перед вами вода (({val2.x:0}, {val2.z:0})) — повернитесь к суше";
				}
				string text = CampWorld.PlayerThings(val2, 12f, PlaceMode.Here);
				if (text != null)
				{
					return $"не поставлен: у ({val2.x:0}, {val2.z:0}) {text}";
				}
				int tier = ((request.Tier > 0) ? request.Tier : Math.Max(1, CampPlanner.TierOfBiome(CampWorld.BiomeAt(val2.x, val2.z))));
				if (request.Mage && !RenegadeStaffs.TierHasMages(tier))
				{
					return "не поставлен: маги — только на тирах 6–7 (Туманные земли и Пепельные земли); перед вами " + RenegadeAdmin.TierName(tier) + " — укажите тир 6 или 7";
				}
				val2.y = num;
				StartPending(val2, tier, scheduled: false, playerInfo.Peer.m_uid, $"в {60f:0} м перед {playerInfo.Name} (here)", here: true, request.Mage);
				string text2 = CampObstacles.ReliefWarning(CampWorld.ReliefSpread(val2, 12f), CampConfig.MaxRelief.Value);
				return $"лагерь ставится в ({val2.x:0}, {val2.z:0}), {RenegadeAdmin.TierName(tier)}: генерируются зоны, о готовности — сообщение и журнал" + ((text2 != null) ? ("\n" + text2) : "");
			}
			List<CampWorld.PlayerInfo> list2 = list.Where((CampWorld.PlayerInfo p) => p.PlayerId != 0L && CampWorld.OnSurface(p.Pos) && !CampWorld.NearTrial(p.Pos)).ToList();
			if (list2.Count == 0)
			{
				return "нет игроков на поверхности мира: место ищется в кольце вокруг них (или camp start here)";
			}
			StartSearch(SearchKind.Start, list2.Select(SiteCenter).ToList(), list, OpenTier(), request.Tier, playerInfo.Peer?.m_uid ?? 0);
			if (_job != null)
			{
				_job.ForceMage = request.Mage;
			}
			return string.Format("ищу место каскадом вокруг игроков ({0}){1}: ", list2.Count, (request.Tier > 0) ? (", " + RenegadeAdmin.TierName(request.Tier)) : "") + "результат и постановка — сообщением и в журнале через несколько секунд";
		}

		public string Probe(CampProbeRequest request, string adminId)
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if (_job != null)
			{
				return "место уже ищется (" + KindName(_job.Kind) + ": " + _job.Search.Summary + ") — подождите несколько секунд";
			}
			List<CampWorld.PlayerInfo> list = CampWorld.Players();
			CampWorld.PlayerInfo p = list.FirstOrDefault(delegate(CampWorld.PlayerInfo playerInfo)
			{
				if (!string.IsNullOrEmpty(adminId))
				{
					ISocket socket = playerInfo.Peer.m_socket;
					return ((socket != null) ? socket.GetHostName() : null) == adminId;
				}
				return false;
			});
			CampSiteCenter campSiteCenter;
			if (request.HasPoint)
			{
				campSiteCenter = new CampSiteCenter
				{
					Name = $"точки ({request.X:0}, {request.Z:0})",
					X = request.X,
					Z = request.Z,
					Biome = CampWorld.BiomeAt(request.X, request.Z)
				};
			}
			else
			{
				if (p.Peer == null)
				{
					return "вашего персонажа нет в мире (команда из файла?): укажите точку — camp probe [x z] [тир 1-7]";
				}
				if (!CampWorld.OnSurface(p.Pos))
				{
					return "вы не на поверхности мира: укажите точку — camp probe [x z] [тир 1-7]";
				}
				campSiteCenter = SiteCenter(p);
			}
			int num = OpenTier();
			int num2 = ((request.Tier > 0) ? request.Tier : num);
			StartSearch(SearchKind.Probe, new List<CampSiteCenter> { campSiteCenter }, list, num2, 0, p.Peer?.m_uid ?? 0);
			return $"пробный поиск места вокруг {campSiteCenter.Name} ({campSiteCenter.Biome}), открытый тир {num2}" + ((request.Tier > 0) ? $" (задан; в мире {num})" : "") + ": лагерь не ставится; результат — в журнале" + ((p.Peer != null) ? " и сообщением" : "") + " через несколько секунд";
		}

		public string Stop(string id)
		{
			if (string.Equals(id, "all", StringComparison.OrdinalIgnoreCase) || id == "все")
			{
				bool flag = _pending != null;
				_pending = null;
				List<Camp> list = _state.Camps.ToList();
				int num = list.Sum((Camp c) => Remove(c, "по команде"));
				if (list.Count != 0 || flag)
				{
					return string.Format("убрано лагерей {0}{1}, объектов и существ {2}, без наград", list.Count, flag ? " (и отменён ставящийся)" : "", num);
				}
				return "лагерей нет";
			}
			Camp camp = _state.Find(id);
			if (camp == null)
			{
				return "лагеря " + id + " нет" + ((_state.Camps.Count > 0) ? ("; есть: " + string.Join(", ", _state.Camps.Select((Camp c) => c.Id))) : "");
			}
			int num2 = Remove(camp, "по команде");
			return $"лагерь {id} убран: объектов и существ {num2}, без наград";
		}
	}
	internal static class CampWorld
	{
		private enum Kind
		{
			Other,
			Player,
			Piece,
			Tombstone,
			ItemStand,
			Character
		}

		internal struct PlayerInfo
		{
			public ZNetPeer Peer;

			public long PlayerId;

			public string Name;

			public Vector3 Pos;
		}

		internal const string PieceKey = "sansara.Renegades.campPiece";

		private static readonly Dictionary<int, Kind> Kinds = new Dictionary<int, Kind>();

		private static readonly Dictionary<int, bool> Clearables = new Dictionary<int, bool>();

		private static readonly List<ZDO> Found = new List<ZDO>();

		private static List<ZDO>[] _bySector;

		private static FieldInfo _ewpSkip;

		private static bool _ewpChecked;

		private static MethodInfo _isZoneGenerated;

		private static MethodInfo _spawnZone;

		private static Vector3? _temple;

		private static bool _templeChecked;

		private static readonly PlatformUserID ServerUser = new PlatformUserID("Steam_0");

		internal static float WaterLevel => ZoneSystem.instance.m_waterLevel;

		private static FieldInfo EwpSkip
		{
			get
			{
				if (!_ewpChecked)
				{
					_ewpChecked = true;
					Type type = AccessTools.TypeByName("ExpandWorld.Prefab.HandleCreated");
					_ewpSkip = ((type == null) ? null : AccessTools.Field(type, "Skip"));
				}
				return _ewpSkip;
			}
		}

		internal static void Reset()
		{
			_bySector = null;
			_temple = null;
			_templeChecked = false;
			Kinds.Clear();
			Clearables.Clear();
		}

		internal static string WorldFileName()
		{
			return string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars()));
		}

		internal static List<PlayerInfo> Players()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerInfo> list = new List<PlayerInfo>();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				ZDO val = (((ZDOID)(ref peer.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer.m_characterID));
				if (val != null)
				{
					list.Add(new PlayerInfo
					{
						Peer = peer,
						PlayerId = val.GetLong(ZDOVars.s_playerID, 0L),
						Name = (peer.m_playerName ?? ""),
						Pos = val.GetPosition()
					});
				}
			}
			return list;
		}

		internal static bool OnSurface(Vector3 pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return pos.y < 1000f;
		}

		internal static string BiomeAt(float x, float z)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			return ((object)WorldGenerator.instance.GetBiome(x, z, 0.02f, false)/*cast due to .constrained prefix*/).ToString();
		}

		internal static float Height(float x, float z)
		{
			return WorldGenerator.instance.GetHeight(x, z);
		}

		internal static string Ground(Vector3 center, float radius, float maxRelief, bool sameBiome, out float height)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			height = Height(center.x, center.z);
			float num = WaterLevel + 1.5f;
			float num2 = float.MaxValue;
			float num3 = float.MinValue;
			Biome biome = WorldGenerator.instance.GetBiome(center.x, center.z, 0.02f, false);
			for (float num4 = 0f - radius; num4 <= radius; num4 += 3f)
			{
				for (float num5 = 0f - radius; num5 <= radius; num5 += 3f)
				{
					if (!(num4 * num4 + num5 * num5 > radius * radius))
					{
						float num6 = center.x + num4;
						float num7 = center.z + num5;
						float num8 = Height(num6, num7);
						if (num8 < num)
						{
							return "вода";
						}
						if (sameBiome && WorldGenerator.instance.GetBiome(num6, num7, 0.02f, false) != biome)
						{
							return "край биома";
						}
						num2 = Mathf.Min(num2, num8);
						num3 = Mathf.Max(num3, num8);
					}
				}
			}
			if (!(num3 - num2 > maxRelief))
			{
				return null;
			}
			return "крутой склон";
		}

		internal static List<Vector2s> ZonesAround(Vector3 center, float radius)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			List<Vector2s> list = new List<Vector2s>();
			Vector2s zone = ZoneSystem.GetZone(center - new Vector3(radius, 0f, radius));
			Vector2s zone2 = ZoneSystem.GetZone(center + new Vector3(radius, 0f, radius));
			for (int i = zone.x; i <= zone2.x; i++)
			{
				for (int j = zone.y; j <= zone2.y; j++)
				{
					list.Add(new Vector2s(i, j));
				}
			}
			return list;
		}

		internal static List<ZDO> ZoneObjects(Vector2s zone)
		{
			//IL_002f: 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)
			if (_bySector == null)
			{
				_bySector = (List<ZDO>[])AccessTools.Field(typeof(ZDOMan), "m_objectsBySector").GetValue(ZDOMan.instance);
			}
			uint sector = ZoneSystem.SectorToIndex(zone).Sector;
			List<ZDO> list = ((sector < _bySector.Length) ? _bySector[sector] : null);
			if (list == null)
			{
				return new List<ZDO>();
			}
			return new List<ZDO>(list);
		}

		private static Kind KindOf(int hash)
		{
			if (!Kinds.TryGetValue(hash, out var value))
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(hash);
				value = ((!((Object)(object)prefab == (Object)null)) ? (((Object)(object)prefab.GetComponent<Player>() != (Object)null) ? Kind.Player : (((Object)(object)prefab.GetComponent<TombStone>() != (Object)null) ? Kind.Tombstone : (((Object)(object)prefab.GetComponent<ItemStand>() != (Object)null && (Object)(object)prefab.GetComponent<Piece>() == (Object)null) ? Kind.ItemStand : (((Object)(object)prefab.GetComponent<Piece>() != (Object)null) ? Kind.Piece : (((Object)(object)prefab.GetComponent<Character>() != (Object)null) ? Kind.Character : Kind.Other))))) : Kind.Other);
				Kinds[hash] = value;
			}
			return value;
		}

		internal static string PlayerThings(Vector3 pos, float radius, PlaceMode mode)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Vector2s> hashSet = new HashSet<Vector2s>();
			double minPlayerDistance = CampConfig.MinPlayerDistance.Value;
			Vector2s val = default(Vector2s);
			foreach (Vector2s item in ZonesAround(pos, radius))
			{
				for (int i = -1; i <= 1; i++)
				{
					for (int j = -1; j <= 1; j++)
					{
						((Vector2s)(ref val))..ctor(item.x + i, item.y + j);
						if (!hashSet.Add(val))
						{
							continue;
						}
						foreach (ZDO item2 in ZoneObjects(val))
						{
							ObstacleKind kind;
							switch (KindOf(item2.GetPrefab()))
							{
							case Kind.Piece:
								if (item2.GetLong(ZDOVars.s_creator, 0L) == 0L)
								{
									continue;
								}
								kind = ObstacleKind.PlayerPiece;
								break;
							case Kind.Tombstone:
								kind = ObstacleKind.Tombstone;
								break;
							case Kind.Player:
								kind = ObstacleKind.Player;
								break;
							case Kind.ItemStand:
								if (item2.GetInt(ZDOVars.s_item, 0) == 0)
								{
									continue;
								}
								kind = ObstacleKind.ItemStand;
								break;
							case Kind.Character:
								if (!item2.GetBool(ZDOVars.s_tamed, false))
								{
									continue;
								}
								kind = ObstacleKind.Tamed;
								break;
							default:
								continue;
							}
							string text = CampObstacles.Reason(kind, mode, Utils.DistanceXZ(item2.GetPosition(), pos), radius, minPlayerDistance);
							if (text != null)
							{
								return text;
							}
						}
					}
				}
			}
			return null;
		}

		internal static float ReliefSpread(Vector3 center, float radius)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			float num2 = float.MinValue;
			for (float num3 = 0f - radius; num3 <= radius; num3 += 3f)
			{
				for (float num4 = 0f - radius; num4 <= radius; num4 += 3f)
				{
					if (!(num3 * num3 + num4 * num4 > radius * radius))
					{
						float num5 = Height(center.x + num3, center.z + num4);
						num = Mathf.Min(num, num5);
						num2 = Mathf.Max(num2, num5);
					}
				}
			}
			return num2 - num;
		}

		internal static bool NearLocation(Vector3 pos, float radius)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			foreach (Vector2s item in ZonesAround(pos, radius + 64f))
			{
				if (ZoneSystem.instance.m_locationInstances.TryGetValue(item, out var value) && Utils.DistanceXZ(value.m_position, pos) < radius + 40f)
				{
					return true;
				}
			}
			return false;
		}

		internal static bool NearTrial(Vector3 pos)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			string path = Path.Combine(Paths.ConfigPath, "sansara.MinePortal", WorldFileName() + ".slots.txt");
			if (!File.Exists(path))
			{
				return false;
			}
			Vector2s zone = ZoneSystem.GetZone(pos);
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
				if (array2.Length == 3 && int.TryParse(array2[1], out var result) && int.TryParse(array2[2], out var result2) && Math.Abs(zone.x - result) <= 6 && Math.Abs(zone.y - result2) <= 6)
				{
					return true;
				}
			}
			return false;
		}

		internal static List<CampZone> BountyHuntBusy()
		{
			string path = Path.Combine(Paths.ConfigPath, "sansara.BountyHunt", WorldFileName() + ".busy.txt");
			if (!File.Exists(path))
			{
				return new List<CampZone>();
			}
			return CampPlanner.ParseZones(File.ReadAllText(path));
		}

		internal static Vector3? StartTemple()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if (!_templeChecked)
			{
				_templeChecked = true;
				foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in ZoneSystem.instance.m_locationInstances)
				{
					ZoneLocation location = locationInstance.Value.m_location;
					if (((location == null) ? null : ((!string.IsNullOrEmpty(location.m_prefabName)) ? location.m_prefabName : location.m_prefab.Name)) == "StartTemple")
					{
						_temple = locationInstance.Value.m_position;
						break;
					}
				}
			}
			return _temple;
		}

		internal static bool IsZoneGenerated(Vector2s zone)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if (_isZoneGenerated == null)
			{
				_isZoneGenerated = AccessTools.Method(typeof(ZoneSystem), "IsZoneGenerated", new Type[1] { typeof(Vector2s) }, (Type[])null);
			}
			return (bool)_isZoneGenerated.Invoke(ZoneSystem.instance, new object[1] { zone });
		}

		internal static void GenerateZone(Vector2s zone)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (_spawnZone == null)
			{
				_spawnZone = AccessTools.Method(typeof(ZoneSystem), "SpawnZone", new Type[3]
				{
					typeof(Vector2s),
					typeof(SpawnMode),
					typeof(GameObject).MakeByRefType()
				}, (Type[])null);
			}
			_spawnZone.Invoke(ZoneSystem.instance, new object[3]
			{
				zone,
				(object)(SpawnMode)2,
				null
			});
		}

		private static bool Clearable(ZDO zdo)
		{
			int prefab = zdo.GetPrefab();
			if (!Clearables.TryGetValue(prefab, out var value))
			{
				GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
				value = (Object)(object)prefab2 != (Object)null && (Object)(object)prefab2.GetComponent<Piece>() == (Object)null && (Object)(object)prefab2.GetComponent<Character>() == (Object)null && (Object)(object)prefab2.GetComponent<ItemDrop>() == (Object)null && (Object)(object)prefab2.GetComponent<TombStone>() == (Object)null && ((Object)(object)prefab2.GetComponent<TreeBase>() != (Object)null || (Object)(object)prefab2.GetComponent<TreeLog>() != (Object)null || (Object)(object)prefab2.GetComponent<Destructible>() != (Object)null || (Object)(object)prefab2.GetComponent<MineRock>() != (Object)null || (Object)(object)prefab2.GetComponent<MineRock5>() != (Object)null || (Object)(object)prefab2.GetComponent<Pickable>() != (Object)null || (Object)(object)prefab2.GetComponent<PickableItem>() != (Object)null);
				Clearables[prefab] = value;
			}
			return value;
		}

		internal static int ClearVegetation(Vector3 center, float radius)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (Vector2s item in ZonesAround(center, radius))
			{
				foreach (ZDO item2 in ZoneObjects(item))
				{
					if (Utils.DistanceXZ(item2.GetPosition(), center) <= radius && Clearable(item2) && item2.GetString("sansara.Renegades.camp", "") == "")
					{
						Destroy(item2);
						num++;
					}
				}
			}
			return num;
		}

		internal static ZDO CreateRaw(Vector3 pos, int hash)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			object obj = EwpSkip?.GetValue(null);
			try
			{
				EwpSkip?.SetValue(null, true);
				return ZDOMan.instance.CreateNewZDO(pos, hash);
			}
			finally
			{
				if (obj != null)
				{
					EwpSkip.SetValue(null, obj);
				}
			}
		}

		private static ZDO Create(GameObject prefab, Vector3 pos, Quaternion rot, string camp, string kind)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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)
			ZNetView component = prefab.GetComponent<ZNetView>();
			int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name);
			ZDO obj = CreateRaw(pos, stableHashCode);
			obj.Persistent = component.m_persistent;
			obj.Type = component.m_type;
			obj.Distant = component.m_distant;
			obj.SetPrefab(stableHashCode);
			obj.SetRotation(rot);
			obj.Set("sansara.Renegades.camp", camp);
			obj.Set("sansara.Renegades.campPiece", kind);
			return obj;
		}

		internal static ZDO CreatePiece(GameObject prefab, Vector3 pos, Quaternion rot, string camp, string kind)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = Create(prefab, pos, rot, camp, kind);
			val.Set("HasFields", true);
			val.Set("HasFieldsPiece", true);
			val.Set("Piece.m_canBeRemoved", false);
			if ((Object)(object)prefab.GetComponent<WearNTear>() != (Object)null)
			{
				val.Set("HasFieldsWearNTear", true);
				val.Set("WearNTear.m_noRoofWear", false);
				val.Set("WearNTear.m_noSupportWear", false);
				val.Set("WearNTear.m_health", -1f);
			}
			if ((Object)(object)prefab.GetComponent<Fireplace>() != (Object)null)
			{
				val.Set("HasFieldsFireplace", true);
				val.Set("Fireplace.m_infiniteFuel", true);
				val.Set("Fireplace.m_disableCoverCheck", true);
			}
			return val;
		}

		internal static ZDO CreateChest(GameObject prefab, Vector3 pos, Quaternion rot, string camp, string item, int amount, out int added)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			added = 0;
			Container component = prefab.GetComponent<Container>();
			Inventory val = new Inventory("camp", (Sprite)null, component.m_width, component.m_height);
			GameObject val2 = ((amount > 0) ? ObjectDB.instance.GetItemPrefab(item) : null);
			if (amount > 0 && ((Object)(object)val2 == (Object)null || (Object)(object)val2.GetComponent<ItemDrop>() == (Object)null))
			{
				Plugin.Log.LogWarning((object)("Отступники: предмета " + item + " нет в ObjectDB — сундук лагеря " + camp + " пуст."));
			}
			else if ((Object)(object)val2 != (Object)null)
			{
				int num = amount;
				int num2 = Mathf.Max(1, val2.GetComponent<ItemDrop>().m_itemData.m_shared.m_maxStackSize);
				while (num > 0)
				{
					int num3 = Mathf.Min(num, num2);
					if (!val.AddItem(val2, num3))
					{
						Plugin.Log.LogWarning((object)$"Отступники: сундук лагеря {camp} полон — {item} ×{num} не поместились.");
						break;
					}
					num -= num3;
					added += num3;
				}
			}
			ZDO obj = CreatePiece(prefab, pos, rot, camp, "chest");
			ZPackage val3 = new ZPackage();
			val.Save(val3);
			obj.Set(ZDOVars.s_items, val3.GetArray());
			obj.Set(ZDOVars.s_addedDefaultItems, true);
			return obj;
		}

		internal static List<ZDO> CampObjects(Vector3 center, string camp)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			Found.Clear();
			ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(center), new SimulationDistance(1, 0, true), Found, (List<ZDO>)null);
			return Found.Where((ZDO z) => z.GetString("sansara.Renegades.campPiece", "") != "" && z.GetString("sansara.Renegades.camp", "") == camp).ToList();
		}

		internal static void Destroy(ZDO zdo)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			ClearNoLootPatch.Removing.Add(zdo.m_uid);
			RenegadeHost.OwnDestroyed.Add(zdo.m_uid);
			zdo.SetOwner(ZDOMan.GetSessionID());
			ZDOMan.instance.DestroyZDO(zdo);
		}

		internal static void Message(ZNetPeer peer, string text)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "ShowMessage", new object[2] { 2, text });
		}

		internal static void MessageAll(string text)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ShowMessage", new object[2] { 2, text });
		}

		internal static void PingAll(Vector3 pos, string name)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "ChatMessage", new object[4]
			{
				pos,
				3,
				(object)new UserInfo
				{
					Name = name,
					UserId = ServerUser
				},
				""
			});
		}

		internal static string OtherHunts()
		{
			string text = WorldFileName();
			bool bountyHunt = OutcastPlanner.BountyHuntActive(SafeRead(Path.Combine(Paths.ConfigPath, "sansara.BountyHunt", text + ".hunt")));
			bool pathHunt = OutcastPlanner.PathHuntActive(SafeRead(Path.Combine(Paths.ConfigPath, "sansara.Path", text + ".hunt.txt")));
			return OutcastPlanner.OtherHunts(bountyHunt, pathHunt);
		}

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

		internal static void Ping(ZNetPeer peer, Vector3 pos, string name)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "ChatMessage", new object[4]
			{
				pos,
				3,
				(object)new UserInfo
				{
					Name = name,
					UserId = ServerUser
				},
				""
			});
		}
	}
	internal static class ClanToken
	{
		internal const string Prefab = "SansaraClanToken";

		private const string BasePrefab = "SilverNecklace";

		internal static void Register()
		{
			PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabs;
		}

		private static void OnVanillaPrefabs()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to i