Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Renegades v0.5.0
plugins/Renegades.dll
Decompiled 10 hours ago
The result has been truncated due to the large size, download it to view full contents!
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