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 JunkBuyer v0.2.2
plugins/JunkBuyer.dll
Decompiled 6 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JunkBuyer.Core; using Microsoft.CodeAnalysis; using Sansara.Admin; using Sansara.Clock; 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("JunkBuyer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.2.0")] [assembly: AssemblyInformationalVersion("0.2.2+ee6a46e381b50add1bc5a1ee15a3276bde7e4e27")] [assembly: AssemblyProduct("JunkBuyer")] [assembly: AssemblyTitle("JunkBuyer")] [assembly: AssemblyVersion("0.2.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.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 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 JunkBuyer { internal static class Buyer { private sealed class Outcome { public int Items; public int Coins; public readonly Dictionary<string, int> Metal = new Dictionary<string, int>(); public Refusal Refusal; public string RefusedItem = ""; public string RefusedDetail = ""; public void Refuse(Refusal r, ItemData item, string detail = "") { if (Refusal == Refusal.None) { Refusal = r; RefusedItem = Name(item); RefusedDetail = detail; } } } private sealed class Depositor { public string Id = ""; public long PlayerId; public string Name = ""; } private static DailyLedger _ledger = new DailyLedger(); private static string _path; private static Dictionary<string, int> _prices = new Dictionary<string, int>(); private static HashSet<string> _excluded = new HashSet<string>(); private static HashSet<string> _metals = new HashSet<string>(); private static readonly Dictionary<ZDOID, string> Handled = new Dictionary<ZDOID, string>(); private static bool _stallMissingReported; private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static readonly Dictionary<ZDOID, Depositor> Last = new Dictionary<ZDOID, Depositor>(); internal static readonly string[] Verbs = new string[4] { "status", "help", "reset", "ui" }; private const string HelpText = "status — начало текущего периода, игроки со счётчиками, выдано монет и разобрано вещей\nreset <игрок|@ID> — сбросить счётчики игрока за текущий период\nui — данные для окна Sansara Admin (машинный ответ): игроки со счётчиками периода"; public static void Load(Dictionary<string, int> prices, HashSet<string> excluded, HashSet<string> metals) { _prices = prices; _excluded = excluded; _metals = metals; string text = Path.Combine(Paths.ConfigPath, "sansara.JunkBuyer"); Directory.CreateDirectory(text); string text2 = string.Concat(ZNet.instance.GetWorldName().Split(Path.GetInvalidFileNameChars())); _path = Path.Combine(text, text2 + ".daily"); try { _ledger = (File.Exists(_path) ? DailyLedger.Parse(File.ReadAllText(_path)) : new DailyLedger()); } catch (FormatException ex) { File.Move(_path, _path + "." + DateTime.UtcNow.ToString("yyyyMMddHHmmss") + ".bad"); _ledger = new DailyLedger(); Plugin.Log.LogWarning((object)("Скупщик: файл счётчиков повреждён (" + ex.Message + ") — отложен как .bad, счёт заново.")); } Plugin.Log.LogInfo((object)$"Скупщик: период {_ledger.Day}, игроков в счётчиках {_ledger.Players.Count}."); } private static bool Save() { try { string text = _path + ".tmp"; File.WriteAllText(text, _ledger.Serialize()); if (File.Exists(_path)) { File.Replace(text, _path, null); } else { File.Move(text, _path); } return true; } catch (Exception ex) { Plugin.Log.LogError((object)("Скупщик: счётчики не сохранены: " + ex.Message)); return false; } } private static bool FindDepositor(ZDO chest, out Depositor d) { //IL_001a: 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) d = new Depositor(); if (chest.GetOwner() == ZDOMan.GetSessionID()) { if (!Last.TryGetValue(chest.m_uid, out var value) || !Stall.Online(value.PlayerId, out var id, out var name)) { return false; } value.Id = id; value.Name = name; d = value; return true; } if (!Stall.Depositor(chest, out var id2, out var playerId, out var name2)) { return false; } d = new Depositor { Id = id2, PlayerId = playerId, Name = name2 }; Last[chest.m_uid] = d; return true; } private static long Today() { return DailyLedger.DayOf(RealClock.Now, Plugin.ResetOffsetHours.Value, (double)Plugin.ResetHours.Value * 60.0); } public static void Tick() { long num = Today(); if (num != _ledger.Day) { _ledger.Roll(num); Save(); Plugin.Log.LogInfo((object)$"Скупщик: новый период {num}, лимиты сброшены."); } Dictionary<string, ZDO> dictionary = Stall.Find(); ZDO value; bool flag = dictionary.TryGetValue("buyer.chest.sell", out value); ZDO value2; bool flag2 = dictionary.TryGetValue("buyer.chest.salvage", out value2); if (!flag || !flag2) { if (!_stallMissingReported) { _stallMissingReported = true; string text = string.Join(", ", new string[2] { flag ? null : "buyer.chest.sell", flag2 ? null : "buyer.chest.salvage" }.Where((string r) => r != null)); Plugin.Log.LogInfo((object)("Скупщик: в Чертоге славы (Portal Hall 0.2.3) не найдено: " + text + " — жду.")); } } else { _stallMissingReported = false; } dictionary.TryGetValue("buyer.sign.sell", out var value3); dictionary.TryGetValue("buyer.sign.salvage", out var value4); Stall.SetSign(value3, BuyerTexts.SellSign(Plugin.CoinLimit.Value, Plugin.ResetHours.Value)); Stall.SetSign(value4, BuyerTexts.SalvageSign(Plugin.SalvageFraction.Value, Plugin.SalvageLimit.Value, Plugin.ResetHours.Value)); if (flag) { Guard(value, selling: true, num); } if (flag2) { Guard(value2, selling: false, num); } } private static void Guard(ZDO chest, bool selling, long day) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { Process(chest, selling, day); } catch (Exception arg) { Handled[chest.m_uid] = chest.DataRevision + ":" + day; Plugin.Log.LogError((object)string.Format("Скупщик: ошибка в сундуке {0}: {1}", selling ? "скупки" : "разборки", arg)); } } private static void Process(ZDO chest, bool selling, long day) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) if (chest.GetInt(ZDOVars.s_inUse, 0) == 1) { return; } byte[] byteArray = chest.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray == null || byteArray.Length == 0) { return; } string text = chest.DataRevision + ":" + day; if ((Handled.TryGetValue(chest.m_uid, out var value) && value == text) || !FindDepositor(chest, out var d)) { return; } string id = d.Id; string name = d.Name; long playerId = d.PlayerId; Inventory val = Stall.Load(chest, byteArray); if (val == null) { Handled[chest.m_uid] = text; return; } LedgerEntry ledgerEntry = _ledger.For(id, name); int coins = ledgerEntry.Coins; int salvaged = ledgerEntry.Salvaged; Outcome outcome; try { outcome = (selling ? Sell(val, ledgerEntry) : Salvage(val, ledgerEntry)); } catch { ledgerEntry.Coins = coins; ledgerEntry.Salvaged = salvaged; throw; } if (outcome.Items > 0) { if (!Save()) { ledgerEntry.Coins = coins; ledgerEntry.Salvaged = salvaged; return; } Stall.Save(chest, val); Plugin.Log.LogInfo((object)(selling ? $"Скупщик: {name} ({id}) продал {outcome.Items} за {outcome.Coins} монет; за период {ledgerEntry.Coins}/{Plugin.CoinLimit.Value}." : string.Format("Скупщик: {0} ({1}) разобрал {2} → {3}; за период {4}/{5}.", name, id, outcome.Items, string.Join(", ", outcome.Metal.Select((KeyValuePair<string, int> m) => m.Value + " " + m.Key)), ledgerEntry.Salvaged, Plugin.SalvageLimit.Value))); } List<string> list = new List<string>(); if (outcome.Items > 0) { list.Add(selling ? BuyerTexts.Sold(outcome.Items, outcome.Coins, Plugin.CoinLimit.Value - ledgerEntry.Coins) : BuyerTexts.Salvaged(outcome.Items, string.Join(", ", outcome.Metal.Select((KeyValuePair<string, int> m) => m.Value + " " + ItemNames.Of(m.Key))), Plugin.SalvageLimit.Value - ledgerEntry.Salvaged)); } if (outcome.Refusal != Refusal.None) { list.Add(BuyerTexts.Refused(outcome.Refusal, outcome.RefusedItem, outcome.RefusedDetail)); } if (list.Count > 0) { Stall.Message(playerId, string.Join("\n", list)); } Handled[chest.m_uid] = chest.DataRevision + ":" + day; } private static Outcome Sell(Inventory inventory, LedgerEntry entry) { Outcome outcome = new Outcome(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins"); foreach (ItemData item in inventory.GetAllItems().ToList()) { string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); byte[] snapshot = Stall.Snapshot(inventory); int coins = entry.Coins; int items = outcome.Items; int coins2 = outcome.Coins; try { if (text == "Coins") { continue; } bool flag = item.m_shared.m_maxStackSize > 1; bool special = item.m_customData != null && item.m_customData.Count > 0; int coins3; Refusal refusal = PriceList.Quote(text, flag, special, !flag && HasMetal(item), item.m_shared.m_value, item.m_quality, _prices, _excluded, Plugin.DefaultPrice.Value, Plugin.DefaultItemPrice.Value, out coins3); if (refusal != Refusal.None) { outcome.Refuse(refusal, item); continue; } int num = SellRules.Sellable(item.m_stack, coins3, Plugin.CoinLimit.Value - entry.Coins); if (num == 0) { outcome.Refuse(Refusal.Limit, item); continue; } if (num == item.m_stack) { inventory.RemoveItem(item); } else { item.m_stack -= num; } if (!Stall.AddAll(inventory, itemPrefab, num * coins3)) { Stall.Restore(inventory, snapshot); outcome.Refuse(Refusal.Full, item); break; } entry.Coins += num * coins3; outcome.Items += num; outcome.Coins += num * coins3; } catch (Exception arg) { Stall.Restore(inventory, snapshot); entry.Coins = coins; outcome.Items = items; outcome.Coins = coins2; Plugin.Log.LogError((object)$"Скупщик: ошибка на предмете {text}: {arg}"); break; } } return outcome; } private static Outcome Salvage(Inventory inventory, LedgerEntry entry) { Outcome outcome = new Outcome(); HashSet<string> keys = new HashSet<string>(ZoneSystem.instance.GetGlobalKeys()); foreach (ItemData item in inventory.GetAllItems().ToList()) { string arg = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : ""); byte[] snapshot = Stall.Snapshot(inventory); int salvaged = entry.Salvaged; int items = outcome.Items; Dictionary<string, int> dictionary = new Dictionary<string, int>(outcome.Metal); try { if ((Object)(object)item.m_dropPrefab != (Object)null && _metals.Contains(((Object)item.m_dropPrefab).name)) { continue; } bool flag = item.m_shared.m_maxStackSize > 1; bool flag2 = item.m_customData != null && item.m_customData.Count > 0; Dictionary<string, int> dictionary2 = new Dictionary<string, int>(); Requirement[] array = (Requirement[])(((object)((flag || flag2) ? null : ObjectDB.instance.GetRecipe(item))?.m_resources) ?? ((object)new Requirement[0])); foreach (Requirement val in array) { string text = (((Object)(object)val?.m_resItem != (Object)null) ? ((Object)val.m_resItem).name : ""); if (_metals.Contains(text)) { dictionary2[text] = (dictionary2.TryGetValue(text, out var value) ? value : 0) + SalvageRules.Spent((Func<int, int>)val.GetAmount, item.m_quality); } } Dictionary<string, int> dictionary3 = dictionary2.ToDictionary((KeyValuePair<string, int> p) => p.Key, (KeyValuePair<string, int> p) => SalvageRules.Yield(p.Value, Plugin.SalvageFraction.Value)); Func<string, bool> portalOpen = PortalOpen(keys); Refusal refusal = SalvageRules.Check(flag, flag2, dictionary3, portalOpen); if (refusal != Refusal.None) { string detail = ((refusal == Refusal.PortalClosed) ? ItemNames.Of(SalvageRules.FirstClosed(dictionary3, portalOpen)) : ""); outcome.Refuse(refusal, item, detail); continue; } if (entry.Salvaged >= Plugin.SalvageLimit.Value) { outcome.Refuse(Refusal.Limit, item); continue; } inventory.RemoveItem(item); if (!dictionary3.Where((KeyValuePair<string, int> y) => y.Value > 0).All((KeyValuePair<string, int> y) => Stall.AddAll(inventory, ObjectDB.instance.GetItemPrefab(y.Key), y.Value))) { Stall.Restore(inventory, snapshot); outcome.Refuse(Refusal.Full, item); break; } entry.Salvaged++; outcome.Items++; foreach (KeyValuePair<string, int> item2 in dictionary3.Where((KeyValuePair<string, int> y) => y.Value > 0)) { outcome.Metal[item2.Key] = (outcome.Metal.TryGetValue(item2.Key, out var value2) ? value2 : 0) + item2.Value; } } catch (Exception arg2) { Stall.Restore(inventory, snapshot); entry.Salvaged = salvaged; outcome.Items = items; outcome.Metal.Clear(); foreach (KeyValuePair<string, int> item3 in dictionary) { outcome.Metal[item3.Key] = item3.Value; } Plugin.Log.LogError((object)$"Скупщик: ошибка на предмете {arg}: {arg2}"); break; } } return outcome; } private static bool HasMetal(ItemData item) { Requirement[] array = (Requirement[])(((object)ObjectDB.instance.GetRecipe(item)?.m_resources) ?? ((object)new Requirement[0])); foreach (Requirement val in array) { if ((Object)(object)val?.m_resItem != (Object)null && _metals.Contains(((Object)val.m_resItem).name)) { return true; } } return false; } private static Func<string, bool> PortalOpen(HashSet<string> keys) { return delegate(string metal) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(metal); bool valueOrDefault = ((itemPrefab == null) ? ((bool?)null) : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_teleportable) == true; string text = Metals.PortalKey(metal); return valueOrDefault || (text != null && keys.Contains(text)); }; } private static string Name(ItemData item) { if (!((Object)(object)item.m_dropPrefab != (Object)null)) { return item.m_shared.m_name; } return ItemNames.Of(((Object)item.m_dropPrefab).name); } public static void CheckCommand() { string path = Path.Combine(Paths.ConfigPath, "sansara.JunkBuyer.command"); if (File.Exists(path)) { string text; try { text = File.ReadAllText(path).Trim(); File.Delete(path); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Скупщик: файл команды не прочитан: " + ex.Message)); return; } Plugin.Log.LogInfo((object)("Скупщик: команда «" + text + "».")); Plugin.Log.LogInfo((object)("Скупщик: " + Handle(text, "файл команд", "").Replace("\n", "; "))); } } public static string Handle(string line, string admin, string adminId) { AdminCommand adminCommand = AdminCommand.Parse(line); switch (adminCommand.Verb) { case "help": return "status — начало текущего периода, игроки со счётчиками, выдано монет и разобрано вещей\nreset <игрок|@ID> — сбросить счётчики игрока за текущий период\nui — данные для окна Sansara Admin (машинный ответ): игроки со счётчиками периода"; case "status": return Status(); case "ui": if (_path != null) { return BuyerUi.Text(_ledger); } return AdminUi.Note(Plugin.Enabled.Value ? "Скупщик ещё не запущен: мир загружается" : "модуль выключен в настройках (Enabled = false)"); case "reset": if (!Plugin.Enabled.Value) { return "модуль выключен в настройках (Enabled = false)"; } if (_path != null) { return Reset(adminCommand.Argument); } return "Скупщик ещё не запущен: мир загружается"; default: return AdminProtocol.Unknown(adminCommand.Verb, Verbs); } } private static string Status() { string text = (Plugin.Enabled.Value ? "" : "выключен в настройках; "); if (_path == null || _ledger.Day < 0) { if (Plugin.Enabled.Value) { return "Скупщик ещё не запущен: мир загружается"; } return "модуль выключен в настройках (Enabled = false)"; } double num = Plugin.ResetOffsetHours.Value; DateTime epoch = Epoch; DateTime dateTime = epoch.AddSeconds(DailyLedger.PeriodStart(_ledger.Day, num, (double)Plugin.ResetHours.Value * 60.0) + num * 3600.0); string text2 = text + "период с " + dateTime.ToString("dd.MM HH:mm", CultureInfo.InvariantCulture) + " (время сервера), длина " + BuyerTexts.Period(Plugin.ResetHours.Value) + "; " + $"игроков {_ledger.Players.Count}, выдано монет {_ledger.Players.Values.Sum((LedgerEntry p) => p.Coins)}, разобрано вещей {_ledger.Players.Values.Sum((LedgerEntry p) => p.Salvaged)}"; if (_ledger.Players.Count != 0) { return text2 + "\n" + string.Join("; ", _ledger.Players.Select((KeyValuePair<string, LedgerEntry> p) => $"{p.Value.Name} ({p.Key}) монет {p.Value.Coins}, вещей {p.Value.Salvaged}")); } return text2; } private static string Reset(string who) { if (who == "") { return "укажите игрока: reset <имя|@ID>"; } if (AdminCommand.TryId(who, out var id)) { who = id; } string text = _ledger.Players.Keys.FirstOrDefault((string k) => k == who || string.Equals(_ledger.Players[k].Name, who, StringComparison.OrdinalIgnoreCase)); if (text == null) { return "игрока «" + who + "» в счётчиках нет"; } string name = _ledger.Players[text].Name; _ledger.Players.Remove(text); bool flag = Save(); Handled.Clear(); return "счётчики " + name + " (" + text + ") сброшены" + (flag ? "" : "; файл счётчиков не записан — см. журнал"); } } internal static class ItemNames { private static readonly Dictionary<string, string> Russian = new Dictionary<string, string>(); public static void Load() { try { foreach (TextAsset item in Assets()) { ReadColumn(item, "Russian", Russian); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Скупщик: русская локализация не прочитана: " + ex.Message)); } Plugin.Log.LogInfo((object)$"Скупщик: строк русской локализации {Russian.Count}."); } public static string Of(string prefab) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); string text = ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name) ?? prefab; string key = text.TrimStart(new char[1] { '$' }); if (Russian.TryGetValue(key, out var value) && value != "") { return value; } if (Localization.instance == null) { return prefab; } return Localization.instance.Localize(text); } public static string Token(string prefab) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); return ((itemPrefab == null) ? null : itemPrefab.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_name) ?? ""; } private static IEnumerable<TextAsset> Assets() { _ = Localization.instance; object obj = AccessTools.Field(typeof(Localization), "m_localizationSettings")?.GetValue(null); if (obj == null) { yield break; } Traverse val = Traverse.Create(obj); if (!((val.Field("Localizations").GetValue() ?? val.Property("Localizations", (object[])null).GetValue()) is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { TextAsset val2 = (TextAsset)((item is TextAsset) ? item : null); if (val2 != null && (Object)(object)val2 != (Object)null) { yield return val2; } } } private static void ReadColumn(TextAsset asset, string language, Dictionary<string, string> into) { List<List<string>> list = SplitCsv(asset.text); if (list.Count == 0) { return; } int num = list[0].FindIndex((string h) => StripQuotes(h).Trim() == language); if (num < 0) { return; } foreach (List<string> item in list.Skip(1)) { if (item.Count != 0 && item[0].Length != 0 && !item[0].StartsWith("//")) { string text = ((item.Count > num) ? item[num].Trim() : ""); if (text.Length == 0 && item.Count > 1) { text = item[1].Trim(); } into[item[0]] = text; } } } private static string StripQuotes(string s) { if (s.Length < 2 || s[0] != '"' || s[s.Length - 1] != '"') { return s; } return s.Substring(1, s.Length - 2); } private static List<List<string>> SplitCsv(string text) { List<List<string>> list = new List<List<string>>(); List<string> list2 = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); bool flag = false; for (int i = 0; i < text.Length; i++) { char c = text[i]; switch (c) { case '"': if (flag && i + 1 < text.Length && text[i + 1] == '"') { stringBuilder.Append('"'); i++; } else { flag = !flag; } continue; case ',': if (!flag) { list2.Add(stringBuilder.ToString()); stringBuilder.Length = 0; continue; } break; } if (c == '\n' && !flag) { list2.Add(stringBuilder.ToString().TrimEnd(new char[1] { '\r' })); stringBuilder.Length = 0; list.Add(list2); list2 = new List<string>(); } else { stringBuilder.Append(c); } } if (stringBuilder.Length > 0 || list2.Count > 0) { list2.Add(stringBuilder.ToString()); list.Add(list2); } return list; } } [BepInPlugin("sansara.JunkBuyer", "Junk Buyer", "0.2.2")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "sansara.JunkBuyer"; public const string Name = "Junk Buyer"; public const string Version = "0.2.2"; internal static ManualLogSource Log; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<string> Catalog; internal static ConfigEntry<string> Excluded; internal static ConfigEntry<int> DefaultPrice; internal static ConfigEntry<int> DefaultItemPrice; internal static ConfigEntry<int> CoinLimit; internal static ConfigEntry<string> MetalList; internal static ConfigEntry<float> SalvageFraction; internal static ConfigEntry<int> SalvageLimit; internal static ConfigEntry<float> ResetHours; internal static ConfigEntry<float> ResetOffsetHours; private float _nextTick; private bool _started; private void Awake() { Log = ((BaseUnityPlugin)this).Logger; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Скупщик в Чертоге славы."); Catalog = ((BaseUnityPlugin)this).Config.Bind<string>("Sell", "Catalog", "Resin:1, LeatherScraps:1, Feathers:1, TrophyBoar:5, TrophyDeer:5, TrophyNeck:5, BoneFragments:2, GreydwarfEye:2, AncientSeed:2, TrophyGreydwarf:10, TrophyGreydwarfBrute:10, TrophyGreydwarfShaman:10, TrophySkeleton:10, TrophyForestTroll:10, Entrails:3, WitheredBone:3, Bloodbag:3, Ooze:3, Guck:3, TrophyDraugr:15, TrophyDraugrElite:15, TrophyBlob:15, TrophyLeech:15, TrophySurtling:15, TrophyWraith:15, TrophyAbomination:15, FreezeGland:4, TrophyWolf:20, TrophyFenring:20, TrophyHatchling:20, TrophyCultist:20, TrophySGolem:20, TrophyUlv:20, Needle:5, TrophyDeathsquito:25, TrophyGoblin:25, TrophyGoblinBrute:25, TrophyGoblinShaman:25, TrophyLox:25, TrophyGrowth:25, Mandible:6, RoyalJelly:6, TrophySeeker:30, TrophySeekerBrute:30, TrophyTick:30, TrophyGjall:30, TrophyHare:30, TrophyDvergr:30, CharredBone:8, TrophyCharredMelee:40, TrophyCharredArcher:40, TrophyCharredMage:40, TrophyAsksvin:40, TrophyVolture:40, TrophyFallenValkyrie:40, TrophyMorgen:40, TrophyBonemawSerpent:40", "Предмет:цена в монетах через запятую — явные цены, они важнее остальных правил."); Excluded = ((BaseUnityPlugin)this).Config.Bind<string>("Sell", "Excluded", "Coins, Copper, Tin, Bronze, Iron, Silver, BlackMetal, FlametalNew, CopperOre, TinOre, CopperScrap, BronzeScrap, IronScrap, IronOre, SilverOre, BlackMetalScrap, FlametalOre, FlametalOreNew, TrophyEikthyr, TrophyTheElder, TrophyBonemass, TrophyDragonQueen, TrophyGoblinKing, TrophySeekerQueen, TrophyFader, TrophyWrithan", "Имена предметов через запятую, которые скупка не берёт (монеты, металлы, трофеи боссов)."); DefaultPrice = ((BaseUnityPlugin)this).Config.Bind<int>("Sell", "DefaultPrice", 1, "Цена за штуку для стекуемых предметов не из прайса; 0 — не брать."); DefaultItemPrice = ((BaseUnityPlugin)this).Config.Bind<int>("Sell", "DefaultItemPrice", 5, "Цена штучной вещи не из прайса за уровень качества; 0 — не брать."); CoinLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "CoinLimit", 500, "Монет со скупки на игрока за период."); MetalList = ((BaseUnityPlugin)this).Config.Bind<string>("Salvage", "Metals", "Copper, Tin, Bronze, Iron, Silver, BlackMetal, FlametalNew", "Металлы, которые возвращает разборка (имена предметов)."); SalvageFraction = ((BaseUnityPlugin)this).Config.Bind<float>("Salvage", "SalvageFraction", 0.5f, "Доля металла рецепта (с улучшениями), которая возвращается; округление вниз."); SalvageLimit = ((BaseUnityPlugin)this).Config.Bind<int>("Limits", "SalvageLimit", 10, "Разобранных вещей на игрока за период."); ResetHours = ((BaseUnityPlugin)this).Config.Bind<float>("Limits", "ResetHours", 4f, "Длина периода в часах (реальное время); лимиты сбрасываются на границе периода."); ResetOffsetHours = ((BaseUnityPlugin)this).Config.Bind<float>("Limits", "ResetOffsetHours", 3f, "Границы периодов отсчитываются от полуночи UTC + столько часов (пояс сервера)."); AdminChannel.Register("buyer", Buyer.Verbs, Buyer.Handle, Log); Log.LogInfo((object)"Junk Buyer 0.2.2 загружен."); } private void Update() { AdminChannel.Tick(); if (!Enabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || !ZoneSystem.instance.LocationsGenerated || Time.time < _nextTick) { return; } _nextTick = Time.time + 5f; try { if (!_started) { try { Prepare(); _started = true; } catch (Exception ex) { Log.LogError((object)("Скупщик: запуск не удался: " + ex)); _nextTick = Time.time + 60f; return; } } Buyer.CheckCommand(); Buyer.Tick(); } catch (Exception ex2) { Log.LogError((object)("Скупщик: ошибка: " + ex2)); } } private static void Prepare() { List<string> list = new List<string>(); Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); foreach (Price item in PriceList.Parse(Catalog.Value, list)) { if ((Object)(object)ObjectDB.instance.GetItemPrefab(item.Item) == (Object)null) { Log.LogWarning((object)("Скупщик: предмета " + item.Item + " нет в игре — строка прайса пропущена.")); } else { dictionary[item.Item] = item.Coins; } } list.ForEach(delegate(string e) { Log.LogWarning((object)("Скупщик: Catalog — " + e)); }); HashSet<string> hashSet = new HashSet<string>(Metals.Parse(MetalList.Value).Where(delegate(string m) { bool num = (Object)(object)ObjectDB.instance.GetItemPrefab(m) != (Object)null; if (!num) { Log.LogWarning((object)("Скупщик: металла " + m + " нет в игре — пропущен.")); } return num; }), StringComparer.Ordinal); HashSet<string> hashSet2 = new HashSet<string>(PriceList.ParseNames(Excluded.Value).Where(delegate(string n) { bool num = (Object)(object)ObjectDB.instance.GetItemPrefab(n) != (Object)null; if (!num) { Log.LogWarning((object)("Скупщик: Excluded — предмета " + n + " нет в игре, пропущен.")); } return num; }), StringComparer.Ordinal); ItemNames.Load(); Buyer.Load(dictionary, hashSet2, hashSet); Log.LogInfo((object)$"Скупщик: в прайсе {dictionary.Count} предметов, исключено {hashSet2.Count}, металлов {hashSet.Count}, лимиты {CoinLimit.Value} монет и {SalvageLimit.Value} вещей за {BuyerTexts.Period(ResetHours.Value)}."); } } internal static class Stall { private const string RoleKey = "sansara.PortalHall.role"; private static readonly List<ZDO> Found = new List<ZDO>(); private static Vector3 FameAnchor => new Vector3(10820f, 0f, 300f); public static Dictionary<string, ZDO> Find() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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) Found.Clear(); ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(FameAnchor), new SimulationDistance(1, 0, true), Found, (List<ZDO>)null); Dictionary<string, ZDO> dictionary = new Dictionary<string, ZDO>(); foreach (ZDO item in Found) { string text = item.GetString("sansara.PortalHall.role", ""); if (text.StartsWith("buyer.", StringComparison.Ordinal) && !dictionary.ContainsKey(text)) { dictionary[text] = item; } } return dictionary; } public static bool Depositor(ZDO chest, out string id, out long playerId, out string name) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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) id = ""; playerId = 0L; name = ""; float num = 10f; 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) { continue; } bool flag = peer.m_uid == chest.GetOwner(); float num2 = Vector3.Distance(val.GetPosition(), chest.GetPosition()); if (flag || num2 <= num) { num = num2; playerId = val.GetLong(ZDOVars.s_playerID, 0L); name = peer.m_playerName; ISocket socket = peer.m_socket; string text = ((socket != null) ? socket.GetHostName() : null) ?? ""; id = ((text != "") ? text : ("player:" + playerId)); if (flag) { break; } } } return playerId != 0; } public static bool Online(long playerId, out string id, out string name) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) id = ""; name = ""; 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 && val.GetLong(ZDOVars.s_playerID, 0L) == playerId) { name = peer.m_playerName; ISocket socket = peer.m_socket; string text = ((socket != null) ? socket.GetHostName() : null) ?? ""; id = ((text != "") ? text : ("player:" + playerId)); return true; } } return false; } public static Inventory Load(ZDO chest, byte[] bytes) { //IL_003a: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_004c: Expected O, but got Unknown GameObject prefab = ZNetScene.instance.GetPrefab(chest.GetPrefab()); Container val = ((prefab != null) ? prefab.GetComponent<Container>() : null); if ((Object)(object)val == (Object)null) { return null; } Inventory val2 = new Inventory("buyer", (Sprite)null, val.m_width, val.m_height); val2.Load(new ZPackage(bytes)); return val2; } public static void Save(ZDO chest, Inventory inventory) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); inventory.Save(val); chest.SetOwner(ZDOMan.GetSessionID()); chest.Set(ZDOVars.s_items, val.GetArray()); ZDOMan.instance.ForceSendZDO(chest.m_uid); } public static byte[] Snapshot(Inventory inventory) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); inventory.Save(val); return val.GetArray(); } public static void Restore(Inventory inventory, byte[] snapshot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown inventory.Load(new ZPackage(snapshot)); } public static bool AddAll(Inventory inventory, GameObject prefab, int amount) { ItemDrop component = prefab.GetComponent<ItemDrop>(); while (amount > 0) { ItemData val = component.m_itemData.Clone(); val.m_dropPrefab = prefab; val.m_stack = Math.Min(amount, Math.Max(1, val.m_shared.m_maxStackSize)); val.m_worldLevel = (byte)Game.m_worldLevel; amount -= val.m_stack; if (!inventory.AddItem(val)) { return false; } } return true; } public static void SetSign(ZDO sign, string text) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) text = "<color=white>" + text + "</color>"; if (sign != null && sign.GetString(ZDOVars.s_text, "") != text) { sign.SetOwner(ZDOMan.GetSessionID()); sign.Set(ZDOVars.s_text, text); ZDOMan.instance.ForceSendZDO(sign.m_uid); } } public static void Message(long playerId, string text) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) 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 && val.GetLong(ZDOVars.s_playerID, 0L) == playerId) { ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "ShowMessage", new object[2] { 2, text }); } } } } } namespace JunkBuyer.Core { public static class BuyerRoles { public const string Prefix = "buyer."; public const string SellChest = "buyer.chest.sell"; public const string SellSign = "buyer.sign.sell"; public const string SalvageChest = "buyer.chest.salvage"; public const string SalvageSign = "buyer.sign.salvage"; } public static class BuyerTexts { public const int SignLimit = 50; public static string SellSign(int coinLimit, double resetHours) { return Fit("Скупка: всё, кроме металла · " + N(coinLimit) + " за " + Period(resetHours)); } public static string SalvageSign(double fraction, int limit, double resetHours) { return Fit("Разборка: " + Share(fraction) + " металла · " + N(limit) + " вещей за " + Period(resetHours)); } public static string Period(double hours) { if (double.IsNaN(hours) || hours <= 0.0) { return "период"; } if (hours < 1.0) { return Math.Max(1, (int)Math.Round(hours * 60.0)).ToString(CultureInfo.InvariantCulture) + " мин"; } return Math.Round(hours, 2).ToString("0.##", CultureInfo.InvariantCulture).Replace('.', ',') + " ч"; } public static string Sold(int items, int coins, int left) { return "Скупщик: продано " + N(items) + ", получено монет " + N(coins) + ". Осталось до сброса: " + N(Math.Max(0, left)) + "."; } public static string Salvaged(int items, string metals, int left) { return "Скупщик: разобрано " + N(items) + " → " + metals + ". Осталось до сброса: " + N(Math.Max(0, left)) + "."; } public static string Refused(Refusal r, string item, string detail) { string text; switch (r) { case Refusal.NotListed: text = "Скупщик такое не берёт"; break; case Refusal.Excluded: text = "металл и трофеи боссов не покупаются"; break; case Refusal.MetalItem: text = "в ней металл — положи в сундук разборки"; break; case Refusal.Limit: text = "лимит до сброса исчерпан"; break; case Refusal.Full: text = "в сундуке нет места для оплаты"; break; case Refusal.Stackable: text = "разбираются только оружие, броня и инструменты"; break; case Refusal.Special: text = "особая вещь, не принимается"; break; case Refusal.NoMetal: text = "в ней нет металла"; break; case Refusal.PortalClosed: text = "портал ещё не пропустит " + detail; break; case Refusal.NothingBack: text = "металла слишком мало"; break; default: return ""; } return "Не принято: " + item + " — " + text + "."; } private static string Share(double f) { if (Math.Abs(f - 0.5) < 1E-06) { return "½"; } if (Math.Abs(f - 0.25) < 1E-06) { return "¼"; } if (Math.Abs(f - 0.75) < 1E-06) { return "¾"; } return Math.Round(f * 100.0).ToString(CultureInfo.InvariantCulture) + " %"; } private static string N(int n) { return n.ToString(CultureInfo.InvariantCulture); } private static string Fit(string s) { if (s.Length > 50) { return s.Substring(0, 49) + "…"; } return s; } } public static class BuyerUi { public static string Text(DailyLedger ledger) { return AdminUi.Text(from p in ledger.Players.OrderBy<KeyValuePair<string, LedgerEntry>, string>((KeyValuePair<string, LedgerEntry> p) => p.Value.Name, StringComparer.OrdinalIgnoreCase).ThenBy<KeyValuePair<string, LedgerEntry>, string>((KeyValuePair<string, LedgerEntry> p) => p.Key, StringComparer.Ordinal) select AdminUi.Row("player", p.Key, p.Value.Name, p.Value.Coins, p.Value.Salvaged)); } } public sealed class LedgerEntry { public string Name = ""; public int Coins; public int Salvaged; } public sealed class DailyLedger { public long Day = -1L; public readonly Dictionary<string, LedgerEntry> Players = new Dictionary<string, LedgerEntry>(StringComparer.Ordinal); public static long DayOf(double realSeconds, double offsetHours, double dayMinutes) { double num = Math.Max(1.0, dayMinutes) * 60.0; return (long)Math.Floor((realSeconds + offsetHours * 3600.0) / num); } public static double PeriodStart(long day, double offsetHours, double dayMinutes) { return (double)day * (Math.Max(1.0, dayMinutes) * 60.0) - offsetHours * 3600.0; } public void Roll(long day) { if (day != Day) { Day = day; Players.Clear(); } } public LedgerEntry For(string id, string name) { if (!Players.TryGetValue(id, out var value)) { value = new LedgerEntry(); Players[id] = value; } if (!string.IsNullOrEmpty(name)) { value.Name = Clean(name); } return value; } public string Serialize() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("day\t").Append(Day.ToString(CultureInfo.InvariantCulture)).Append('\n'); foreach (KeyValuePair<string, LedgerEntry> player in Players) { stringBuilder.Append(Clean(player.Key)).Append('\t').Append(player.Value.Coins.ToString(CultureInfo.InvariantCulture)) .Append('\t') .Append(player.Value.Salvaged.ToString(CultureInfo.InvariantCulture)) .Append('\t') .Append(Clean(player.Value.Name)) .Append('\n'); } return stringBuilder.ToString(); } public static DailyLedger Parse(string text) { DailyLedger dailyLedger = new DailyLedger(); string[] array = (text ?? "").Replace("\r", "").Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return dailyLedger; } string[] array2 = array[0].Split(new char[1] { '\t' }); if (array2.Length != 2 || array2[0] != "day" || !long.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out dailyLedger.Day)) { throw new FormatException("нет строки day"); } for (int i = 1; i < array.Length; i++) { string[] array3 = array[i].Split(new char[1] { '\t' }); if (array3.Length != 4 || array3[0] == "" || !int.TryParse(array3[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !int.TryParse(array3[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { throw new FormatException("строка " + (i + 1)); } dailyLedger.Players[array3[0]] = new LedgerEntry { Coins = result, Salvaged = result2, Name = array3[3] }; } return dailyLedger; } private static string Clean(string s) { return (s ?? "").Replace('\t', ' ').Replace('\n', ' ').Replace("\r", ""); } } public sealed class Price { public string Item { get; } public int Coins { get; } public Price(string item, int coins) { Item = item; Coins = coins; } } public static class PriceList { public const string Default = "Resin:1, LeatherScraps:1, Feathers:1, TrophyBoar:5, TrophyDeer:5, TrophyNeck:5, BoneFragments:2, GreydwarfEye:2, AncientSeed:2, TrophyGreydwarf:10, TrophyGreydwarfBrute:10, TrophyGreydwarfShaman:10, TrophySkeleton:10, TrophyForestTroll:10, Entrails:3, WitheredBone:3, Bloodbag:3, Ooze:3, Guck:3, TrophyDraugr:15, TrophyDraugrElite:15, TrophyBlob:15, TrophyLeech:15, TrophySurtling:15, TrophyWraith:15, TrophyAbomination:15, FreezeGland:4, TrophyWolf:20, TrophyFenring:20, TrophyHatchling:20, TrophyCultist:20, TrophySGolem:20, TrophyUlv:20, Needle:5, TrophyDeathsquito:25, TrophyGoblin:25, TrophyGoblinBrute:25, TrophyGoblinShaman:25, TrophyLox:25, TrophyGrowth:25, Mandible:6, RoyalJelly:6, TrophySeeker:30, TrophySeekerBrute:30, TrophyTick:30, TrophyGjall:30, TrophyHare:30, TrophyDvergr:30, CharredBone:8, TrophyCharredMelee:40, TrophyCharredArcher:40, TrophyCharredMage:40, TrophyAsksvin:40, TrophyVolture:40, TrophyFallenValkyrie:40, TrophyMorgen:40, TrophyBonemawSerpent:40"; public const string DefaultExcluded = "Coins, Copper, Tin, Bronze, Iron, Silver, BlackMetal, FlametalNew, CopperOre, TinOre, CopperScrap, BronzeScrap, IronScrap, IronOre, SilverOre, BlackMetalScrap, FlametalOre, FlametalOreNew, TrophyEikthyr, TrophyTheElder, TrophyBonemass, TrophyDragonQueen, TrophyGoblinKing, TrophySeekerQueen, TrophyFader, TrophyWrithan"; public static HashSet<string> ParseNames(string text) { return new HashSet<string>((text ?? "").Split(new char[5] { ',', ';', ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal); } public static Refusal Quote(string prefab, bool stackable, bool special, bool hasMetal, int vanillaValue, int quality, IDictionary<string, int> catalog, ISet<string> excluded, int defaultPrice, int defaultItemPrice, out int coins) { coins = 0; if (excluded != null && excluded.Contains(prefab)) { return Refusal.Excluded; } if (special) { return Refusal.Special; } if (catalog != null && catalog.TryGetValue(prefab, out var value)) { coins = value; } else { if (!stackable && hasMetal) { return Refusal.MetalItem; } if (vanillaValue > 0) { coins = vanillaValue; } else if (stackable) { coins = defaultPrice; } else { coins = defaultItemPrice * Math.Max(1, quality); } } if (coins <= 0) { coins = 0; return Refusal.NotListed; } return Refusal.None; } public static List<Price> Parse(string text, List<string> errors) { List<Price> list = new List<Price>(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = (text ?? "").Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0) { continue; } string[] array2 = text2.Split(new char[1] { ':' }); if (array2.Length != 2 || array2[0].Trim() == "" || !int.TryParse(array2[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 1) { errors.Add("строка «" + text2 + "» пропущена: нужно Предмет:цена (цена ≥ 1)"); continue; } string text3 = array2[0].Trim(); if (!hashSet.Add(text3)) { errors.Add("предмет " + text3 + " указан повторно, оставлена первая строка"); } else { list.Add(new Price(text3, result)); } } return list; } } public static class SellRules { public static int Sellable(int stack, int price, int coinsLeft) { if (stack > 0 && price > 0 && coinsLeft > 0) { return Math.Min(stack, coinsLeft / price); } return 0; } } public enum Refusal { None, NotListed, Excluded, MetalItem, Limit, Full, Stackable, Special, NoMetal, PortalClosed, NothingBack } public static class Metals { public const string Default = "Copper, Tin, Bronze, Iron, Silver, BlackMetal, FlametalNew"; public static HashSet<string> Parse(string text) { return new HashSet<string>((text ?? "").Split(new char[4] { ',', ';', ' ', '\n' }, StringSplitOptions.RemoveEmptyEntries), StringComparer.Ordinal); } public static string PortalKey(string metal) { switch (metal) { case "Bronze": case "Copper": case "Tin": return "defeated_gdking"; case "Iron": return "defeated_bonemass"; case "Silver": return "defeated_dragon"; case "BlackMetal": return "defeated_goblinking"; case "FlametalNew": return "defeated_fader"; default: return null; } } } public static class SalvageRules { public static int Spent(Func<int, int> amountAtLevel, int quality) { int num = 0; for (int i = 1; i <= Math.Max(1, quality); i++) { num += amountAtLevel(i); } return num; } public static int Yield(int spent, double fraction) { if (spent > 0 && !double.IsNaN(fraction) && !(fraction <= 0.0)) { return (int)Math.Floor((double)spent * Math.Min(fraction, 1.0) + 1E-06); } return 0; } public static string FirstClosed(IDictionary<string, int> yields, Func<string, bool> portalOpen) { return (from p in yields where p.Value > 0 && !portalOpen(p.Key) select p.Key).FirstOrDefault(); } public static Refusal Check(bool stackable, bool special, IDictionary<string, int> yields, Func<string, bool> portalOpen) { if (stackable) { return Refusal.Stackable; } if (special) { return Refusal.Special; } if (yields.Count == 0) { return Refusal.NoMetal; } if (yields.Values.All((int v) => v <= 0)) { return Refusal.NothingBack; } if (FirstClosed(yields, portalOpen) != null) { return Refusal.PortalClosed; } return Refusal.None; } } }