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 QuantumObliterator v0.3.0
QuantumObliterator.dll
Decompiled 3 weeks agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using QuantumObliterator.Net; using QuantumObliterator.Patches; using QuantumObliterator.Server; using QuantumObliterator.Util; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("QuantumObliterator")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.0.0")] [assembly: AssemblyInformationalVersion("0.3.0+98b868c3fa43a2042c7f50ecf2909e13aa0a7f13")] [assembly: AssemblyProduct("QuantumObliterator")] [assembly: AssemblyTitle("QuantumObliterator")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.0.0")] [module: UnverifiableCode] [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; } } } public sealed class ConfigurationManagerAttributes { public bool? IsAdminOnly; public bool? ShowRangeAsPercent; public Action<ConfigEntryBase> CustomDrawer; public bool? Browsable; public string Category; public object DefaultValue; public bool? HideDefaultButton; public bool? HideSettingName; public string Description; public string DispName; public int? Order; public bool? ReadOnly; public bool? IsAdvanced; public Func<object, string> ObjToStr; public Func<string, object> StrToObj; } namespace QuantumObliterator { [BepInPlugin("wheezl.quantumobliterator", "QuantumObliterator", "0.3.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "wheezl.quantumobliterator"; public const string PluginName = "QuantumObliterator"; public const string PluginVersion = "0.3.0"; private Harmony _harmony; private void Awake() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown QOLog.Source = ((BaseUnityPlugin)this).Logger; QOConfig.Bind(((BaseUnityPlugin)this).Config); AddLocalization(); QORpc.Register(); _harmony = new Harmony("wheezl.quantumobliterator"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"QuantumObliterator 0.3.0 loaded."); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private static void AddLocalization() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "English"; localization.AddTranslation(ref text, new Dictionary<string, string> { { "qo_msg_success", "Shipment sent to \"{tag}\"" }, { "qo_msg_nopartner", "No other Obliterator is tagged \"{tag}\"" }, { "qo_msg_ambiguous", "More than one Obliterator is tagged \"{tag}\"" }, { "qo_msg_nofuel", "Requires {n} {item} to charge" }, { "qo_msg_full", "Destination Obliterator is full" }, { "qo_msg_desync", "Obliterator contents changed - try again" }, { "qo_msg_nothingtoship", "Nothing to ship except the fuel" }, { "qo_msg_timeout", "No response from the server" }, { "qo_msg_disabled", "Obliterator linking is disabled" }, { "qo_msg_error", "Transfer failed" }, { "qo_hover_shipto", "Ship to \"{tag}\"" } }); } } internal static class QOConfig { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> SignSearchRadius; internal static ConfigEntry<bool> CaseSensitiveTags; internal static ConfigEntry<bool> RequireFuel; internal static ConfigEntry<string> FuelItems; internal static ConfigEntry<float> WeightPerFuelUnit; internal static ConfigEntry<bool> DestinationEffect; internal static ConfigEntry<bool> VerboseLogging; private static string[] _fuelCache; private static string _fuelCacheRaw; internal static void Bind(ConfigFile cfg) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Expected O, but got Unknown Enabled = cfg.Bind<bool>("1 - General", "Enabled", true, new ConfigDescription("Master switch. When false every Obliterator behaves exactly like vanilla.", (AcceptableValueBase)null, new object[1] { Admin() })); SignSearchRadius = cfg.Bind<float>("2 - Tagging", "SignSearchRadius", 5f, new ConfigDescription("How far from an Obliterator to look for a sign, in metres. The nearest sign within this radius supplies the tag.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 32f), new object[1] { Admin() })); CaseSensitiveTags = cfg.Bind<bool>("2 - Tagging", "CaseSensitiveTags", false, new ConfigDescription("When false, \"Ashlands\" and \"ashlands\" are the same tag. Tags are always trimmed of surrounding whitespace.", (AcceptableValueBase)null, new object[1] { Admin() })); RequireFuel = cfg.Bind<bool>("3 - Fuel", "RequireFuel", true, new ConfigDescription("Whether transfers cost fuel at all. Set to false for free, unlimited shipping.", (AcceptableValueBase)null, new object[1] { Admin() })); FuelItems = cfg.Bind<string>("3 - Fuel", "FuelItems", "Thunderstone", new ConfigDescription("Comma-separated item PREFAB names that can charge a transfer. They are tried in order and the first one the Obliterator holds enough of is consumed. Exact capitalisation matters (e.g. 'Thunderstone', 'SurtlingCore').", (AcceptableValueBase)null, new object[1] { Admin() })); WeightPerFuelUnit = cfg.Bind<float>("3 - Fuel", "WeightPerFuelUnit", 1080f, new ConfigDescription("How much shipment weight a single unit of fuel pays for. 1080 is roughly three full stacks of metal bars. The consumed fuel's own weight is on top of this, so it never eats into the allowance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100000f), new object[1] { Admin() })); DestinationEffect = cfg.Bind<bool>("4 - Effects", "DestinationEffect", true, new ConfigDescription("Play the lightning effect at the receiving Obliterator too, so players standing there see the delivery arrive.", (AcceptableValueBase)null, new object[1] { Admin() })); VerboseLogging = cfg.Bind<bool>("5 - Debug", "VerboseLogging", false, "Log detailed tag resolution and transfer decisions to LogOutput.log."); } private static ConfigurationManagerAttributes Admin() { return new ConfigurationManagerAttributes { IsAdminOnly = true }; } internal static string[] FuelPrefabNames() { string text = FuelItems.Value ?? string.Empty; if (text != _fuelCacheRaw) { List<string> list = new List<string>(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length > 0) { list.Add(text2); } } _fuelCache = list.ToArray(); _fuelCacheRaw = text; } return _fuelCache ?? Array.Empty<string>(); } internal static StringComparison TagComparison() { if (!CaseSensitiveTags.Value) { return StringComparison.OrdinalIgnoreCase; } return StringComparison.Ordinal; } } internal static class QOLog { internal static ManualLogSource Source; internal static void Info(string msg) { ManualLogSource source = Source; if (source != null) { source.LogInfo((object)msg); } } internal static void Warn(string msg) { ManualLogSource source = Source; if (source != null) { source.LogWarning((object)msg); } } internal static void Error(string msg) { ManualLogSource source = Source; if (source != null) { source.LogError((object)msg); } } internal static void Debug(string msg) { if (QOConfig.VerboseLogging != null && QOConfig.VerboseLogging.Value) { ManualLogSource source = Source; if (source != null) { source.LogInfo((object)msg); } } } } internal enum QOResult { Success, NoPartner, Ambiguous, NoFuel, DestinationFull, Disabled, Error, Timeout, NotServer, Desync, NothingToShip } internal struct TransferOutcome { internal QOResult Result; internal string Detail; internal int Amount; internal ZDOID Destination; internal static TransferOutcome Fail(QOResult r, string detail = "", int amount = 0) { return new TransferOutcome { Result = r, Detail = (detail ?? ""), Amount = amount }; } } internal static class QOTokens { internal const string Success = "$qo_msg_success"; internal const string NoPartner = "$qo_msg_nopartner"; internal const string Ambiguous = "$qo_msg_ambiguous"; internal const string NoFuel = "$qo_msg_nofuel"; internal const string Full = "$qo_msg_full"; internal const string Error = "$qo_msg_error"; internal const string Timeout = "$qo_msg_timeout"; internal const string Disabled = "$qo_msg_disabled"; internal const string Desync = "$qo_msg_desync"; internal const string NothingToShip = "$qo_msg_nothingtoship"; internal const string HoverShipTo = "$qo_hover_shipto"; internal static string For(QOResult r) { return r switch { QOResult.Success => "$qo_msg_success", QOResult.NoPartner => "$qo_msg_nopartner", QOResult.Ambiguous => "$qo_msg_ambiguous", QOResult.NoFuel => "$qo_msg_nofuel", QOResult.DestinationFull => "$qo_msg_full", QOResult.Disabled => "$qo_msg_disabled", QOResult.Timeout => "$qo_msg_timeout", QOResult.Desync => "$qo_msg_desync", QOResult.NothingToShip => "$qo_msg_nothingtoship", _ => "$qo_msg_error", }; } } internal static class PluginInfo { internal const string Version = "0.3.0"; } } namespace QuantumObliterator.Util { internal static class HarmlessLightning { internal static bool Arming; private static int _armedCount; private static bool _loggedComponentCount; internal static GameObject Spawn(GameObject prefab, Vector3 position, Quaternion rotation) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) if ((Object)(object)prefab == (Object)null) { return null; } _armedCount = 0; Arming = true; GameObject result; try { result = Object.Instantiate<GameObject>(prefab, position, rotation); } finally { Arming = false; } QOLog.Debug($"Transfer lightning spawned at {position}; {_armedCount} Aoe component(s) neutered."); if (!_loggedComponentCount) { _loggedComponentCount = true; if (_armedCount == 0) { QOLog.Warn("The Obliterator lightning prefab carries no Aoe components, so transfers may still damage nearby structures. The damage source is something else and QuantumObliterator cannot disarm it."); } else { QOLog.Info($"Transfer lightning disarmed: {_armedCount} Aoe component(s) neutered."); } } return result; } internal static void Neuter(Aoe aoe) { //IL_004a: 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) _armedCount++; aoe.m_hitCharacters = false; aoe.m_hitProps = false; aoe.m_hitTerrain = false; aoe.m_hitFriendly = false; aoe.m_hitEnemy = false; aoe.m_hitParent = false; aoe.m_hitOwner = false; aoe.m_launchCharacters = false; aoe.m_damage = default(DamageTypes); aoe.m_damagePerLevel = default(DamageTypes); aoe.m_toolTier = 0; aoe.m_attackForce = 0f; aoe.m_knockBackForce = 0f; aoe.m_damageSelf = 0f; aoe.m_hitNoise = 0f; aoe.m_statusEffect = string.Empty; aoe.m_statusEffectIfBoss = string.Empty; aoe.m_statusEffectIfPlayer = string.Empty; aoe.m_spawnOnHitTerrain = null; } } [HarmonyPatch(typeof(Aoe), "Awake")] internal static class Aoe_Awake_Patch { [HarmonyPrefix] private static void Prefix(Aoe __instance) { if (HarmlessLightning.Arming) { HarmlessLightning.Neuter(__instance); } } } internal static class InventoryUtil { internal struct FuelQuote { internal bool Satisfied; internal string SharedName; internal int UnitsNeeded; internal string ShortfallName; internal int ShortfallUnits; } internal static bool TryGetGrid(ZDO zdo, out int width, out int height, out string name) { width = (height = 0); name = "container"; ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return false; } GameObject prefab = instance.GetPrefab(zdo.GetPrefab()); if ((Object)(object)prefab == (Object)null) { return false; } Container componentInChildren = prefab.GetComponentInChildren<Container>(true); if ((Object)(object)componentInChildren == (Object)null) { return false; } width = componentInChildren.m_width; height = componentInChildren.m_height; name = (string.IsNullOrEmpty(componentInChildren.m_name) ? "container" : componentInChildren.m_name); if (width > 0) { return height > 0; } return false; } internal static Inventory Read(ZDO zdo, int width, int height, string name) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown Inventory val = new Inventory(name, (Sprite)null, width, height); byte[] byteArray = zdo.GetByteArray(ZDOVars.s_items, (byte[])null); if (byteArray != null && byteArray.Length != 0) { val.Load(new ZPackage(byteArray)); } return val; } internal static byte[] Serialise(Inventory inv) { //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(); inv.Save(val); return val.GetArray(); } internal static bool MoveAllOrNothing(Inventory from, Inventory to, out int movedStacks) { movedStacks = 0; List<ItemData> list = new List<ItemData>(from.GetAllItems()); foreach (ItemData item in list) { to.MoveItemToThis(from, item); } movedStacks = list.Count; if (from.NrOfItems() > 0) { movedStacks = 0; return false; } return true; } internal static float TotalWeight(Inventory inv) { float num = 0f; foreach (ItemData allItem in inv.GetAllItems()) { if (allItem != null) { num += allItem.GetWeight(-1); } } return num; } internal static FuelQuote QuoteFuel(Inventory inv) { if (!QOConfig.RequireFuel.Value) { return new FuelQuote { Satisfied = true, UnitsNeeded = 0, SharedName = null }; } FuelQuote result = new FuelQuote { Satisfied = false }; string[] array = QOConfig.FuelPrefabNames(); if (array.Length == 0) { QOLog.Warn("RequireFuel is on but FuelItems is empty; no transfer can be paid for."); return result; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { QOLog.Warn("ObjectDB unavailable; cannot resolve fuel items."); return result; } float num = TotalWeight(inv); float value = QOConfig.WeightPerFuelUnit.Value; string[] array2 = array; foreach (string text in array2) { GameObject itemPrefab = instance.GetItemPrefab(text); if ((Object)(object)itemPrefab == (Object)null) { QOLog.Warn("Fuel item '" + text + "' is not a known item prefab - check the FuelItems config."); continue; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null) { string name = component.m_itemData.m_shared.m_name; float nonStackedWeight = component.m_itemData.GetNonStackedWeight(); ItemData item = inv.GetItem(name, -1, false); if (item != null) { nonStackedWeight = item.GetNonStackedWeight(); } float num2 = Mathf.Max(value + nonStackedWeight, 0.01f); int num3 = Mathf.Max(1, Mathf.CeilToInt(num / num2 - 0.0001f)); if (result.ShortfallName == null) { result.ShortfallName = name; result.ShortfallUnits = num3; } if (inv.CountItems(name, -1, true) >= num3) { result.Satisfied = true; result.SharedName = name; result.UnitsNeeded = num3; return result; } } } return result; } } internal static class SignTags { internal static readonly string[] SignPrefabs = new string[2] { "sign", "sign_notext" }; internal static string Normalise(string raw) { if (string.IsNullOrEmpty(raw)) { return string.Empty; } return raw.Replace("\r", " ").Replace("\n", " ").Trim(); } internal static bool Matches(string a, string b) { if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) { return false; } return string.Equals(a, b, QOConfig.TagComparison()); } internal static string TextOf(ZDO signZdo) { if (signZdo == null || !signZdo.IsValid()) { return string.Empty; } return Normalise(signZdo.GetString(ZDOVars.s_text, string.Empty)); } internal static string ResolveFromZdos(ZDO obliterator, List<ZDO> allSigns) { //IL_0012: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (obliterator == null || !obliterator.IsValid()) { return string.Empty; } Vector3 position = obliterator.GetPosition(); float value = QOConfig.SignSearchRadius.Value; float num = value * value; string result = string.Empty; for (int i = 0; i < allSigns.Count; i++) { ZDO val = allSigns[i]; if (val == null || !val.IsValid()) { continue; } Vector3 val2 = val.GetPosition() - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude > num)) { string text = TextOf(val); if (text.Length != 0) { num = sqrMagnitude; result = text; } } } return result; } internal static string ResolveLocal(Vector3 origin) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) float value = QOConfig.SignSearchRadius.Value; Collider[] array = Physics.OverlapSphere(origin, value); if (array == null || array.Length == 0) { return string.Empty; } float num = float.MaxValue; string result = string.Empty; Collider[] array2 = array; foreach (Collider val in array2) { if ((Object)(object)val == (Object)null) { continue; } Sign componentInParent = ((Component)val).GetComponentInParent<Sign>(); if ((Object)(object)componentInParent == (Object)null) { continue; } ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { continue; } string text = TextOf(component.GetZDO()); if (text.Length != 0) { Vector3 val2 = ((Component)componentInParent).transform.position - origin; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude >= num)) { num = sqrMagnitude; result = text; } } } return result; } } } namespace QuantumObliterator.Server { internal static class ObliteratorIndex { private sealed class Entry { internal List<ZDO> Zdos; internal float Expires; } internal const string IncineratorPrefab = "incinerator"; private const int MaxBatches = 100000; private const float CacheSeconds = 5f; private static readonly Dictionary<string, Entry> Cache = new Dictionary<string, Entry>(); internal static void Invalidate() { Cache.Clear(); } internal static List<ZDO> AllWithPrefabCached(string prefab) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!Cache.TryGetValue(prefab, out var value) || realtimeSinceStartup >= value.Expires) { value = new Entry { Zdos = AllWithPrefab(prefab), Expires = realtimeSinceStartup + 5f }; Cache[prefab] = value; return new List<ZDO>(value.Zdos); } List<ZDO> list = new List<ZDO>(value.Zdos.Count); foreach (ZDO zdo in value.Zdos) { if (zdo != null && zdo.IsValid()) { list.Add(zdo); } } return list; } internal static List<ZDO> AllWithPrefab(string prefab) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) List<ZDO> list = new List<ZDO>(); ZDOMan instance = ZDOMan.instance; if (instance == null) { return list; } List<ZDO> list2 = new List<ZDO>(); int num = 0; int num2 = 0; while (!instance.GetAllZDOsWithPrefabIterative(prefab, list2, ref num)) { if (++num2 > 100000) { QOLog.Error("GetAllZDOsWithPrefabIterative('" + prefab + "') did not terminate after " + $"{100000} batches; aborting the scan. Vanilla behaviour may have changed."); break; } } HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); foreach (ZDO item in list2) { if (item != null && item.IsValid() && hashSet.Add(item.m_uid)) { list.Add(item); } } QOLog.Debug($"Scanned world for '{prefab}': {list2.Count} raw hit(s), {list.Count} unique, " + $"{num2} batch call(s)."); return list; } } internal static class TransferService { internal static TransferOutcome Execute(ZDOID sourceId, long requesterPeerId, int expectedStacks) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_03ad: 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) //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_041e: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_03a4: Unknown result type (might be due to invalid IL or missing references) if (!QOConfig.Enabled.Value) { return TransferOutcome.Fail(QOResult.Disabled); } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { QOLog.Warn("TransferService.Execute called on a non-server instance; refusing."); return TransferOutcome.Fail(QOResult.NotServer); } ZDOMan instance2 = ZDOMan.instance; if (instance2 == null) { return TransferOutcome.Fail(QOResult.Error); } ZDO zDO = instance2.GetZDO(sourceId); if (zDO == null || !zDO.IsValid()) { QOLog.Warn($"Transfer source {sourceId} is not a live ZDO."); return TransferOutcome.Fail(QOResult.Error); } List<ZDO> list = new List<ZDO>(); string[] signPrefabs = SignTags.SignPrefabs; foreach (string prefab in signPrefabs) { list.AddRange(ObliteratorIndex.AllWithPrefabCached(prefab)); } string text = SignTags.ResolveFromZdos(zDO, list); if (text.Length == 0) { QOLog.Debug($"Source {sourceId} has no sign within {QOConfig.SignSearchRadius.Value}m."); return TransferOutcome.Fail(QOResult.Error); } List<ZDO> list2 = new List<ZDO>(); foreach (ZDO item in ObliteratorIndex.AllWithPrefabCached("incinerator")) { if (item != null && item.IsValid() && !(item.m_uid == sourceId) && SignTags.Matches(SignTags.ResolveFromZdos(item, list), text)) { list2.Add(item); } } if (list2.Count == 0) { QOLog.Debug("No partner Obliterator tagged '" + text + "'."); return TransferOutcome.Fail(QOResult.NoPartner, text); } if (list2.Count > 1) { QOLog.Debug($"{list2.Count} Obliterators share the tag '{text}'; refusing."); return TransferOutcome.Fail(QOResult.Ambiguous, text, list2.Count); } ZDO val = list2[0]; if (!InventoryUtil.TryGetGrid(zDO, out var width, out var height, out var name) || !InventoryUtil.TryGetGrid(val, out var width2, out var height2, out var name2)) { QOLog.Error("Could not read Obliterator container dimensions from ZNetScene."); return TransferOutcome.Fail(QOResult.Error); } Inventory val2 = InventoryUtil.Read(zDO, width, height, name); Inventory val3 = InventoryUtil.Read(val, width2, height2, name2); if (expectedStacks >= 0 && val2.NrOfItems() != expectedStacks) { QOLog.Warn($"Source {sourceId} desynced: client saw {expectedStacks} stack(s), " + $"server sees {val2.NrOfItems()}. Aborting."); return TransferOutcome.Fail(QOResult.Desync); } InventoryUtil.FuelQuote fuelQuote = InventoryUtil.QuoteFuel(val2); if (!fuelQuote.Satisfied) { QOLog.Debug($"Source {sourceId} cannot pay: needs {fuelQuote.ShortfallUnits} " + "x " + (fuelQuote.ShortfallName ?? "<none configured>") + "."); return TransferOutcome.Fail(QOResult.NoFuel, fuelQuote.ShortfallName ?? "", fuelQuote.ShortfallUnits); } if (fuelQuote.UnitsNeeded > 0) { val2.RemoveItem(fuelQuote.SharedName, fuelQuote.UnitsNeeded, -1, true); if (val2.NrOfItems() == 0) { QOLog.Debug($"Source {sourceId} holds nothing but fuel; refusing."); return TransferOutcome.Fail(QOResult.NothingToShip); } } if (!InventoryUtil.MoveAllOrNothing(val2, val3, out var movedStacks)) { QOLog.Debug("Destination '" + text + "' could not hold the whole shipment; aborting."); return TransferOutcome.Fail(QOResult.DestinationFull, text); } byte[] array = InventoryUtil.Serialise(val2); byte[] array2 = InventoryUtil.Serialise(val3); val.Set(ZDOVars.s_items, array2); zDO.Set(ZDOVars.s_items, array); if (requesterPeerId != 0L) { instance2.ForceSendZDO(requesterPeerId, sourceId); } instance2.ForceSendZDO(val.m_uid); ObliteratorIndex.Invalidate(); QOLog.Info($"Transferred {movedStacks} stack(s) via tag '{text}': {sourceId} -> {val.m_uid}."); return new TransferOutcome { Result = QOResult.Success, Detail = text, Amount = movedStacks, Destination = val.m_uid }; } } } namespace QuantumObliterator.Patches { [HarmonyPatch(typeof(Container), "Interact")] internal static class ContainerGuardPatch { private static bool Prefix(Container __instance, ref bool __result) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (IncineratorPatch.InFlightContainers.Count == 0) { return true; } ZNetView nview = __instance.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return true; } if (!IncineratorPatch.InFlightContainers.Contains(nview.GetZDO().m_uid)) { return true; } __result = false; return false; } } [HarmonyPatch] internal static class IncineratorPatch { private const string ResponsRpc = "QO_Respons"; private const float RequestTimeoutSeconds = 10f; internal static readonly HashSet<ZDOID> InFlightContainers = new HashSet<ZDOID>(); private static bool _warnedSplitZdo; [HarmonyPostfix] [HarmonyPatch(typeof(Incinerator), "Awake")] private static void Awake_Postfix(Incinerator __instance) { ZNetView nview = __instance.m_nview; if (!((Object)(object)nview == (Object)null)) { nview.Register<int, string, int>("QO_Respons", (Action<long, int, string, int>)delegate(long sender, int result, string detail, int amount) { ShowMessage((QOResult)result, detail, amount); }); } } [HarmonyPrefix] [HarmonyPatch(typeof(Incinerator), "RPC_RequestIncinerate")] private static bool RPC_RequestIncinerate_Prefix(Incinerator __instance, long uid, long playerID) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if (!QOConfig.Enabled.Value) { return true; } ZNetView nview = __instance.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return true; } if (!nview.IsOwner()) { return true; } Container container = __instance.m_container; if ((Object)(object)container == (Object)null) { return true; } if (container.IsInUse() || __instance.isInUse) { return true; } Inventory inventory = container.GetInventory(); if (inventory == null || inventory.NrOfItems() == 0) { return true; } ZNetView nview2 = container.m_nview; if ((Object)(object)nview2 == (Object)null || !nview2.IsValid()) { return true; } ZDOID uid2 = nview2.GetZDO().m_uid; if (uid2 != nview.GetZDO().m_uid) { WarnSplitZdoOnce(); return true; } string text = SignTags.ResolveLocal(((Component)__instance).transform.position); if (text.Length == 0) { QOLog.Debug("Obliterator has no sign nearby; obliterating as vanilla."); return true; } QOLog.Debug($"Obliterator tagged '{text}' pulled by peer {uid}; requesting transfer."); __instance.isInUse = true; InFlightContainers.Add(uid2); ((MonoBehaviour)__instance).StartCoroutine(TransferRoutine(__instance, uid, uid2, inventory.NrOfItems())); return false; } private static void WarnSplitZdoOnce() { if (!_warnedSplitZdo) { _warnedSplitZdo = true; QOLog.Error("The Obliterator's Container does not share the piece's ZDO. QuantumObliterator cannot address the destination's inventory safely in that layout, so every Obliterator will fall back to vanilla behaviour. Please report this."); } } private static IEnumerator TransferRoutine(Incinerator inc, long uid, ZDOID containerZdoid, int expectedStacks) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) ZNetView nview = inc.m_nview; nview.InvokeRPC(ZNetView.Everybody, "RPC_AnimateLever", Array.Empty<object>()); EffectList leverEffects = inc.m_leverEffects; if (leverEffects != null) { leverEffects.Create(((Component)inc).transform.position, ((Component)inc).transform.rotation, (Transform)null, 1f, -1, default(ZDOID)); } yield return (object)new WaitForSeconds(Random.Range(inc.m_effectDelayMin, inc.m_effectDelayMax)); nview.InvokeRPC(ZNetView.Everybody, "RPC_AnimateLeverReturn", Array.Empty<object>()); QORpc.Pending pending = QORpc.BeginRequest(containerZdoid, expectedStacks); float deadline = Time.realtimeSinceStartup + 10f; while (!pending.Done && Time.realtimeSinceStartup < deadline) { yield return null; } TransferOutcome transferOutcome = (pending.Done ? pending.Outcome : TransferOutcome.Fail(QOResult.Timeout)); if (!pending.Done) { QORpc.Abandon(containerZdoid); QOLog.Warn($"Transfer request for {containerZdoid} timed out after {10f}s."); } if (transferOutcome.Result == QOResult.Success) { PlayLightning(inc); } if (nview.IsValid()) { nview.InvokeRPC(uid, "QO_Respons", new object[3] { (int)transferOutcome.Result, transferOutcome.Detail ?? string.Empty, transferOutcome.Amount }); } InFlightContainers.Remove(containerZdoid); inc.isInUse = false; } internal static void PlayArrivalEffect(ZDOID destination) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (destination == ZDOID.None || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated())) { return; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return; } GameObject val = instance.FindInstance(destination); if (!((Object)(object)val == (Object)null)) { Incinerator component = val.GetComponent<Incinerator>(); if ((Object)(object)component != (Object)null) { PlayLightning(component); } } } private static void PlayLightning(Incinerator inc) { //IL_001b: 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) if (!((Object)(object)inc.m_lightingAOEs == (Object)null)) { HarmlessLightning.Spawn(inc.m_lightingAOEs, ((Component)inc).transform.position, ((Component)inc).transform.rotation); } } private static void ShowMessage(QOResult result, string detail, int amount) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(QOTokens.For(result)) : QOTokens.For(result)); if (!string.IsNullOrEmpty(detail)) { string newValue = ((detail.StartsWith("$") && instance != null) ? instance.Localize(detail) : detail); text = text.Replace("{tag}", newValue).Replace("{item}", newValue); } text = text.Replace("{n}", amount.ToString()); ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false); } } } [HarmonyPatch(typeof(Incinerator), "GetLeverHoverText")] internal static class LeverHoverPatch { private sealed class Entry { internal string Suffix; internal float Expires; } private const float CacheSeconds = 0.5f; private static readonly Dictionary<ZDOID, Entry> Cache = new Dictionary<ZDOID, Entry>(); private static void Postfix(Incinerator __instance, ref string __result) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (!QOConfig.Enabled.Value || string.IsNullOrEmpty(__result) || __result.Contains("$piece_noaccess")) { return; } ZNetView nview = __instance.m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid()) { string text = SuffixFor(__instance, nview.GetZDO().m_uid); if (!string.IsNullOrEmpty(text)) { __result = __result + "\n" + text; } } } private static string SuffixFor(Incinerator inc, ZDOID id) { //IL_000b: 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) float realtimeSinceStartup = Time.realtimeSinceStartup; if (Cache.TryGetValue(id, out var value) && realtimeSinceStartup < value.Expires) { return value.Suffix; } string text = Build(inc); Cache[id] = new Entry { Suffix = text, Expires = realtimeSinceStartup + 0.5f }; return text; } private static string Build(Incinerator inc) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) string text = SignTags.ResolveLocal(((Component)inc).transform.position); if (text.Length == 0) { return string.Empty; } Container container = inc.m_container; Inventory val = (((Object)(object)container != (Object)null) ? container.GetInventory() : null); if (val == null) { return string.Empty; } Localization instance = Localization.instance; string text2 = Localize(instance, "$qo_hover_shipto").Replace("{tag}", text); float num = InventoryUtil.TotalWeight(val); text2 = text2 + " · " + Mathf.RoundToInt(num); if (!QOConfig.RequireFuel.Value) { return text2; } InventoryUtil.FuelQuote fuelQuote = InventoryUtil.QuoteFuel(val); int num2 = (fuelQuote.Satisfied ? fuelQuote.UnitsNeeded : fuelQuote.ShortfallUnits); string text3 = (fuelQuote.Satisfied ? fuelQuote.SharedName : fuelQuote.ShortfallName); if (string.IsNullOrEmpty(text3)) { return text2; } string text4 = num2 + " " + Localize(instance, text3); if (!fuelQuote.Satisfied) { text4 = "<color=#ff6d6d>" + text4 + "</color>"; } return text2 + " · " + text4; } private static string Localize(Localization loc, string token) { if (loc == null) { return token; } return loc.Localize(token); } } } namespace QuantumObliterator.Net { internal static class QORpc { internal sealed class Pending { internal bool Done; internal TransferOutcome Outcome; } [CompilerGenerated] private static class <>O { public static CoroutineHandler <0>__Dispatch; } private const string RpcName = "qo_transfer"; private const byte DirRequest = 1; private const byte DirResponse = 2; private const byte DirDestinationEffect = 3; private static CustomRPC _rpc; private static readonly Dictionary<ZDOID, Pending> InFlight = new Dictionary<ZDOID, Pending>(); internal static void Register() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0035: 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) //IL_0040: Expected O, but got Unknown NetworkManager instance = NetworkManager.Instance; object obj = <>O.<0>__Dispatch; if (obj == null) { CoroutineHandler val = Dispatch; <>O.<0>__Dispatch = val; obj = (object)val; } object obj2 = <>O.<0>__Dispatch; if (obj2 == null) { CoroutineHandler val2 = Dispatch; <>O.<0>__Dispatch = val2; obj2 = (object)val2; } _rpc = instance.AddRPC("qo_transfer", (CoroutineHandler)obj, (CoroutineHandler)obj2); } internal static Pending BeginRequest(ZDOID source, int expectedStacks) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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_0079: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0069: 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) Pending pending = new Pending(); InFlight[source] = pending; ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null) { Complete(source, TransferOutcome.Fail(QOResult.Error)); return pending; } if (instance.IsServer()) { TransferOutcome outcome = TransferService.Execute(source, 0L, expectedStacks); if (outcome.Result == QOResult.Success && QOConfig.DestinationEffect.Value) { BroadcastDestinationEffect(outcome.Destination); } Complete(source, outcome); return pending; } ZPackage val = new ZPackage(); val.Write((byte)1); val.Write(source); val.Write(expectedStacks); ZNetPeer serverPeer = instance.GetServerPeer(); if (serverPeer == null) { Complete(source, TransferOutcome.Fail(QOResult.Error)); return pending; } _rpc.SendPackage(serverPeer.m_uid, val); return pending; } internal static void Abandon(ZDOID source) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) InFlight.Remove(source); } private static void Complete(ZDOID source, TransferOutcome outcome) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (InFlight.TryGetValue(source, out var value)) { value.Outcome = outcome; value.Done = true; InFlight.Remove(source); } } private static IEnumerator Dispatch(long sender, ZPackage package) { if (package != null && package.Size() != 0) { byte b; try { b = package.ReadByte(); } catch (Exception ex) { QOLog.Error($"Malformed QO package from {sender}: {ex.Message}"); yield break; } switch (b) { case 1: HandleRequest(sender, package); break; case 2: HandleResponse(package); break; case 3: HandleDestinationEffect(package); break; default: QOLog.Warn($"Unknown QO package direction {b} from {sender}."); break; } } } private static void HandleRequest(long sender, ZPackage package) { //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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { QOLog.Warn($"Ignoring a transfer request from {sender} on a non-server instance."); return; } ZDOID val = package.ReadZDOID(); int expectedStacks = package.ReadInt(); TransferOutcome transferOutcome = TransferService.Execute(val, sender, expectedStacks); ZPackage val2 = new ZPackage(); val2.Write((byte)2); val2.Write(val); val2.Write((int)transferOutcome.Result); val2.Write(transferOutcome.Detail ?? string.Empty); val2.Write(transferOutcome.Amount); _rpc.SendPackage(sender, val2); if (transferOutcome.Result == QOResult.Success && QOConfig.DestinationEffect.Value) { BroadcastDestinationEffect(transferOutcome.Destination); } } private static void BroadcastDestinationEffect(ZDOID destination) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_0014: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (!(destination == ZDOID.None)) { ZPackage val = new ZPackage(); val.Write((byte)3); val.Write(destination); List<ZNetPeer> connectedPeers = ZNet.instance.GetConnectedPeers(); if (connectedPeers != null && connectedPeers.Count > 0) { _rpc.SendPackage(connectedPeers, val); } if (ZNet.instance.IsServer()) { IncineratorPatch.PlayArrivalEffect(destination); } } } private static void HandleResponse(ZPackage package) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ZDOID source = package.ReadZDOID(); TransferOutcome outcome = new TransferOutcome { Result = (QOResult)package.ReadInt(), Detail = package.ReadString(), Amount = package.ReadInt() }; Complete(source, outcome); } private static void HandleDestinationEffect(ZPackage package) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) IncineratorPatch.PlayArrivalEffect(package.ReadZDOID()); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }