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 BetterBetterTeleporter v1.3.0
BepInEx/plugins/BetterBetterTeleporter.dll
Decompiled a day 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; using UnityEngine.UI; [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.3.0.0")] [assembly: AssemblyInformationalVersion("1.3.0+db6442bfa249634e318526944a5aafff9735ed4e")] [assembly: AssemblyProduct("BetterBetterTeleporter")] [assembly: AssemblyTitle("BetterBetterTeleporter")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.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.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] [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] [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.3.0")] [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.3.0"; } } 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); case "utilslot": return new UtilitySlotFilter(itemList); case "nonutilslot": return new InventorySlotFilter(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(string id) { public abstract bool IsMatch(IPlayerInfo player, IItemInfo item); public override string ToString() { return id; } } public class ItemNameRule(string name) : ItemRule(name) { private const StringComparison caseInsensitive = StringComparison.OrdinalIgnoreCase; private readonly string name = 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 { 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 class UtilitySlotFilter : ItemFilter { public const string Id = "utilslot"; public UtilitySlotFilter(ItemFilterList except) : base("utilslot", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { return player.ItemOnlySlot == item; } } public class InventorySlotFilter : ItemFilter { public const string Id = "nonutilslot"; public InventorySlotFilter(ItemFilterList except) : base("nonutilslot", except) { } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { return player.ItemOnlySlot != item; } } 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] = (Raw: () => config.TeleporterAlwaysKeep.Value, Rules: null); _cache[0, 1] = (Raw: () => config.TeleporterAlwaysDrop.Value, Rules: null); _cache[1, 0] = (Raw: () => config.InverseTeleporterAlwaysKeep.Value, Rules: null); _cache[1, 1] = (Raw: () => config.InverseTeleporterAlwaysDrop.Value, Rules: 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 { 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; } } private static IEnumerator DelayedForceSync(PlayerControllerB player) { yield return (object)new WaitForFixedUpdate(); UpdatePositionForNewlyJoinedClient?.SetValue(player, true); yield return (object)new WaitForSecondsRealtime(0.2f); UpdatePositionForNewlyJoinedClient?.SetValue(player, true); } } [HarmonyPatch(typeof(ShipTeleporter), "TeleportPlayerOutWithInverseTeleporter")] public static class InverseTeleporterBatteryDrainPatch { private static readonly FieldInfo ItemOnlySlot = AccessTools.Field(typeof(PlayerControllerB), "ItemOnlySlot"); [HarmonyPostfix] public static void TeleportPlayerOutWithInverseTeleporterPostfix(int playerObj) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown float num = (float)Plugin.ModConfig.BatteryDrainPercent.Value / 100f; if (num == 0f) { return; } try { PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[playerObj]; List<GrabbableObject> list = val.ItemSlots.ToList(); try { list.Add((GrabbableObject)ItemOnlySlot.GetValue(val)); } catch { } foreach (GrabbableObject item in list) { Battery val2 = item?.insertedBattery; if (val2 != null) { val2.charge = Mathf.Max(0f, val2.charge - num); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to drain batteries after inverse teleport: {arg}"); } } } [HarmonyPatch(typeof(PlayerControllerB), "DropAllHeldItems")] public static class KeepItemsOnTeleporterPatch { private static readonly Dictionary<PlayerControllerB, GrabbableObject[]> tempInventories = new Dictionary<PlayerControllerB, GrabbableObject[]>(); private static readonly Dictionary<PlayerControllerB, GrabbableObject> tempUtilityItem = new Dictionary<PlayerControllerB, GrabbableObject>(); private static readonly MethodInfo SwitchToItemSlotMethod = AccessTools.Method(typeof(PlayerControllerB), "SwitchToItemSlot", (Type[])null, (Type[])null); private static readonly MethodInfo SendChangedWeightEvent = AccessTools.Method(typeof(StartOfRound), "SendChangedWeightEvent", (Type[])null, (Type[])null); private static readonly FieldInfo ItemOnlySlot = AccessTools.Field(typeof(PlayerControllerB), "ItemOnlySlot"); private static readonly FieldInfo ItemOnlySlotIcon = AccessTools.Field(typeof(HUDManager), "itemOnlySlotIcon"); [HarmonyPrefix] public static void DropAllHeldItemsPrefix(PlayerControllerB __instance) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown 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; } } if (playerInfo.ItemOnlySlot != null && !playerInfo.ShouldDropItem(playerInfo.ItemOnlySlot, teleportState)) { tempUtilityItem[__instance] = (GrabbableObject)ItemOnlySlot.GetValue(__instance); ItemOnlySlot.SetValue(__instance, null); } if (__instance.currentItemSlot == 50) { __instance.isHoldingObject = !tempUtilityItem.ContainsKey(__instance); } else { __instance.isHoldingObject = (Object)(object)__instance.ItemSlots[__instance.currentItemSlot] != (Object)null; } tempInventories[__instance] = array2; } catch { tempInventories.Remove(__instance); for (int j = 0; j < __instance.ItemSlots.Length; j++) { __instance.ItemSlots[j] = array[j]; } } } [HarmonyPostfix] public static void DropAllHeldItemsPostfix(PlayerControllerB __instance) { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) try { float num = 0f; bool isInverse = TeleportDetectionPatch.IsInverseTeleporting(__instance); if (tempInventories.ContainsKey(__instance)) { GrabbableObject[] array = tempInventories[__instance]; tempInventories.Remove(__instance); 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; if (__instance.actualClientId == NetworkManager.Singleton.LocalClientId) { ((Behaviour)HUDManager.Instance.itemSlotIcons[i]).enabled = true; } } } } if (tempUtilityItem.ContainsKey(__instance)) { GrabbableObject val2 = tempUtilityItem[__instance]; tempUtilityItem.Remove(__instance); ItemOnlySlot.SetValue(__instance, val2); num += val2.itemProperties.weight - 1f; Image val3 = (Image)ItemOnlySlotIcon.GetValue(HUDManager.Instance); if ((int)val3 != 0) { ((Behaviour)val3).enabled = true; } } ((MonoBehaviour)NetworkManager.Singleton).StartCoroutine(RefreshInventory(__instance, num, isInverse)); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Failed to restore inventory. Error: {arg}"); } try { if (__instance.currentItemSlot == 50) { __instance.isHoldingObject = ItemOnlySlot.GetValue(__instance) != null; SwitchToItemSlotMethod?.Invoke(__instance, new object[2] { 50, null }); } else { __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}"); } TeleportDetectionPatch.AfterTeleporterDropAllHeldItems(); } private static IEnumerator RefreshInventory(PlayerControllerB __instance, float carryWeightDelta, bool isInverse) { yield return (object)new WaitForEndOfFrame(); __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; } } SendChangedWeightEvent?.Invoke(StartOfRound.Instance, null); } 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 { 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 (player.isPlayerDead || player.disableInteract) { return false; } if (IsRegularTeleporting()) { return true; } if (IsInverseTeleporting(player)) { return true; } return false; } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> TeleporterTranspiler(IEnumerable<CodeInstruction> instructions) { MethodInfo beforeMethod = AccessTools.Method(typeof(TeleportDetectionPatch), "BeforeTeleporterDropAllHeldItems", (Type[])null, (Type[])null); List<CodeInstruction> code = new List<CodeInstruction>(instructions); int stateResetCount = 0; for (int i = 0; i < code.Count; i++) { CodeInstruction instruction = code[i]; yield return instruction; if (instruction.opcode == OpCodes.Ldc_I4_M1 && i + 1 < code.Count && code[i + 1].opcode == OpCodes.Stfld) { stateResetCount++; if (stateResetCount == 3) { yield return new CodeInstruction(OpCodes.Call, (object)beforeMethod); } } } } 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 (inverse: Plugin.ModConfig.InverseTeleporterCooldown.Value, regular: 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 unsafe 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*)(&val2))/*cast due to .constrained prefix*/).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; } 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()); } } } private static IEnumerator Broadcast() { yield return (object)new WaitForSecondsRealtime(0.05f); if (!Object.op_Implicit((Object)(object)NetworkManager.Singleton) || !NetworkManager.Singleton.IsServer || NetworkManager.Singleton.ConnectedClientsList.Count <= 1) { _isBroadcasting = false; yield break; } 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; } public static void SendAllToClient(ulong clientId) { if (clientId != 0L) { SendPayload("BetterBetterTeleporter.ConfigSync", AllEntries, clientId); } } private unsafe 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*)(&val))/*cast due to .constrained prefix*/).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 num) { //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 num, 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 num) { //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 num, 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 flag) { //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 flag, 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 text) => FastBufferWriter.GetWriteSize(text, true), delegate(FastBufferReader reader) { string result = default(string); ((FastBufferReader)(ref reader)).ReadValueSafe(ref result, true); return result; }, delegate(FastBufferWriter writer, string text) { ((FastBufferWriter)(ref writer)).WriteValueSafe(text, 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 num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); return (T)(object)num; }, delegate(FastBufferWriter writer, T val) { //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(val); ((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(GrabbableObject source) : IItemInfo { public string Name => TryGet(() => ((Object)source.itemProperties).name, "itemProperties.name"); public string TypeName => ((object)source).GetType().Name; public string DisplayName => TryGet(() => source.itemProperties.itemName, "itemProperties.itemName"); public bool? IsScrap => TryGet(() => source.itemProperties.isScrap, "itemProperties.isScrap"); public int? Value => TryGet(() => source.scrapValue, "scrapValue"); public bool? IsMetal => TryGet(() => source.itemProperties.isConductiveMetal, "itemProperties.isConductiveMetal"); public bool? IsWeapon => TryGet(() => source.itemProperties.isDefensiveWeapon, "itemProperties.isDefensiveWeapon"); public bool? IsPocketed => TryGet(() => source.isPocketed, "isPocketed"); public bool? HasBattery => TryGet(() => source.itemProperties.requiresBattery, "itemProperties.requiresBattery"); public float? BatteryCharge => TryGet(() => source.insertedBattery?.charge ?? 0f, "insertedBattery.charge"); public bool? IsTwoHanded => TryGet(() => source.itemProperties.twoHanded, "itemProperties.twoHanded"); public float? Weight => TryGet(() => source.itemProperties.weight, "itemProperties.weight"); 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; } IItemInfo ItemOnlySlot { get; } int CurrentItemSlotIndex { get; } } public sealed class PlayerInfo(PlayerControllerB player) : IPlayerInfo { private readonly IReadOnlyList<IItemInfo> _slots; private readonly IItemInfo _itemOnlySlot; public IReadOnlyList<IItemInfo> Slots => _slots; public IItemInfo ItemOnlySlot => _itemOnlySlot; public int CurrentItemSlotIndex => TryGet(() => player.currentItemSlot, "currentItemSlot"); private static T TryGet<T>(Func<T> getter, string propertyName, bool logError = true) { try { return getter(); } catch (Exception ex) { if (logError) { 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(); } }