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 TeleportCarts v0.1.4
TeleportCarts.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using LocalizationManager; using Microsoft.CodeAnalysis; using Odinplus.Shared.YamlEditor; using ServerSync; using TMPro; using UnityEngine; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("TeleportCarts")] [assembly: AssemblyDescription("https://valheim.hexium.gg/mods/OdinPlus/TeleportCarts")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("odinplus")] [assembly: AssemblyProduct("TeleportCarts")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: AssemblyFileVersion("0.1.4")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.4.0")] [module: UnverifiableCode] 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; } } } namespace LocalizationManager { public class Localizer { private static readonly Dictionary<string, Dictionary<string, Func<string>>> PlaceholderProcessors; private static readonly Dictionary<string, Dictionary<string, string>> loadedTexts; private static readonly ConditionalWeakTable<Localization, string> localizationLanguage; private static readonly List<WeakReference<Localization>> localizationObjects; private static BaseUnityPlugin? _plugin; private static readonly List<string> fileExtensions; private static BaseUnityPlugin plugin { get { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown if (_plugin == null) { IEnumerable<TypeInfo> source; try { source = Assembly.GetExecutingAssembly().DefinedTypes.ToList(); } catch (ReflectionTypeLoadException ex) { source = from t in ex.Types where t != null select t.GetTypeInfo(); } _plugin = (BaseUnityPlugin)Chainloader.ManagerObject.GetComponent((Type)source.First((TypeInfo t) => t.IsClass && typeof(BaseUnityPlugin).IsAssignableFrom(t))); } return _plugin; } } public static event Action? OnLocalizationComplete; private static void UpdatePlaceholderText(Localization localization, string key) { localizationLanguage.TryGetValue(localization, out string value); string text = loadedTexts[value][key]; if (PlaceholderProcessors.TryGetValue(key, out Dictionary<string, Func<string>> value2)) { text = value2.Aggregate(text, (string current, KeyValuePair<string, Func<string>> kv) => current.Replace("{" + kv.Key + "}", kv.Value())); } localization.AddWord(key, text); } public static void AddPlaceholder<T>(string key, string placeholder, ConfigEntry<T> config, Func<T, string>? convertConfigValue = null) where T : notnull { if (convertConfigValue == null) { convertConfigValue = (T val) => val.ToString(); } if (!PlaceholderProcessors.ContainsKey(key)) { PlaceholderProcessors[key] = new Dictionary<string, Func<string>>(); } config.SettingChanged += delegate { UpdatePlaceholder(); }; if (loadedTexts.ContainsKey(Localization.instance.GetSelectedLanguage())) { UpdatePlaceholder(); } void UpdatePlaceholder() { PlaceholderProcessors[key][placeholder] = () => convertConfigValue(config.Value); UpdatePlaceholderText(Localization.instance, key); } } public static void AddText(string key, string text) { List<WeakReference<Localization>> list = new List<WeakReference<Localization>>(); foreach (WeakReference<Localization> localizationObject in localizationObjects) { if (localizationObject.TryGetTarget(out var target)) { Dictionary<string, string> dictionary = loadedTexts[localizationLanguage.GetOrCreateValue(target)]; if (!target.m_translations.ContainsKey(key)) { dictionary[key] = text; target.AddWord(key, text); } } else { list.Add(localizationObject); } } foreach (WeakReference<Localization> item in list) { localizationObjects.Remove(item); } } public static void Load() { _ = plugin; } public static void LoadLocalizationLater(Localization __instance) { LoadLocalization(Localization.instance, __instance.GetSelectedLanguage()); } public static void SafeCallLocalizeComplete() { Localizer.OnLocalizationComplete?.Invoke(); } private static void LoadLocalization(Localization __instance, string language) { if (!localizationLanguage.Remove(__instance)) { localizationObjects.Add(new WeakReference<Localization>(__instance)); } localizationLanguage.Add(__instance, language); Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (string item in from f in Directory.GetFiles(Path.GetDirectoryName(Paths.PluginPath), plugin.Info.Metadata.Name + ".*", SearchOption.AllDirectories) where fileExtensions.IndexOf(Path.GetExtension(f)) >= 0 select f) { string[] array = Path.GetFileNameWithoutExtension(item).Split(new char[1] { '.' }); if (array.Length >= 2) { string text = array[1]; if (dictionary.ContainsKey(text)) { Debug.LogWarning((object)("Duplicate key " + text + " found for " + plugin.Info.Metadata.Name + ". The duplicate file found at " + item + " will be skipped.")); } else { dictionary[text] = item; } } } byte[] array2 = LoadTranslationFromAssembly("English"); if (array2 == null) { throw new Exception("Found no English localizations in mod " + plugin.Info.Metadata.Name + ". Expected an embedded resource translations/English.json or translations/English.yml."); } Dictionary<string, string> dictionary2 = new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(Encoding.UTF8.GetString(array2)); if (dictionary2 == null) { throw new Exception("Localization for mod " + plugin.Info.Metadata.Name + " failed: Localization file was empty."); } string text2 = null; if (language != "English") { if (dictionary.TryGetValue(language, out var value)) { text2 = File.ReadAllText(value); } else { byte[] array3 = LoadTranslationFromAssembly(language); if (array3 != null) { text2 = Encoding.UTF8.GetString(array3); } } } if (text2 == null && dictionary.TryGetValue("English", out var value2)) { text2 = File.ReadAllText(value2); } if (text2 != null) { foreach (KeyValuePair<string, string> item2 in new DeserializerBuilder().IgnoreFields().Build().Deserialize<Dictionary<string, string>>(text2) ?? new Dictionary<string, string>()) { dictionary2[item2.Key] = item2.Value; } } loadedTexts[language] = dictionary2; foreach (KeyValuePair<string, string> item3 in dictionary2) { UpdatePlaceholderText(__instance, item3.Key); } } static Localizer() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown PlaceholderProcessors = new Dictionary<string, Dictionary<string, Func<string>>>(); loadedTexts = new Dictionary<string, Dictionary<string, string>>(); localizationLanguage = new ConditionalWeakTable<Localization, string>(); localizationObjects = new List<WeakReference<Localization>>(); fileExtensions = new List<string> { ".json", ".yml" }; Harmony val = new Harmony("org.bepinex.helpers.LocalizationManager"); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(Localization), "SetupLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalization", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "LoadLocalizationLater", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); val.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(FejdStartup), "Start", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(Localizer), "SafeCallLocalizeComplete", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static byte[]? LoadTranslationFromAssembly(string language) { foreach (string fileExtension in fileExtensions) { byte[] array = ReadEmbeddedFileBytes("translations." + language + fileExtension); if (array != null) { return array; } } return null; } public static byte[]? ReadEmbeddedFileBytes(string resourceFileName, Assembly? containingAssembly = null) { using MemoryStream memoryStream = new MemoryStream(); if ((object)containingAssembly == null) { containingAssembly = Assembly.GetCallingAssembly(); } string text = containingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(resourceFileName, StringComparison.Ordinal)); if (text != null) { containingAssembly.GetManifestResourceStream(text)?.CopyTo(memoryStream); } return (memoryStream.Length == 0L) ? null : memoryStream.ToArray(); } } public static class LocalizationManagerVersion { public const string Version = "1.4.1"; } } namespace TeleportCarts { public sealed class EffectiveCartPolicy { public CartServerPolicy ServerPolicy { get; } public PlayerCartPreferences PlayerPreferences { get; } public float SearchRadius => ServerPolicy.Carts.SearchRadius; public float ForwardOffset => ServerPolicy.Placement.ForwardOffset; public float ReattachRadius => ServerPolicy.Placement.ReattachRadius; private EffectiveCartPolicy(CartServerPolicy serverPolicy, PlayerCartPreferences playerPreferences) { ServerPolicy = serverPolicy; PlayerPreferences = playerPreferences; } public static EffectiveCartPolicy Create(CartServerPolicy server, PlayerCartPreferences player) { CartServerPolicyValidator.Validate(server); PlayerCartPreferencesValidator.Validate(player); return new EffectiveCartPolicy(server, player); } public bool IsTransitionEnabled(CartTransitionKind kind, bool basementsInstalled) { if (ServerPolicy.Carts.Enabled && PlayerPreferences.Carts.Enabled && CartTransitionPolicy.IsEnabled(ServerPolicy.Carts.Teleport, kind, basementsInstalled)) { return CartTransitionPolicy.IsEnabled(PlayerPreferences.Carts.Teleport, kind, basementsInstalled); } return false; } } internal static class CartEnvironmentDecision { public static bool IsBasement(string? environmentName) { if (!string.Equals(environmentName, "Basement", StringComparison.OrdinalIgnoreCase)) { return string.Equals(environmentName, "Undercroft", StringComparison.OrdinalIgnoreCase); } return true; } } internal static class CartPlacementDecision { public static bool UsesNativeTargetWithoutPreOffset() { return true; } public static bool UsesNativeTargetHeight(CartTransitionKind transition) { return transition != CartTransitionKind.Portal; } } public sealed class PlayerCartPreferences { public int SchemaVersion { get; set; } = 1; public PlayerCartFeaturePreferences Carts { get; set; } = new PlayerCartFeaturePreferences(); public static PlayerCartPreferences CreateDefault() { return new PlayerCartPreferences(); } } public sealed class PlayerCartFeaturePreferences { public bool Enabled { get; set; } = true; public PlayerCartTeleportConfig Teleport { get; set; } = new PlayerCartTeleportConfig(); } public sealed class PlayerCartTeleportConfig { public CartPortalTransportConfig Portals { get; set; } = new CartPortalTransportConfig(); public CartTransitionToggleConfig Dungeons { get; set; } = new CartTransitionToggleConfig(); public CartTransitionToggleConfig Basements { get; set; } = new CartTransitionToggleConfig(); } public static class PlayerCartPreferencesValidator { public static void Validate(PlayerCartPreferences preferences) { if (preferences == null) { throw new InvalidDataException("Player cart preferences cannot be null."); } if (preferences.SchemaVersion != 1) { throw new InvalidDataException("Only SchemaVersion 1 is supported."); } if (preferences.Carts == null || preferences.Carts.Teleport == null || preferences.Carts.Teleport.Portals == null || preferences.Carts.Teleport.Dungeons == null || preferences.Carts.Teleport.Basements == null) { throw new InvalidDataException("Carts and Carts.Teleport sections are required."); } } } public static class PlayerCartPreferenceSnapshot { public static bool TryReplace(PlayerCartPreferences lastValid, string yaml, out PlayerCartPreferences retained, out string error) { try { PlayerCartPreferences playerCartPreferences = new DeserializerBuilder().Build().Deserialize<PlayerCartPreferences>(yaml); PlayerCartPreferencesValidator.Validate(playerCartPreferences); retained = playerCartPreferences; error = string.Empty; return true; } catch (Exception ex) { retained = lastValid; error = ex.Message; return false; } } } public sealed class CartServerPolicy { public int SchemaVersion { get; set; } = 1; public CartFeatureConfig Carts { get; set; } = new CartFeatureConfig(); public CartPlacementConfig Placement { get; set; } = new CartPlacementConfig(); } public sealed class CartFeatureConfig { public bool Enabled { get; set; } = true; public CartTeleportConfig Teleport { get; set; } = new CartTeleportConfig(); public float SearchRadius { get; set; } = 10f; } public sealed class CartTeleportConfig { public CartPortalTransportConfig Portals { get; set; } = new CartPortalTransportConfig(); public CartTransitionToggleConfig Dungeons { get; set; } = new CartTransitionToggleConfig(); public CartTransitionToggleConfig Basements { get; set; } = new CartTransitionToggleConfig(); } public sealed class CartPortalTransportConfig { public bool Enabled { get; set; } = true; } public sealed class CartTransitionToggleConfig { public bool Enabled { get; set; } = true; } public sealed class CartPlacementConfig { public float ForwardOffset { get; set; } = 0.8f; public float ReattachRadius { get; set; } = 15f; } public static class CartServerPolicyValidator { public static void Validate(CartServerPolicy policy) { if (policy == null) { throw new InvalidDataException("Cart server policy cannot be null."); } if (policy.SchemaVersion != 1) { throw new InvalidDataException("Only SchemaVersion 1 is supported."); } if (policy.Carts == null || policy.Carts.Teleport == null || policy.Placement == null || policy.Carts.Teleport.Portals == null || policy.Carts.Teleport.Dungeons == null || policy.Carts.Teleport.Basements == null) { throw new InvalidDataException("Carts, Carts.Teleport, Carts.Teleport.Portals, Carts.Teleport.Dungeons, Carts.Teleport.Basements, and Placement sections are required."); } if (policy.Carts.SearchRadius <= 0f || policy.Carts.SearchRadius > 100f) { throw new InvalidDataException("Carts.SearchRadius must be between 0 and 100."); } if (policy.Placement.ForwardOffset <= 0f || policy.Placement.ForwardOffset > 20f || policy.Placement.ReattachRadius <= 0f || policy.Placement.ReattachRadius > 100f) { throw new InvalidDataException("Placement.ForwardOffset must be between 0 and 20 and Placement.ReattachRadius must be between 0 and 100."); } } } public enum CartTransitionKind { Portal, Dungeon, Basement } internal static class CartTransitionPolicy { public static bool IsEnabled(CartTeleportConfig config, CartTransitionKind kind, bool basementsInstalled) { switch (kind) { default: return config.Dungeons.Enabled; case CartTransitionKind.Basement: if (basementsInstalled) { return config.Basements.Enabled; } return false; case CartTransitionKind.Portal: return config.Portals.Enabled; } } public static bool IsEnabled(PlayerCartTeleportConfig config, CartTransitionKind kind, bool basementsInstalled) { switch (kind) { default: return config.Dungeons.Enabled; case CartTransitionKind.Basement: if (basementsInstalled) { return config.Basements.Enabled; } return false; case CartTransitionKind.Portal: return config.Portals.Enabled; } } } internal static class CartTransportDecision { public static bool CanUseUnattachedTarget(bool hasExplicitTarget, bool targetIsAttached, bool targetInSearchRadius) { return hasExplicitTarget && !targetIsAttached && targetInSearchRadius; } } internal static class CartWorldGate { public static bool ShouldBlock(bool noPortals, bool noBossPortals, bool bossEventActive) { if (!noPortals) { return noBossPortals && bossEventActive; } return true; } } internal static class DirectCartTeleportDecision { public static bool ShouldPrepareUnattachedCart(bool teleportIsDirect, bool isLocalPlayer, bool sourcePortalIsValid, bool sourceDistanceIsValid, bool attachedCartAvailable, bool transitionEnabled) { return teleportIsDirect && isLocalPlayer && sourcePortalIsValid && sourceDistanceIsValid && !attachedCartAvailable && transitionEnabled; } } internal static class CartTransportDiagnostics { public static string Player(Player player) { return "player=" + SafeName(player) + " playerId=" + SafeId(player); } public static string Cart(Vagon cart) { return "cart=" + SafePrefab(cart) + " cartId=" + SafeId(cart); } public static string Event(Player player, CartTransitionKind transition, string outcome, Vagon? cart = null, bool? attached = null, int? attempt = null, Vector3? position = null) { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) return $"cart-transition outcome={outcome} transition={transition} {Player(player)}" + (((Object)(object)cart == (Object)null) ? string.Empty : (" " + Cart(cart))) + (attached.HasValue ? $" attached={attached.Value}" : string.Empty) + (attempt.HasValue ? $" attempt={attempt.Value}" : string.Empty) + (position.HasValue ? (" position=" + Format(position.Value)) : string.Empty); } public static string Format(Vector3 position) { //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_001b: Unknown result type (might be due to invalid IL or missing references) return $"({position.x:0.##},{position.y:0.##},{position.z:0.##})"; } private static string SafeName(Player player) { try { return string.IsNullOrWhiteSpace(player.GetPlayerName()) ? "<unknown>" : player.GetPlayerName(); } catch { return "<unknown>"; } } private static string SafePrefab(Vagon cart) { try { ZNetView nview = cart.m_nview; return ((nview != null) ? nview.GetPrefabName() : null) ?? "<unknown>"; } catch { return "<unknown>"; } } private static string SafeId(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) try { return ((object)((Character)player).GetZDOID()/*cast due to .constrained prefix*/).ToString(); } catch { return "<unknown>"; } } private static string SafeId(Vagon cart) { try { ZNetView nview = cart.m_nview; return ((nview == null) ? null : ((object)Unsafe.As<ZDOID, ZDOID>(ref nview.GetZDO()?.m_uid)/*cast due to .constrained prefix*/).ToString()) ?? "<unknown>"; } catch { return "<unknown>"; } } } internal static class CartTransport { private static bool _portalTransition; private static bool _dungeonTransition; private static Vector3 _portalSourcePosition; private static float _portalSourceCapturedAt; private static bool _hasPortalSource; private static bool _directPortalTransition; private static ZDOID? _pendingCartId; private static bool _pendingCartAttached; private static int _pendingCartAttempts; private static bool _pendingCartUnavailableLogged; private static CartTransitionKind _pendingCartTransition; private static Quaternion _pendingCartRotation; private static Vector3 _pendingCartTarget; private static Vagon? _explicitTargetCart; private static CartTransitionKind? _dungeonTransitionKind; private static Vector3? _teleportOrigin; private const float DirectPortalSourceLifetimeSeconds = 30f; private const float DirectPortalSourceDistance = 10f; private const int MaximumPendingCartAttempts = 30; public static bool HasPendingTransport => _pendingCartId.HasValue; public static void ObservePortalEntry(Player player) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer)) { _portalSourcePosition = ((Component)player).transform.position; _portalSourceCapturedAt = Time.time; _hasPortalSource = true; _directPortalTransition = false; } } public static void EnterPortalTransition() { _portalTransition = true; _directPortalTransition = false; } public static void ExitPortalTransition() { _portalTransition = false; _directPortalTransition = false; ClearPortalSource(); } public static void EnterDungeonTransition() { _dungeonTransition = true; _dungeonTransitionKind = ((!IsInBasement()) ? CartTransitionKind.Dungeon : CartTransitionKind.Basement); } public static void ExitDungeonTransition() { _dungeonTransition = false; } public static void ClearDungeonTransition() { _dungeonTransition = false; _dungeonTransitionKind = null; } public static void SetExplicitTargetCart(Vagon? cart) { _explicitTargetCart = cart; } public static void ClearExplicitTargetCart() { _explicitTargetCart = null; } public static void PrepareDirectPortalCart(Player player, EffectiveCartPolicy policy) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) _directPortalTransition = _hasPortalSource && Time.time - _portalSourceCapturedAt <= 30f && Vector3.Distance(((Component)player).transform.position, _portalSourcePosition) <= 10f; } public static void EndDirectPortalCart() { _directPortalTransition = false; ClearPortalSource(); } public static void CancelPending() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) _portalTransition = false; _dungeonTransition = false; _directPortalTransition = false; ClearPortalSource(); _pendingCartId = null; _pendingCartAttached = false; _pendingCartAttempts = 0; _pendingCartUnavailableLogged = false; _pendingCartTransition = CartTransitionKind.Portal; _dungeonTransitionKind = null; _pendingCartRotation = default(Quaternion); _pendingCartTarget = default(Vector3); _explicitTargetCart = null; } public static CartTransitionKind ClassifyCurrentTransition(Player player) { if (_portalTransition) { return CartTransitionKind.Portal; } if (_dungeonTransition) { CartTransitionKind? dungeonTransitionKind = _dungeonTransitionKind; if (!dungeonTransitionKind.HasValue) { if (!IsInBasement()) { return CartTransitionKind.Dungeon; } return CartTransitionKind.Basement; } return dungeonTransitionKind.GetValueOrDefault(); } if (_dungeonTransitionKind.HasValue) { return _dungeonTransitionKind.Value; } if (!((Character)player).InInterior() && !IsInBasement()) { return CartTransitionKind.Portal; } return CartTransitionKind.Dungeon; } public static bool TryGetAttached(Player player, EffectiveCartPolicy policy, out Vagon? cart) { cart = null; if ((Object)(object)player == (Object)null) { return false; } cart = ((IEnumerable<Vagon>)Vagon.m_instances).FirstOrDefault((Func<Vagon, bool>)((Vagon v) => (Object)(object)v != (Object)null && Vector3.Distance(((Component)v).transform.position, ((Component)player).transform.position) <= policy.SearchRadius && v.IsAttached((Character)(object)player))); return (Object)(object)cart != (Object)null; } public static bool TryGetClosestUnattached(Player player, EffectiveCartPolicy policy, out Vagon? cart) { cart = null; if ((Object)(object)player == (Object)null) { return false; } cart = (from v in Vagon.m_instances where (Object)(object)v != (Object)null && !v.IsAttached((Character)(object)player) && Vector3.Distance(((Component)v).transform.position, ((Component)player).transform.position) <= policy.SearchRadius orderby Vector3.Distance(((Component)v).transform.position, ((Component)player).transform.position) select v).FirstOrDefault(); return (Object)(object)cart != (Object)null; } public static bool TryGetTransportCart(Player player, EffectiveCartPolicy policy, out Vagon? cart, out bool isAttached) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (TryGetAttached(player, policy, out cart)) { isAttached = true; return true; } if ((Object)(object)_explicitTargetCart != (Object)null && CartTransportDecision.CanUseUnattachedTarget(hasExplicitTarget: true, _explicitTargetCart.IsAttached((Character)(object)player), Vector3.Distance(((Component)_explicitTargetCart).transform.position, ((Component)player).transform.position) <= policy.SearchRadius)) { cart = _explicitTargetCart; isAttached = false; return true; } isAttached = false; cart = null; return false; } public static string AppendHoverHint(string baseText) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!TeleportCartsPlugin.IsEnabled || !TeleportCartsPlugin.IsHotkeyConfigured || (Object)(object)Player.m_localPlayer == (Object)null) { return baseText; } if (TryGetAttached(Player.m_localPlayer, TeleportCartsPlugin.Policy, out Vagon cart)) { return baseText; } if (!TryGetClosestUnattached(Player.m_localPlayer, TeleportCartsPlugin.Policy, out cart)) { return baseText; } string text = FormatHotkey(TeleportCartsPlugin.TeleportCartHotkey); string text2 = ((Localization.instance != null) ? Localization.instance.Localize("$tcart_hover_teleport_cart") : "Teleport with nearest cart"); return baseText + "\n[<color=yellow><b>" + text + "</b></color>] " + text2; } internal unsafe static string FormatHotkey(KeyboardShortcut shortcut) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected I4, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers) { List<string> list2 = list; string item; switch (modifier - 303) { case 0: case 1: item = "Shift"; break; case 2: case 3: item = "Ctrl"; break; case 4: case 5: item = "Alt"; break; default: item = ((object)(*(KeyCode*)(&modifier))/*cast due to .constrained prefix*/).ToString(); break; } list2.Add(item); } list.Add(((object)((KeyboardShortcut)(ref shortcut)).MainKey/*cast due to .constrained prefix*/).ToString()); return string.Join(" + ", list); } public static bool CartIsTeleportable(Vagon cart, bool allowAllItems) { Container componentInChildren = ((Component)cart).GetComponentInChildren<Container>(); if (componentInChildren == null) { return false; } Inventory inventory = componentInChildren.m_inventory; return ((inventory != null) ? new bool?(inventory.IsTeleportable(allowAllItems)) : ((bool?)null)) == true; } public static bool IsWorldTeleportBlocked() { ZoneSystem instance = ZoneSystem.instance; RandEventSystem instance2 = RandEventSystem.instance; return CartWorldGate.ShouldBlock((Object)(object)instance != (Object)null && instance.GetGlobalKey("noportals"), (Object)(object)instance != (Object)null && instance.GetGlobalKey("nobossportals"), (Object)(object)instance2 != (Object)null && instance2.GetBossEvent() != null); } public static float GetCartHeight(Vagon cart) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)cart == (Object)null) { return 2f; } Collider[] componentsInChildren = ((Component)cart).GetComponentsInChildren<Collider>(); if (componentsInChildren == null || componentsInChildren.Length == 0) { return 2f; } float num = float.MaxValue; float num2 = float.MinValue; bool flag = false; Collider[] array = componentsInChildren; foreach (Collider val in array) { if (!((Object)(object)val == (Object)null) && !val.isTrigger) { Bounds bounds = val.bounds; if (((Bounds)(ref bounds)).min.y < num) { num = ((Bounds)(ref bounds)).min.y; } if (((Bounds)(ref bounds)).max.y > num2) { num2 = ((Bounds)(ref bounds)).max.y; } flag = true; } } if (!flag) { return 2f; } return Mathf.Max(1f, num2 - num); } public static bool IsPlacementSafe(Vagon cart, Vector3 targetPosition, Quaternion rotation, out string failureReason) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) failureReason = ""; if ((Object)(object)cart == (Object)null) { failureReason = "Cart reference missing."; return false; } if (!HasVerticalClearance(cart, targetPosition, rotation, TeleportCartsPlugin.Policy, out var availableHeight, out var cartHeight)) { failureReason = $"Insufficient vertical clearance at {targetPosition}. Required height: {cartHeight:0.##}m, Available: {availableHeight:0.##}m."; return false; } Vector3 val = targetPosition + rotation * Vector3.forward * TeleportCartsPlugin.Policy.ForwardOffset; Bounds val2 = default(Bounds); ((Bounds)(ref val2))..ctor(val, Vector3.one * 5f); Bounds val3 = default(Bounds); foreach (Vagon instance in Vagon.m_instances) { if ((Object)(object)instance == (Object)(object)cart || (Object)(object)instance == (Object)null) { continue; } ((Bounds)(ref val3))..ctor(((Component)instance).transform.position, Vector3.one * 5f); if (((Bounds)(ref val2)).Intersects(val3)) { object arg; if (instance == null) { arg = null; } else { ZNetView nview = instance.m_nview; arg = ((nview != null) ? nview.GetZDO() : null); } failureReason = $"Destination is obstructed by another stationary cart ({arg}) at {((Component)instance).transform.position}."; return false; } } failureReason = "OK"; return true; } internal static bool HasVerticalClearance(Vagon cart, Vector3 target, Quaternion rotation, EffectiveCartPolicy policy, out float availableHeight, out float cartHeight, CartTransitionKind transition = CartTransitionKind.Portal) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_0023: Unknown result type (might be due to invalid IL or missing references) //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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) cartHeight = GetCartHeight(cart); availableHeight = float.MaxValue; Vector3 val = target + rotation * Vector3.forward * policy.ForwardOffset; float num = default(float); if (!CartPlacementDecision.UsesNativeTargetHeight(transition) && (Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(val, ref num) && Mathf.Abs(num - val.y) < 10f) { val.y = num + 0.1f; } Vector3 val2 = val + Vector3.up * 0.2f; int num2 = (((Object)(object)ZoneSystem.instance != (Object)null) ? (ZoneSystem.instance.m_blockRayMask | ZoneSystem.instance.m_solidRayMask) : (-5)); RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(val2, Vector3.up, ref val3, 20f, num2, (QueryTriggerInteraction)1)) { availableHeight = ((RaycastHit)(ref val3)).distance + 0.2f; if (availableHeight < cartHeight + 0.15f) { return false; } } return true; } public static Vector3 Transport(Vagon cart, Vector3 target, Quaternion rotation, EffectiveCartPolicy policy, CartTransitionKind transition = CartTransitionKind.Portal) { //IL_000b: 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) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) cart.m_nview.ClaimOwnership(); Vector3 val = target + rotation * Vector3.forward * policy.ForwardOffset; float num = default(float); float num2 = default(float); Vector3 val2 = default(Vector3); GameObject val3 = default(GameObject); if (CartPlacementDecision.UsesNativeTargetHeight(transition)) { val.y = target.y + 0.1f; } else if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(val, ref num) && Mathf.Abs(num - val.y) < 10f) { val.y = num + 0.1f; } else if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(val, ref num2, ref val2, ref val3) && Mathf.Abs(num2 - val.y) < 10f) { val.y = num2 + 0.5f; } else if ((Object)(object)ZoneSystem.instance != (Object)null) { float solidHeight = ZoneSystem.instance.GetSolidHeight(val); if (Mathf.Abs(solidHeight - val.y) < 10f) { val.y = solidHeight + 0.5f; } } ((Component)cart).transform.SetPositionAndRotation(val, rotation); if ((Object)(object)cart.m_nview != (Object)null && cart.m_nview.GetZDO() != null) { cart.m_nview.GetZDO().SetPosition(val); cart.m_nview.GetZDO().SetRotation(rotation); } if ((Object)(object)cart.m_body != (Object)null) { cart.m_body.linearVelocity = Vector3.zero; } return val; } public static void BeginPendingTransport(Vagon cart, bool isAttached, CartTransitionKind transition, Vector3 target, Quaternion rotation) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) _pendingCartId = cart.m_nview.GetZDO().m_uid; _pendingCartAttached = isAttached; _pendingCartAttempts = 0; _pendingCartUnavailableLogged = false; _pendingCartTransition = transition; _pendingCartTarget = target; _pendingCartRotation = rotation; } public static void CompleteAfterArrival(Player player, EffectiveCartPolicy policy) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) if (!_pendingCartId.HasValue) { return; } ZDOID value = _pendingCartId.Value; _pendingCartAttempts++; bool pendingCartAttached = _pendingCartAttached; ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(value) : null); object obj; if (val != null) { ZNetScene instance2 = ZNetScene.instance; if (instance2 == null) { obj = null; } else { ZNetView obj2 = instance2.FindInstance(val); obj = ((obj2 != null) ? ((Component)obj2).GetComponent<Vagon>() : null); } } else { obj = null; } Vagon val2 = (Vagon)obj; if ((Object)(object)val2 == (Object)null) { if (!_pendingCartUnavailableLogged) { TeleportCartsPlugin.LogDiagnostic($"cart-transition outcome=waiting-for-cart transition={_pendingCartTransition} player={player.GetPlayerName()} playerId={((Character)player).GetZDOID()} cartId={value} attempt={_pendingCartAttempts}"); _pendingCartUnavailableLogged = true; } if (_pendingCartAttempts >= 30) { TeleportCartsPlugin.LogDiagnostic($"cart-transition outcome=cart-not-loaded transition={_pendingCartTransition} player={player.GetPlayerName()} playerId={((Character)player).GetZDOID()} cartId={value} attempts={_pendingCartAttempts}"); ClearPendingTransport(); } } else { Vector3 value2 = Transport(val2, ((Component)player).transform.position, ((Component)player).transform.rotation, policy, _pendingCartTransition); TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(player, _pendingCartTransition, "arrival-target", val2, pendingCartAttached, _pendingCartAttempts, value2) + " " + $"nativeTarget={CartTransportDiagnostics.Format(_pendingCartTarget)} rotation={((Quaternion)(ref _pendingCartRotation)).eulerAngles} offset={policy.ForwardOffset:0.##}"); if (pendingCartAttached) { Transform transform = ((Component)player).transform; transform.position += _pendingCartRotation * Vector3.forward * (policy.ForwardOffset + 2.5f); } if (!pendingCartAttached) { TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(player, _pendingCartTransition, "placed", val2, false, _pendingCartAttempts, value2)); ClearPendingTransport(); } else if (val2.CanAttach(((Component)player).gameObject)) { val2.AttachTo(((Component)player).gameObject); TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(player, _pendingCartTransition, "reattached", val2, true, _pendingCartAttempts, value2)); ClearPendingTransport(); } else if (_pendingCartAttempts >= 30) { TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(player, _pendingCartTransition, "placed-reattach-pending", val2, true, _pendingCartAttempts, ((Component)val2).transform.position)); ClearPendingTransport(); } } } private static void ClearPendingTransport() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) _pendingCartId = null; _pendingCartAttached = false; _pendingCartAttempts = 0; _pendingCartUnavailableLogged = false; _pendingCartTransition = CartTransitionKind.Portal; _pendingCartRotation = default(Quaternion); _pendingCartTarget = default(Vector3); } private static void ClearPortalSource() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) _hasPortalSource = false; _portalSourcePosition = default(Vector3); _portalSourceCapturedAt = 0f; } private static bool IsInBasement() { if (TeleportCartsPlugin.BasementsInstalled && (Object)(object)EnvMan.instance != (Object)null) { return CartEnvironmentDecision.IsBasement(EnvMan.instance.GetCurrentEnvironment().m_name); } return false; } public static void SetTeleportOrigin(Vector3 origin) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) _teleportOrigin = origin; } public static bool HasTeleported(Vagon cart) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!_teleportOrigin.HasValue) { return false; } Vector3 val = ((Component)cart).transform.position - _teleportOrigin.Value; return ((Vector3)(ref val)).sqrMagnitude > 0.01f; } } [HarmonyPatch(typeof(Humanoid), "IsTeleportable")] internal static class HumanoidCartTeleportablePatch { private static void Postfix(Humanoid __instance, bool allowAllItems, ref bool __result) { if (__result && TeleportCartsPlugin.IsEnabled) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && CartTransport.TryGetAttached(val, TeleportCartsPlugin.Policy, out Vagon cart)) { __result = CartTransport.CartIsTeleportable(cart, allowAllItems); } } } } [HarmonyPatch(typeof(Player), "TeleportTo")] internal static class PlayerCartTeleportPatch { private const float CartSize = 2.5f; private static bool Prefix(Player __instance, Vector3 pos, Quaternion rot, bool distantTeleport, out bool __state) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) __state = false; if (TeleportCartsPlugin.IsEnabled) { CartTransport.PrepareDirectPortalCart(__instance, TeleportCartsPlugin.Policy); } if (!TeleportCartsPlugin.IsEnabled || !CartTransport.TryGetTransportCart(__instance, TeleportCartsPlugin.Policy, out Vagon cart, out bool _)) { return true; } CartTransitionKind cartTransitionKind = CartTransport.ClassifyCurrentTransition(__instance); if (!TeleportCartsPlugin.Policy.IsTransitionEnabled(cartTransitionKind, TeleportCartsPlugin.BasementsInstalled)) { TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, cartTransitionKind, "skipped-policy", null, true)); return true; } if (cartTransitionKind == CartTransitionKind.Portal && CartTransport.IsWorldTeleportBlocked()) { ((Character)__instance).Message((MessageType)2, "$tcart_blocked_world", 0, (Sprite)null, false); return false; } if (!CartTransport.HasVerticalClearance(cart, pos, rot, TeleportCartsPlugin.Policy, out var availableHeight, out var cartHeight, cartTransitionKind)) { TeleportCartsPlugin.Log.LogWarning((object)string.Format("{0} availableHeight={1:0.#} cartHeight={2:0.#}", CartTransportDiagnostics.Event(__instance, cartTransitionKind, "blocked-clearance", cart, true), availableHeight, cartHeight)); if ((Object)(object)MessageHud.instance != (Object)null && Localization.instance != null) { MessageHud.instance.ShowMessage((MessageType)2, Localization.instance.Localize("$tcart_blocked_low_ceiling"), 0, (Sprite)null, false, true); } return true; } __state = true; return true; } private static void Postfix(Player __instance, Vector3 pos, Quaternion rot, bool distantTeleport, bool __state, bool __result) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) CartTransport.EndDirectPortalCart(); if (!__state || !__result || !CartTransport.TryGetTransportCart(__instance, TeleportCartsPlugin.Policy, out Vagon cart, out bool isAttached)) { CartTransport.ClearExplicitTargetCart(); CartTransport.ClearDungeonTransition(); return; } if (isAttached) { cart.Detach(); } CartTransitionKind transition = CartTransport.ClassifyCurrentTransition(__instance); if (distantTeleport) { CartTransport.BeginPendingTransport(cart, isAttached, transition, pos, rot); TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, transition, "deferred", cart, isAttached)); CartTransport.ClearExplicitTargetCart(); CartTransport.ClearDungeonTransition(); return; } Vector3 value = CartTransport.Transport(cart, pos, rot, TeleportCartsPlugin.Policy, transition); Vagon cart2 = cart; bool? attached = isAttached; Vector3? position = value; TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, transition, "target", cart2, attached, null, position) + " " + $"nativeTarget={CartTransportDiagnostics.Format(pos)} rotation={((Quaternion)(ref rot)).eulerAngles} offset={TeleportCartsPlugin.Policy.ForwardOffset:0.##} distant={distantTeleport}"); if (isAttached) { Vector3 val = rot * Vector3.forward * (TeleportCartsPlugin.Policy.ForwardOffset + 2.5f); Transform transform = ((Component)__instance).transform; transform.position += val; if (cart.CanAttach(((Component)__instance).gameObject)) { cart.AttachTo(((Component)__instance).gameObject); Vagon cart3 = cart; bool? attached2 = true; position = ((Component)cart).transform.position; TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, transition, "reattached", cart3, attached2, null, position)); } } else { Vagon cart4 = cart; bool? attached3 = false; position = value; TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, transition, "placed", cart4, attached3, null, position)); } CartTransport.ClearExplicitTargetCart(); if (CartTransport.HasTeleported(cart) && (Object)(object)MessageHud.instance != (Object)null && Localization.instance != null) { MessageHud.instance.ShowMessage((MessageType)1, Localization.instance.Localize("$tcart_transported_message"), 0, (Sprite)null, false, true); } Vagon cart5 = cart; bool? attached4 = isAttached; position = value; TeleportCartsPlugin.LogDiagnostic(string.Format("{0} target={1} distant={2}", CartTransportDiagnostics.Event(__instance, transition, "completed", cart5, attached4, null, position), CartTransportDiagnostics.Format(pos), distantTeleport)); CartTransport.ClearDungeonTransition(); } } [HarmonyPatch(typeof(Player), "Update")] internal static class PlayerCartHotkeyPatch { private static void Prefix(Player __instance) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || ((Character)__instance).IsDead() || !TeleportCartsPlugin.IsEnabled || !TeleportCartsPlugin.IsHotkeyConfigured) { return; } KeyboardShortcut teleportCartHotkey = TeleportCartsPlugin.TeleportCartHotkey; if (!((KeyboardShortcut)(ref teleportCartHotkey)).IsDown() || CartTransport.TryGetAttached(__instance, TeleportCartsPlugin.Policy, out Vagon _) || !CartTransport.TryGetClosestUnattached(__instance, TeleportCartsPlugin.Policy, out Vagon cart2)) { return; } GameObject hovering = __instance.m_hovering; if ((Object)(object)hovering == (Object)null) { return; } TeleportWorld componentInParent = hovering.GetComponentInParent<TeleportWorld>(); if ((Object)(object)componentInParent != (Object)null && componentInParent.TargetFound()) { bool allowAllItems = componentInParent.m_allowAllItems; if (!CartTransport.CartIsTeleportable(cart2, allowAllItems)) { if ((Object)(object)MessageHud.instance != (Object)null && Localization.instance != null) { MessageHud.instance.ShowMessage((MessageType)2, Localization.instance.Localize("$tcart_items_prevent"), 0, (Sprite)null, false, true); } TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, CartTransitionKind.Portal, "hotkey-blocked-cargo", cart2, false)); return; } Vector3 position = ((Component)componentInParent).transform.position; if (!CartTransport.IsPlacementSafe(cart2, position, ((Component)componentInParent).transform.rotation, out string failureReason)) { TeleportCartsPlugin.Log.LogWarning((object)(CartTransportDiagnostics.Event(__instance, CartTransitionKind.Portal, "hotkey-blocked-placement", cart2, false) + " reason=" + failureReason)); if ((Object)(object)MessageHud.instance != (Object)null && Localization.instance != null) { MessageHud.instance.ShowMessage((MessageType)2, Localization.instance.Localize("$tcart_blocked_placement"), 0, (Sprite)null, false, true); } TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, CartTransitionKind.Portal, "hotkey-blocked-placement", cart2, false)); } else if (CartTransport.IsWorldTeleportBlocked()) { if ((Object)(object)MessageHud.instance != (Object)null && Localization.instance != null) { MessageHud.instance.ShowMessage((MessageType)2, Localization.instance.Localize("$tcart_blocked_world"), 0, (Sprite)null, false, true); } } else { CartTransport.SetTeleportOrigin(((Component)cart2).transform.position); CartTransport.SetExplicitTargetCart(cart2); componentInParent.Teleport(__instance); } return; } Teleport componentInParent2 = hovering.GetComponentInParent<Teleport>(); if ((Object)(object)componentInParent2 != (Object)null && (Object)(object)componentInParent2.m_targetPoint != (Object)null) { CartTransport.EnterDungeonTransition(); CartTransport.SetTeleportOrigin(((Component)cart2).transform.position); CartTransport.SetExplicitTargetCart(cart2); if (!componentInParent2.Interact((Humanoid)(object)__instance, false, false)) { CartTransport.ClearDungeonTransition(); CartTransport.ClearExplicitTargetCart(); TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, CartTransitionKind.Dungeon, "hotkey-interaction-rejected", cart2, false)); } else { TeleportCartsPlugin.LogDiagnostic(CartTransportDiagnostics.Event(__instance, CartTransitionKind.Dungeon, "hotkey-interaction-started", cart2, false)); } } } } [HarmonyPatch(typeof(Player), "UpdateTeleport")] [HarmonyPriority(800)] internal static class PlayerCartArrivalPatch { private static void Prefix(Player __instance, out bool __state) { __state = ((Character)__instance).IsTeleporting(); } private static void Postfix(Player __instance, bool __state) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!((Character)__instance).IsTeleporting() && CartTransport.HasPendingTransport && !((Object)(object)ZNetScene.instance == (Object)null) && ZNetScene.instance.IsAreaReady(((Component)__instance).transform.position)) { CartTransport.CompleteAfterArrival(__instance, TeleportCartsPlugin.Policy); } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class PlayerCartDeathPatch { private static void Postfix() { CartTransport.CancelPending(); } } [HarmonyPatch(typeof(Teleport), "Interact")] internal static class TeleportCartTransitionPatch { private static void Prefix(Teleport __instance) { if (TeleportCartsPlugin.IsEnabled && !((Object)(object)__instance.m_targetPoint == (Object)null)) { CartTransport.EnterDungeonTransition(); } } private static void Postfix() { CartTransport.ExitDungeonTransition(); } } [HarmonyPatch(typeof(Teleport), "GetHoverText")] internal static class TeleportHoverPatch { private static void Postfix(Teleport __instance, ref string __result) { if (!string.IsNullOrEmpty(__result) && !((Object)(object)__instance.m_targetPoint == (Object)null)) { __result = CartTransport.AppendHoverHint(__result); } } } [HarmonyPatch(typeof(TeleportWorld), "Teleport")] internal static class TeleportWorldCartTransitionPatch { private static void Prefix() { if (TeleportCartsPlugin.IsEnabled) { CartTransport.EnterPortalTransition(); } } private static void Postfix() { CartTransport.ExitPortalTransition(); } } [HarmonyPatch(typeof(TeleportWorldTrigger), "OnTriggerEnter")] internal static class TeleportWorldTriggerCartTransitionPatch { private static void Prefix(Collider colliderIn) { Player component = ((Component)colliderIn).GetComponent<Player>(); if ((Object)(object)component != (Object)null) { CartTransport.ObservePortalEntry(component); } } } [HarmonyPatch(typeof(TeleportWorld), "GetHoverText")] internal static class TeleportWorldHoverPatch { private static void Postfix(TeleportWorld __instance, ref string __result) { if (!string.IsNullOrEmpty(__result) && __instance.TargetFound()) { __result = CartTransport.AppendHoverHint(__result); } } } [BepInPlugin("odinplus.TeleportCarts", "TeleportCarts", "0.1.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class TeleportCartsPlugin : BaseUnityPlugin { private enum Toggle { On = 1, Off = 0 } internal const string Author = "odinplus"; internal const string ModGuid = "odinplus.TeleportCarts"; internal const string ModName = "TeleportCarts"; internal const string ModVersion = "0.1.4"; private const string ConfigFileName = "odinplus.TeleportCarts.cfg"; private static readonly ConfigSync Sync = new ConfigSync("odinplus.TeleportCarts") { DisplayName = "TeleportCarts", CurrentVersion = "0.1.4", MinimumRequiredVersion = "0.1.4" }; private static readonly CustomSyncedValue<string> Snapshot = new CustomSyncedValue<string>(Sync, "YamlPolicy", string.Empty); private static ConfigEntry<Toggle>? _forceServerConfig; private static ConfigEntry<bool>? _enableMod; private static ConfigEntry<bool>? _enableDiagnostics; private static ConfigEntry<KeyboardShortcut>? _teleportCartHotkey; private readonly Harmony _harmony = new Harmony("odinplus.TeleportCarts"); private readonly ISerializer _serializer = new SerializerBuilder().Build(); private FileSystemWatcher? _serverYamlWatcher; private FileSystemWatcher? _playerYamlWatcher; private FileSystemWatcher? _configWatcher; private string _serverYamlPath; private string _playerYamlPath; private GameObject? _yamlEditorHost; public static readonly ManualLogSource TeleportCartsLogger = Logger.CreateLogSource("TeleportCarts"); internal static CartServerPolicy ServerPolicy { get; private set; } = new CartServerPolicy(); internal static PlayerCartPreferences PlayerPreferences { get; private set; } = PlayerCartPreferences.CreateDefault(); internal static EffectiveCartPolicy Policy { get; private set; } = EffectiveCartPolicy.Create(ServerPolicy, PlayerPreferences); internal static ManualLogSource Log { get; private set; } = TeleportCartsLogger; internal static bool IsEnabled => _enableMod?.Value ?? false; internal static bool DiagnosticsEnabled => _enableDiagnostics?.Value ?? false; internal static KeyboardShortcut TeleportCartHotkey => (KeyboardShortcut)(((??)_teleportCartHotkey?.Value) ?? new KeyboardShortcut((KeyCode)304, (KeyCode[])(object)new KeyCode[1] { (KeyCode)116 })); internal static bool IsHotkeyConfigured { get { //IL_000b: 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 ConfigEntry<KeyboardShortcut>? teleportCartHotkey = _teleportCartHotkey; if (teleportCartHotkey == null) { return true; } KeyboardShortcut value = teleportCartHotkey.Value; return (int)((KeyboardShortcut)(ref value)).MainKey > 0; } } internal static bool BasementsInstalled { get; private set; } internal static bool IsBlocked { get; private set; } private void Awake() { Log = TeleportCartsLogger; FindIncompatibleMods(); BindConfiguration(); _serverYamlPath = Path.Combine(Paths.ConfigPath, "odinplus.TeleportCarts.server.yml"); _playerYamlPath = Path.Combine(Paths.ConfigPath, "odinplus.TeleportCarts.yml"); Snapshot.ValueChanged += ReadSnapshot; if (Sync.IsSourceOfTruth) { ReloadServerYaml(); SetupServerYamlWatcher(); } ReloadPlayerYaml(); SetupPlayerYamlWatcher(); SetupConfigWatcher(); RegisterYamlEditor(); Localizer.Load(); _harmony.PatchAll(Assembly.GetExecutingAssembly()); } private void Start() { if (!IsBlocked) { BasementsInstalled = Chainloader.PluginInfos.ContainsKey("com.rolopogo.Basement"); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} loaded. enabled={1}, yamlSource={2}.", "TeleportCarts", IsEnabled, Sync.IsSourceOfTruth)); } } private void OnDestroy() { if (!IsBlocked) { _serverYamlWatcher?.Dispose(); _playerYamlWatcher?.Dispose(); _configWatcher?.Dispose(); if ((Object)(object)_yamlEditorHost != (Object)null) { Object.Destroy((Object)(object)_yamlEditorHost); } Snapshot.ValueChanged -= ReadSnapshot; CartTransport.CancelPending(); _harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Config.Save(); } } private void BindConfiguration() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { _forceServerConfig = BindSynced("General", "Force Server Config", Toggle.On, "When On (default), the server's synchronized TeleportCarts settings are enforced for connected clients."); Sync.AddLockingConfigEntry<Toggle>(_forceServerConfig); _enableMod = BindSynced("General", "Enable Mod", defaultValue: true, "Master switch for TeleportCarts cart transport."); _teleportCartHotkey = BindLocal<KeyboardShortcut>("Controls", "Teleport Nearest Cart Hotkey", new KeyboardShortcut((KeyCode)304, (KeyCode[])(object)new KeyCode[1] { (KeyCode)99 }), "Hotkey to teleport the nearest unattached cart when hovering over a portal or dungeon entrance."); YamlEditorButton.Bind(((BaseUnityPlugin)this).Config, "Advanced Configuration", "Teleport Rules"); _enableDiagnostics = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Enable Cart Teleport Diagnostics", false, new ConfigDescription("Writes transition capture, landing position, and reattach details to this machine's log.", (AcceptableValueBase)null, new object[1] { new Odinplus.Shared.YamlEditor.ConfigurationManagerAttributes { IsAdvanced = true } })); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; ((BaseUnityPlugin)this).Config.Save(); } } private ConfigEntry<T> BindSynced<T>(string group, string key, T defaultValue, string description) { ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, key, defaultValue, Describe(description, "Server synchronized")); Sync.AddConfigEntry<T>(val).SynchronizedConfig = true; return val; } private ConfigEntry<T> BindLocal<T>(string group, string key, T defaultValue, string description) { return ((BaseUnityPlugin)this).Config.Bind<T>(group, key, defaultValue, Describe(description, "Local only")); } private static ConfigDescription Describe(string description, string scope) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown return new ConfigDescription(description + " [" + scope + "]", (AcceptableValueBase)null, Array.Empty<object>()); } private void SetupServerYamlWatcher() { _serverYamlWatcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(_serverYamlPath)) { IncludeSubdirectories = false, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime), SynchronizingObject = ThreadingHelper.SynchronizingObject, EnableRaisingEvents = true }; _serverYamlWatcher.Changed += delegate { ReloadServerYaml(); }; _serverYamlWatcher.Created += delegate { ReloadServerYaml(); }; _serverYamlWatcher.Renamed += delegate { ReloadServerYaml(); }; } private void SetupPlayerYamlWatcher() { _playerYamlWatcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(_playerYamlPath)) { IncludeSubdirectories = false, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime), SynchronizingObject = ThreadingHelper.SynchronizingObject, EnableRaisingEvents = true }; _playerYamlWatcher.Changed += delegate { ReloadPlayerYaml(); }; _playerYamlWatcher.Created += delegate { ReloadPlayerYaml(); }; _playerYamlWatcher.Renamed += delegate { ReloadPlayerYaml(); }; } private void SetupConfigWatcher() { _configWatcher = new FileSystemWatcher(Paths.ConfigPath, "odinplus.TeleportCarts.cfg") { IncludeSubdirectories = false, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.CreationTime), SynchronizingObject = ThreadingHelper.SynchronizingObject, EnableRaisingEvents = true }; _configWatcher.Changed += ReloadBepInExConfig; _configWatcher.Created += ReloadBepInExConfig; _configWatcher.Renamed += ReloadBepInExConfig; } private void RegisterYamlEditor() { //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown YamlEditorRegistry.Initialize("odinplus.TeleportCarts", "TeleportCarts"); YamlEditorRegistry.Register(new YamlDocument("server", "Server policy (" + Path.GetFileName(_serverYamlPath) + ")", _serverYamlPath, YamlValidation.FromExceptional(delegate(string text) { CartServerPolicyValidator.Validate(new DeserializerBuilder().Build().Deserialize<CartServerPolicy>(text) ?? throw new InvalidDataException("The document is empty.")); }), () => Sync.IsSourceOfTruth, () => "only the server host can edit the server policy")); YamlEditorRegistry.Register(new YamlDocument("player", "Player preferences (" + Path.GetFileName(_playerYamlPath) + ")", _playerYamlPath, YamlValidation.FromTryPattern((string text) => (!PlayerCartPreferenceSnapshot.TryReplace(PlayerPreferences, text, out PlayerCartPreferences _, out string error)) ? error : null), () => true)); _yamlEditorHost = new GameObject("TeleportCartsYamlEditor"); Object.DontDestroyOnLoad((Object)(object)_yamlEditorHost); _yamlEditorHost.AddComponent<YamlEditorBehaviour>(); } private void ReloadBepInExConfig(object sender, FileSystemEventArgs eventArgs) { if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { return; } try { ((BaseUnityPlugin)this).Config.Reload(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Could not reload odinplus.TeleportCarts.cfg. Check its entries and format. " + ex.Message)); } } private void ReloadServerYaml() { if (!Sync.IsSourceOfTruth) { return; } if (!File.Exists(_serverYamlPath)) { File.WriteAllText(_serverYamlPath, DefaultServerYamlTemplate()); } try { CartServerPolicy cartServerPolicy = new DeserializerBuilder().Build().Deserialize<CartServerPolicy>(File.ReadAllText(_serverYamlPath)); CartServerPolicyValidator.Validate(cartServerPolicy); ServerPolicy = cartServerPolicy; RebuildEffectivePolicy(); Snapshot.AssignLocalValue(_serializer.Serialize(cartServerPolicy)); LogDiagnostic($"Accepted server YAML: enabled={cartServerPolicy.Carts.Enabled}, searchRadius={cartServerPolicy.Carts.SearchRadius:0.##}, forwardOffset={cartServerPolicy.Placement.ForwardOffset:0.##}."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("TeleportCarts retained the previous valid server YAML: " + ex.Message)); } } private void ReloadPlayerYaml() { if (!File.Exists(_playerYamlPath)) { File.WriteAllText(_playerYamlPath, DefaultPlayerYamlTemplate()); } string yaml = File.ReadAllText(_playerYamlPath); if (PlayerCartPreferenceSnapshot.TryReplace(PlayerPreferences, yaml, out PlayerCartPreferences retained, out string error)) { PlayerPreferences = retained; RebuildEffectivePolicy(); LogDiagnostic($"Accepted local player YAML: enabled={retained.Carts.Enabled}."); } else { ((BaseUnityPlugin)this).Logger.LogError((object)("TeleportCarts retained the previous valid local player YAML: " + error)); } } private static void ReadSnapshot() { if (Sync.IsSourceOfTruth || string.IsNullOrWhiteSpace(Snapshot.Value)) { return; } try { CartServerPolicy cartServerPolicy = new DeserializerBuilder().Build().Deserialize<CartServerPolicy>(Snapshot.Value); CartServerPolicyValidator.Validate(cartServerPolicy); ServerPolicy = cartServerPolicy; RebuildEffectivePolicy(); LogDiagnostic($"Accepted server YAML snapshot: enabled={cartServerPolicy.Carts.Enabled}, searchRadius={cartServerPolicy.Carts.SearchRadius:0.##}."); } catch (Exception ex) { Log.LogError((object)("Rejected TeleportCarts server snapshot: " + ex.Message)); } } private static void RebuildEffectivePolicy() { Policy = EffectiveCartPolicy.Create(ServerPolicy, PlayerPreferences); } private void FindIncompatibleMods() { string[] array = new string[2] { "com.kpro.TeleportEverything", "zenox.teleporteverything" }; foreach (string text in array) { if (Chainloader.PluginInfos.TryGetValue(text, out var value)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)($"Incompatible mod detected: {value.Metadata.Name} ({value.Metadata.Version}), GUID: {text}.\n" + "This mod can conflict with TeleportCarts.\nTo avoid issues, please either:\n- Uninstall the detected mod, or\n- Disable the conflicting features in one of the mods.")); } } } internal static void LogDiagnostic(string message) { if (DiagnosticsEnabled) { Log.LogInfo((object)("TeleportCarts: " + message)); } } private static string DefaultServerYamlTemplate() { return "# Server policy. This file is synchronized to clients.\n# The server owns transport availability and placement numbers. A player file can only narrow them.\n# Native ore teleport rules always apply to cart cargo; they are never overridden.\nSchemaVersion: 1\n\nCarts:\n # Disable cart transport for every player.\n Enabled: true\n # Attached-cart detection radius around the teleporting player.\n SearchRadius: 10\n\n Teleport:\n Portals:\n # Disable cart transport through normal and stone portals.\n Enabled: true\n Dungeons:\n # Covers both dungeon entrances and exits.\n Enabled: true\n Basements:\n # Requires the Basements mod on the server.\n Enabled: true\n\nPlacement:\n # Drop the cart this far ahead of the player's arrival point.\n ForwardOffset: 0.8\n # Reattach only when the cart is within this distance of the arrived player.\n ReattachRadius: 15\n"; } private static string DefaultPlayerYamlTemplate() { return "# Local player preference. This file is never synchronized and can only restrict the server policy.\n# Native ore teleport rules always apply to cart cargo; they are never overridden.\nSchemaVersion: 1\n\nCarts:\n # Disable cart transport only for this player.\n Enabled: true\n\n Teleport:\n Portals:\n # Disable cart transport through portals only for this player.\n Enabled: true\n Dungeons:\n # Disable dungeon entrance and exit cart transport only for this player.\n Enabled: true\n Basements:\n # Disable Basements cart transport only for this player.\n Enabled: true\n"; } } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ServerSync { [PublicAPI] internal abstract class OwnConfigEntryBase { public object? LocalBaseValue; public bool SynchronizedConfig = true; public abstract ConfigEntryBase BaseConfig { get; } } [PublicAPI] internal class SyncedConfigEntry<T>(ConfigEntry<T> sourceConfig) : OwnConfigEntryBase() { public readonly ConfigEntry<T> SourceConfig = sourceConfig; public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig; public T Value { get { return SourceConfig.Value; } set { SourceConfig.Value = value; } } public void AssignLocalValue(T value) { if (LocalBaseValue == null) { Value = value; } else { LocalBaseValue = value; } } } internal abstract class CustomSyncedValueBase { public object? LocalBaseValue; public readonly string Identifier; public readonly Type Type; private object? boxedValue; protected bool localIsOwner; public readonly int Priority; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(); } } public event Action? ValueChanged; protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority) { Priority = priority; Identifier = identifier; Type = type; configSync.AddCustomValue(this); localIsOwner = configSync.IsSourceOfTruth; configSync.SourceOfTruthChanged += delegate(bool truth) { localIsOwner = truth; }; } } [PublicAPI] internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; } } public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0) : base(configSync, identifier, typeof(T), priority) { Value = value; } public void AssignLocalValue(T value) { if (localIsOwner) { Value = value; } else { LocalBaseValue = value; } } } internal class ConfigurationManagerAttributes { [UsedImplicitly] public bool? ReadOnly = false; } [PublicAPI] internal class ConfigSync { [HarmonyPatch(typeof(ZRpc), "HandlePackage")] private static class SnatchCurrentlyHandlingRPC { public static ZRpc? currentRpc; [HarmonyPrefix] private static void Prefix(ZRpc __instance) { currentRpc = __instance; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class RegisterRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance) { isServer = __instance.IsServer(); foreach (ConfigSync configSync2 in configSyncs) { ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync); if (isServer) { configSync2.InitialSyncDone = true; Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections")); } } if (isServer) { ((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges()); } static void SendAdmin(List<ZNetPeer> peers, bool isAdmin) { ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1] { new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = isAdmin } }); ConfigSync configSync = configSyncs.First(); if (configSync != null) { ((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package)); } } static IEnumerator WatchAdminListChanges() { MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); List<string> CurrentList = new List<string>(adminList.GetList()); while (true) { yield return (object)new WaitForSeconds(30f); if (!adminList.GetList().SequenceEqual(CurrentList)) { CurrentList = new List<string>(adminList.GetList()); List<ZNetPeer> adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p) { string hostName = p.m_rpc.GetSocket().GetHostName(); return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName })); }).ToList(); List<ZNetPeer> nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList(); SendAdmin(nonAdminPeer, isAdmin: false); SendAdmin(adminPeer, isAdmin: true); } } } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterClientRPCPatch { [HarmonyPostfix] private static void Postfix(ZNet __instance, ZNetPeer peer) { if (__instance.IsServer()) { return; } foreach (ConfigSync configSync in configSyncs) { peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_FromServerConfigSync); } } } private class ParsedConfigs { public readonly Dictionary<OwnConfigEntryBase, object?> configValues = new Dictionary<OwnConfigEntryBase, object>(); public readonly Dictionary<CustomSyncedValueBase, object?> customValues = new Dictionary<CustomSyncedValueBase, object>(); } [HarmonyPatch(typeof(ZNet), "Shutdown")] private class ResetConfigsOnShutdown { [HarmonyPostfix] private static void Postfix() { ProcessingServerUpdate = true; foreach (ConfigSync configSync in configSyncs) { configSync.resetConfigsFromServer(); configSync.IsSourceOfTruth = true; configSync.InitialSyncDone = false; } ProcessingServerUpdate = false; } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private class SendConfigsAfterLogin { private class BufferingSocket : ZPlayFabSocket, ISocket { public volatile bool finished = false; public volatile int versionMatchQueued = -1; public readonly List<ZPackage> Package = new List<ZPackage>(); public readonly ISocket Original; public BufferingSocket(ISocket original) { Original = original; ((ZPlayFabSocket)this)..ctor(); } public bool IsConnected() { return Original.IsConnected(); } public ZPackage Recv() { return Original.Recv(); } public int GetSendQueueSize() { return Original.GetSendQueueSize(); } public int GetCurrentSendRate() { return Original.GetCurrentSendRate(); } public bool IsHost() { return Original.IsHost(); } public void Dispose() { Original.Dispose(); } public bool GotNewData() { return Original.GotNewData(); } public void Close() { Original.Close(); } public string GetEndPointString() { return Original.GetEndPointString(); } public void GetAndResetStats(out int totalSent, out int totalRecv) { Original.GetAndResetStats(ref totalSent, ref totalRecv); } public void GetConnectionQuality(out float localQuality, out float remoteQuality, out int ping, out float outByteSec, out float inByteSec) { Original.GetConnectionQuality(ref localQuality, ref remoteQuality, ref ping, ref outByteSec, ref inByteSec); } public ISocket Accept() { return Original.Accept(); } public int GetHostPort() { return Original.GetHostPort(); } public bool Flush() { return Original.Flush(); } public string GetHostName() { return Original.GetHostName(); } public void VersionMatch() { if (finished) { Original.VersionMatch(); } else { versionMatchQueued = Package.Count; } } public void Send(ZPackage pkg) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown int pos = pkg.GetPos(); pkg.SetPos(0); int num = pkg.ReadInt(); if ((num == StringExtensionMethods.GetStableHashCode("PeerInfo") || num == StringExtensionMethods.GetStableHashCode("RoutedRPC") || num == StringExtensionMethods.GetStableHashCode("ZDOData")) && !finished) { ZPackage val = new ZPackage(pkg.GetArray()); val.SetPos(pos); Package.Add(val); } else { pkg.SetPos(pos); Original.Send(pkg); } } } [HarmonyPriority(800)] [HarmonyPrefix] private static void Prefix(ref Dictionary<Assembly, BufferingSocket>? __state, ZNet __instance, ZRpc rpc) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Invalid comparison between Unknown and I4 if (!__instance.IsServer()) { return; } BufferingSocket bufferingSocket = new BufferingSocket(rpc.GetSocket()); AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket); object? obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (val != null && (int)ZNet.m_onlineBackend > 0) { FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket"); object? value = fieldInfo.GetValue(val); ZPlayFabSocket val2 = (ZPlayFabSocket)((value is ZPlayFabSocket) ? value : null); if (val2 != null) { typeof(ZPlayFabSocket).GetField("m_remotePlayerId").SetValue(bufferingSocket, val2.m_remotePlayerId); } fieldInfo.SetValue(val, bufferingSocket); } if (__state == null) { __state = new Dictionary<Assembly, BufferingSocket>(); } __state[Assembly.GetExecutingAssembly()] = bufferingSocket; } [HarmonyPostfix] private static void Postfix(Dictionary<Assembly, BufferingSocket> __state, ZNet __instance, ZRpc rpc) { ZNetPeer peer; if (__instance.IsServer()) { object obj = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); peer = (ZNetPeer)((obj is ZNetPeer) ? obj : null); if (peer == null) { SendBufferedData(); } else { ((MonoBehaviour)__instance).StartCoroutine(sendAsync()); } } void SendBufferedData() { if (rpc.GetSocket() is BufferingSocket bufferingSocket) { AccessTools.DeclaredField(typeof(ZRpc), "m_socket").SetValue(rpc, bufferingSocket.Original); object? obj2 = AccessTools.DeclaredMethod(typeof(ZNet), "GetPeer", new Type[1] { typeof(ZRpc) }, (Type[])null).Invoke(__instance, new object[1] { rpc }); ZNetPeer val = (ZNetPeer)((obj2 is ZNetPeer) ? obj2 : null); if (val != null) { AccessTools.DeclaredField(typeof(ZNetPeer), "m_socket").SetValue(val, bufferingSocket.Original); } } BufferingSocket bufferingSocket2 = __state[Assembly.GetExecutingAssembly()]; bufferingSocket2.finished = true; for (int i = 0; i < bufferingSocket2.Package.Count; i++) { if (i == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } bufferingSocket2.Original.Send(bufferingSocket2.Package[i]); } if (bufferingSocket2.Package.Count == bufferingSocket2.versionMatchQueued) { bufferingSocket2.Original.VersionMatch(); } } IEnumerator sendAsync() { foreach (ConfigSync configSync in configSyncs) { List<PackageEntry> entries = new List<PackageEntry>(); if (configSync.CurrentVersion != null) { entries.Add(new PackageEntry { section = "Internal", key = "serverversion", type = typeof(string), value = configSync.CurrentVersion }); } MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); entries.Add(new PackageEntry { section = "Internal", key = "lockexempt", type = typeof(bool), value = (((object)listContainsId == null) ? ((object)adminList.Contains(rpc.GetSocket().GetHostName())) : listContainsId.Invoke(ZNet.instance, new object[2] { adminList, rpc.GetSocket().GetHostName() })) }); ZPackage package = ConfigsToPackage(configSync.allConfigs.Select((OwnConfigEntryBase c) => c.BaseConfig), configSync.allCustomValues, entries, partial: false); yield return ((MonoBehaviour)__instance).StartCoroutine(configSync.sendZPackage(new List<ZNetPeer> { peer }, package)); } SendBufferedData(); } } } private class PackageEntry { public string section = null; public string key = null; public Type type = null; public object? value; } [HarmonyPatch(typeof(ConfigEntryBase), "GetSerializedValue")] private static class PreventSavingServerInfo { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, ref string __result) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || isWritableConfig(ownConfigEntryBase)) { return true; } __result = TomlTypeConverter.ConvertToString(ownConfigEntryBase.LocalBaseValue, __instance.SettingType); return false; } } [HarmonyPatch(typeof(ConfigEntryBase), "SetSerializedValue")] private static class PreventConfigRereadChangingValues { [HarmonyPrefix] private static bool Prefix(ConfigEntryBase __instance, string value) { OwnConfigEntryBase ownConfigEntryBase = configData(__instance); if (ownConfigEntryBase == null || ownConfigEntryBase.LocalBaseValue == null) { return true; } try { ownConfigEntryBase.LocalBaseValue = TomlTypeConverter.ConvertToValue(value, __instance.SettingType); } catch (Exception ex) { Debug.LogWarning((object)$"Config value of setting \"{__instance.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } return false; } } private class InvalidDeserializationTypeException : Exception { public string expected = null; public string received = null; public string field = ""; } public static bool ProcessingServerUpdate; public readonly string Name; public string? DisplayName; public string? CurrentVersion; public string? MinimumRequiredVersion; public bool ModRequired = false; private bool? forceConfigLocking; private bool isSourceOfTruth = true; private static readonly HashSet<ConfigSync> configSyncs; private readonly HashSet<OwnConfigEntryBase> allConfigs = new HashSet<OwnConfigEntryBase>(); private HashSet<CustomSyncedValueBase> allCustomValues = new HashSet<CustomSyncedValueBase>(); private static bool isServer; private static bool lockExempt; private OwnConfigEntryBase? lockedConfig = null; private const byte PARTIAL_CONFIGS = 1; private const byte FRAGMENTED_CONFIG = 2; private const byte COMPRESSED_CONFIG = 4; private readonly Dictionary<string, SortedDictionary<int, byte[]>> configValueCache = new Dictionary<string, SortedDictionary<int, byte[]>>(); private readonly List<KeyValuePair<long, string>> cacheExpirations = new List<KeyValuePair<long, string>>(); private static long packageCounter; public bool IsLocked { get { bool? flag = forceConfigLocking; bool num; if (!flag.HasValue) { if (lockedConfig == null) { goto IL_0052; } num = ((IConvertible)lockedConfig.BaseConfig.BoxedValue).ToInt32(CultureInfo.InvariantCulture) != 0; } else { num = flag == true; } if (!num) { goto IL_0052; } int result = ((!lockExempt) ? 1 : 0); goto IL_0053; IL_0052: result = 0; goto IL_0053; IL_0053: return (byte)result != 0; } set { forceConfigLocking = value; } } public bool IsAdmin => lockExempt || isSourceOfTruth; public bool IsSourceOfTruth { get { return isSourceOfTruth; } private set { if (value != isSourceOfTruth) { isSourceOfTruth = value; this.SourceOfTruthChanged?.Invoke(value); } } } public bool InitialSyncDone { get; private set; } = false; public event Action<bool>? SourceOfTruthChanged; private event Action? lockedConfigChanged; static ConfigSync() { ProcessingServerUpdate = false; configSyncs = new HashSet<ConfigSync>(); lockExempt = false; packageCounter = 0L; RuntimeHelpers.RunClassConstructor(typeof(VersionCheck).TypeHandle); } public ConfigSync(string name) { Name = name; configSyncs.Add(this); new VersionCheck(this); } public SyncedConfigEntry<T> AddConfigEntry<T>(ConfigEntry<T> configEntry) { OwnConfigEntryBase ownConfigEntryBase = configData((ConfigEntryBase)(object)configEntry); SyncedConfigEntry<T> syncedEntry = ownConfigEntryBase as SyncedConfigEntry<T>; if (syncedEntry == null) { syncedEntry = new SyncedConfigEntry<T>(configEntry); AccessTools.DeclaredField(typeof(ConfigDescription), "<Tags>k__BackingField").SetValue(((ConfigEntryBase)configEntry).Description, new object[1] { new ConfigurationManagerAttributes() }.Concat(((ConfigEntryBase)configEntry).Description.Tags ?? Array.Empty<object>()).Concat(new SyncedConfigEntry<T>[1] { syncedEntry }).ToArray()); configEntry.SettingChanged += delegate { if (!ProcessingServerUpdate && syncedEntry.SynchronizedConfig) { Broadcast(0L, (ConfigEntryBase)configEntry); } }; allConfigs.Add(syncedEntry); } return syncedEntry; } public SyncedConfigEntry<T> AddLockingConfigEntry<T>(ConfigEntry<T> lockingConfig) where T : IConvertible { if (lockedConfig != null) { throw new Exception("Cannot initialize locking ConfigEntry twice"); } lockedConfig = AddConfigEntry<T>(lockingConfig); lockingConfig.SettingChanged += delegate { this.lockedConfigChanged?.Invoke(); }; return (SyncedConfigEntry<T>)lockedConfig; } internal void AddCustomValue(CustomSyncedValueBase customValue) { if (allCustomValues.Select((CustomSyncedValueBase v) => v.Identifier).Concat(new string[1] { "serverversion" }).Contains(customValue.Identifier)) { throw new Exception("Cannot have multiple settings with the same name or with a reserved name (serverversion)"); } allCustomValues.Add(customValue); allCustomValues = new HashSet<CustomSyncedValueBase>(allCustomValues.OrderByDescending((CustomSyncedValueBase v) => v.Priority)); customValue.ValueChanged += delegate { if (!ProcessingServerUpdate) { Broadcast(0L, customValue); } }; } private void RPC_FromServerConfigSync(ZRpc rpc, ZPackage package) { lockedConfigChanged += serverLockedSettingChanged; IsSourceOfTruth = false; if (HandleConfigSyncRPC(0L, package, clientUpdate: false)) { InitialSyncDone = true; } } private void RPC_FromOtherClientConfigSync(long sender, ZPackage package) { HandleConfigSyncRPC(sender, package, clientUpdate: true); } private bool HandleConfigSyncRPC(long sender, ZPackage package, bool clientUpdate) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown try { if (isServer && IsLocked) { ZRpc? currentRpc = SnatchCurrentlyHandlingRPC.currentRpc; object obj; if (currentRpc == null) { obj = null; } else { ISocket socket = currentRpc.GetSocket(); obj = ((socket != null) ? socket.GetHostName() : null); } string text = (string)obj; if (text != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); SyncedList val = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance); if (!(((object)methodInfo == null) ? val.Contains(text) : ((bool)methodInfo.Invoke(ZNet.instance, new object[2] { val, text })))) { return false; } } } cacheExpirations.RemoveAll(delegate(KeyValuePair<long, string> kv) { if (kv.Key < DateTimeOffset.Now.Ticks) { configValueCache.Remove(kv.Value); return true; } return false; }); byte b = package.ReadByte(); if ((b & 2) != 0) { long num = package.ReadLong(); string text2 = sender.ToString() + num; if (!configValueCache.TryGetValue(text2, out SortedDictionary<int, byte[]> value)) { value = new SortedDictionary<int, byte[]>(); configValueCache[text2] = value; cacheExpirations.Add(new KeyValuePair<long, string>(DateTimeOffset.Now.AddSeconds(60.0).Ticks, text2)); } int key = package.ReadInt(); int num2 = packa