Please disclose if your mod was created primarily 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 BetterBetterTeleporter v1.2.3
BepInEx/plugins/BetterBetterTeleporter.dll
Decompiled 2 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BetterBetterTeleporter.Adapters; using BetterBetterTeleporter.Integrations; using BetterBetterTeleporter.Networking; using BetterBetterTeleporter.Utility; using GameNetcodeStuff; using HarmonyLib; using LethalConfig; using LethalConfig.ConfigItems; using LethalConfig.ConfigItems.Options; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BetterBetterTeleporter")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A fully configurable Teleporter and Inverse Teleporter mod with advanced item filtering.")] [assembly: AssemblyFileVersion("1.2.3.0")] [assembly: AssemblyInformationalVersion("1.2.3+1fc40039458e3fabed4a21983577abec8e0b3d38")] [assembly: AssemblyProduct("BetterBetterTeleporter")] [assembly: AssemblyTitle("BetterBetterTeleporter")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.2.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 BetterBetterTeleporter { public enum ItemTeleportBehavior { Keep, Drop } public class ModConfig { public SyncedEntry<bool> ResetCooldownOnOrbit; public SyncedEntry<int> TeleporterCooldown; public SyncedEntry<ItemTeleportBehavior> TeleporterBehavior; public SyncedEntry<string> TeleporterAlwaysKeep; public SyncedEntry<string> TeleporterAlwaysDrop; public SyncedEntry<int> InverseTeleporterCooldown; public SyncedEntry<ItemTeleportBehavior> InverseTeleporterBehavior; public SyncedEntry<string> InverseTeleporterAlwaysKeep; public SyncedEntry<string> InverseTeleporterAlwaysDrop; public SyncedEntry<int> BatteryDrainPercent; public ModConfig(ConfigFile config) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Expected O, but got Unknown //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Expected O, but got Unknown //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Expected O, but got Unknown config.SaveOnConfigSet = false; ResetCooldownOnOrbit = config.BindSynced("General", "ResetCooldownOnOrbit", value: false, new ConfigDescription("Resets the cooldown time on teleporters between days.", (AcceptableValueBase)null, Array.Empty<object>())); TeleporterCooldown = config.BindSynced("Teleporter", "TeleporterCooldown", 10, new ConfigDescription("Cooldown time (in seconds) for using the Teleporter.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, int.MaxValue), Array.Empty<object>())); TeleporterBehavior = config.BindSynced("Teleporter", "TeleporterBehavior", ItemTeleportBehavior.Drop, new ConfigDescription("Makes the Teleporter \"Drop\" or \"Keep\" items on teleport.", (AcceptableValueBase)null, Array.Empty<object>())); TeleporterAlwaysKeep = config.BindSynced("Teleporter", "TeleporterAlwaysKeep", "", new ConfigDescription("Keep these items regardless of Teleporter behavior (comma-separated item names).\n\nDoes nothing if TeleporterBehavior is set to \"Keep\".", (AcceptableValueBase)null, Array.Empty<object>())); TeleporterAlwaysDrop = config.BindSynced("Teleporter", "TeleporterAlwaysDrop", "", new ConfigDescription("Drop these items regardless of Teleporter behavior (comma-separated item names).\n\nDoes nothing if TeleporterBehavior is set to \"Drop\".", (AcceptableValueBase)null, Array.Empty<object>())); InverseTeleporterCooldown = config.BindSynced("Inverse Teleporter", "InverseTeleporterCooldown", 210, new ConfigDescription("Cooldown time (in seconds) for using the Inverse Teleporter.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, int.MaxValue), Array.Empty<object>())); InverseTeleporterBehavior = config.BindSynced("Inverse Teleporter", "InverseTeleporterBehavior", ItemTeleportBehavior.Drop, new ConfigDescription("Makes the Inverse Teleporter \"Drop\" or \"Keep\" items on teleport.", (AcceptableValueBase)null, Array.Empty<object>())); InverseTeleporterAlwaysKeep = config.BindSynced("Inverse Teleporter", "InverseTeleporterAlwaysKeep", "", new ConfigDescription("Keep these items regardless of Inverse Teleporter behavior (comma-separated item names).\n\nDoes nothing if InverseTeleporterBehavior is set to \"Keep\".", (AcceptableValueBase)null, Array.Empty<object>())); InverseTeleporterAlwaysDrop = config.BindSynced("Inverse Teleporter", "InverseTeleporterAlwaysDrop", "", new ConfigDescription("Drop these items regardless of Inverse Teleporter behavior (comma-separated item names).\n\nDoes nothing if InverseTeleporterBehavior is set to \"Drop\".", (AcceptableValueBase)null, Array.Empty<object>())); BatteryDrainPercent = config.BindSynced("Inverse Teleporter", "BatteryDrainPercent", 0, new ConfigDescription("Drains all held battery items by a percentage when using the Inverse Teleporter.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ((Dictionary<ConfigDefinition, string>)AccessTools.Property(typeof(ConfigFile), "OrphanedEntries").GetValue(config)).Clear(); config.Save(); config.SaveOnConfigSet = true; } } [BepInPlugin("BetterBetterTeleporter", "BetterBetterTeleporter", "1.2.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger { get; private set; } public static Plugin Instance { get; private set; } public static ModConfig ModConfig { get; private set; } internal void Awake() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Instance != (Object)null)) { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; ModConfig = new ModConfig(((BaseUnityPlugin)this).Config); InitSoftDependencyIntegrations(); new Harmony("BetterBetterTeleporter").PatchAll(); Logger.LogInfo((object)"BetterBetterTeleporter loaded successfully."); } } private void InitSoftDependencyIntegrations() { if (Chainloader.PluginInfos.ContainsKey("ainavt.lc.lethalconfig")) { LethalConfigIntegration.Initialize(); } } } public static class PluginInfo { public const string PLUGIN_GUID = "BetterBetterTeleporter"; public const string PLUGIN_NAME = "BetterBetterTeleporter"; public const string PLUGIN_VERSION = "1.2.3"; } } namespace BetterBetterTeleporter.Utility { public static class ItemParser { public static List<ItemRule> ParseConfig(string source) { if (string.IsNullOrEmpty(source)) { return new List<ItemRule>(); } List<ItemRule> list = new List<ItemRule>(); if (!IndexOfUnescaped(source, ',').HasValue) { return new List<ItemRule>(1) { ParseItemRule(source) }; } foreach (string item in SplitCurrentGeneration(source)) { list.Add(ParseItemRule(item)); } return list; } private static ItemRule ParseItemRule(string source) { if (source[0] == '[') { if (source[source.Length - 1] == ']') { string text = source; string[] array = text.Substring(1, text.Length - 1 - 1).ToLowerInvariant().Split(":", 2, StringSplitOptions.RemoveEmptyEntries); bool flag = false; List<ItemRule> rules = new List<ItemRule>(); if (array.Length > 1) { int num; if (array[1].StartsWith("not(")) { string obj = array[1]; num = ((obj[obj.Length - 1] == ')') ? 1 : 0); } else { num = 0; } flag = (byte)num != 0; object source2; if (!flag) { source2 = array[1]; } else { text = array[1]; source2 = text.Substring(4, text.Length - 1 - 4); } rules = ParseConfig((string)source2); } return ItemRules.FromId(Unescape(array[0]), new ItemFilterList(flag, rules)); } } return new ItemNameRule(Unescape(source)); } private static List<string> SplitCurrentGeneration(string source) { List<string> list = new List<string>(); int num = 0; int num2 = 0; for (int i = 0; i < source.Length; i++) { switch (source[i]) { case '[': num++; break; case ']': num = Math.Max(num - 1, 0); break; case ',': if (num == 0) { if (i - num2 > 0) { int num3 = num2; list.Add(source.Substring(num3, i - num3)); } num2 = i + 1; } break; } } if (num2 < source.Length) { int num3 = num2; list.Add(source.Substring(num3, source.Length - num3)); } return list; } private static int? IndexOfUnescaped(string source, char delimiter, char escapeChar = '\\') { if (string.IsNullOrEmpty(source)) { return null; } bool flag = false; for (int i = 0; i < source.Length; i++) { if (source[i] == escapeChar) { flag = !flag; continue; } if (source[i] == delimiter && !flag) { return i; } flag = false; } return null; } private static string Unescape(string source, char escapeChar = '\\') { bool flag = false; for (int i = 0; i < source.Length; i++) { if (source[i] == escapeChar && !flag) { flag = !flag; } else if (source[i] == escapeChar) { source = source.Remove(i, 1); } else { flag = false; } } return source; } } public static class ItemRules { public static bool ShouldDropItem(this IPlayerInfo player, IItemInfo item, TeleporterConfigState state) { bool isDropDefault = state.Behavior == ItemTeleportBehavior.Drop; return player.ShouldDropItem(item, isDropDefault, state.Rules); } public static bool ShouldDropItem(this IPlayerInfo player, IItemInfo item, bool isDropDefault, List<ItemRule> rules) { if (item != null) { return isDropDefault ^ rules.Any((ItemRule rule) => rule.IsMatch(player, item)); } return false; } public static ItemRule FromId(string id, ItemFilterList itemList) { switch (id) { case "all": return new AllFilter(itemList); case "none": return new NoneFilter(itemList); case "held": return new HeldItemFilter(itemList); case "pocketed": return new PocketedItemFilter(itemList); case "scrap": return new ScrapItemFilter(itemList); case "nonscrap": return new NonScrapItemFilter(itemList); case "value": return new ValueItemFilter(itemList); case "worthless": return new WorthlessItemFilter(itemList); case "metal": return new MetalItemFilter(itemList); case "nonmetal": return new NonMetalItemFilter(itemList); case "weapon": return new WeaponItemFilter(itemList); case "nonweapon": return new NonWeaponItemFilter(itemList); case "battery": return new BatteryItemFilter(itemList); case "nonbattery": return new NonBatteryItemFilter(itemList); case "charged": return new ChargedItemFilter(itemList); case "discharged": return new DischargedItemFilter(itemList); case "onehanded": return new OneHandedItemFilter(itemList); case "twohanded": return new TwoHandedItemFilter(itemList); case "weighted": return new WeightedItemFilter(itemList); case "weightless": return new WeightlessItemFilter(itemList); case "gordion": return new GordionFilter(itemList); case "gordioff": return new GordioffFilter(itemList); default: { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogWarning((object)("Unknown item filter: " + id + ". Falling back to item name matching.")); } return new ItemNameRule(id); } } } } public abstract class ItemRule { [CompilerGenerated] private string <id>P; protected ItemRule(string id) { <id>P = id; base..ctor(); } public abstract bool IsMatch(IPlayerInfo player, IItemInfo item); public override string ToString() { return <id>P; } } public class ItemNameRule : ItemRule { private const StringComparison caseInsensitive = StringComparison.OrdinalIgnoreCase; private readonly string name; public ItemNameRule(string name) { this.name = name; base..ctor(name); } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (item.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) { return true; } if (item.TypeName.Equals(name, StringComparison.OrdinalIgnoreCase)) { return true; } if (item.DisplayName.Equals(name, StringComparison.OrdinalIgnoreCase)) { return true; } return false; } } public record ItemFilterList { public bool IsExclusive { get; set; } public List<ItemRule> Rules { get; set; } public ItemFilterList(bool isExclusive, List<ItemRule> rules) { IsExclusive = isExclusive; Rules = rules; } } public abstract class ItemFilter : ItemRule { [CompilerGenerated] private ItemFilterList <itemList>P; protected ItemFilter(string id, ItemFilterList itemList) { <itemList>P = itemList; base..ctor(id); } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (<itemList>P.Rules.Count == 0) { return true; } return <itemList>P.IsExclusive ^ <itemList>P.Rules.Any((ItemRule rule) => rule.IsMatch(player, item)); } } public class AllFilter : ItemFilter { public const string Id = "all"; public AllFilter(ItemFilterList except) : base("all", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { return base.IsMatch(player, item); } } public class NoneFilter : ItemFilter { public const string Id = "none"; public NoneFilter(ItemFilterList except) : base("none", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { return !base.IsMatch(player, item); } } public class HeldItemFilter : ItemFilter { public const string Id = "held"; public HeldItemFilter(ItemFilterList except) : base("held", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsPocketed.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [held] due to read issues on GrabbableObject. Skipping filter."); return false; } if (!item.IsPocketed.Value) { return base.IsMatch(player, item); } return false; } } public class PocketedItemFilter : ItemFilter { public const string Id = "pocketed"; public PocketedItemFilter(ItemFilterList except) : base("pocketed", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsPocketed.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [pocketed] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.IsPocketed.Value) { return base.IsMatch(player, item); } return false; } } public class ScrapItemFilter : ItemFilter { public const string Id = "scrap"; public ScrapItemFilter(ItemFilterList except) : base("scrap", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsScrap.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [scrap] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.IsScrap.Value) { return base.IsMatch(player, item); } return false; } } public class NonScrapItemFilter : ItemFilter { public const string Id = "nonscrap"; public NonScrapItemFilter(ItemFilterList except) : base("nonscrap", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsScrap.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [nonscrap] due to read issues on GrabbableObject. Skipping filter."); return false; } if (!item.IsScrap.Value) { return base.IsMatch(player, item); } return false; } } public class ValueItemFilter : ItemFilter { public const string Id = "value"; public ValueItemFilter(ItemFilterList except) : base("value", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsScrap.HasValue || !item.Value.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [value] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.IsScrap.Value && item.Value.Value > 0) { return base.IsMatch(player, item); } return false; } } public class WorthlessItemFilter : ItemFilter { public const string Id = "worthless"; public WorthlessItemFilter(ItemFilterList except) : base("worthless", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsScrap.HasValue || !item.Value.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [worthless] due to read issues on GrabbableObject. Skipping filter."); return false; } if (!item.IsScrap.Value || item.Value.Value == 0) { return base.IsMatch(player, item); } return false; } } public class MetalItemFilter : ItemFilter { public const string Id = "metal"; public MetalItemFilter(ItemFilterList except) : base("metal", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsMetal.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [metal] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.IsMetal.Value) { return base.IsMatch(player, item); } return false; } } public class NonMetalItemFilter : ItemFilter { public const string Id = "nonmetal"; public NonMetalItemFilter(ItemFilterList except) : base("nonmetal", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsMetal.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [nonmetal] due to read issues on GrabbableObject. Skipping filter."); return false; } if (!item.IsMetal.Value) { return base.IsMatch(player, item); } return false; } } public class WeaponItemFilter : ItemFilter { public const string Id = "weapon"; public WeaponItemFilter(ItemFilterList except) : base("weapon", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsWeapon.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [weapon] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.IsWeapon.Value) { return base.IsMatch(player, item); } return false; } } public class NonWeaponItemFilter : ItemFilter { public const string Id = "nonweapon"; public NonWeaponItemFilter(ItemFilterList except) : base("nonweapon", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsWeapon.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [nonweapon] due to read issues on GrabbableObject. Skipping filter."); return false; } if (!item.IsWeapon.Value) { return base.IsMatch(player, item); } return false; } } public class BatteryItemFilter : ItemFilter { public const string Id = "battery"; public BatteryItemFilter(ItemFilterList except) : base("battery", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.HasBattery.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [battery] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.HasBattery.Value) { return base.IsMatch(player, item); } return false; } } public class NonBatteryItemFilter : ItemFilter { public const string Id = "nonbattery"; public NonBatteryItemFilter(ItemFilterList except) : base("nonbattery", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.HasBattery.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [nonbattery] due to read issues on GrabbableObject. Skipping filter."); return false; } if (!item.HasBattery.Value) { return base.IsMatch(player, item); } return false; } } public class ChargedItemFilter : ItemFilter { public const string Id = "charged"; public ChargedItemFilter(ItemFilterList except) : base("charged", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.HasBattery.HasValue || !item.BatteryCharge.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [charged] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.HasBattery.Value && item.BatteryCharge.Value > 0f) { return base.IsMatch(player, item); } return false; } } public class DischargedItemFilter : ItemFilter { public const string Id = "discharged"; public DischargedItemFilter(ItemFilterList except) : base("discharged", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.HasBattery.HasValue || !item.BatteryCharge.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [discharged] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.HasBattery.Value && item.BatteryCharge.Value == 0f) { return base.IsMatch(player, item); } return false; } } public class OneHandedItemFilter : ItemFilter { public const string Id = "onehanded"; public OneHandedItemFilter(ItemFilterList except) : base("onehanded", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsTwoHanded.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [onehanded] due to read issues on GrabbableObject. Skipping filter."); return false; } if (!item.IsTwoHanded.Value) { return base.IsMatch(player, item); } return false; } } public class TwoHandedItemFilter : ItemFilter { public const string Id = "twohanded"; public TwoHandedItemFilter(ItemFilterList except) : base("twohanded", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.IsTwoHanded.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [twohanded] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.IsTwoHanded.Value) { return base.IsMatch(player, item); } return false; } } public class WeightedItemFilter : ItemFilter { public const string Id = "weighted"; public WeightedItemFilter(ItemFilterList except) : base("weighted", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.Weight.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [weighted] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.Weight.Value > 1f) { return base.IsMatch(player, item); } return false; } } public class WeightlessItemFilter : ItemFilter { public const string Id = "weightless"; public WeightlessItemFilter(ItemFilterList except) : base("weightless", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (!item.Weight.HasValue) { Plugin.Logger.LogWarning((object)"Unable to use filter [weightless] due to read issues on GrabbableObject. Skipping filter."); return false; } if (item.Weight.Value <= 1f) { return base.IsMatch(player, item); } return false; } } public class GordionFilter : ItemFilter { public const string Id = "gordion"; public GordionFilter(ItemFilterList except) : base("gordion", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { try { return !StartOfRound.Instance.inShipPhase && StartOfRound.Instance.currentLevel.levelID == 3 && base.IsMatch(player, item); } catch { Plugin.Logger.LogWarning((object)"Unable to use filter [gordion] due to read issues on StartOfRound. Skipping filter."); return false; } } } public class GordioffFilter : ItemFilter { public const string Id = "gordioff"; public GordioffFilter(ItemFilterList except) : base("gordioff", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { try { return !StartOfRound.Instance.inShipPhase && StartOfRound.Instance.currentLevel.levelID != 3 && base.IsMatch(player, item); } catch { Plugin.Logger.LogWarning((object)"Unable to use filter [gordioff] due to read issues on StartOfRound. Skipping filter."); return false; } } } public static class ReflectionHelper { private static FieldInfo _cooldownTimeField; private static bool _cooldownTimeFieldChecked; public static FieldInfo GetShipTeleporterCooldownTimeField() { if (_cooldownTimeFieldChecked) { return _cooldownTimeField; } _cooldownTimeFieldChecked = true; try { _cooldownTimeField = typeof(ShipTeleporter).GetField("cooldownTime", BindingFlags.Instance | BindingFlags.NonPublic); if (_cooldownTimeField == null) { Plugin.Logger.LogWarning((object)"Reflection failed: Could not find ShipTeleporter.cooldownTime field. Teleporter cooldown features will be disabled."); } } catch (Exception arg) { Plugin.Logger.LogError((object)$"Reflection error finding ShipTeleporter.cooldownTime: {arg}"); _cooldownTimeField = null; } return _cooldownTimeField; } } public class TeleporterConfigState { public readonly ItemTeleportBehavior Behavior; public readonly List<ItemRule> Rules; private static bool _init; private static readonly (Func<string> Raw, List<ItemRule> Rules)[,] _cache = new(Func<string>, List<ItemRule>)[2, 2]; public TeleporterConfigState(bool inverse) { ModConfig config = Plugin.ModConfig; if (!_init) { _init = true; _cache[0, 0] = (() => config.TeleporterAlwaysKeep.Value, null); _cache[0, 1] = (() => config.TeleporterAlwaysDrop.Value, null); _cache[1, 0] = (() => config.InverseTeleporterAlwaysKeep.Value, null); _cache[1, 1] = (() => config.InverseTeleporterAlwaysDrop.Value, null); SyncedEntry<string> teleporterAlwaysKeep = config.TeleporterAlwaysKeep; teleporterAlwaysKeep.OnChanged = (Action<string, string>)Delegate.Combine(teleporterAlwaysKeep.OnChanged, (Action<string, string>)delegate { Reparse(ref _cache[0, 0]); }); SyncedEntry<string> teleporterAlwaysDrop = config.TeleporterAlwaysDrop; teleporterAlwaysDrop.OnChanged = (Action<string, string>)Delegate.Combine(teleporterAlwaysDrop.OnChanged, (Action<string, string>)delegate { Reparse(ref _cache[0, 1]); }); SyncedEntry<string> inverseTeleporterAlwaysKeep = config.InverseTeleporterAlwaysKeep; inverseTeleporterAlwaysKeep.OnChanged = (Action<string, string>)Delegate.Combine(inverseTeleporterAlwaysKeep.OnChanged, (Action<string, string>)delegate { Reparse(ref _cache[1, 0]); }); SyncedEntry<string> inverseTeleporterAlwaysDrop = config.InverseTeleporterAlwaysDrop; inverseTeleporterAlwaysDrop.OnChanged = (Action<string, string>)Delegate.Combine(inverseTeleporterAlwaysDrop.OnChanged, (Action<string, string>)delegate { Reparse(ref _cache[1, 1]); }); } Behavior = (inverse ? config.InverseTeleporterBehavior.Value : config.TeleporterBehavior.Value); int num = (inverse ? 1 : 0); int num2 = ((Behavior == ItemTeleportBehavior.Keep) ? 1 : 0); ref(Func<string>, List<ItemRule>) reference = ref _cache[num, num2]; ref List<ItemRule> item = ref reference.Item2; Rules = item ?? (item = ItemParser.ParseConfig(reference.Item1())); } private static void Reparse(ref (Func<string> Raw, List<ItemRule> Rules) entry) { entry.Rules = ItemParser.ParseConfig(entry.Raw()); } } } namespace BetterBetterTeleporter.Patches { [HarmonyPatch] public class FixTeleporterBugsPatch { [CompilerGenerated] private sealed class <DelayedForceSync>d__3 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PlayerControllerB player; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <DelayedForceSync>d__3(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForFixedUpdate(); <>1__state = 1; return true; case 1: <>1__state = -1; UpdatePositionForNewlyJoinedClient?.SetValue(player, true); <>2__current = (object)new WaitForSecondsRealtime(0.2f); <>1__state = 2; return true; case 2: <>1__state = -1; UpdatePositionForNewlyJoinedClient?.SetValue(player, true); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly FieldInfo UpdatePositionForNewlyJoinedClient = AccessTools.Field(typeof(PlayerControllerB), "updatePositionForNewlyJoinedClient"); private static bool HasFailedToDirtyTeleportPosition = false; [HarmonyPatch(typeof(PlayerControllerB), "TeleportPlayer")] [HarmonyPrefix] public static void TeleportPlayer(PlayerControllerB __instance, ref Vector3 pos) { if (HasFailedToDirtyTeleportPosition || !TeleportDetectionPatch.IsRegularTeleporting()) { return; } try { pos.y -= 0.5f; ((MonoBehaviour)__instance).StartCoroutine(DelayedForceSync(__instance)); } catch { Plugin.Logger.LogWarning((object)"Failed to apply teleport positioning fix. Disabling future attempts."); HasFailedToDirtyTeleportPosition = true; } } [IteratorStateMachine(typeof(<DelayedForceSync>d__3))] private static IEnumerator DelayedForceSync(PlayerControllerB player) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <DelayedForceSync>d__3(0) { player = player }; } } [HarmonyPatch(typeof(ShipTeleporter), "TeleportPlayerOutWithInverseTeleporter")] public static class InverseTeleporterBatteryDrainPatch { [HarmonyPostfix] public static void TeleportPlayerOutWithInverseTeleporterPostfix(int playerObj, Vector3 teleportPos) { float num = (float)Plugin.ModConfig.BatteryDrainPercent.Value / 100f; if (num == 0f) { return; } try { GrabbableObject[] itemSlots = StartOfRound.Instance.allPlayerScripts[playerObj].ItemSlots; foreach (GrabbableObject val in itemSlots) { Battery val2 = val?.insertedBattery; if (val2 != null) { val2.charge = Mathf.Max(0f, val2.charge - num); val.SyncBatteryServerRpc((int)(val2.charge * 100f)); } } Plugin.Logger.LogDebug((object)$"Client {playerObj} batteries drained by {num}."); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to drain batteries after inverse teleport: {arg}"); } } } [HarmonyPatch(typeof(PlayerControllerB), "DropAllHeldItems")] public static class KeepItemsOnTeleporterPatch { [CompilerGenerated] private sealed class <RefreshInventory>d__4 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public PlayerControllerB __instance; public float carryWeightDelta; public bool isInverse; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RefreshInventory>d__4(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForEndOfFrame(); <>1__state = 1; return true; case 1: { <>1__state = -1; __instance.carryWeight = Mathf.Clamp(__instance.carryWeight + carryWeightDelta, 1f, 10f); for (int i = 0; i < __instance.ItemSlots.Length; i++) { GrabbableObject val = __instance.ItemSlots[i]; if (!((Object)(object)val == (Object)null)) { val.isInElevator = !isInverse; val.isInFactory = isInverse; val.isInShipRoom = !isInverse; } } return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly Dictionary<PlayerControllerB, GrabbableObject[]> tempInventories = new Dictionary<PlayerControllerB, GrabbableObject[]>(); private static readonly MethodInfo SwitchToItemSlotMethod = AccessTools.Method(typeof(PlayerControllerB), "SwitchToItemSlot", (Type[])null, (Type[])null); [HarmonyPrefix] public static void DropAllHeldItemsPrefix(PlayerControllerB __instance) { if (!TeleportDetectionPatch.IsTeleporting(__instance)) { return; } GrabbableObject[] array = (GrabbableObject[])__instance.ItemSlots.Clone(); try { PlayerInfo playerInfo = new PlayerInfo(__instance); TeleporterConfigState teleportState = GetTeleportState(__instance); GrabbableObject[] array2 = (GrabbableObject[])__instance.ItemSlots.Clone(); for (int i = 0; i < __instance.ItemSlots.Length; i++) { if (playerInfo.ShouldDropItem(playerInfo.Slots[i], teleportState)) { array2[i] = null; } else { __instance.ItemSlots[i] = null; } } __instance.isHoldingObject = (Object)(object)__instance.ItemSlots[__instance.currentItemSlot] != (Object)null; tempInventories[__instance] = array2; } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to intercept DropAllHeldItems (Prefix). Falling back to native behavior. Error: {arg}"); tempInventories.Remove(__instance); for (int j = 0; j < __instance.ItemSlots.Length; j++) { __instance.ItemSlots[j] = array[j]; } } } [HarmonyPostfix] public static void DropAllHeldItemsPostfix(PlayerControllerB __instance) { if (!tempInventories.ContainsKey(__instance)) { return; } GrabbableObject[] array = tempInventories[__instance]; tempInventories.Remove(__instance); try { bool isInverse = TeleportDetectionPatch.IsInverseTeleporting(__instance); float num = 0f; for (int i = 0; i < __instance.ItemSlots.Length; i++) { GrabbableObject val = array[i]; if (!((Object)(object)val == (Object)null)) { __instance.ItemSlots[i] = val; num += val.itemProperties.weight - 1f; } } ((MonoBehaviour)NetworkManager.Singleton).StartCoroutine(RefreshInventory(__instance, num, isInverse)); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to restore inventory. Error: {arg}"); } try { __instance.isHoldingObject = (Object)(object)__instance.ItemSlots[__instance.currentItemSlot] != (Object)null; SwitchToItemSlotMethod.Invoke(__instance, new object[2] { __instance.currentItemSlot, null }); } catch (Exception arg2) { Plugin.Logger.LogWarning((object)$"Unable to verify current item is being held correctly. Error: {arg2}"); } } [IteratorStateMachine(typeof(<RefreshInventory>d__4))] private static IEnumerator RefreshInventory(PlayerControllerB __instance, float carryWeightDelta, bool isInverse) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RefreshInventory>d__4(0) { __instance = __instance, carryWeightDelta = carryWeightDelta, isInverse = isInverse }; } private static TeleporterConfigState GetTeleportState(PlayerControllerB player) { return new TeleporterConfigState(TeleportDetectionPatch.IsInverseTeleporting(player)); } } [HarmonyPatch(typeof(StartOfRound))] public static class ResetCooldownOnOrbitPatch { [HarmonyPostfix] [HarmonyPatch("StartGame")] [HarmonyPatch("EndOfGame")] [HarmonyPatch("EndOfGameClientRpc")] private static void ResetCooldowns() { if (!Plugin.ModConfig.ResetCooldownOnOrbit.Value) { return; } FieldInfo shipTeleporterCooldownTimeField = ReflectionHelper.GetShipTeleporterCooldownTimeField(); if (shipTeleporterCooldownTimeField == null) { return; } ShipTeleporter[] array = Object.FindObjectsOfType<ShipTeleporter>(); foreach (ShipTeleporter val in array) { try { if ((float)shipTeleporterCooldownTimeField.GetValue(val) > 0f) { shipTeleporterCooldownTimeField.SetValue(val, 0); Plugin.Logger.LogDebug((object)("Reset cooldown on " + (val.isInverseTeleporter ? "Inverse Teleporter" : "Teleporter") + ".")); } } catch (Exception ex) { Plugin.Logger.LogError((object)("Error resetting cooldown on teleporter " + ((Object)val).name + ": " + ex.Message)); } } } } [HarmonyPatch(typeof(ShipTeleporter))] public static class TeleportDetectionPatch { [CompilerGenerated] private sealed class <TeleporterTranspiler>d__2 : IEnumerable<CodeInstruction>, IEnumerable, IEnumerator<CodeInstruction>, IEnumerator, IDisposable { private int <>1__state; private CodeInstruction <>2__current; private int <>l__initialThreadId; private IEnumerable<CodeInstruction> instructions; public IEnumerable<CodeInstruction> <>3__instructions; private MethodInfo <dropAllHeldItemsMethod>5__2; private MethodInfo <beforeMethod>5__3; private IEnumerator<CodeInstruction> <>7__wrap3; private CodeInstruction <instruction>5__5; CodeInstruction IEnumerator<CodeInstruction>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <TeleporterTranspiler>d__2(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 1u) { try { } finally { <>m__Finally1(); } } <dropAllHeldItemsMethod>5__2 = null; <beforeMethod>5__3 = null; <>7__wrap3 = null; <instruction>5__5 = null; <>1__state = -2; } private bool MoveNext() { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <dropAllHeldItemsMethod>5__2 = AccessTools.Method(typeof(PlayerControllerB), "DropAllHeldItems", (Type[])null, (Type[])null); <beforeMethod>5__3 = AccessTools.Method(typeof(TeleportDetectionPatch), "BeforeTeleporterDropAllHeldItems", (Type[])null, (Type[])null); <>7__wrap3 = instructions.GetEnumerator(); <>1__state = -3; goto IL_00ed; case 1: <>1__state = -3; goto IL_00c7; case 2: { <>1__state = -3; <instruction>5__5 = null; goto IL_00ed; } IL_00ed: if (<>7__wrap3.MoveNext()) { <instruction>5__5 = <>7__wrap3.Current; if (CodeInstructionExtensions.Calls(<instruction>5__5, <dropAllHeldItemsMethod>5__2)) { <>2__current = new CodeInstruction(OpCodes.Call, (object)<beforeMethod>5__3); <>1__state = 1; return true; } goto IL_00c7; } <>m__Finally1(); <>7__wrap3 = null; return false; IL_00c7: <>2__current = <instruction>5__5; <>1__state = 2; return true; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap3 != null) { <>7__wrap3.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<CodeInstruction> IEnumerable<CodeInstruction>.GetEnumerator() { <TeleporterTranspiler>d__2 <TeleporterTranspiler>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <TeleporterTranspiler>d__ = this; } else { <TeleporterTranspiler>d__ = new <TeleporterTranspiler>d__2(0); } <TeleporterTranspiler>d__.instructions = <>3__instructions; return <TeleporterTranspiler>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<CodeInstruction>)this).GetEnumerator(); } } private static bool isTeleporting; private static readonly HashSet<int> InverseTeleportingPlayers = new HashSet<int>(); public static bool IsTeleporting(PlayerControllerB player) { if (!StartOfRound.Instance.ClientPlayerList.ContainsKey(player.actualClientId)) { return false; } if (IsRegularTeleporting()) { return true; } if (IsInverseTeleporting(player)) { return true; } return false; } [IteratorStateMachine(typeof(<TeleporterTranspiler>d__2))] [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> TeleporterTranspiler(IEnumerable<CodeInstruction> instructions) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <TeleporterTranspiler>d__2(-2) { <>3__instructions = instructions }; } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyFinalizer] private static Exception TeleporterFinalizer(Exception __exception) { AfterTeleporterDropAllHeldItems(); return __exception; } public static void BeforeTeleporterDropAllHeldItems() { isTeleporting = true; } public static void AfterTeleporterDropAllHeldItems() { isTeleporting = false; } public static bool IsRegularTeleporting() { return isTeleporting; } public static bool IsInverseTeleporting(PlayerControllerB player) { return InverseTeleportingPlayers.Contains((int)player.playerClientId); } [HarmonyPatch("TeleportPlayerOutWithInverseTeleporter")] [HarmonyPrefix] public static void TeleportPlayerOutWithInverseTeleporterPrefix(int playerObj) { InverseTeleportingPlayers.Add(playerObj); } [HarmonyPatch("TeleportPlayerOutWithInverseTeleporter")] [HarmonyPostfix] public static void TeleportPlayerOutWithInverseTeleporterPostfix(int playerObj) { InverseTeleportingPlayers.Remove(playerObj); } } [HarmonyPatch(typeof(ShipTeleporter))] public static class TeleporterCooldownPatch { static TeleporterCooldownPatch() { SyncedEntry<int> teleporterCooldown = Plugin.ModConfig.TeleporterCooldown; teleporterCooldown.OnChanged = (Action<int, int>)Delegate.Combine(teleporterCooldown.OnChanged, new Action<int, int>(UpdateAllTeleporterCooldowns)); SyncedEntry<int> inverseTeleporterCooldown = Plugin.ModConfig.InverseTeleporterCooldown; inverseTeleporterCooldown.OnChanged = (Action<int, int>)Delegate.Combine(inverseTeleporterCooldown.OnChanged, new Action<int, int>(UpdateAllTeleporterCooldowns)); } [HarmonyPatch("Awake")] [HarmonyPostfix] public static void AwakePostfix(ShipTeleporter __instance) { (int inverse, int regular) cooldowns = GetCooldowns(); int item = cooldowns.inverse; int item2 = cooldowns.regular; __instance.cooldownAmount = (__instance.isInverseTeleporter ? item : item2); } private static void UpdateAllTeleporterCooldowns(int oldValue, int newValue) { if (oldValue == newValue) { return; } FieldInfo shipTeleporterCooldownTimeField = ReflectionHelper.GetShipTeleporterCooldownTimeField(); if (shipTeleporterCooldownTimeField == null) { return; } (int inverse, int regular) cooldowns = GetCooldowns(); int item = cooldowns.inverse; int item2 = cooldowns.regular; ShipTeleporter[] array = Object.FindObjectsOfType<ShipTeleporter>(); foreach (ShipTeleporter val in array) { try { val.cooldownAmount = (val.isInverseTeleporter ? item : item2); shipTeleporterCooldownTimeField.SetValue(val, Mathf.Min(val.cooldownAmount, (float)(shipTeleporterCooldownTimeField.GetValue(val) ?? ((object)0)))); } catch (Exception ex) { Plugin.Logger.LogError((object)$"Error setting cooldown on teleporter {val.teleporterId}: {ex.Message}"); } } } private static (int inverse, int regular) GetCooldowns() { return (Plugin.ModConfig.InverseTeleporterCooldown.Value, Plugin.ModConfig.TeleporterCooldown.Value); } } } namespace BetterBetterTeleporter.Networking { public interface ISyncable { int GetSize(); void ResetValue(); void SetFromReader(FastBufferReader reader); void WriteToWriter(FastBufferWriter writer); } [HarmonyPatch] public static class PlayerConnectionPatch { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static HandleNamedMessageDelegate <>9__1_0; internal void <ConnectClientToPlayerObject>b__1_0(ulong clientId, FastBufferReader reader) { SyncedEntries.SendAllToClient(clientId); } } private const string MessageName = "BetterBetterTeleporter.Connect"; [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] public static void ConnectClientToPlayerObject() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; if (NetworkManager.Singleton.IsServer) { SyncedEntries.ResetToLocalConfig(); object obj = <>c.<>9__1_0; if (obj == null) { HandleNamedMessageDelegate val = delegate(ulong clientId, FastBufferReader reader) { SyncedEntries.SendAllToClient(clientId); }; <>c.<>9__1_0 = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("BetterBetterTeleporter.Connect", (HandleNamedMessageDelegate)obj); return; } SyncedEntries.BeginListening(); FastBufferWriter val2 = default(FastBufferWriter); ((FastBufferWriter)(ref val2))..ctor(0, (Allocator)2, -1); try { customMessagingManager.SendNamedMessage("BetterBetterTeleporter.Connect", 0uL, val2, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val2)).Dispose(); } } [HarmonyPatch(typeof(GameNetworkManager), "StartDisconnect")] [HarmonyPostfix] public static void PlayerLeave() { SyncedEntries.StopListening(); } } public static class SyncedEntries { private class PayloadChunk { public int Size; public ICollection<KeyValuePair<byte, ISyncable>> Items = new List<KeyValuePair<byte, ISyncable>>(); } [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__ReadPayload; } [CompilerGenerated] private sealed class <Broadcast>d__8 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <Broadcast>d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSecondsRealtime(0.05f); <>1__state = 1; return true; case 1: { <>1__state = -1; if (!Object.op_Implicit((Object)(object)NetworkManager.Singleton) || !NetworkManager.Singleton.IsServer || NetworkManager.Singleton.ConnectedClientsList.Count <= 1) { _isBroadcasting = false; return false; } KeyValuePair<byte, ISyncable>[] payload = AllEntries.Where((KeyValuePair<byte, ISyncable> x) => UnsyncedEntries.Contains(x.Key)).ToArray(); SendPayload("BetterBetterTeleporter.ConfigSync", payload, NetworkManager.Singleton.ConnectedClientsIds.ToArray()[1..]); UnsyncedEntries.Clear(); _isBroadcasting = false; return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static byte _idGen = 0; private const string SyncMessage = "BetterBetterTeleporter.ConfigSync"; private static readonly Dictionary<byte, ISyncable> AllEntries = new Dictionary<byte, ISyncable>(); private static readonly HashSet<byte> UnsyncedEntries = new HashSet<byte>(); private static bool _isBroadcasting = false; private static SyncedEntry<T> Add<T>(SyncedEntry<T> item) { byte id = _idGen++; AllEntries.Add(id, item); item.Entry.SettingChanged += delegate { ConfigEntryChanged(id, item); }; return item; } private static void ConfigEntryChanged<T>(byte key, SyncedEntry<T> item) { if (!Object.op_Implicit((Object)(object)NetworkManager.Singleton) || !NetworkManager.Singleton.IsConnectedClient) { item.Value = item.Entry.Value; } else if (NetworkManager.Singleton.IsServer) { ScheduleBroadcastFor(key, item); } } private static void ScheduleBroadcastFor<T>(byte id, SyncedEntry<T> item) { if (!Object.op_Implicit((Object)(object)NetworkManager.Singleton) || !NetworkManager.Singleton.IsServer) { return; } item.Value = item.Entry.Value; if (NetworkManager.Singleton.ConnectedClientsList.Count > 1) { UnsyncedEntries.Add(id); if (!_isBroadcasting) { _isBroadcasting = true; ((MonoBehaviour)NetworkManager.Singleton).StartCoroutine(Broadcast()); } } } [IteratorStateMachine(typeof(<Broadcast>d__8))] private static IEnumerator Broadcast() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <Broadcast>d__8(0); } public static void SendAllToClient(ulong clientId) { if (clientId != 0L) { SendPayload("BetterBetterTeleporter.ConfigSync", AllEntries, clientId); } } private static void SendPayload(string messageName, ICollection<KeyValuePair<byte, ISyncable>> payload, params ulong[] clients) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) List<PayloadChunk> list = new List<PayloadChunk>(new <>z__ReadOnlySingleElementList<PayloadChunk>(new PayloadChunk())); foreach (KeyValuePair<byte, ISyncable> item in payload) { int num = 1 + item.Value.GetSize(); if (num + list[list.Count - 1].Size > 1024) { list.Add(new PayloadChunk()); } list[list.Count - 1].Size += num; list[list.Count - 1].Items.Add(item); } FastBufferWriter val = default(FastBufferWriter); foreach (PayloadChunk item2 in list) { ((FastBufferWriter)(ref val))..ctor(item2.Size, (Allocator)2, -1); try { foreach (KeyValuePair<byte, ISyncable> item3 in item2.Items) { ((FastBufferWriter)(ref val)).WriteByteSafe(item3.Key); item3.Value.WriteToWriter(val); } NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(messageName, (IReadOnlyList<ulong>)clients, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } } public static bool BeginListening() { //IL_0034: 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) //IL_003f: Expected O, but got Unknown NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) == null) { return false; } CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; object obj = <>O.<0>__ReadPayload; if (obj == null) { HandleNamedMessageDelegate val = ReadPayload; <>O.<0>__ReadPayload = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("BetterBetterTeleporter.ConfigSync", (HandleNamedMessageDelegate)obj); return true; } private static void ReadPayload(ulong clientId, FastBufferReader reader) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)NetworkManager.Singleton) && !NetworkManager.Singleton.IsServer) { byte key = default(byte); while (((FastBufferReader)(ref reader)).TryBeginRead(1)) { ((FastBufferReader)(ref reader)).ReadByteSafe(ref key); AllEntries[key].SetFromReader(reader); } } } public static void ResetToLocalConfig() { foreach (ISyncable value in AllEntries.Values) { value.ResetValue(); } } public static void StopListening(bool resetToLocalConfig = true) { if (resetToLocalConfig) { ResetToLocalConfig(); } UnsyncedEntries.Clear(); NetworkManager singleton = NetworkManager.Singleton; if (((singleton != null) ? singleton.CustomMessagingManager : null) != null) { NetworkManager.Singleton.CustomMessagingManager.UnregisterNamedMessageHandler("BetterBetterTeleporter.ConfigSync"); } } public static SyncedEntry<int> BindSynced(this ConfigFile config, string section, string key, int value, ConfigDescription description) { return Add(new SyncedEntry<int>(config.Bind<int>(section, key, value, description), (int _) => 4, delegate(FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) int result = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref result, default(ForPrimitives)); return result; }, delegate(FastBufferWriter writer, int value) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) ((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref value, default(ForPrimitives)); })); } public static SyncedEntry<float> BindSynced(this ConfigFile config, string section, string key, float value, ConfigDescription description) { return Add(new SyncedEntry<float>(config.Bind<float>(section, key, value, description), (float _) => 4, delegate(FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) float result = default(float); ((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref result, default(ForPrimitives)); return result; }, delegate(FastBufferWriter writer, float value) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) ((FastBufferWriter)(ref writer)).WriteValueSafe<float>(ref value, default(ForPrimitives)); })); } public static SyncedEntry<bool> BindSynced(this ConfigFile config, string section, string key, bool value, ConfigDescription description) { return Add(new SyncedEntry<bool>(config.Bind<bool>(section, key, value, description), (bool _) => 1, delegate(FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) bool result = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe<bool>(ref result, default(ForPrimitives)); return result; }, delegate(FastBufferWriter writer, bool value) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) ((FastBufferWriter)(ref writer)).WriteValueSafe<bool>(ref value, default(ForPrimitives)); })); } public static SyncedEntry<string> BindSynced(this ConfigFile config, string section, string key, string value, ConfigDescription description) { return Add(new SyncedEntry<string>(config.Bind<string>(section, key, value, description), (string value) => FastBufferWriter.GetWriteSize(value, true), delegate(FastBufferReader reader) { string result = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref result, true); return result; }, delegate(FastBufferWriter writer, string value) { ((FastBufferWriter)(ref writer)).WriteValueSafe(value, true); })); } public static SyncedEntry<T> BindSynced<T>(this ConfigFile config, string section, string key, T value, ConfigDescription description) where T : Enum { return Add(new SyncedEntry<T>(config.Bind<T>(section, key, value, description), (T _) => 4, delegate(FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) int num2 = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num2, default(ForPrimitives)); return (T)(object)num2; }, delegate(FastBufferWriter writer, T value) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) int num = Convert.ToInt32(value); ((FastBufferWriter)(ref writer)).WriteValueSafe<int>(ref num, default(ForPrimitives)); })); } } public class SyncedEntry<T> : ISyncable { private readonly Func<FastBufferReader, T> read; private readonly Action<FastBufferWriter, T> write; public ConfigEntry<T> Entry; public Action<T, T> OnChanged; public readonly Func<T, int> calcSize; public T Value { get { return <Value>k__BackingField; } set { T arg = <Value>k__BackingField; <Value>k__BackingField = value; OnChanged?.Invoke(arg, value); } } public SyncedEntry(ConfigEntry<T> entry, Func<T, int> calcSize, Func<FastBufferReader, T> read, Action<FastBufferWriter, T> write) { Entry = entry; Value = entry.Value; this.calcSize = calcSize; this.read = read; this.write = write; } public int GetSize() { return calcSize(Value); } public void ResetValue() { Value = Entry.Value; } public void SetFromReader(FastBufferReader reader) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) Value = read(reader); } public void WriteToWriter(FastBufferWriter writer) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) write(writer, Value); } } } namespace BetterBetterTeleporter.Integrations { internal static class LethalConfigIntegration { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<GrabbableObject, bool> <>9__9_1; public static Func<GrabbableObject, string> <>9__9_2; public static GenericButtonHandler <>9__9_0; public static Func<ShipTeleporter, bool> <>9__10_1; public static GenericButtonHandler <>9__10_0; internal void <RegisterShowInventoryButton>b__9_0() { GrabbableObject[] array = GameNetworkManager.Instance?.localPlayerController?.ItemSlots; if (array != null && (Object)(object)HUDManager.Instance != (Object)null) { string text = string.Join(",", from x in array where (Object)(object)x != (Object)null select ((Object)x.itemProperties).name); HUDManager.Instance.DisplayTip("Current Inventory", text, false, false, "LC_Tip1"); } } internal bool <RegisterShowInventoryButton>b__9_1(GrabbableObject x) { return (Object)(object)x != (Object)null; } internal string <RegisterShowInventoryButton>b__9_2(GrabbableObject x) { return ((Object)x.itemProperties).name; } internal void <RegisterSelfTeleportButton>b__10_0() { HUDManager instance = HUDManager.Instance; StartOfRound instance2 = StartOfRound.Instance; PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || (Object)(object)val == (Object)null) { return; } NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.ConnectedClientsList.Count > 1) { instance.DisplayTip("No cheating", "There's more than one player in the game.", true, false, "LC_Tip1"); return; } ShipTeleporter val2 = ((IEnumerable<ShipTeleporter>)Object.FindObjectsOfType<ShipTeleporter>()).FirstOrDefault((Func<ShipTeleporter, bool>)((ShipTeleporter tp) => !tp.isInverseTeleporter)); if ((Object)(object)val2 == (Object)null) { instance.DisplayTip("No cheating", "No teleporter on the ship.", true, false, "LC_Tip1"); return; } FieldInfo shipTeleporterCooldownTimeField = ReflectionHelper.GetShipTeleporterCooldownTimeField(); if (shipTeleporterCooldownTimeField != null && shipTeleporterCooldownTimeField.GetValue(val2) is float num && num > 0f) { instance.DisplayTip("No cheating", "The teleporter is on cooldown.", true, false, "LC_Tip1"); } else { val2.PressTeleportButtonOnLocalClient(); } } internal bool <RegisterSelfTeleportButton>b__10_1(ShipTeleporter tp) { return !tp.isInverseTeleporter; } } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] public static void Initialize() { Plugin.Logger.LogInfo((object)"LethalConfig detected — integrating BetterBetterTeleporter config."); RegisterAll(); } private static void RegisterAll() { RegisterCheckbox(Plugin.ModConfig.ResetCooldownOnOrbit.Entry); RegisterInput(Plugin.ModConfig.TeleporterCooldown.Entry); RegisterDropdown(Plugin.ModConfig.TeleporterBehavior.Entry); RegisterTextArea(Plugin.ModConfig.TeleporterAlwaysKeep.Entry); RegisterTextArea(Plugin.ModConfig.TeleporterAlwaysDrop.Entry); RegisterInput(Plugin.ModConfig.InverseTeleporterCooldown.Entry); RegisterDropdown(Plugin.ModConfig.InverseTeleporterBehavior.Entry); RegisterTextArea(Plugin.ModConfig.InverseTeleporterAlwaysKeep.Entry); RegisterTextArea(Plugin.ModConfig.InverseTeleporterAlwaysDrop.Entry); RegisterSlider(Plugin.ModConfig.BatteryDrainPercent.Entry, 0, 100); RegisterShowInventoryButton(); RegisterSelfTeleportButton(); } private static void RegisterInput(ConfigEntry<int> entry) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown IntInputFieldOptions val = new IntInputFieldOptions { RequiresRestart = false }; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntInputFieldConfigItem(entry, val)); } private static void RegisterSlider(ConfigEntry<int> entry, int min, int max) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown IntSliderOptions val = new IntSliderOptions(); ((BaseRangeOptions<int>)val).Min = min; ((BaseRangeOptions<int>)val).Max = max; ((BaseOptions)val).RequiresRestart = false; IntSliderOptions val2 = val; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(entry, val2)); } private static void RegisterSlider(ConfigEntry<float> entry, float min, float max) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown FloatSliderOptions val = new FloatSliderOptions(); ((BaseRangeOptions<float>)val).Min = min; ((BaseRangeOptions<float>)val).Max = max; ((BaseOptions)val).RequiresRestart = false; FloatSliderOptions val2 = val; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(entry, val2)); } private static void RegisterCheckbox(ConfigEntry<bool> entry) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown LethalConfigManager.AddConfigItem((BaseConfigItem)new BoolCheckBoxConfigItem(entry, false)); } private static void RegisterDropdown(ConfigEntry<ItemTeleportBehavior> entry) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown EnumDropDownOptions val = new EnumDropDownOptions { RequiresRestart = false }; LethalConfigManager.AddConfigItem((BaseConfigItem)(object)new EnumDropDownConfigItem<ItemTeleportBehavior>(entry, val)); } private static void RegisterTextInput(ConfigEntry<string> entry) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(entry, false)); } private static void RegisterTextArea(ConfigEntry<string> entry, int lines = 3) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown TextInputFieldOptions val = new TextInputFieldOptions { CharacterLimit = 500, NumberOfLines = 50, RequiresRestart = false }; LethalConfigManager.AddConfigItem((BaseConfigItem)new TextInputFieldConfigItem(entry, val)); } private static void RegisterShowInventoryButton() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown object obj = <>c.<>9__9_0; if (obj == null) { GenericButtonHandler val = delegate { GrabbableObject[] array = GameNetworkManager.Instance?.localPlayerController?.ItemSlots; if (array != null && (Object)(object)HUDManager.Instance != (Object)null) { string text = string.Join(",", from x in array where (Object)(object)x != (Object)null select ((Object)x.itemProperties).name); HUDManager.Instance.DisplayTip("Current Inventory", text, false, false, "LC_Tip1"); } }; <>c.<>9__9_0 = val; obj = (object)val; } LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("Help", "What items am I holding?", "See currently held items to help figure out what to add to the keep/drop lists.", "Show inventory", (GenericButtonHandler)obj)); } private static void RegisterSelfTeleportButton() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown object obj = <>c.<>9__10_0; if (obj == null) { GenericButtonHandler val = delegate { HUDManager instance = HUDManager.Instance; StartOfRound instance2 = StartOfRound.Instance; PlayerControllerB val2 = GameNetworkManager.Instance?.localPlayerController; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance2 == (Object)null) && !((Object)(object)val2 == (Object)null)) { NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.ConnectedClientsList.Count > 1) { instance.DisplayTip("No cheating", "There's more than one player in the game.", true, false, "LC_Tip1"); } else { ShipTeleporter val3 = ((IEnumerable<ShipTeleporter>)Object.FindObjectsOfType<ShipTeleporter>()).FirstOrDefault((Func<ShipTeleporter, bool>)((ShipTeleporter tp) => !tp.isInverseTeleporter)); if ((Object)(object)val3 == (Object)null) { instance.DisplayTip("No cheating", "No teleporter on the ship.", true, false, "LC_Tip1"); } else { FieldInfo shipTeleporterCooldownTimeField = ReflectionHelper.GetShipTeleporterCooldownTimeField(); if (shipTeleporterCooldownTimeField != null && shipTeleporterCooldownTimeField.GetValue(val3) is float num && num > 0f) { instance.DisplayTip("No cheating", "The teleporter is on cooldown.", true, false, "LC_Tip1"); } else { val3.PressTeleportButtonOnLocalClient(); } } } } }; <>c.<>9__10_0 = val; obj = (object)val; } LethalConfigManager.AddConfigItem((BaseConfigItem)new GenericButtonConfigItem("Help", "Activate Teleporter", "Pushes the teleport button. Only works if the Teleporter is unlocked and there are no other players connected to the game.", "Beam me up, Scotty", (GenericButtonHandler)obj)); } } } namespace BetterBetterTeleporter.Adapters { public interface IItemInfo { string Name { get; } string TypeName { get; } string DisplayName { get; } bool? IsScrap { get; } int? Value { get; } bool? IsMetal { get; } bool? IsWeapon { get; } bool? IsPocketed { get; } bool? HasBattery { get; } float? BatteryCharge { get; } bool? IsTwoHanded { get; } float? Weight { get; } } public sealed class ItemInfo : IItemInfo { [CompilerGenerated] private GrabbableObject <source>P; public string Name => TryGet(() => ((Object)<source>P.itemProperties).name, "itemProperties.name"); public string TypeName => ((object)<source>P).GetType().Name; public string DisplayName => TryGet(() => <source>P.itemProperties.itemName, "itemProperties.itemName"); public bool? IsScrap => TryGet(() => <source>P.itemProperties.isScrap, "itemProperties.isScrap"); public int? Value => TryGet(() => <source>P.scrapValue, "scrapValue"); public bool? IsMetal => TryGet(() => <source>P.itemProperties.isConductiveMetal, "itemProperties.isConductiveMetal"); public bool? IsWeapon => TryGet(() => <source>P.itemProperties.isDefensiveWeapon, "itemProperties.isDefensiveWeapon"); public bool? IsPocketed => TryGet(() => <source>P.isPocketed, "isPocketed"); public bool? HasBattery => TryGet(() => <source>P.itemProperties.requiresBattery, "itemProperties.requiresBattery"); public float? BatteryCharge => TryGet(() => <source>P.insertedBattery?.charge ?? 0f, "insertedBattery.charge"); public bool? IsTwoHanded => TryGet(() => <source>P.itemProperties.twoHanded, "itemProperties.twoHanded"); public float? Weight => TryGet(() => <source>P.itemProperties.weight, "itemProperties.weight"); public ItemInfo(GrabbableObject source) { <source>P = source; base..ctor(); } private static T TryGet<T>(Func<T> getter, string propertyName) { try { return getter(); } catch (Exception ex) { Plugin.Logger.LogError((object)("Failed to read 'GrabbableObject." + propertyName + "'. Game structure may have changed. Error: " + ex.Message)); return default(T); } } } public interface IPlayerInfo { IReadOnlyList<IItemInfo> Slots { get; } int CurrentItemSlotIndex { get; } } public sealed class PlayerInfo : IPlayerInfo { [CompilerGenerated] private PlayerControllerB <player>P; private readonly IReadOnlyList<IItemInfo> _slots; public IReadOnlyList<IItemInfo> Slots => _slots; public int CurrentItemSlotIndex => TryGet(() => <player>P.currentItemSlot, "currentItemSlot"); public PlayerInfo(PlayerControllerB player) { <player>P = player; List<IItemInfo> list = new List<IItemInfo>(); list.AddRange(TryGet(() => <player>P.ItemSlots.Select((GrabbableObject item) => (!((Object)(object)item == (Object)null)) ? new ItemInfo(item) : null), "ItemSlots") ?? Array.Empty<ItemInfo>()); _slots = new <>z__ReadOnlyList<IItemInfo>(list); base..ctor(); } private static T TryGet<T>(Func<T> getter, string propertyName) { try { return getter(); } catch (Exception ex) { Plugin.Logger.LogError((object)("Failed to read 'PlayerControllerB." + propertyName + "'. Game structure may have changed. Error: " + ex.Message)); return default(T); } } } } [CompilerGenerated] internal sealed class <>z__ReadOnlyList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { int ICollection.Count => _items.Count; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => _items.Count; T IReadOnlyList<T>.this[int index] => _items[index]; int ICollection<T>.Count => _items.Count; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyList(List<T> items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.CopyTo(Array array, int index) { ((ICollection)_items).CopyTo(array, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return ((IList)_items).Contains(value); } int IList.IndexOf(object value) { return ((IList)_items).IndexOf(value); } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return ((IEnumerable<T>)_items).GetEnumerator(); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return _items.Contains(item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { _items.CopyTo(array, arrayIndex); } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { return _items.IndexOf(item); } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } } [CompilerGenerated] internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T> { object IEnumerator.Current => _item; T IEnumerator<T>.Current => _item; public Enumerator(T item) { _item = item; } bool IEnumerator.MoveNext() { if (!_moveNextCalled) { return _moveNextCalled = true; } return false; } void IEnumerator.Reset() { _moveNextCalled = false; } void IDisposable.Dispose() { } } int ICollection.Count => 1; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => 1; T IReadOnlyList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } } int ICollection<T>.Count => 1; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } public <>z__ReadOnlySingleElementList(T item) { _item = item; } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_item); } void ICollection.CopyTo(Array array, int index) { array.SetValue(_item, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return EqualityComparer<T>.Default.Equals(_item, (T)value); } int IList.IndexOf(object value) { if (!EqualityComparer<T>.Default.Equals(_item, (T)value)) { return -1; } return 0; } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return new Enumerator(_item); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return EqualityComparer<T>.Default.Equals(_item, item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { array[arrayIndex] = _item; } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { if (!EqualityComparer<T>.Default.Equals(_item, item)) { return -1; } return 0; } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } }