using 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 ResetCooldownOnOrbit; public SyncedEntry TeleporterCooldown; public SyncedEntry TeleporterBehavior; public SyncedEntry TeleporterAlwaysKeep; public SyncedEntry TeleporterAlwaysDrop; public SyncedEntry InverseTeleporterCooldown; public SyncedEntry InverseTeleporterBehavior; public SyncedEntry InverseTeleporterAlwaysKeep; public SyncedEntry InverseTeleporterAlwaysDrop; public SyncedEntry 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())); TeleporterCooldown = config.BindSynced("Teleporter", "TeleporterCooldown", 10, new ConfigDescription("Cooldown time (in seconds) for using the Teleporter.", (AcceptableValueBase)(object)new AcceptableValueRange(0, int.MaxValue), Array.Empty())); TeleporterBehavior = config.BindSynced("Teleporter", "TeleporterBehavior", ItemTeleportBehavior.Drop, new ConfigDescription("Makes the Teleporter \"Drop\" or \"Keep\" items on teleport.", (AcceptableValueBase)null, Array.Empty())); 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())); 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())); InverseTeleporterCooldown = config.BindSynced("Inverse Teleporter", "InverseTeleporterCooldown", 210, new ConfigDescription("Cooldown time (in seconds) for using the Inverse Teleporter.", (AcceptableValueBase)(object)new AcceptableValueRange(0, int.MaxValue), Array.Empty())); InverseTeleporterBehavior = config.BindSynced("Inverse Teleporter", "InverseTeleporterBehavior", ItemTeleportBehavior.Drop, new ConfigDescription("Makes the Inverse Teleporter \"Drop\" or \"Keep\" items on teleport.", (AcceptableValueBase)null, Array.Empty())); 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())); 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())); 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(0, 100), Array.Empty())); ((Dictionary)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 ParseConfig(string source) { if (string.IsNullOrEmpty(source)) { return new List(); } List list = new List(); if (!IndexOfUnescaped(source, ',').HasValue) { return new List(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 rules = new List(); 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 SplitCurrentGeneration(string source) { List list = new List(); 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 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 Rules { get; set; } public ItemFilterList(bool isExclusive, List rules) { IsExclusive = isExclusive; Rules = rules; } } public abstract class ItemFilter : ItemRule { protected ItemFilter(string id, ItemFilterList itemList) { P = itemList; base..ctor(id); } public override bool IsMatch(IPlayerInfo player, IItemInfo item) { if (P.Rules.Count == 0) { return true; } return P.IsExclusive ^ 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 Rules; private static bool _init; private static readonly (Func Raw, List Rules)[,] _cache = new(Func, List)[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 teleporterAlwaysKeep = config.TeleporterAlwaysKeep; teleporterAlwaysKeep.OnChanged = (Action)Delegate.Combine(teleporterAlwaysKeep.OnChanged, (Action)delegate { Reparse(ref _cache[0, 0]); }); SyncedEntry teleporterAlwaysDrop = config.TeleporterAlwaysDrop; teleporterAlwaysDrop.OnChanged = (Action)Delegate.Combine(teleporterAlwaysDrop.OnChanged, (Action)delegate { Reparse(ref _cache[0, 1]); }); SyncedEntry inverseTeleporterAlwaysKeep = config.InverseTeleporterAlwaysKeep; inverseTeleporterAlwaysKeep.OnChanged = (Action)Delegate.Combine(inverseTeleporterAlwaysKeep.OnChanged, (Action)delegate { Reparse(ref _cache[1, 0]); }); SyncedEntry inverseTeleporterAlwaysDrop = config.InverseTeleporterAlwaysDrop; inverseTeleporterAlwaysDrop.OnChanged = (Action)Delegate.Combine(inverseTeleporterAlwaysDrop.OnChanged, (Action)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, List) reference = ref _cache[num, num2]; ref List item = ref reference.Item2; Rules = item ?? (item = ItemParser.ParseConfig(reference.Item1())); } private static void Reparse(ref (Func Raw, List 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 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 tempInventories = new Dictionary(); private static readonly Dictionary tempUtilityItem = new Dictionary(); 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(); 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 InverseTeleportingPlayers = new HashSet(); 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 TeleporterTranspiler(IEnumerable instructions) { MethodInfo beforeMethod = AccessTools.Method(typeof(TeleportDetectionPatch), "BeforeTeleporterDropAllHeldItems", (Type[])null, (Type[])null); List code = new List(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 teleporterCooldown = Plugin.ModConfig.TeleporterCooldown; teleporterCooldown.OnChanged = (Action)Delegate.Combine(teleporterCooldown.OnChanged, new Action(UpdateAllTeleporterCooldowns)); SyncedEntry inverseTeleporterCooldown = Plugin.ModConfig.InverseTeleporterCooldown; inverseTeleporterCooldown.OnChanged = (Action)Delegate.Combine(inverseTeleporterCooldown.OnChanged, new Action(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(); 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 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> Items = new List>(); } [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 AllEntries = new Dictionary(); private static readonly HashSet UnsyncedEntries = new HashSet(); private static bool _isBroadcasting = false; private static SyncedEntry Add(SyncedEntry item) { byte id = _idGen++; AllEntries.Add(id, item); item.Entry.SettingChanged += delegate { ConfigEntryChanged(id, item); }; return item; } private static void ConfigEntryChanged(byte key, SyncedEntry 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(byte id, SyncedEntry 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[] payload = AllEntries.Where((KeyValuePair 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> 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 list = new List(new <>z__ReadOnlySingleElementList(new PayloadChunk())); foreach (KeyValuePair 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 item3 in item2.Items) { ((FastBufferWriter)(ref val)).WriteByteSafe(item3.Key); item3.Value.WriteToWriter(val); } NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(messageName, (IReadOnlyList)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 BindSynced(this ConfigFile config, string section, string key, int value, ConfigDescription description) { return Add(new SyncedEntry(config.Bind(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(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(ref num, default(ForPrimitives)); })); } public static SyncedEntry BindSynced(this ConfigFile config, string section, string key, float value, ConfigDescription description) { return Add(new SyncedEntry(config.Bind(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(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(ref num, default(ForPrimitives)); })); } public static SyncedEntry BindSynced(this ConfigFile config, string section, string key, bool value, ConfigDescription description) { return Add(new SyncedEntry(config.Bind(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(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(ref flag, default(ForPrimitives)); })); } public static SyncedEntry BindSynced(this ConfigFile config, string section, string key, string value, ConfigDescription description) { return Add(new SyncedEntry(config.Bind(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 BindSynced(this ConfigFile config, string section, string key, T value, ConfigDescription description) where T : Enum { return Add(new SyncedEntry(config.Bind(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(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(ref num, default(ForPrimitives)); })); } } public class SyncedEntry : ISyncable { private readonly Func read; private readonly Action write; public ConfigEntry Entry; public Action OnChanged; public readonly Func calcSize; public T Value { get { return k__BackingField; } set { T arg = k__BackingField; k__BackingField = value; OnChanged?.Invoke(arg, value); } } public SyncedEntry(ConfigEntry entry, Func calcSize, Func read, Action 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 <>9__9_1; public static Func <>9__9_2; public static GenericButtonHandler <>9__9_0; public static Func <>9__10_1; public static GenericButtonHandler <>9__10_0; internal void 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 b__9_1(GrabbableObject x) { return (Object)(object)x != (Object)null; } internal string b__9_2(GrabbableObject x) { return ((Object)x.itemProperties).name; } internal void 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)Object.FindObjectsOfType()).FirstOrDefault((Func)((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 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 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 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)val).Min = min; ((BaseRangeOptions)val).Max = max; ((BaseOptions)val).RequiresRestart = false; IntSliderOptions val2 = val; LethalConfigManager.AddConfigItem((BaseConfigItem)new IntSliderConfigItem(entry, val2)); } private static void RegisterSlider(ConfigEntry 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)val).Min = min; ((BaseRangeOptions)val).Max = max; ((BaseOptions)val).RequiresRestart = false; FloatSliderOptions val2 = val; LethalConfigManager.AddConfigItem((BaseConfigItem)new FloatSliderConfigItem(entry, val2)); } private static void RegisterCheckbox(ConfigEntry 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 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(entry, val)); } private static void RegisterTextInput(ConfigEntry 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 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)Object.FindObjectsOfType()).FirstOrDefault((Func)((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(Func 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 Slots { get; } IItemInfo ItemOnlySlot { get; } int CurrentItemSlotIndex { get; } } public sealed class PlayerInfo(PlayerControllerB player) : IPlayerInfo { private readonly IReadOnlyList _slots; private readonly IItemInfo _itemOnlySlot; public IReadOnlyList Slots => _slots; public IItemInfo ItemOnlySlot => _itemOnlySlot; public int CurrentItemSlotIndex => TryGet(() => player.currentItemSlot, "currentItemSlot"); private static T TryGet(Func 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 : IEnumerable, ICollection, IList, IEnumerable, IReadOnlyCollection, IReadOnlyList, ICollection, IList { 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.Count => _items.Count; T IReadOnlyList.this[int index] => _items[index]; int ICollection.Count => _items.Count; bool ICollection.IsReadOnly => true; T IList.this[int index] { get { return _items[index]; } set { throw new NotSupportedException(); } } public <>z__ReadOnlyList(List 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 IEnumerable.GetEnumerator() { return ((IEnumerable)_items).GetEnumerator(); } void ICollection.Add(T item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Contains(T item) { return _items.Contains(item); } void ICollection.CopyTo(T[] array, int arrayIndex) { _items.CopyTo(array, arrayIndex); } bool ICollection.Remove(T item) { throw new NotSupportedException(); } int IList.IndexOf(T item) { return _items.IndexOf(item); } void IList.Insert(int index, T item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } } [CompilerGenerated] internal sealed class <>z__ReadOnlySingleElementList : IEnumerable, ICollection, IList, IEnumerable, IReadOnlyCollection, IReadOnlyList, ICollection, IList { private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator { object IEnumerator.Current => _item; T IEnumerator.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.Count => 1; T IReadOnlyList.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } } int ICollection.Count => 1; bool ICollection.IsReadOnly => true; T IList.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.Default.Equals(_item, (T)value); } int IList.IndexOf(object value) { if (!EqualityComparer.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 IEnumerable.GetEnumerator() { return new Enumerator(_item); } void ICollection.Add(T item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Contains(T item) { return EqualityComparer.Default.Equals(_item, item); } void ICollection.CopyTo(T[] array, int arrayIndex) { array[arrayIndex] = _item; } bool ICollection.Remove(T item) { throw new NotSupportedException(); } int IList.IndexOf(T item) { if (!EqualityComparer.Default.Equals(_item, item)) { return -1; } return 0; } void IList.Insert(int index, T item) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } }