using System; using System.Buffers; using System.Buffers.Binary; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using DG.Tweening; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Steamworks; using Steamworks.Data; using TMPro; using Unity.VisualScripting; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; using UnityEngine.UI; using WKMPMod.Asset; using WKMPMod.Component; using WKMPMod.Core; using WKMPMod.Data; using WKMPMod.MK_Component; using WKMPMod.NetWork; using WKMPMod.Patch; using WKMPMod.RemotePlayer; using WKMPMod.UI; using WKMPMod.Util; using WKMPMod.World; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WKMultiPlayerMod")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.7.3.0")] [assembly: AssemblyInformationalVersion("1.7.3+b98d64200e307c3673484e728fc0fe1c77208fc9")] [assembly: AssemblyProduct("WKMultiPlayerMod")] [assembly: AssemblyTitle("WKMultiPlayerMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.7.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } public abstract class Singleton where T : Singleton { private static readonly Lazy _instance = new Lazy(CreateInstance, isThreadSafe: true); public static T Instance => _instance.Value; public static bool IsCreated => _instance.IsValueCreated; private static T CreateInstance() { try { ConstructorInfo constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (constructor == null) { throw new InvalidOperationException("Singleton<" + typeof(T).Name + ">: Type must have a parameterless constructor"); } return (T)constructor.Invoke(null); } catch (Exception innerException) { throw new InvalidOperationException("Singleton<" + typeof(T).Name + ">: Failed to create instance", innerException); } } protected Singleton() { if (_instance.IsValueCreated) { throw new InvalidOperationException("Singleton<" + typeof(T).Name + ">: Instance already exists. Use " + typeof(T).Name + ".Instance instead."); } } } namespace WKMPMod.World { public enum PitonSyncAction : byte { Create, Update, Remove } public static class ClimbableItemSyncManager { private const float PeriodicUpdateInterval = 0.15f; private const float PositionEpsilonSqr = 0.0004f; private const float RotationEpsilon = 0.5f; private const float SecureAmountEpsilon = 0.01f; private static List CapturedPitons = new List(); private static readonly FieldRef _projectileSourceEntityField = AccessTools.FieldRefAccess("sourceEntity"); private static readonly Dictionary _pitons = new Dictionary(); private static ulong _nextLocalId = 1uL; public static bool ApplyingRemoteState { get; private set; } public static void SaveCapturedPiton(GameObject go) { if ((Object)(object)go != (Object)null && (Object)(object)go.GetComponentInChildren(true) != (Object)null) { CapturedPitons.Add(go); } } public static void RegisterNewLocalPiton(HandItem_Piton source) { if (!MPCore.CanSync || ApplyingRemoteState || (Object)(object)source == (Object)null || CapturedPitons.Count == 0) { CapturedPitons.Clear(); return; } foreach (GameObject capturedPiton in CapturedPitons) { GameObject climbableRoot = GetClimbableRoot(capturedPiton); if (!((Object)(object)climbableRoot == (Object)null)) { RegisterLocalClimbable(climbableRoot, ((Object)source.pitonWorldObject).name); } } CapturedPitons.Clear(); } public static void RegisterNewLocalProjectileClimbable(Projectile source, RaycastHit hit) { if (!MPCore.CanSync || ApplyingRemoteState || (Object)(object)source == (Object)null || !IsLocalProjectile(source)) { CapturedPitons.Clear(); return; } foreach (GameObject capturedPiton in CapturedPitons) { GameObject climbableRoot = GetClimbableRoot(capturedPiton); if (!((Object)(object)climbableRoot == (Object)null)) { RegisterLocalClimbable(climbableRoot, ((Object)(object)climbableRoot != (Object)null) ? ((Object)climbableRoot).name : ((Object)((Component)source).gameObject).name); } } CapturedPitons.Clear(); } public static void BroadcastHammerUpdate(CL_Handhold handhold) { if (MPCore.CanSync && !ApplyingRemoteState && !((Object)(object)handhold == (Object)null)) { NetworkedClimableItem networkedClimableItem = FindIdentity(handhold); if (!((Object)(object)networkedClimableItem == (Object)null) && !string.IsNullOrEmpty(networkedClimableItem.NetworkId)) { Broadcast(networkedClimableItem, PitonSyncAction.Update, force: true); } } } public static void BroadcastPeriodicUpdate(CL_Handhold handhold) { if (!MPCore.CanSync || ApplyingRemoteState || (Object)(object)handhold == (Object)null) { return; } NetworkedClimableItem networkedClimableItem = FindIdentity(handhold); if ((Object)(object)networkedClimableItem == (Object)null || string.IsNullOrEmpty(networkedClimableItem.NetworkId)) { return; } if (!((Component)networkedClimableItem).gameObject.activeSelf) { Broadcast(networkedClimableItem, PitonSyncAction.Remove, force: true); } else if (!(Time.time - networkedClimableItem.LastSentTime < 0.15f)) { CL_Handhold trackedHandhold = GetTrackedHandhold(((Component)networkedClimableItem).gameObject); if (HasMeaningfulStateChange(networkedClimableItem, trackedHandhold)) { Broadcast(networkedClimableItem, PitonSyncAction.Update, force: false); } } } public static void HandlePitonState(ulong senderId, DataReader reader) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) PitonSyncAction pitonSyncAction = (PitonSyncAction)reader.GetByte(); string text = reader.GetString(); string prefabKey = reader.GetString(); Vector3 vector = reader.GetVector3(); Quaternion quaternion = reader.GetQuaternion(); float secureAmount = reader.GetFloat(); bool secure = reader.GetBool(); bool active = reader.GetBool(); if (string.IsNullOrEmpty(text)) { return; } ApplyingRemoteState = true; try { switch (pitonSyncAction) { case PitonSyncAction.Create: ApplyCreate(senderId, text, prefabKey, vector, quaternion, secureAmount, secure, active); break; case PitonSyncAction.Update: ApplyUpdate(text, vector, quaternion, secureAmount, secure, active); break; case PitonSyncAction.Remove: ApplyRemove(text); break; } } catch (Exception ex) { MPMain.LogError($"[MP ClimbableSync] Failed to apply {pitonSyncAction} for {text}: {ex.Message}"); } finally { ApplyingRemoteState = false; } } private static void RegisterLocalClimbable(GameObject root, string prefabKey) { if ((Object)(object)root == (Object)null) { return; } string text = MPUtil.CleanCloneName(prefabKey); if (!string.IsNullOrEmpty(text)) { NetworkedClimableItem orCreateIdentity = GetOrCreateIdentity(root); if (string.IsNullOrEmpty(orCreateIdentity.NetworkId)) { orCreateIdentity.NetworkId = $"{MPSteamworks.UserSteamId}:{_nextLocalId++}"; orCreateIdentity.OwnerId = MPSteamworks.UserSteamId; orCreateIdentity.IsRemote = false; } orCreateIdentity.PrefabKey = text; _pitons[orCreateIdentity.NetworkId] = orCreateIdentity; Broadcast(orCreateIdentity, PitonSyncAction.Create, force: true); } } private static void ApplyCreate(ulong senderId, string networkId, string prefabKey, Vector3 position, Quaternion rotation, float secureAmount, bool secure, bool active) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) if (_pitons.TryGetValue(networkId, out NetworkedClimableItem value) && (Object)(object)value != (Object)null) { value.PrefabKey = MPUtil.CleanCloneName(prefabKey); ApplyState(value, position, rotation, secureAmount, secure, active); return; } GameObject handholdPrefab = MPAssetManager.GetHandholdPrefab(prefabKey); if ((Object)(object)handholdPrefab == (Object)null) { MPMain.LogError("[MP ClimbableSync] Could not resolve prefab '" + prefabKey + "' for " + networkId + "."); return; } GameObject val = Object.Instantiate(handholdPrefab, position, rotation); Transform currentLevelParentRoot = WorldLoader.GetCurrentLevelParentRoot(); if ((Object)(object)currentLevelParentRoot != (Object)null) { val.transform.SetParent(currentLevelParentRoot); } TryAddPlacedObjectToLevel(val); NetworkedClimableItem orCreateIdentity = GetOrCreateIdentity(val); orCreateIdentity.NetworkId = networkId; orCreateIdentity.PrefabKey = MPUtil.CleanCloneName(prefabKey); orCreateIdentity.OwnerId = senderId; orCreateIdentity.IsRemote = true; _pitons[networkId] = orCreateIdentity; ApplyState(orCreateIdentity, position, rotation, secureAmount, secure, active); } private static void ApplyUpdate(string networkId, Vector3 position, Quaternion rotation, float secureAmount, bool secure, bool active) { //IL_0021: 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 (_pitons.TryGetValue(networkId, out NetworkedClimableItem value) && !((Object)(object)value == (Object)null)) { ApplyState(value, position, rotation, secureAmount, secure, active); } } private static void ApplyRemove(string networkId) { if (_pitons.TryGetValue(networkId, out NetworkedClimableItem value) && !((Object)(object)value == (Object)null)) { if ((Object)(object)((Component)value).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } _pitons.Remove(networkId); } } private static void ApplyState(NetworkedClimableItem identity, Vector3 position, Quaternion rotation, float secureAmount, bool secure, bool active) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)identity).transform; transform.position = position; transform.rotation = rotation; CL_Handhold trackedHandhold = GetTrackedHandhold(((Component)identity).gameObject); if ((Object)(object)trackedHandhold != (Object)null) { trackedHandhold.Initialize(); trackedHandhold.secureAmount = secureAmount; trackedHandhold.secure = secure; } ((Component)identity).gameObject.SetActive(active); RecordState(identity, trackedHandhold); } private static GameObject GetClimbableRoot(GameObject obj) { if ((Object)(object)obj == (Object)null) { return null; } Transform val = (WorldLoader.initialized ? WorldLoader.GetCurrentLevelParentRoot() : null); Transform val2 = obj.transform; while ((Object)(object)val2.parent != (Object)null && (Object)(object)val2.parent != (Object)(object)val) { val2 = val2.parent; } return ((Component)val2).gameObject; } private static CL_Handhold GetTrackedHandhold(GameObject root) { if ((Object)(object)root == (Object)null) { return null; } return root.GetComponent() ?? root.GetComponentInChildren(true); } private static NetworkedClimableItem FindIdentity(CL_Handhold handhold) { return ((Component)handhold).GetComponent() ?? ((Component)handhold).GetComponentInParent(); } private static bool IsLocalProjectile(Projectile projectile) { ENT_Player player = ENT_Player.GetPlayer(); if ((Object)(object)projectile == (Object)null || (Object)(object)player == (Object)null || _projectileSourceEntityField == null) { return false; } GameEntity val = _projectileSourceEntityField.Invoke(projectile); return (Object)(object)val == (Object)(object)player; } private static void TryAddPlacedObjectToLevel(GameObject climbableObject) { if (!WorldLoader.initialized || (Object)(object)climbableObject == (Object)null) { return; } try { M_Level level = WorldLoader.instance.GetCurrentLevel().GetLevel(); typeof(M_Level).GetMethod("AddPlacedObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(level, new object[1] { climbableObject }); } catch (Exception ex) { MPMain.LogWarning("[MP ClimbableSync] Could not register remote climbable as placed object: " + ex.Message); } } private static NetworkedClimableItem GetOrCreateIdentity(GameObject obj) { NetworkedClimableItem networkedClimableItem = obj.GetComponent(); if ((Object)(object)networkedClimableItem == (Object)null) { networkedClimableItem = obj.AddComponent(); } return networkedClimableItem; } private static void Broadcast(NetworkedClimableItem identity, PitonSyncAction action, bool force) { //IL_006e: 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_0122: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)identity == (Object)null || string.IsNullOrEmpty(identity.NetworkId)) { return; } CL_Handhold trackedHandhold = GetTrackedHandhold(((Component)identity).gameObject); DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, 0uL, PacketType.PitonStateSync); writer.Put((byte)action); writer.Put(identity.NetworkId); writer.Put(identity.PrefabKey ?? string.Empty); writer.Put(((Component)identity).transform.position); writer.Put(((Component)identity).transform.rotation); writer.Put(((Object)(object)trackedHandhold != (Object)null) ? trackedHandhold.secureAmount : 0f); writer.Put((Object)(object)trackedHandhold != (Object)null && trackedHandhold.secure); writer.Put(((Component)identity).gameObject.activeSelf); if (action == PitonSyncAction.Create || action == PitonSyncAction.Remove) { MonoSingleton.Instance.Broadcast(writer, (SendType)8, 0); } else if (action == PitonSyncAction.Update) { foreach (ulong item in MonoSingleton.Instance._nearPlayersBuffer) { MonoSingleton.Instance.SendToPeer(SteamId.op_Implicit(item), writer, (SendType)0, 0); } } RecordState(identity, trackedHandhold); if (force) { MPMain.LogInfo($"[MP ClimbableSync] Sent {action} for {identity.NetworkId} ({identity.PrefabKey})"); } } private static bool HasMeaningfulStateChange(NetworkedClimableItem identity, CL_Handhold handhold) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //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_0050: 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) if (identity.LastActive != ((Component)identity).gameObject.activeSelf) { return true; } Vector3 val = identity.LastPosition - ((Component)identity).transform.position; if (((Vector3)(ref val)).sqrMagnitude > 0.0004f) { return true; } if (Quaternion.Angle(identity.LastRotation, ((Component)identity).transform.rotation) > 0.5f) { return true; } if ((Object)(object)handhold == (Object)null) { return false; } if (Mathf.Abs(identity.LastSecureAmount - handhold.secureAmount) > 0.01f) { return true; } return identity.LastSecure != handhold.secure; } private static void RecordState(NetworkedClimableItem identity, CL_Handhold handhold) { //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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) identity.LastSentTime = Time.time; identity.LastPosition = ((Component)identity).transform.position; identity.LastRotation = ((Component)identity).transform.rotation; identity.LastActive = ((Component)identity).gameObject.activeSelf; if ((Object)(object)handhold != (Object)null) { identity.LastSecureAmount = handhold.secureAmount; identity.LastSecure = handhold.secure; } } } public enum ItemSyncAction : byte { SnapshotReset, SnapshotFinalize, Create, PickupRequest, DropRequest, Remove } public static class ItemSyncManager { private sealed class PendingLocalDrop { public Item_Object ItemObject { get; } public string PrefabKey { get; } public float CreatedAt { get; } public PendingLocalDrop(Item_Object itemObject, string prefabKey, float createdAt) { ItemObject = itemObject; PrefabKey = prefabKey; CreatedAt = createdAt; } } private const float CandidateMatchDistanceSqr = 0.5f; private const float LocalDropMatchDistanceSqr = 25f; private const float LocalDropMaxAge = 3f; private const float LocalDropPickupSuppressWindow = 0.2f; private const float VelocityEpsilonSqr = 0.0025f; private const float WorldReadyTimeout = 12f; private const float StableIdPositionPrecision = 20f; private const float StableIdRotationPrecision = 5f; private const int SnapshotItemsPerFrame = 10; private static readonly FieldRef _dropObjectField = AccessTools.FieldRefAccess("dropObject"); private static readonly Dictionary _items = new Dictionary(); private static readonly List _clientCandidates = new List(); private static readonly List _pendingLocalDrops = new List(); private static readonly List _snapshotCandidates = new List(); private static readonly Dictionary _snapshotRoutines = new Dictionary(); private static readonly Dictionary _suppressedPickupObjects = new Dictionary(); private static ulong _nextHostItemId = 1uL; private static Coroutine _prepareRoutine; private static Coroutine _hostDiscoveryRoutine; private static bool _hostSceneItemsRegistered; private static readonly HashSet _blacklistedPrefabNames = new HashSet(StringComparer.OrdinalIgnoreCase) { "Item_Flashlight", "Item_Flaregun", "Item_Cryogun" }; private static readonly HashSet _blacklistedItemTags = new HashSet(StringComparer.OrdinalIgnoreCase) { "artifact", "disk" }; public static bool ApplyingRemoteState { get; private set; } public static void NotifyWorldInitialized() { ResetState(); if ((Object)(object)MonoSingleton.Instance == (Object)null) { MPMain.LogWarning("[MP ItemSync] Prepare skipped: MPCore.Instance is null."); } else { _prepareRoutine = ((MonoBehaviour)MonoSingleton.Instance).StartCoroutine(PrepareWorldRoutine()); } } public static void ResetState() { if (_prepareRoutine != null && (Object)(object)MonoSingleton.Instance != (Object)null) { ((MonoBehaviour)MonoSingleton.Instance).StopCoroutine(_prepareRoutine); _prepareRoutine = null; } if (_hostDiscoveryRoutine != null && (Object)(object)MonoSingleton.Instance != (Object)null) { ((MonoBehaviour)MonoSingleton.Instance).StopCoroutine(_hostDiscoveryRoutine); _hostDiscoveryRoutine = null; } if ((Object)(object)MonoSingleton.Instance != (Object)null) { foreach (Coroutine value in _snapshotRoutines.Values) { if (value != null) { ((MonoBehaviour)MonoSingleton.Instance).StopCoroutine(value); } } } _snapshotRoutines.Clear(); foreach (NetworkedItem value2 in _items.Values) { if (!((Object)(object)value2 == (Object)null) && !((Object)(object)((Component)value2).gameObject == (Object)null) && value2.WasInstantiatedBySync) { Object.Destroy((Object)(object)((Component)value2).gameObject); } } _items.Clear(); _clientCandidates.Clear(); _pendingLocalDrops.Clear(); _snapshotCandidates.Clear(); _suppressedPickupObjects.Clear(); _hostSceneItemsRegistered = false; ApplyingRemoteState = false; MPMain.LogInfo("[MP ItemSync] ResetState completed."); } public static void SendSnapshotToClient(ulong clientId) { if (MPCore.CanSync && MonoSingleton.Instance.IsHost && !((Object)(object)MonoSingleton.Instance == (Object)null) && clientId != 0L && clientId != MPSteamworks.UserSteamId) { if (_snapshotRoutines.TryGetValue(clientId, out Coroutine value) && value != null) { ((MonoBehaviour)MonoSingleton.Instance).StopCoroutine(value); } _snapshotRoutines[clientId] = ((MonoBehaviour)MonoSingleton.Instance).StartCoroutine(SendSnapshotToClientRoutine(clientId)); } } public static void NotifyLocalPickup(Item_Object itemObject) { if (ApplyingRemoteState || (Object)(object)itemObject == (Object)null || !MPCore.CanSync) { return; } if (IsPendingLocalDrop(itemObject)) { MPMain.LogInfo("[MP ItemSync] Suppressed pickup for pending local drop. Item=" + ((Object)itemObject).name); } else { if (ShouldSuppressLocalPickup(itemObject)) { return; } NetworkedItem component = ((Component)itemObject).GetComponent(); if ((Object)(object)component == (Object)null || string.IsNullOrEmpty(component.NetworkId)) { MPMain.LogWarning("[MP ItemSync] Pickup ignored: missing NetworkedItem or NetworkId. Item=" + ((Object)itemObject).name + ", PrefabKey=" + GetPrefabKey(itemObject) + ", " + $"HasIdentity={(Object)(object)component != (Object)null}"); return; } MPMain.LogInfo("[MP ItemSync] Local pickup. " + $"Host={MonoSingleton.Instance.IsHost}, " + "Item=" + ((Object)itemObject).name + ", NetworkId=" + component.NetworkId + ", PrefabKey=" + component.PrefabKey); if (MonoSingleton.Instance.IsHost) { BroadcastRemove(component.NetworkId); Forget(component.NetworkId); } else { SendPickupRequest(component.NetworkId); } } } public static void NotifyLocalDrop(Item item) { //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) if (ApplyingRemoteState || item == null || !MPCore.CanSync) { return; } Item_Object val = _dropObjectField.Invoke(item); if (!IsSyncableWorldItem(val)) { return; } string prefabKey = GetPrefabKey(val); if (!string.IsNullOrEmpty(prefabKey)) { if (MonoSingleton.Instance.IsHost) { NetworkedItem networkedItem = RegisterHostItem(val, prefabKey); Vector3 velocity = GetVelocity(val); RememberSuppressedPickup(val); MPMain.LogInfo("[MP ItemSync] Host local drop. Item=" + ((Object)val).name + ", NetworkId=" + networkedItem.NetworkId + ", PrefabKey=" + prefabKey); BroadcastCreate(networkedItem, val, velocity, isDropSpawn: true); } else { RememberCandidate(val, snapshotCandidate: false); RememberPendingLocalDrop(val, prefabKey); RememberSuppressedPickup(val); MPMain.LogInfo("[MP ItemSync] Client local drop request. Item=" + ((Object)val).name + ", PrefabKey=" + prefabKey); SendDropRequest(prefabKey, ((Component)val).transform.position, ((Component)val).transform.rotation, GetVelocity(val)); } } } public static void SpawnSyncedWorldDrop(string prefabKey, Vector3 position, Quaternion rotation, Vector3 velocity) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0088: 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) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if (ApplyingRemoteState || !MPCore.CanSync || string.IsNullOrWhiteSpace(prefabKey)) { return; } if (!MonoSingleton.Instance.IsHost) { SendDropRequest(prefabKey, position, rotation, velocity); return; } Item_Object val = InstantiateWorldItem(prefabKey, position, rotation); if ((Object)(object)val == (Object)null) { MPMain.LogWarning("[MP ItemSync] Could not instantiate synced drop '" + prefabKey + "'."); return; } NetworkedItem identity = RegisterHostItem(val, prefabKey); RememberSuppressedPickup(val); ApplyCreate(identity, position, rotation, velocity, isDropSpawn: true, skipDropCallbacks: false); BroadcastCreate(identity, val, velocity, isDropSpawn: true); } public static void HandleItemState(ulong senderId, DataReader reader) { ItemSyncAction itemSyncAction = (ItemSyncAction)reader.GetByte(); try { switch (itemSyncAction) { case ItemSyncAction.SnapshotReset: HandleSnapshotReset(); break; case ItemSyncAction.SnapshotFinalize: HandleSnapshotFinalize(); break; case ItemSyncAction.Create: HandleCreate(senderId, reader); break; case ItemSyncAction.PickupRequest: HandlePickupRequest(reader); break; case ItemSyncAction.DropRequest: HandleDropRequest(senderId, reader); break; case ItemSyncAction.Remove: HandleRemove(reader); break; } } catch (Exception ex) { MPMain.LogError($"[MP ItemSync] Failed to apply {itemSyncAction}: {ex.Message}"); } } private static IEnumerator PrepareWorldRoutine() { MPMain.LogInfo("[MP ItemSync] PrepareWorldRoutine started. " + $"IsHost={MonoSingleton.Instance.IsHost}, IsInLobby={MPCore.IsInLobby}, CanSync={MPCore.CanSync}, " + $"WorldInitialized={WorldLoader.initialized}, WorldLoaded={WorldLoader.isLoaded}"); yield return WaitForWorldReady(); yield return null; yield return null; if (MonoSingleton.Instance.IsHost) { yield return RegisterHostSceneItemsRoutine(); if ((Object)(object)MonoSingleton.Instance != (Object)null && _hostDiscoveryRoutine == null) { _hostDiscoveryRoutine = ((MonoBehaviour)MonoSingleton.Instance).StartCoroutine(HostDiscoveryRoutine()); } } else if (MPCore.IsInLobby) { CaptureSceneCandidates(snapshotCandidate: true); MPMain.LogInfo("[MP ItemSync] Captured client scene candidates. " + $"Candidates={_clientCandidates.Count}, SnapshotCandidates={_snapshotCandidates.Count}"); } else { MPMain.LogInfo("[MP ItemSync] Prepare skipped candidate capture because client is not in lobby."); } _prepareRoutine = null; MPMain.LogInfo("[MP ItemSync] PrepareWorldRoutine finished. " + $"Items={_items.Count}, HostSceneItemsRegistered={_hostSceneItemsRegistered}"); } private static IEnumerator SendSnapshotToClientRoutine(ulong clientId) { yield return WaitForWorldReady(); if (!_hostSceneItemsRegistered) { yield return RegisterHostSceneItemsRoutine(); } SendSnapshotReset(clientId); List snapshot = new List(EnumerateKnownHostWorldItems()); int sentThisFrame = 0; foreach (NetworkedItem identity in snapshot) { if ((Object)(object)identity == (Object)null || (Object)(object)((Component)identity).gameObject == (Object)null) { continue; } Item_Object itemObject = ((Component)identity).GetComponent(); if (IsSyncableWorldItem(itemObject)) { SendCreate(clientId, identity, itemObject, GetVelocity(itemObject), isDropSpawn: false); sentThisFrame++; if (sentThisFrame >= 10) { sentThisFrame = 0; yield return null; } } } SendSnapshotFinalize(clientId); _snapshotRoutines.Remove(clientId); MPMain.LogInfo($"[MP ItemSync] Sent item snapshot to {clientId}. Items={snapshot.Count}"); } private static IEnumerator WaitForWorldReady() { float elapsed = 0f; while ((!WorldLoader.initialized || !WorldLoader.isLoaded) && elapsed < 12f) { elapsed += Time.unscaledDeltaTime; yield return null; } MPMain.LogInfo("[MP ItemSync] World ready wait finished. " + $"Elapsed={elapsed:F2}, Initialized={WorldLoader.initialized}, Loaded={WorldLoader.isLoaded}"); } private static IEnumerator RegisterHostSceneItemsRoutine() { if (!MonoSingleton.Instance.IsHost) { yield break; } int registeredThisFrame = 0; int totalRegistered = 0; foreach (Item_Object itemObject in EnumerateSceneItems()) { string prefabKey = GetPrefabKey(itemObject); if (!string.IsNullOrEmpty(prefabKey)) { RegisterHostItem(itemObject, prefabKey, preferStableSceneIdentity: true); totalRegistered++; registeredThisFrame++; if (registeredThisFrame >= 10) { registeredThisFrame = 0; yield return null; } } } _hostSceneItemsRegistered = true; MPMain.LogInfo($"[MP ItemSync] Registered host scene items. Items={totalRegistered}"); } private static IEnumerator HostDiscoveryRoutine() { WaitForSecondsRealtime wait = new WaitForSecondsRealtime(0.5f); while ((Object)(object)MonoSingleton.Instance != (Object)null) { if (MPCore.CanSync && MonoSingleton.Instance.IsHost && _hostSceneItemsRegistered) { DiscoverAndBroadcastNewHostWorldItems(); } yield return wait; } _hostDiscoveryRoutine = null; } private static void DiscoverAndBroadcastNewHostWorldItems() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) foreach (Item_Object item in EnumerateSceneItems()) { if (TryPrepareNewHostWorldItem(item, out string prefabKey)) { NetworkedItem networkedItem = RegisterHostItem(item, prefabKey, preferStableSceneIdentity: true); RememberSuppressedPickup(item); MPMain.LogInfo("[MP ItemSync] Registered dynamic host world item. Item=" + ((Object)item).name + ", NetworkId=" + networkedItem.NetworkId + ", PrefabKey=" + prefabKey); BroadcastCreate(networkedItem, item, GetVelocity(item), isDropSpawn: false); } } } private static bool TryPrepareNewHostWorldItem(Item_Object itemObject, out string prefabKey) { prefabKey = string.Empty; if (!IsSyncableWorldItem(itemObject)) { return false; } prefabKey = GetPrefabKey(itemObject); if (string.IsNullOrEmpty(prefabKey)) { return false; } NetworkedItem component = ((Component)itemObject).GetComponent(); if ((Object)(object)component == (Object)null) { return true; } if (string.IsNullOrEmpty(component.NetworkId)) { return true; } return !_items.ContainsKey(component.NetworkId); } private static IEnumerable EnumerateKnownHostWorldItems() { foreach (NetworkedItem identity in _items.Values) { if (!((Object)(object)identity == (Object)null) && !((Object)(object)((Component)identity).gameObject == (Object)null)) { Item_Object itemObject = ((Component)identity).GetComponent(); if (IsSyncableWorldItem(itemObject)) { yield return identity; } } } } private static NetworkedItem RegisterHostItem(Item_Object itemObject, string prefabKey, bool preferStableSceneIdentity = false) { NetworkedItem orCreateIdentity = GetOrCreateIdentity(((Component)itemObject).gameObject); if (preferStableSceneIdentity) { TryAssignStableSceneIdentity(itemObject, orCreateIdentity); } if (string.IsNullOrEmpty(orCreateIdentity.NetworkId)) { orCreateIdentity.NetworkId = $"{MPSteamworks.UserSteamId}:item:{_nextHostItemId++}"; } orCreateIdentity.PrefabKey = prefabKey; orCreateIdentity.OwnerId = MPSteamworks.UserSteamId; orCreateIdentity.IsRemote = false; orCreateIdentity.WasInstantiatedBySync = false; if (_items.TryGetValue(orCreateIdentity.NetworkId, out NetworkedItem value) && (Object)(object)value != (Object)null && (Object)(object)value != (Object)(object)orCreateIdentity) { MPMain.LogWarning("[MP ItemSync] Duplicate NetworkId detected. NetworkId=" + orCreateIdentity.NetworkId + ", Existing=" + ((Object)value).name + ", New=" + ((Object)itemObject).name); } _items[orCreateIdentity.NetworkId] = orCreateIdentity; return orCreateIdentity; } private static void HandleSnapshotReset() { if (!MonoSingleton.Instance.IsHost) { MPMain.LogInfo("[MP ItemSync] Received snapshot reset."); ResetState(); CaptureSceneCandidates(snapshotCandidate: true); } } private static void HandleSnapshotFinalize() { if (MonoSingleton.Instance.IsHost) { return; } int num = 0; for (int num2 = _snapshotCandidates.Count - 1; num2 >= 0; num2--) { Item_Object val = _snapshotCandidates[num2]; if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null)) { NetworkedItem component = ((Component)val).GetComponent(); if (!((Object)(object)component != (Object)null) || string.IsNullOrEmpty(component.NetworkId) || !_items.ContainsKey(component.NetworkId)) { ((Component)val).gameObject.SetActive(false); num++; } } } _snapshotCandidates.Clear(); MPMain.LogInfo($"[MP ItemSync] Snapshot finalized. Hidden unmatched scene items={num}."); } private static void HandleCreate(ulong senderId, DataReader reader) { //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0255: 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) if (MonoSingleton.Instance.IsHost) { return; } string text = reader.GetString(); string text2 = reader.GetString(); Vector3 vector = reader.GetVector3(); Quaternion quaternion = reader.GetQuaternion(); Vector3 vector2 = reader.GetVector3(); bool flag = reader.GetBool(); if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2)) { return; } if (_items.TryGetValue(text, out NetworkedItem value) && (Object)(object)value != (Object)null) { ApplyCreate(value, vector, quaternion, vector2, flag, skipDropCallbacks: true); return; } Item_Object val = (flag ? FindPendingLocalDrop(text2, vector) : null); bool wasSnapshotCandidate = false; if ((Object)(object)val == (Object)null) { val = FindSceneItemByStableId(text); if ((Object)(object)val != (Object)null) { wasSnapshotCandidate = _snapshotCandidates.Contains(val); RemoveCandidate(val); } } if ((Object)(object)val == (Object)null) { val = FindClientCandidate(text2, vector, out wasSnapshotCandidate); } Item_Object val2 = val; bool wasInstantiatedBySync = false; if ((Object)(object)val2 == (Object)null) { val2 = InstantiateWorldItem(text2, vector, quaternion); wasInstantiatedBySync = (Object)(object)val2 != (Object)null; } if ((Object)(object)val2 == (Object)null) { MPMain.LogWarning("[MP ItemSync] Could not create item '" + text2 + "' for " + text + "."); return; } NetworkedItem orCreateIdentity = GetOrCreateIdentity(((Component)val2).gameObject); if (text.StartsWith("sceneitem:", StringComparison.Ordinal)) { orCreateIdentity.StableSceneId = text; } orCreateIdentity.NetworkId = text; orCreateIdentity.PrefabKey = text2; orCreateIdentity.OwnerId = senderId; orCreateIdentity.IsRemote = true; orCreateIdentity.WasInstantiatedBySync = wasInstantiatedBySync; if (_items.TryGetValue(text, out NetworkedItem value2) && (Object)(object)value2 != (Object)null && (Object)(object)value2 != (Object)(object)orCreateIdentity) { MPMain.LogWarning("[MP ItemSync] Duplicate client NetworkId detected on create. NetworkId=" + text + ", Existing=" + ((Object)value2).name + ", New=" + ((Object)val2).name); } _items[text] = orCreateIdentity; bool skipDropCallbacks = !wasSnapshotCandidate && (Object)(object)val != (Object)null && flag; ApplyCreate(orCreateIdentity, vector, quaternion, vector2, flag, skipDropCallbacks); } private static void HandlePickupRequest(DataReader reader) { if (!MonoSingleton.Instance.IsHost) { return; } string text = reader.GetString(); MPMain.LogInfo("[MP ItemSync] Host received pickup request. NetworkId=" + text); if (!string.IsNullOrEmpty(text)) { if (!_items.ContainsKey(text)) { MPMain.LogWarning("[MP ItemSync] Pickup request ignored. Unknown NetworkId=" + text); return; } BroadcastRemove(text); Forget(text); } } private static void HandleDropRequest(ulong senderId, DataReader reader) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if (!MonoSingleton.Instance.IsHost) { return; } string text = reader.GetString(); Vector3 vector = reader.GetVector3(); Quaternion quaternion = reader.GetQuaternion(); Vector3 vector2 = reader.GetVector3(); if (!string.IsNullOrEmpty(text)) { Item_Object val = InstantiateWorldItem(text, vector, quaternion); if ((Object)(object)val == (Object)null) { MPMain.LogWarning("[MP ItemSync] Could not instantiate dropped item '" + text + "'."); return; } NetworkedItem networkedItem = RegisterHostItem(val, text); networkedItem.OwnerId = senderId; MPMain.LogInfo("[MP ItemSync] Host accepted drop request. " + $"Sender={senderId}, Item={((Object)val).name}, NetworkId={networkedItem.NetworkId}, PrefabKey={text}"); ApplyCreate(networkedItem, vector, quaternion, vector2, isDropSpawn: true, skipDropCallbacks: false); BroadcastCreate(networkedItem, val, vector2, isDropSpawn: true); } } private static void HandleRemove(DataReader reader) { string text = reader.GetString(); MPMain.LogInfo("[MP ItemSync] Received remove. NetworkId=" + text); if (!string.IsNullOrEmpty(text)) { Forget(text); } } private static void ApplyCreate(NetworkedItem identity, Vector3 position, Quaternion rotation, Vector3 velocity, bool isDropSpawn, bool skipDropCallbacks) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)identity == (Object)null || (Object)(object)((Component)identity).gameObject == (Object)null) { return; } ApplyingRemoteState = true; try { ((Component)identity).transform.position = position; ((Component)identity).transform.rotation = rotation; Item_Object component = ((Component)identity).GetComponent(); if ((Object)(object)component != (Object)null && component.itemData != null) { _dropObjectField.Invoke(component.itemData) = component; } if (isDropSpawn && (Object)(object)component != (Object)null && !skipDropCallbacks) { component.OnDrop(); } Rigidbody rigidbody = GetRigidbody(((Component)identity).gameObject); if ((Object)(object)rigidbody != (Object)null) { rigidbody.isKinematic = false; rigidbody.velocity = velocity; } ((Component)identity).gameObject.SetActive(true); } finally { ApplyingRemoteState = false; } } private static void Forget(string networkId) { if (!_items.TryGetValue(networkId, out NetworkedItem value) || (Object)(object)value == (Object)null) { if (!TryForgetUnknownSceneItem(networkId)) { MPMain.LogWarning("[MP ItemSync] Forget ignored. Unknown NetworkId=" + networkId); } return; } Item_Object component = ((Component)value).GetComponent(); if ((Object)(object)component != (Object)null) { RemoveCandidate(component); } MPMain.LogInfo("[MP ItemSync] Forget item. NetworkId=" + networkId + ", Item=" + (((Object)(object)((Component)value).gameObject != (Object)null) ? ((Object)((Component)value).gameObject).name : "null") + ", " + $"WasInstantiatedBySync={value.WasInstantiatedBySync}"); if ((Object)(object)((Component)value).gameObject != (Object)null) { ((Component)value).gameObject.SetActive(false); if (value.WasInstantiatedBySync) { Object.Destroy((Object)(object)((Component)value).gameObject); } } _items.Remove(networkId); } private static bool TryForgetUnknownSceneItem(string networkId) { if (string.IsNullOrEmpty(networkId)) { return false; } if (!networkId.StartsWith("sceneitem:", StringComparison.Ordinal)) { return false; } Item_Object val = FindSceneItemByStableId(networkId); if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { return false; } NetworkedItem component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null && string.IsNullOrEmpty(component.NetworkId)) { component.NetworkId = networkId; component.StableSceneId = networkId; } RemoveCandidate(val); ((Component)val).gameObject.SetActive(false); _items.Remove(networkId); MPMain.LogWarning("[MP ItemSync] Forgot unknown scene item by stable ID fallback. NetworkId=" + networkId + ", Item=" + ((Object)val).name); return true; } private static void SendSnapshotReset(ulong clientId) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, clientId, PacketType.ItemStateSync); writer.Put((byte)0); MonoSingleton.Instance.SendToPeer(SteamId.op_Implicit(clientId), writer, (SendType)8, 0); } private static void SendSnapshotFinalize(ulong clientId) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, clientId, PacketType.ItemStateSync); writer.Put((byte)1); MonoSingleton.Instance.SendToPeer(SteamId.op_Implicit(clientId), writer, (SendType)8, 0); } private static void SendCreate(ulong clientId, NetworkedItem identity, Item_Object itemObject, Vector3 velocity, bool isDropSpawn) { //IL_005e: 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_007c: 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) if (!((Object)(object)identity == (Object)null) && !((Object)(object)itemObject == (Object)null) && !string.IsNullOrEmpty(identity.NetworkId)) { DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, clientId, PacketType.ItemStateSync); writer.Put((byte)2); writer.Put(identity.NetworkId); writer.Put(identity.PrefabKey); writer.Put(((Component)itemObject).transform.position); writer.Put(((Component)itemObject).transform.rotation); writer.Put(velocity); writer.Put(isDropSpawn); MonoSingleton.Instance.SendToPeer(SteamId.op_Implicit(clientId), writer, (SendType)8, 0); } } private static void BroadcastCreate(NetworkedItem identity, Item_Object itemObject, Vector3 velocity, bool isDropSpawn) { //IL_005f: 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_007d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)identity == (Object)null) && !((Object)(object)itemObject == (Object)null) && !string.IsNullOrEmpty(identity.NetworkId)) { DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, 0uL, PacketType.ItemStateSync); writer.Put((byte)2); writer.Put(identity.NetworkId); writer.Put(identity.PrefabKey); writer.Put(((Component)itemObject).transform.position); writer.Put(((Component)itemObject).transform.rotation); writer.Put(velocity); writer.Put(isDropSpawn); MonoSingleton.Instance.Broadcast(writer, (SendType)8, 0); } } private static void BroadcastRemove(string networkId) { DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, 0uL, PacketType.ItemStateSync); writer.Put((byte)5); writer.Put(networkId); MPMain.LogInfo("[MP ItemSync] Broadcast remove. NetworkId=" + networkId); MonoSingleton.Instance.Broadcast(writer, (SendType)8, 0); } private static void SendPickupRequest(string networkId) { DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, MonoSingleton.Instance.HostSteamId, PacketType.ItemStateSync); writer.Put((byte)3); writer.Put(networkId); MPMain.LogInfo("[MP ItemSync] Sent pickup request. NetworkId=" + networkId); MonoSingleton.Instance.SendToHost(writer, (SendType)8, 0); } private static void SendDropRequest(string prefabKey, Vector3 position, Quaternion rotation, Vector3 velocity) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) DataWriter writer = MPWriterPool.GetWriter(MPSteamworks.UserSteamId, MonoSingleton.Instance.HostSteamId, PacketType.ItemStateSync); writer.Put((byte)4); writer.Put(prefabKey); writer.Put(position); writer.Put(rotation); writer.Put(velocity); MonoSingleton.Instance.SendToHost(writer, (SendType)8, 0); } private static void CaptureSceneCandidates(bool snapshotCandidate) { foreach (Item_Object item in EnumerateSceneItems()) { RememberCandidate(item, snapshotCandidate); } } private static IEnumerable EnumerateSceneItems() { Item_Object[] array = Object.FindObjectsOfType(); foreach (Item_Object itemObject in array) { if (IsSyncableWorldItem(itemObject) && !IsBlacklisted(itemObject)) { yield return itemObject; } } } private static void RememberCandidate(Item_Object itemObject, bool snapshotCandidate) { if (!IsSyncableWorldItem(itemObject)) { return; } NetworkedItem orCreateIdentity = GetOrCreateIdentity(((Component)itemObject).gameObject); if (snapshotCandidate && TryAssignStableSceneIdentity(itemObject, orCreateIdentity)) { orCreateIdentity.PrefabKey = GetPrefabKey(itemObject); if (_items.TryGetValue(orCreateIdentity.NetworkId, out NetworkedItem value) && (Object)(object)value != (Object)null && (Object)(object)value != (Object)(object)orCreateIdentity) { MPMain.LogWarning("[MP ItemSync] Duplicate client candidate NetworkId detected. NetworkId=" + orCreateIdentity.NetworkId + ", Existing=" + ((Object)value).name + ", New=" + ((Object)itemObject).name); } _items[orCreateIdentity.NetworkId] = orCreateIdentity; } else if (!string.IsNullOrEmpty(orCreateIdentity.NetworkId)) { if (_items.TryGetValue(orCreateIdentity.NetworkId, out NetworkedItem value2) && (Object)(object)value2 != (Object)null && (Object)(object)value2 != (Object)(object)orCreateIdentity) { MPMain.LogWarning("[MP ItemSync] Duplicate remembered NetworkId detected. NetworkId=" + orCreateIdentity.NetworkId + ", Existing=" + ((Object)value2).name + ", New=" + ((Object)itemObject).name); } _items[orCreateIdentity.NetworkId] = orCreateIdentity; } else { if (!_clientCandidates.Contains(itemObject)) { _clientCandidates.Add(itemObject); } if (snapshotCandidate && !_snapshotCandidates.Contains(itemObject)) { _snapshotCandidates.Add(itemObject); } } } private static void RemoveCandidate(Item_Object itemObject) { _clientCandidates.Remove(itemObject); _snapshotCandidates.Remove(itemObject); RemovePendingLocalDrop(itemObject); } private static Item_Object FindClientCandidate(string prefabKey, Vector3 position, out bool wasSnapshotCandidate) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) RemoveDestroyedCandidates(); Item_Object val = null; float num = float.MaxValue; wasSnapshotCandidate = false; foreach (Item_Object clientCandidate in _clientCandidates) { if (IsSyncableWorldItem(clientCandidate) && PrefabKeysMatch(prefabKey, GetPrefabKey(clientCandidate))) { Vector3 val2 = ((Component)clientCandidate).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude > 0.5f) && !(sqrMagnitude >= num)) { val = clientCandidate; num = sqrMagnitude; } } } if ((Object)(object)val != (Object)null) { wasSnapshotCandidate = _snapshotCandidates.Contains(val); RemoveCandidate(val); } return val; } private static void RememberPendingLocalDrop(Item_Object itemObject, string prefabKey) { if (IsSyncableWorldItem(itemObject) && !string.IsNullOrEmpty(prefabKey)) { RemoveDestroyedPendingLocalDrops(); RemovePendingLocalDrop(itemObject); _pendingLocalDrops.Add(new PendingLocalDrop(itemObject, prefabKey, Time.time)); } } private static Item_Object FindPendingLocalDrop(string prefabKey, Vector3 position) { //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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) RemoveDestroyedPendingLocalDrops(); Item_Object val = null; float num = float.MaxValue; for (int num2 = _pendingLocalDrops.Count - 1; num2 >= 0; num2--) { PendingLocalDrop pendingLocalDrop = _pendingLocalDrops[num2]; if (PrefabKeysMatch(prefabKey, pendingLocalDrop.PrefabKey)) { Vector3 val2 = ((Component)pendingLocalDrop.ItemObject).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude > 25f) && !(sqrMagnitude >= num)) { val = pendingLocalDrop.ItemObject; num = sqrMagnitude; } } } if ((Object)(object)val != (Object)null) { RemoveCandidate(val); } return val; } private static bool IsPendingLocalDrop(Item_Object itemObject) { if ((Object)(object)itemObject == (Object)null) { return false; } RemoveDestroyedPendingLocalDrops(); for (int i = 0; i < _pendingLocalDrops.Count; i++) { if ((Object)(object)_pendingLocalDrops[i].ItemObject == (Object)(object)itemObject) { return true; } } return false; } private static void RemovePendingLocalDrop(Item_Object itemObject) { if ((Object)(object)itemObject == (Object)null) { return; } for (int num = _pendingLocalDrops.Count - 1; num >= 0; num--) { if ((Object)(object)_pendingLocalDrops[num].ItemObject == (Object)(object)itemObject) { _pendingLocalDrops.RemoveAt(num); } } } private static void RemoveDestroyedCandidates() { for (int num = _clientCandidates.Count - 1; num >= 0; num--) { if (!IsSyncableWorldItem(_clientCandidates[num])) { _clientCandidates.RemoveAt(num); } } for (int num2 = _snapshotCandidates.Count - 1; num2 >= 0; num2--) { if (!IsSyncableWorldItem(_snapshotCandidates[num2])) { _snapshotCandidates.RemoveAt(num2); } } RemoveDestroyedPendingLocalDrops(); } private static void RemoveDestroyedPendingLocalDrops() { float num = Time.time - 3f; for (int num2 = _pendingLocalDrops.Count - 1; num2 >= 0; num2--) { PendingLocalDrop pendingLocalDrop = _pendingLocalDrops[num2]; if (!IsSyncableWorldItem(pendingLocalDrop.ItemObject) || pendingLocalDrop.CreatedAt < num) { _pendingLocalDrops.RemoveAt(num2); } } } private static void RememberSuppressedPickup(Item_Object itemObject) { if (!((Object)(object)itemObject == (Object)null) && !((Object)(object)((Component)itemObject).gameObject == (Object)null)) { RemoveExpiredSuppressedPickups(); _suppressedPickupObjects[((Object)((Component)itemObject).gameObject).GetInstanceID()] = Time.time + 0.2f; } } private static bool ShouldSuppressLocalPickup(Item_Object itemObject) { if ((Object)(object)itemObject == (Object)null || (Object)(object)((Component)itemObject).gameObject == (Object)null) { return false; } RemoveExpiredSuppressedPickups(); int instanceID = ((Object)((Component)itemObject).gameObject).GetInstanceID(); if (!_suppressedPickupObjects.TryGetValue(instanceID, out var value)) { return false; } if (Time.time > value) { _suppressedPickupObjects.Remove(instanceID); return false; } MPMain.LogInfo("[MP ItemSync] Suppressed pickup immediately after drop. Item=" + ((Object)itemObject).name); return true; } private static void RemoveExpiredSuppressedPickups() { if (_suppressedPickupObjects.Count == 0) { return; } foreach (int item in new List(_suppressedPickupObjects.Keys)) { if (_suppressedPickupObjects.TryGetValue(item, out var value) && Time.time > value) { _suppressedPickupObjects.Remove(item); } } } private static bool IsSyncableWorldItem(Item_Object itemObject) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)itemObject == (Object)null || (Object)(object)((Component)itemObject).gameObject == (Object)null) { return false; } if (!((Component)itemObject).gameObject.activeInHierarchy) { return false; } Scene scene = ((Component)itemObject).gameObject.scene; if (string.IsNullOrEmpty(((Scene)(ref scene)).name)) { return false; } if (itemObject.itemData == null) { return false; } if (itemObject.itemData.inBag) { return false; } return true; } private static Item_Object InstantiateWorldItem(string prefabKey, Vector3 position, Quaternion rotation) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) GameObject assetGameObject = CL_AssetManager.GetAssetGameObject(prefabKey, ""); if ((Object)(object)assetGameObject == (Object)null) { return null; } ApplyingRemoteState = true; try { GameObject val = Object.Instantiate(assetGameObject, position, rotation); Transform currentLevelParentRoot = WorldLoader.GetCurrentLevelParentRoot(); if ((Object)(object)currentLevelParentRoot != (Object)null) { val.transform.SetParent(currentLevelParentRoot); } Item_Object val2 = val.GetComponent() ?? val.GetComponentInChildren(true); if ((Object)(object)val2 != (Object)null && val2.itemData != null) { val2.itemData.InitializeItemData(val2); } return val2; } finally { ApplyingRemoteState = false; } } private static Vector3 GetVelocity(Item_Object itemObject) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) Rigidbody rigidbody = GetRigidbody(((Component)itemObject).gameObject); if ((Object)(object)rigidbody == (Object)null) { return Vector3.zero; } Vector3 velocity = rigidbody.velocity; return (((Vector3)(ref velocity)).sqrMagnitude > 0.0025f) ? rigidbody.velocity : Vector3.zero; } private static Rigidbody GetRigidbody(GameObject gameObject) { if ((Object)(object)gameObject == (Object)null) { return null; } return gameObject.GetComponent() ?? gameObject.GetComponentInChildren(); } private static NetworkedItem GetOrCreateIdentity(GameObject gameObject) { NetworkedItem networkedItem = gameObject.GetComponent(); if ((Object)(object)networkedItem == (Object)null) { networkedItem = gameObject.AddComponent(); } return networkedItem; } private static Item_Object FindSceneItemByStableId(string networkId) { if (string.IsNullOrEmpty(networkId) || !networkId.StartsWith("sceneitem:", StringComparison.Ordinal)) { return null; } foreach (Item_Object item in EnumerateSceneItems()) { if (string.Equals(GetStableSceneItemId(item), networkId, StringComparison.Ordinal)) { return item; } } return null; } private static bool TryAssignStableSceneIdentity(Item_Object itemObject, NetworkedItem identity) { if ((Object)(object)itemObject == (Object)null || (Object)(object)identity == (Object)null) { return false; } string stableSceneItemId = GetStableSceneItemId(itemObject); if (string.IsNullOrEmpty(stableSceneItemId)) { return false; } identity.StableSceneId = stableSceneItemId; identity.NetworkId = stableSceneItemId; return true; } private static string GetStableSceneItemId(Item_Object itemObject) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)itemObject == (Object)null || (Object)(object)((Component)itemObject).gameObject == (Object)null) { return string.Empty; } Scene scene = ((Component)itemObject).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { scene = ((Component)itemObject).gameObject.scene; if (!string.IsNullOrEmpty(((Scene)(ref scene)).name)) { NetworkedItem component = ((Component)itemObject).GetComponent(); if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.StableSceneId)) { return component.StableSceneId; } string text = BuildTransformPath(((Component)itemObject).transform); if (string.IsNullOrEmpty(text)) { return string.Empty; } string text2 = BuildStableTransformAnchor(((Component)itemObject).transform); string[] obj = new string[6] { "sceneitem:", null, null, null, null, null }; scene = ((Component)itemObject).gameObject.scene; obj[1] = ((Scene)(ref scene)).name; obj[2] = ":"; obj[3] = text; obj[4] = "|"; obj[5] = text2; return string.Concat(obj); } } return string.Empty; } private static string BuildTransformPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); Stack stack = new Stack(); Transform val = transform; while ((Object)(object)val != (Object)null) { stack.Push($"{MPUtil.CleanCloneName(((Object)val).name)}[{val.GetSiblingIndex()}]"); val = val.parent; } while (stack.Count > 0) { if (stringBuilder.Length > 0) { stringBuilder.Append('/'); } stringBuilder.Append(stack.Pop()); } return stringBuilder.ToString(); } private static string BuildStableTransformAnchor(Transform transform) { //IL_0018: 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_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) //IL_0032: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)transform == (Object)null) { return string.Empty; } Vector3 localPosition = transform.localPosition; Vector3 localEulerAngles = transform.localEulerAngles; return $"lp:{Quantize(localPosition.x, 20f)}," + $"{Quantize(localPosition.y, 20f)}," + $"{Quantize(localPosition.z, 20f)}" + $"|lr:{Quantize(NormalizeAngle(localEulerAngles.x), 5f)}," + $"{Quantize(NormalizeAngle(localEulerAngles.y), 5f)}," + $"{Quantize(NormalizeAngle(localEulerAngles.z), 5f)}"; } private static int Quantize(float value, float precision) { return Mathf.RoundToInt(value * precision); } private static float NormalizeAngle(float value) { value %= 360f; if (value < 0f) { value += 360f; } return value; } private static string GetPrefabKey(Item_Object itemObject) { if ((Object)(object)itemObject == (Object)null) { return string.Empty; } if (itemObject.itemData != null && !string.IsNullOrEmpty(itemObject.itemData.prefabName)) { return itemObject.itemData.prefabName; } return MPUtil.CleanCloneName(((Object)((Component)itemObject).gameObject).name); } private static bool PrefabKeysMatch(string a, string b) { if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) { return false; } return string.Equals(MPUtil.CleanCloneName(a), MPUtil.CleanCloneName(b), StringComparison.OrdinalIgnoreCase); } public static bool IsBlacklisted(Item item) { return item != null && (_blacklistedPrefabNames.Contains(MPUtil.CleanCloneName(item.prefabName)) || _blacklistedItemTags.Intersect(item.itemTags).Any()); } public static bool IsBlacklisted(Item_Object itemObj) { return (Object)(object)itemObj != (Object)null && IsBlacklisted(itemObj.itemData); } } } namespace WKMPMod.Util { public static class Localization { public struct LocalizedValue { private readonly object _data; public string[] AsArray => _data as string[]; public string AsString => _data as string; public bool IsArray => _data is string[]; public int Count => (!IsArray) ? 1 : ((string[])_data).Length; public LocalizedValue(object data) { _data = data; } public string GetValue(int index = 0) { if (_data is string[] array) { return array[Math.Clamp(index, 0, array.Length - 1)]; } return _data?.ToString() ?? string.Empty; } public string GetValue(Random rand) { if (_data is string[] array) { return array[rand.Next(array.Length)]; } return _data?.ToString() ?? string.Empty; } } private static Dictionary> _table; private static Dictionary _flatCache; private const string FILE_PREFIX = "texts"; private static readonly Random _staticRandom = new Random(); public static void Load() { string path = MPMain.path; _table = new Dictionary>(); string path2 = "texts_en.json"; string text = Path.Combine(path, path2); bool flag = false; if (File.Exists(text)) { LoadAndMerge(text); flag = true; } else { MPMain.LogWarning("[Localization] Base English file not found at: " + text); } string gameLanguage = GetGameLanguage(); if (gameLanguage != "en") { string text2 = "texts_" + gameLanguage.ToLower() + ".json"; string text3 = Path.Combine(path, text2); if (File.Exists(text3)) { LoadAndMerge(text3); MPMain.LogInfo("[Localization] Loaded local language file: " + text2 + " and merged over English."); } else { MPMain.LogInfo("[Localization] Local language file " + text2 + " not found. Using English fallback."); } } if (_table.Count == 0 && !flag) { MPMain.LogError("[Localization] CRITICAL: No localization files could be loaded!"); return; } BuildFlatCache(); int num = 0; foreach (KeyValuePair> item in _table) { num += item.Value.Count; } MPMain.LogInfo($"[Localization] Successfully loaded {_table.Count} categories with {num} entries"); } private static void LoadAndMerge(string filePath) { try { string text = File.ReadAllText(filePath); Dictionary> dictionary = JsonConvert.DeserializeObject>>(text); if (dictionary == null) { return; } foreach (KeyValuePair> item in dictionary) { if (!_table.ContainsKey(item.Key)) { _table[item.Key] = new Dictionary(); } foreach (KeyValuePair item2 in item.Value) { object value = item2.Value; JArray val = (JArray)((value is JArray) ? value : null); if (val != null) { _table[item.Key][item2.Key] = new LocalizedValue(((IEnumerable)val).Select((JToken x) => ((object)x).ToString()).ToArray()); } else { _table[item.Key][item2.Key] = new LocalizedValue(item2.Value?.ToString()); } } } } catch (Exception ex) { MPMain.LogError("[Localization] Unable to parse localization file " + Path.GetFileName(filePath) + ": " + ex.Message); } } private static void BuildFlatCache() { _flatCache = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> item in _table) { foreach (KeyValuePair item2 in item.Value) { string key = item.Key + "." + item2.Key; _flatCache[key] = item2.Value; } } } public static bool TryGetValueSplit(string category, string key, out LocalizedValue value) { if (string.IsNullOrEmpty(category)) { MPMain.LogWarning("[MP Localization] Category is null or empty"); value = new LocalizedValue("[" + category + "." + key + "]"); return false; } if (!_table.TryGetValue(category, out Dictionary value2)) { MPMain.LogWarning("[MP Localization] Category not found: " + category); value = new LocalizedValue("[" + category + "." + key + "]"); return false; } if (!value2.TryGetValue(key, out var value3)) { MPMain.LogWarning("[MP Localization] Key '" + key + "' not found in category '" + category + "'"); value = new LocalizedValue("[" + category + "." + key + "]"); return false; } value = value3; return true; } public static string GetSplit(string category, string key, params object[] args) { if (!TryGetValueSplit(category, key, out var value)) { return value.AsString; } return SafeFormat(value.AsString, args); } public static string GetRandomSplit(string category, string key, params object[] args) { if (!TryGetValueSplit(category, key, out var value)) { return value.AsString; } return SafeFormat(value.GetValue(_staticRandom), args); } public static string GetByIndexSplit(string category, string key, int index, params object[] args) { if (!TryGetValueSplit(category, key, out var value)) { return value.AsString; } return SafeFormat(value.GetValue(index), args); } public static int GetCountSplit(string category, string key) { LocalizedValue value; return TryGetValueSplit(category, key, out value) ? value.Count : 0; } public static string[] GetAllSplit(string category, string key) { if (!TryGetValueSplit(category, key, out var value)) { return new string[0]; } return value.IsArray ? value.AsArray : new string[1] { value.AsString }; } public static string SafeFormat(string pattern, params object[] args) { if (string.IsNullOrEmpty(pattern)) { return string.Empty; } if (args == null || args.Length == 0) { return pattern; } try { MatchCollection matchCollection = Regex.Matches(pattern, "\\{([0-9]+)\\}"); int num = -1; foreach (Match item in matchCollection) { if (int.TryParse(item.Groups[1].Value, out var result) && result > num) { num = result; } } if (num == -1) { return pattern; } int num2 = num + 1; object[] array = new object[num2]; for (int i = 0; i < num2; i++) { array[i] = ((i < args.Length && args[i] != null) ? args[i] : ""); } return string.Format(pattern, array); } catch (Exception ex) { MPMain.LogError("[Localization] Format error: " + ex.Message + " | Pattern: " + pattern); return pattern; } } public static bool TryGetValue(string key, out LocalizedValue value) { if (!_flatCache.TryGetValue(key, out var value2)) { value = new LocalizedValue("[" + key + "]"); return false; } value = value2; return true; } public static string Get(string key, params object[] args) { if (!TryGetValue(key, out var value)) { return value.AsString; } return SafeFormat(value.AsString, args); } public static string GetRandom(string key, params object[] args) { if (!TryGetValue(key, out var value)) { return value.AsString; } return SafeFormat(value.GetValue(_staticRandom), args); } public static string GetByIndex(string key, int index, params object[] args) { if (!TryGetValue(key, out var value)) { return value.AsString; } return SafeFormat(value.GetValue(index), args); } public static int GetCount(string key) { LocalizedValue value; return TryGetValue(key, out value) ? value.Count : 0; } public static string[] GetAll(string key) { if (!TryGetValue(key, out var value)) { return new string[0]; } return value.IsArray ? value.AsArray : new string[1] { value.AsString }; } public static bool HasKey(string key) { return _flatCache.ContainsKey(key); } public static bool HasKey(string category, string key) { if (_table.TryGetValue(category, out Dictionary value)) { return value.ContainsKey(key); } return false; } public static IEnumerable GetAllCategories() { return _table.Keys; } public static IEnumerable GetKeysInCategory(string category) { if (_table.TryGetValue(category, out Dictionary value)) { return value.Keys; } return new List(); } public static string GetGameLanguage() { //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) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 SystemLanguage systemLanguage = Application.systemLanguage; SystemLanguage val = systemLanguage; if ((int)val <= 22) { if ((int)val <= 14) { if ((int)val == 6) { goto IL_0056; } if ((int)val == 14) { return "fr"; } } else { if ((int)val == 15) { return "de"; } if ((int)val == 22) { return "ja"; } } } else if ((int)val <= 30) { if ((int)val == 23) { return "ko"; } if ((int)val == 30) { return "ru"; } } else { if ((int)val == 34) { return "es"; } if ((int)val == 40) { goto IL_0056; } if ((int)val == 41) { return "zh_tw"; } } return "en"; IL_0056: return "zh"; } } public abstract class MonoSingleton : MonoBehaviour where T : MonoSingleton { private static T _instance; private static readonly object _lock = new object(); private static bool _applicationIsQuitting = false; public static T Instance { get { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown if (_applicationIsQuitting) { return null; } lock (_lock) { if ((Object)(object)_instance == (Object)null) { _instance = Object.FindObjectOfType(); if ((Object)(object)_instance == (Object)null) { GameObject val = new GameObject(typeof(T).Name); _instance = val.AddComponent(); Object.DontDestroyOnLoad((Object)(object)val); } } return _instance; } } } protected virtual void Awake() { if ((Object)(object)_instance == (Object)null) { _instance = (T)this; if ((Object)(object)((Component)this).transform.parent == (Object)null) { Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } } else if ((Object)(object)_instance != (Object)(object)this) { Debug.LogWarning((object)("MonoSingleton<" + typeof(T).Name + ">: Duplicate components were found in the scene and have been automatically destroyed")); Object.Destroy((Object)(object)((Component)this).gameObject); } } protected virtual void OnApplicationQuit() { _applicationIsQuitting = true; } protected virtual void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } } public static class MPUtil { public static readonly Dictionary PlayerColorPresets = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "default", new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue) }, { "white", new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue) }, { "red", new Color32(byte.MaxValue, (byte)80, (byte)80, byte.MaxValue) }, { "orange", new Color32(byte.MaxValue, (byte)165, (byte)0, byte.MaxValue) }, { "yellow", new Color32(byte.MaxValue, (byte)220, (byte)64, byte.MaxValue) }, { "green", new Color32((byte)80, (byte)220, (byte)120, byte.MaxValue) }, { "cyan", new Color32((byte)64, (byte)220, byte.MaxValue, byte.MaxValue) }, { "blue", new Color32((byte)90, (byte)140, byte.MaxValue, byte.MaxValue) }, { "purple", new Color32((byte)170, (byte)90, byte.MaxValue, byte.MaxValue) }, { "pink", new Color32(byte.MaxValue, (byte)110, (byte)180, byte.MaxValue) }, { "black", new Color32((byte)32, (byte)32, (byte)32, byte.MaxValue) } }; public static string CleanCloneName(string prefabKey) { if (string.IsNullOrEmpty(prefabKey)) { return string.Empty; } return prefabKey.Replace("(Clone)", string.Empty).Trim(); } public static string SerializePlayerColor(Color32 color) { //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 $"{color.r},{color.g},{color.b}"; } public static bool TryParsePlayerColor(string value, out Color32 color) { //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_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) color = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); if (string.IsNullOrWhiteSpace(value)) { return false; } string[] array = value.Split(','); if (array.Length != 3) { return false; } if (!TryParseColorChannel(array[0], out var channel) || !TryParseColorChannel(array[1], out var channel2) || !TryParseColorChannel(array[2], out var channel3)) { return false; } color = new Color32((byte)channel, (byte)channel2, (byte)channel3, byte.MaxValue); return true; } public static bool TryParseColorChannel(string value, out int channel) { if (int.TryParse(value.Trim(), out channel)) { return channel >= 0 && channel <= 255; } return false; } } public static class NestedCommandEngine { public static readonly FieldRef StatesRef; public static readonly FieldRef CommandsRef; public static readonly FieldRef> AutocompleteRef; public static readonly FieldRef> AutocompleteArgsRef; public static readonly Action SetAutocompleteActiveArgAction; public static readonly FieldRef> ValidatorRef; public static readonly FieldRef> ValidatorArgsRef; public static readonly Action SetValidatorActiveArgAction; static NestedCommandEngine() { try { Type typeFromHandle = typeof(CommandConsole); StatesRef = AccessTools.FieldRefAccess("states"); Type type = AccessTools.Inner(typeFromHandle, "CommandLineState"); FieldInfo fieldInfo = AccessTools.Field(type, "commands"); CommandsRef = AccessTools.FieldRefAccess(fieldInfo); Type type2 = AccessTools.Inner(typeFromHandle, "Command"); FieldInfo fieldInfo2 = AccessTools.Field(type2, "autocomplete"); AutocompleteRef = AccessTools.FieldRefAccess>(fieldInfo2); FieldInfo fieldInfo3 = AccessTools.Field(type2, "validator"); ValidatorRef = AccessTools.FieldRefAccess>(fieldInfo3); Type typeFromHandle2 = typeof(CommandAutocomplete); AutocompleteArgsRef = AccessTools.FieldRefAccess>("args"); MethodInfo methodInfo = AccessTools.PropertySetter(typeFromHandle2, "activeArg"); if (methodInfo != null) { SetAutocompleteActiveArgAction = AccessTools.MethodDelegate>(methodInfo, (object)null, true); } Type typeFromHandle3 = typeof(CommandValidator); ValidatorArgsRef = AccessTools.FieldRefAccess>("args"); MethodInfo methodInfo2 = AccessTools.PropertySetter(typeFromHandle3, "activeArg"); if (methodInfo2 != null) { SetValidatorActiveArgAction = AccessTools.MethodDelegate>(methodInfo2, (object)null, true); } } catch (Exception ex) { MPMain.LogError(Localization.Get("Patch.HarmonyReflectionInitFailed", ex.Message)); } } public static void ForwardAutocomplete(CommandAutocomplete autocomplete, int defaultStartIndex) { if (StatesRef == null || CommandsRef == null) { return; } int num = defaultStartIndex; for (int i = defaultStartIndex; i <= autocomplete.activeArg; i++) { if (autocomplete.ArgumentAt(i) == "::") { num = i + 1; } } IEnumerable source = StatesRef.Invoke(CommandConsole.instance); object obj = source.Cast().First(); IDictionary dictionary = CommandsRef.Invoke(obj); if (autocomplete.activeArg == num) { List list = new List(); foreach (object key2 in dictionary.Keys) { list.Add((string)key2); } list.Add("::"); autocomplete.FromArray((IReadOnlyList)list); } else { if (autocomplete.activeArg <= num) { return; } string key = autocomplete.ArgumentAt(num).ToLower(); if (dictionary.Contains(key)) { object obj2 = dictionary[key]; Action action = AutocompleteRef.Invoke(obj2); if (action != null) { int activeArg = autocomplete.activeArg; List list2 = AutocompleteArgsRef.Invoke(autocomplete); SetAutocompleteActiveArgAction(autocomplete, activeArg - num); AutocompleteArgsRef.Invoke(autocomplete) = list2.Skip(num).ToList(); action(autocomplete); SetAutocompleteActiveArgAction(autocomplete, activeArg); AutocompleteArgsRef.Invoke(autocomplete) = list2; } } } } public static void ForwardValidator(CommandValidator validator, int defaultStartIndex) { if (StatesRef == null || CommandsRef == null || ValidatorArgsRef == null) { return; } int activeArg = validator.activeArg; List list = ValidatorArgsRef.Invoke(validator); if (activeArg < defaultStartIndex) { return; } int num = defaultStartIndex; int num2 = Math.Min(activeArg, list.Count - 1); for (int i = defaultStartIndex; i <= num2; i++) { if (list[i] == "::") { num = i + 1; } } if (activeArg <= num || num >= list.Count) { return; } string key = list[num].ToLower(); IEnumerable source = StatesRef.Invoke(CommandConsole.instance); object obj = source.Cast().First(); IDictionary dictionary = CommandsRef.Invoke(obj); if (dictionary.Contains(key)) { object obj2 = dictionary[key]; Action action = ValidatorRef.Invoke(obj2); if (action != null) { SetValidatorActiveArgAction(validator, activeArg - num); ValidatorArgsRef.Invoke(validator) = list.Skip(num).ToList(); action(validator); SetValidatorActiveArgAction(validator, activeArg); ValidatorArgsRef.Invoke(validator) = list; } } } } } namespace WKMPMod.UI { [HarmonyPatch(typeof(UI_GamemodeScreen), "Initialize")] public class Patch_UI_GamemodeScreen_Initialize { private static void Postfix(UI_GamemodeScreen __instance, M_Gamemode mode) { string id = mode.gamemodePanel.id; if (!__instance.activePanels.TryGetValue(id, out var value) || (Object)(object)((Component)value).gameObject.GetComponentInChildren(true) != (Object)null) { return; } Transform val = ((Component)value).transform.Find("Pages/Gamemode_Info_Screen/Tab Selection Hor/Play"); TextMeshProUGUI componentInChildren = ((Component)val).GetComponentInChildren(); if (((componentInChildren != null) ? ((TMP_Text)componentInChildren).text : null) != "Play") { val = ((Component)value).transform.Find("Pages/Gamemode_NewGame_Screen/Tab Selection Hor/Play"); } if ((Object)(object)val != (Object)null) { GameObject val2 = Object.Instantiate(((Component)val).gameObject, val.parent); ((Object)val2).name = "Multi Play"; val2.transform.SetSiblingIndex(val.GetSiblingIndex() + 1); UI_LobbyCreateButton uI_LobbyCreateButton = val2.AddComponent(); uI_LobbyCreateButton.gamemodePanel = value; TMP_Text componentInChildren2 = val2.GetComponentInChildren(); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.text = "Multi Play"; } value.noSaveObjects.Add(val2); value.hasSaveObjects.Add(val2); } } } [HarmonyPatch(typeof(UI_MenuButton), "Initialize")] public class Patch_UI_MenuButton_Initialize { private static bool Prefix(UI_MenuButton __instance, UI_Menu menu) { if (UI_Manager.IsCloningMultiplayerMenu && ((Object)((Component)__instance).gameObject).name == "Facility Button") { return false; } return true; } } public class UI_LoadingDisplay : MonoBehaviour { private Coroutine? _hideCoroutine; private float _hideTime = -1f; private float _remainingTime = 0f; private void Awake() { MPEventBusGame.OnShowLoading += HandleLoadingRequest; MPEventBusGame.OnHideLoading += HideImmediately; ((Component)this).gameObject.SetActive(false); } private void OnDestroy() { MPEventBusGame.OnShowLoading -= HandleLoadingRequest; MPEventBusGame.OnHideLoading -= HideImmediately; } private void HandleLoadingRequest(float duration) { if (duration <= 0f) { ShowPermanent(); } else { ShowForSeconds(duration); } } public void ShowPermanent() { if (_hideCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_hideCoroutine); _hideCoroutine = null; } _hideTime = -1f; _remainingTime = 0f; ((Component)this).gameObject.SetActive(true); } public void ShowForSeconds(float seconds) { if (!(seconds <= 0f)) { if (_hideCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_hideCoroutine); _hideCoroutine = null; } _hideTime = seconds; _remainingTime = seconds; ((Component)this).gameObject.SetActive(true); _hideCoroutine = ((MonoBehaviour)this).StartCoroutine(HideAfterDelay()); } } public void ExtendDuration(float seconds) { if (!(_hideTime < 0f) && _hideCoroutine != null) { _remainingTime += seconds; if (_remainingTime <= 0f) { HideImmediately(); } } } public void HideImmediately() { if (_hideCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_hideCoroutine); _hideCoroutine = null; } _hideTime = -1f; _remainingTime = 0f; ((Component)this).gameObject.SetActive(false); } private IEnumerator HideAfterDelay() { while (_remainingTime > 0f) { yield return null; _remainingTime -= Time.deltaTime; } ((Component)this).gameObject.SetActive(false); _hideCoroutine = null; } public float GetRemainingTime() { return _remainingTime; } public bool IsShowing() { return ((Component)this).gameObject.activeSelf; } public bool IsPermanent() { return _hideTime < 0f && ((Component)this).gameObject.activeSelf; } } public class UI_LobbyCreateButton : MonoBehaviour { public Button? button; public UI_GamemodeScreen_Panel? gamemodePanel; public List