using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Logging; using GorillaLocomotion; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using UnityEngine; using UnityEngine.AI; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.SceneManagement; using UnityEngine.UI; using UnityEngine.Video; using UnityEngine.XR; using UnityEngine.XR.Interaction.Toolkit; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("Crawlspace2MP")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+e0bbb7bd81d1b81b69a578e3d5a19b1355888e4b")] [assembly: AssemblyProduct("Crawlspace2MP")] [assembly: AssemblyTitle("Crawlspace2MP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Crawlspace2MP { public class ActorRecorder { public struct Frame { public Vector3 HeadPos; public Quaternion HeadRot; public Vector3 LeftHandPos; public Quaternion LeftHandRot; public Vector3 RightHandPos; public Quaternion RightHandRot; public float LeftGrip; public float LeftTrigger; public float RightGrip; public float RightTrigger; } public class Recording { public List Frames = new List(); public string Name; public float FrameInterval; } public class Actor { public string Name; public Recording Recording; public RemotePlayer Visual; public int CurrentFrame; public float FrameTimer; public bool IsPlaying; public bool IsLooping; } private Recording _currentRecording; private float _recordTimer; private const float RECORD_INTERVAL = 0.033f; private int _nextActorId = 1; public bool IsRecording { get; private set; } public List Recordings { get; } = new List(); public List Actors { get; } = new List(); public void StartRecording() { _currentRecording = new Recording { Name = $"Recording {Recordings.Count + 1}", FrameInterval = 0.033f, Frames = new List() }; _recordTimer = 0f; IsRecording = true; } public void StopRecording() { if (IsRecording) { IsRecording = false; if (_currentRecording.Frames.Count > 0) { Recordings.Add(_currentRecording); } _currentRecording = null; } } public void CaptureFrame() { //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_0109: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) if (!IsRecording || _currentRecording == null) { return; } _recordTimer += Time.deltaTime; if (_recordTimer < 0.033f) { return; } _recordTimer -= 0.033f; BackpackControl val = Object.FindObjectOfType(); Camera main = Camera.main; Transform val2 = null; Transform val3 = null; Transform val4 = null; if ((Object)(object)val != (Object)null) { if ((Object)(object)val.cam != (Object)null) { val2 = val.cam.transform; } if ((Object)(object)val.leftHand != (Object)null) { val3 = val.leftHand.transform; } if ((Object)(object)val.rightHand != (Object)null) { val4 = val.rightHand.transform; } } Vector3 position; Quaternion rotation; Vector3 leftHandPos; Quaternion leftHandRot; Vector3 rightHandPos; Quaternion rightHandRot; if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null && (Object)(object)val4 != (Object)null) { position = val2.position; rotation = val2.rotation; leftHandPos = val3.position; leftHandRot = val3.rotation; rightHandPos = val4.position; rightHandRot = val4.rotation; } else { if (!((Object)(object)main != (Object)null)) { return; } position = ((Component)main).transform.position; rotation = ((Component)main).transform.rotation; leftHandPos = position + ((Component)main).transform.right * -0.3f + ((Component)main).transform.forward * 0.2f; leftHandRot = rotation; rightHandPos = position + ((Component)main).transform.right * 0.3f + ((Component)main).transform.forward * 0.2f; rightHandRot = rotation; } float leftGrip = 0f; float leftTrigger = 0f; float rightGrip = 0f; float rightTrigger = 0f; if ((Object)(object)val != (Object)null) { try { InputActionProperty val5; if ((Object)(object)val.controllerLeft != (Object)null) { val5 = val.controllerLeft.selectActionValue; leftGrip = ((InputActionProperty)(ref val5)).action.ReadValue(); val5 = val.controllerLeft.uiPressActionValue; leftTrigger = ((InputActionProperty)(ref val5)).action.ReadValue(); } if ((Object)(object)val.controllerRight != (Object)null) { val5 = val.controllerRight.selectActionValue; rightGrip = ((InputActionProperty)(ref val5)).action.ReadValue(); val5 = val.controllerRight.uiPressActionValue; rightTrigger = ((InputActionProperty)(ref val5)).action.ReadValue(); } } catch { } } _currentRecording.Frames.Add(new Frame { HeadPos = position, HeadRot = rotation, LeftHandPos = leftHandPos, LeftHandRot = leftHandRot, RightHandPos = rightHandPos, RightHandRot = rightHandRot, LeftGrip = leftGrip, LeftTrigger = leftTrigger, RightGrip = rightGrip, RightTrigger = rightTrigger }); } public Actor SpawnActor(Recording recording, bool loop = true) { RemotePlayer visual = new RemotePlayer(-100 - _nextActorId); Actor actor = new Actor { Name = $"Actor {_nextActorId} ({recording.Name})", Recording = recording, Visual = visual, CurrentFrame = 0, FrameTimer = 0f, IsPlaying = true, IsLooping = loop }; _nextActorId++; Actors.Add(actor); if (recording.Frames.Count > 0) { ApplyFrame(actor, recording.Frames[0]); } return actor; } public void RemoveActor(Actor actor) { actor.Visual?.Destroy(); Actors.Remove(actor); } public void RemoveAllActors() { for (int num = Actors.Count - 1; num >= 0; num--) { Actors[num].Visual?.Destroy(); } Actors.Clear(); } public void DeleteRecording(Recording recording) { for (int num = Actors.Count - 1; num >= 0; num--) { if (Actors[num].Recording == recording) { Actors[num].Visual?.Destroy(); Actors.RemoveAt(num); } } Recordings.Remove(recording); } public void ToggleActor(Actor actor) { actor.IsPlaying = !actor.IsPlaying; if (actor.IsPlaying) { actor.CurrentFrame = 0; actor.FrameTimer = 0f; } } public void Update() { //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0186: 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_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) foreach (Actor actor in Actors) { if (!actor.IsPlaying || actor.Recording.Frames.Count == 0) { continue; } actor.FrameTimer += Time.deltaTime; if (actor.FrameTimer >= actor.Recording.FrameInterval) { actor.FrameTimer -= actor.Recording.FrameInterval; actor.CurrentFrame++; if (actor.CurrentFrame >= actor.Recording.Frames.Count) { if (!actor.IsLooping) { actor.CurrentFrame = actor.Recording.Frames.Count - 1; actor.IsPlaying = false; continue; } actor.CurrentFrame = 0; } } int index = (actor.CurrentFrame + 1) % actor.Recording.Frames.Count; float num = actor.FrameTimer / actor.Recording.FrameInterval; Frame frame = actor.Recording.Frames[actor.CurrentFrame]; Frame frame2 = actor.Recording.Frames[index]; Frame frame3 = new Frame { HeadPos = Vector3.Lerp(frame.HeadPos, frame2.HeadPos, num), HeadRot = Quaternion.Slerp(frame.HeadRot, frame2.HeadRot, num), LeftHandPos = Vector3.Lerp(frame.LeftHandPos, frame2.LeftHandPos, num), LeftHandRot = Quaternion.Slerp(frame.LeftHandRot, frame2.LeftHandRot, num), RightHandPos = Vector3.Lerp(frame.RightHandPos, frame2.RightHandPos, num), RightHandRot = Quaternion.Slerp(frame.RightHandRot, frame2.RightHandRot, num), LeftGrip = Mathf.Lerp(frame.LeftGrip, frame2.LeftGrip, num), LeftTrigger = Mathf.Lerp(frame.LeftTrigger, frame2.LeftTrigger, num), RightGrip = Mathf.Lerp(frame.RightGrip, frame2.RightGrip, num), RightTrigger = Mathf.Lerp(frame.RightTrigger, frame2.RightTrigger, num) }; ApplyFrame(actor, frame3); } } private void ApplyFrame(Actor actor, Frame frame) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0030: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_004f: 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) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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) if (actor.Visual != null) { Vector3 bodyPos = frame.HeadPos - Vector3.up * 0.5f; Quaternion bodyRot = Quaternion.Euler(0f, ((Quaternion)(ref frame.HeadRot)).eulerAngles.y, 0f); actor.Visual.SetTargets(isStanding: true, bodyPos, bodyRot, frame.HeadPos, frame.HeadRot, frame.LeftHandPos, frame.LeftHandRot, frame.RightHandPos, frame.RightHandRot, frame.LeftGrip, frame.LeftTrigger, frame.RightGrip, frame.RightTrigger); actor.Visual.UpdateInterpolation(); } } public void Cleanup() { if (IsRecording) { StopRecording(); } RemoveAllActors(); Recordings.Clear(); } } public class PlayerSync { public class RemoteBatteryState { public float Charge; public int LocationID; public bool InBackpack; public bool LeftHolding; public bool RightHolding; } private class PendingPuzzleInit { public bool[] ActiveStates; public int[] PresetIDs; public bool[] CompletedStates; public int[][] BlockStates; public int TotalCompleted; public int RequiredPuzzles; } private const byte PACKET_PLAYER_POSITION = 1; private const byte PACKET_FLASHLIGHT = 2; private const byte PACKET_SCENE_CHANGE = 3; private const byte PACKET_NIGHT_SELECTED = 4; private const byte PACKET_TV_SYNC = 5; private const byte PACKET_PAINTING_SYNC = 6; private const byte PACKET_PAINTING_FLASH = 7; private const byte PACKET_MONSTER_SYNC = 8; private const byte PACKET_JEFF_FLASH = 9; private const byte PACKET_BATTERY_SYNC = 10; private const byte PACKET_PAINTING_ENTITY = 11; private const byte PACKET_PUZZLE_INIT = 12; private const byte PACKET_PUZZLE_COMPLETE = 13; private const byte PACKET_PUZZLE_BLOCK = 14; private const byte PACKET_CLOWN_HONK = 15; private const byte PACKET_VENT_SOUND = 16; private const byte PACKET_CLOWN_STATE = 25; private const byte PACKET_CLOWN_ATTACK = 26; private const byte PACKET_SMILE_TRIGGER = 27; private const byte PACKET_VENT_DOOR = 28; private const byte PACKET_PAINTING_DEATH = 29; private const byte PACKET_END_PROGRESS = 40; private const byte PACKET_INTERACTION_LOCK = 17; private const byte PACKET_EXIT_DOOR_PROGRESS = 18; private const byte PACKET_CRANK_SYNC = 19; private const byte PACKET_DEATH_GHOST = 20; private const byte PACKET_VERSION_CHECK = 21; private const byte PACKET_PING = 22; private const byte PACKET_PONG = 23; private const byte PACKET_HOST_MIGRATION = 24; private const byte PACKET_EAR_COVERING = 41; private const byte PACKET_TARGETED_KILL = 42; private Dictionary _remotePlayers = new Dictionary(); private bool _remotePlayerCoveringEars; private Dictionary _peerVersions = new Dictionary(); private Dictionary _peerPings = new Dictionary(); private Dictionary _pendingPings = new Dictionary(); private float _lastPingTime; private const float PING_INTERVAL = 2f; private Vector3 _levelSpawnPoint = Vector3.zero; private Quaternion _levelSpawnRotation = Quaternion.identity; private bool _spawnPointCaptured; private HashSet _connectedPeerIds = new HashSet(); private bool _localIsGhost; private bool _pendingGhostTeleport; private bool _pendingHomeLoad; private float _pendingHomeLoadTimer; private string _ghostSceneToReload; public static bool IsGhostSceneReload = false; private Player _gorillaPlayer; private MoveTypeController _moveController; private HandControl _handControl; private Camera _mainCamera; private Transform _playerTransform; private Transform _ovrLeftHand; private Transform _ovrRightHand; private Transform _ovrHead; private float _syncInterval = 0.033f; private float _lastSyncTime; private Flashlight _localFlashlight; private FlashlightControl _localFlashlightControl; private bool _lastFlashlightState; private string _lastScene = ""; public static bool IsLoadingFromSync = false; private int _lastNightSelected = -1; private VideoPlayer _tvVideoPlayer; private float _lastTvSyncTime; private float _tvSyncInterval = 5f; private paintingControl _paintingControl; private float _lastPaintingSyncTime; private float _paintingSyncInterval = 1f; private sparkyBrain _sparky; private jeffBrain _jeff; private SmileBrain _smile; private henryBrain _henry; private mapEnBrain _harold; private clownRandom _clown; private float _lastMonsterSyncTime; private float _monsterSyncInterval = 0.1f; private bool _hostDiedInLevel; private float _lastBatterySyncTime; private float _batterySyncInterval = 0.1f; private int _lastBatteryLocationID = -999; private bool _lastBatteryInBackpack; private float _lastBatteryCharge = -1f; private bool _lastLeftHolding; private bool _lastRightHolding; private Dictionary _remoteBatteryStates = new Dictionary(); private crankControl _crankControl; private float _lastCrankSyncTime; private float _crankSyncInterval = 0.2f; private float _lastCrankCharge = -1f; private bool _lastCrankHasBattery; private static Dictionary _interactionLocks = new Dictionary(); private static Dictionary _lockTimestamps = new Dictionary(); private const float LOCK_TIMEOUT = 5f; private float _lastLockCleanupTime; private HashSet _localActiveLocks = new HashSet(); private float _sceneLoadDelayTimer; private const float SCENE_LOAD_DELAY = 0.5f; private PuzzleMaster _puzzleMaster; private bool _puzzleInitSent; private PendingPuzzleInit _savedGhostPuzzleState; private LeaveDoorControl _leaveDoor; private int _lastDoorLeaveTimer; private float _lastDoorSyncTime; private bool _exitDoorFullyCharged; private PacketWriter _writer = new PacketWriter(1024); private INetworkTransport _steam; private bool _recreateRemotePlayersNextFrame; private int _updateCount; private int _sendCount; private string _pendingSceneLoad; private bool _isReceivingPaintingFlash; private bool _isReceivingJeffFlash; private float _lastTargetedKillTime; private Quaternion _lastCrankRotation = Quaternion.identity; private bool _remoteCrankHasBattery; private float _remoteCrankChargeTarget; private float _remoteCrankChargeDisplay; private Quaternion _remoteCrankRotTarget = Quaternion.identity; private Quaternion _remoteCrankRotDisplay = Quaternion.identity; private int _lastTotalCompletedPuzzles = -1; private PendingPuzzleInit _pendingPuzzleInit; private bool _puzzleInitStartWaited; private bool _puzzleInitApplied; private float _puzzleInitReapplyTimer; private const float PUZZLE_INIT_REAPPLY_DURATION = 3f; private bool[] _puzzleInitCompletedStates; private bool[] _puzzleInitActiveStates; private HashSet _completedPuzzleIDs = new HashSet(); private bool _isReceivingPuzzleBlock; private bool _isReceivingHonk; private clownRandom _clownRandom; private bool _isReceivingVentSound; public bool DebugForceGhostOnDeath; private bool _pendingGhostPuzzleRestore; private int _ghostPuzzleRestoreFrames; private GameObject _ghostVisionLight; private float _ghostTeleportDelay; private Dictionary _ventDoorTimers = new Dictionary(); private EndControl _endControl; private int _lastEndImageID = -1; private float _lastEndBarFill = -1f; private bool _lastEarCoveringState; public bool IsAnyPlayerCoveringEars { get { if (!earMaster.isCoveringEars) { return _remotePlayerCoveringEars; } return true; } } public float AveragePing { get { if (_peerPings.Count <= 0) { return -1f; } return GetAveragePing(); } } public bool IsLocalGhost => _localIsGhost; public bool IsDyingThisFrame { get; set; } public bool ShouldControlMonsters { get { if (_steam != null) { if (!_steam.IsHost) { return _hostDiedInLevel; } return true; } return false; } } public bool IsReceivingPaintingFlash => _isReceivingPaintingFlash; public bool IsReceivingJeffFlash => _isReceivingJeffFlash; public bool RemoteHasBatteryInCrank => _remoteCrankHasBattery; public float RemoteCrankChargeDisplay => _remoteCrankChargeDisplay; public Quaternion RemoteCrankRotationDisplay => _remoteCrankRotDisplay; public bool PuzzleInitApplied { get { if (!_puzzleInitApplied) { return _pendingPuzzleInit != null; } return true; } } public bool IsReceivingPuzzleBlock => _isReceivingPuzzleBlock; public bool IsReceivingHonk => _isReceivingHonk; public bool IsReceivingVentSound => _isReceivingVentSound; public event Action OnVersionMismatch; public event Action OnBecameHost; public float GetPing(int peerId) { if (!_peerPings.TryGetValue(peerId, out var value)) { return -1f; } return value; } private float GetAveragePing() { if (_peerPings.Count == 0) { return -1f; } float num = 0f; foreach (float value in _peerPings.Values) { num += value; } return num / (float)_peerPings.Count; } public static bool IsLockedByOther(string interactionId) { if (_interactionLocks.TryGetValue(interactionId, out var value)) { if (_lockTimestamps.TryGetValue(interactionId, out var value2) && Time.time - value2 > 5f) { _interactionLocks.Remove(interactionId); _lockTimestamps.Remove(interactionId); return false; } return value != -1; } return false; } public static bool IsLockedByUs(string interactionId) { if (_interactionLocks.TryGetValue(interactionId, out var value)) { return value == -1; } return false; } public void RefreshLock(string interactionId) { if (_interactionLocks.TryGetValue(interactionId, out var value) && value != -1) { if (_lockTimestamps.TryGetValue(interactionId, out var value2) && Time.time - value2 > 5f) { _interactionLocks[interactionId] = -1; _lockTimestamps[interactionId] = Time.time; _localActiveLocks.Add(interactionId); SendInteractionLock(interactionId, locked: true); } return; } bool num = !_interactionLocks.ContainsKey(interactionId) || _interactionLocks[interactionId] != -1; _interactionLocks[interactionId] = -1; _lockTimestamps[interactionId] = Time.time; if (num) { _localActiveLocks.Add(interactionId); SendInteractionLock(interactionId, locked: true); } } public bool TryAcquireLock(string interactionId) { if (IsLockedByOther(interactionId)) { return false; } RefreshLock(interactionId); return true; } public void ReleaseLock(string interactionId) { if (_interactionLocks.TryGetValue(interactionId, out var value) && value == -1) { _interactionLocks.Remove(interactionId); _lockTimestamps.Remove(interactionId); _localActiveLocks.Remove(interactionId); SendInteractionLock(interactionId, locked: false); } } private void CleanupExpiredLocks() { if (Time.time - _lastLockCleanupTime < 1f) { return; } _lastLockCleanupTime = Time.time; List list = new List(); foreach (KeyValuePair lockTimestamp in _lockTimestamps) { if (Time.time - lockTimestamp.Value > 10f) { list.Add(lockTimestamp.Key); } } foreach (string item in list) { _interactionLocks.Remove(item); _lockTimestamps.Remove(item); _localActiveLocks.Remove(item); } } public void ClearAllLocks() { _interactionLocks.Clear(); _lockTimestamps.Clear(); _localActiveLocks.Clear(); } public void Initialize(INetworkTransport steam) { _steam = steam; _steam.OnPeerConnected += OnPeerConnected; _steam.OnPeerDisconnected += OnPeerDisconnected; _steam.OnDataReceived += OnDataReceived; SceneManager.sceneLoaded += OnSceneLoaded; } private void OnPeerConnected(int peerId) { _connectedPeerIds.Add(peerId); if (!_remotePlayers.ContainsKey(peerId)) { RemotePlayer value = new RemotePlayer(peerId); _remotePlayers[peerId] = value; } SendVersionCheck(peerId); SendBatterySync(); } private void OnPeerDisconnected(int peerId) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) _connectedPeerIds.Remove(peerId); _peerVersions.Remove(peerId); _peerPings.Remove(peerId); _pendingPings.Remove(peerId); if (_remotePlayers.TryGetValue(peerId, out var value)) { value.Destroy(); _remotePlayers.Remove(peerId); } MPManager.Instance?.Spectate?.StopReceiving(); MPManager.Instance?.Spectate?.StopSending(); if (_localIsGhost && _remotePlayers.Count == 0) { Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name.Contains("Night") && !_pendingHomeLoad) { _pendingHomeLoad = true; _pendingHomeLoadTimer = 1f; } } } private void OnDataReceived(int peerId, PacketReader reader) { if (reader.AvailableBytes >= 1) { byte b = reader.GetByte(); switch (b) { case 1: HandlePositionPacket(peerId, reader); break; case 2: HandleFlashlightPacket(peerId, reader); break; case 3: HandleSceneChangePacket(reader); break; case 4: HandleNightSelectedPacket(reader); break; case 5: HandleTvSyncPacket(reader); break; case 6: HandlePaintingSyncPacket(reader); break; case 7: HandlePaintingFlashPacket(reader); break; case 8: HandleMonsterSyncPacket(reader); break; case 9: HandleJeffFlashPacket(); break; case 10: HandleBatterySync(peerId, reader); break; case 11: HandlePaintingEntity(reader); break; case 12: HandlePuzzleInit(reader); break; case 13: HandlePuzzleComplete(reader); break; case 14: HandlePuzzleBlock(reader); break; case 15: HandleClownHonk(reader); break; case 16: HandleVentSoundPacket(reader); break; case 17: HandleInteractionLockPacket(peerId, reader); break; case 18: HandleExitDoorProgressPacket(reader); break; case 19: HandleCrankSyncPacket(reader); break; case 20: HandleDeathGhostPacket(peerId, reader); break; case 21: HandleVersionCheck(peerId, reader); break; case 22: HandlePing(peerId, reader); break; case 23: HandlePong(peerId, reader); break; case 24: HandleHostMigration(peerId, reader); break; case 25: HandleClownState(reader); break; case 26: HandleClownAttack(reader); break; case 27: HandleSmileTrigger(reader); break; case 28: HandleVentDoor(reader); break; case 29: HandlePaintingDeath(); break; case 40: HandleEndProgress(reader); break; case 41: HandleEarCovering(reader); break; case 42: HandleTargetedKill(reader); break; case 200: MPManager.Instance?.VoiceChat?.OnVoiceDataReceived(peerId, reader); break; case 30: case 31: case 32: MPManager.Instance?.Spectate?.OnPacketReceived(b, reader); break; default: Plugin.Log.LogWarning((object)$"Unknown packet type: {b}"); break; } } } private void HandlePositionPacket(int peerId, PacketReader reader) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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) //IL_0124: 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) if (_remotePlayers.TryGetValue(peerId, out var value)) { bool isStanding = reader.GetBool(); Vector3 bodyPos = default(Vector3); ((Vector3)(ref bodyPos))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); Quaternion bodyRot = default(Quaternion); ((Quaternion)(ref bodyRot))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); Vector3 headPos = default(Vector3); ((Vector3)(ref headPos))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); Quaternion headRot = default(Quaternion); ((Quaternion)(ref headRot))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); Vector3 leftHandPos = default(Vector3); ((Vector3)(ref leftHandPos))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); Quaternion leftHandRot = default(Quaternion); ((Quaternion)(ref leftHandRot))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); Vector3 rightHandPos = default(Vector3); ((Vector3)(ref rightHandPos))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); Quaternion rightHandRot = default(Quaternion); ((Quaternion)(ref rightHandRot))..ctor(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); float leftGrip = reader.GetFloat(); float leftTrigger = reader.GetFloat(); float rightGrip = reader.GetFloat(); float rightTrigger = reader.GetFloat(); value.SetTargets(isStanding, bodyPos, bodyRot, headPos, headRot, leftHandPos, leftHandRot, rightHandPos, rightHandRot, leftGrip, leftTrigger, rightGrip, rightTrigger); } } private void HandleFlashlightPacket(int peerId, PacketReader reader) { bool flashlightState = reader.GetBool(); if (_remotePlayers.TryGetValue(peerId, out var value)) { value.SetFlashlightState(flashlightState); } } private void HandleSceneChangePacket(PacketReader reader) { //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) string text = reader.GetString(); int nightSelected = reader.GetInt(); Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (text.Equals("Home", StringComparison.OrdinalIgnoreCase) && name.Contains("Night")) { sceneLeave val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { val.loadSelectedNight(); } } calenderControl.nightSelected = nightSelected; if (_steam.IsHost && _localIsGhost) { IsLoadingFromSync = true; ResetGhostState(); SceneManager.LoadScene(text); } else if (_steam.IsHost) { IsLoadingFromSync = true; TriggerClientFade(); SceneManager.LoadScene(text); } else { IsLoadingFromSync = true; _lastScene = text; _pendingSceneLoad = text; _sceneLoadDelayTimer = 0.1f; TriggerClientFade(); } } private void HandleNightSelectedPacket(PacketReader reader) { calenderControl.nightSelected = reader.GetInt(); calenderControl val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { val.setNightText(); } } private void HandleTvSyncPacket(PacketReader reader) { long num = reader.GetLong(); if ((Object)(object)_tvVideoPlayer == (Object)null) { tvControl val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && (Object)(object)val.TVVP != (Object)null) { _tvVideoPlayer = val.TVVP; } } if ((Object)(object)_tvVideoPlayer != (Object)null && _tvVideoPlayer.isPrepared && Math.Abs(_tvVideoPlayer.frame - num) > 30) { _tvVideoPlayer.frame = num; } } private void HandlePaintingSyncPacket(PacketReader reader) { if ((Object)(object)_paintingControl == (Object)null) { _paintingControl = Object.FindObjectOfType(); } if (!((Object)(object)_paintingControl == (Object)null)) { int num = reader.GetInt(); int num2 = reader.GetInt(); int num3 = reader.GetInt(); int num4 = reader.GetInt(); int num5 = reader.GetInt(); int num6 = reader.GetInt(); _paintingControl.intpaintingTall1 = num; _paintingControl.intpaintingTall2 = num2; _paintingControl.intpaintingTall3 = num3; _paintingControl.intpaintingSquare1 = num4; _paintingControl.intpaintingSquare2 = num5; _paintingControl.intpaintingSquare3 = num6; Type? typeFromHandle = typeof(paintingControl); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; bool flag = (bool)typeFromHandle.GetField("boolpaintingTall1", bindingAttr).GetValue(_paintingControl); bool flag2 = (bool)typeFromHandle.GetField("boolpaintingTall2", bindingAttr).GetValue(_paintingControl); bool flag3 = (bool)typeFromHandle.GetField("boolpaintingTall3", bindingAttr).GetValue(_paintingControl); bool flag4 = (bool)typeFromHandle.GetField("boolpaintingSquare1", bindingAttr).GetValue(_paintingControl); bool flag5 = (bool)typeFromHandle.GetField("boolpaintingSquare2", bindingAttr).GetValue(_paintingControl); bool flag6 = (bool)typeFromHandle.GetField("boolpaintingSquare3", bindingAttr).GetValue(_paintingControl); _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall1, num, flag, true); _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall2, num2, flag2, true); _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall3, num3, flag3, true); _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare1, num4, flag4, false); _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare2, num5, flag5, false); _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare3, num6, flag6, false); } } private void HandlePaintingFlashPacket(PacketReader reader) { int num = reader.GetInt(); _isReceivingPaintingFlash = true; paintingControl paintingControl = _paintingControl; if (paintingControl != null) { paintingControl.onPaintingFlash(num); } _isReceivingPaintingFlash = false; } private void HandleMonsterSyncPacket(PacketReader reader) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: 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_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) if (_steam.IsHost || _hostDiedInLevel) { return; } FindMonsters(); if (reader.GetBool()) { int num = reader.GetInt(); if ((Object)(object)_sparky != (Object)null) { FieldInfo field = typeof(sparkyBrain).GetField("currentState", BindingFlags.Instance | BindingFlags.NonPublic); int num2 = ((!(field != null)) ? 1 : ((int)field.GetValue(_sparky))); if (num == 2 && num2 == 1) { field?.SetValue(_sparky, num); } } } if (reader.GetBool()) { reader.GetBool(); int num3 = reader.GetInt(); reader.GetInt(); if ((Object)(object)_jeff != (Object)null) { FieldInfo field2 = typeof(jeffBrain).GetField("currentState", BindingFlags.Instance | BindingFlags.NonPublic); int num4 = ((!(field2 != null)) ? 1 : ((int)field2.GetValue(_jeff))); if (num3 == 2 && num4 == 1) { field2?.SetValue(_jeff, 2); } } } if (reader.GetBool()) { reader.GetBool(); reader.GetInt(); } if (reader.GetBool()) { Vector3 position = ReadVector3(reader); Quaternion rotation = ReadQuaternion(reader); bool flag = reader.GetBool(); bool flag2 = reader.GetBool(); if ((Object)(object)_henry != (Object)null) { if ((Object)(object)_henry.agent != (Object)null && ((Behaviour)_henry.agent).enabled) { ((Behaviour)_henry.agent).enabled = false; } ((Component)_henry).transform.position = position; ((Component)_henry).transform.rotation = rotation; typeof(henryBrain).GetField("resetSwitch", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_henry, flag); typeof(henryBrain).GetField("chaseSwitch", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_henry, flag2); } } if (reader.GetBool()) { Vector3 position2 = ReadVector3(reader); Quaternion rotation2 = ReadQuaternion(reader); if ((Object)(object)_harold != (Object)null) { if ((Object)(object)_harold.agent != (Object)null && ((Behaviour)_harold.agent).enabled) { ((Behaviour)_harold.agent).enabled = false; } ((Component)_harold).transform.position = position2; ((Component)_harold).transform.rotation = rotation2; } } if (!reader.GetBool()) { return; } int num5 = reader.GetInt(); if ((Object)(object)_clown != (Object)null) { if ((Object)(object)_clown.clown1 != (Object)null) { _clown.clown1.SetActive(num5 == 0); } if ((Object)(object)_clown.clown2 != (Object)null) { _clown.clown2.SetActive(num5 == 1); } if ((Object)(object)_clown.clown3 != (Object)null) { _clown.clown3.SetActive(num5 == 2); } if ((Object)(object)_clown.clown4 != (Object)null) { _clown.clown4.SetActive(num5 == 3); } if ((Object)(object)_clown.clown5 != (Object)null) { _clown.clown5.SetActive(num5 == 4); } if ((Object)(object)_clown.clown6 != (Object)null) { _clown.clown6.SetActive(num5 == 5); } if ((Object)(object)_clown.clown7 != (Object)null) { _clown.clown7.SetActive(num5 == 6); } } } private void HandleJeffFlashPacket() { _isReceivingJeffFlash = true; jeffBrain jeff = _jeff; if (jeff != null) { jeff.onFlash(); } _isReceivingJeffFlash = false; } private void HandleVentSoundPacket(PacketReader reader) { HandleVentSound(reader); } private void HandleInteractionLockPacket(int peerId, PacketReader reader) { string key = reader.GetString(); if (reader.GetBool()) { _interactionLocks[key] = peerId; _lockTimestamps[key] = Time.time; } else { _interactionLocks.Remove(key); _lockTimestamps.Remove(key); } } private void HandleExitDoorProgressPacket(PacketReader reader) { int num = reader.GetInt(); int num2 = reader.GetInt(); if (BackpackControl.batteryLocationID == 100 && BackpackControl.batteryCharge >= 0.1f) { return; } if ((Object)(object)_leaveDoor == (Object)null) { _leaveDoor = Object.FindObjectOfType(); } if (!((Object)(object)_leaveDoor != (Object)null)) { return; } typeof(LeaveDoorControl).GetField("doorLeaveTimer", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_leaveDoor, num); typeof(LeaveDoorControl).GetField("loadingBarLock", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_leaveDoor, num > 0); if ((Object)(object)_leaveDoor.fillImg != (Object)null && num2 > 0) { _leaveDoor.fillImg.fillAmount = (float)num / (float)num2; } if ((Object)(object)_leaveDoor.fillIcon != (Object)null) { _leaveDoor.fillIcon.SetActive(num > 0); } FieldInfo field = typeof(LeaveDoorControl).GetField("puzzleCount", BindingFlags.Instance | BindingFlags.Public); if (field != null && num > 0 && num2 > 0) { object value = field.GetValue(_leaveDoor); if (value != null) { PropertyInfo property = value.GetType().GetProperty("text"); if (property != null) { int num3 = (int)Math.Round((float)num / (float)num2 * 100f); if (num >= num2) { property.SetValue(value, "100%"); if ((Object)(object)_leaveDoor.doorLeaveHitbox != (Object)null) { _leaveDoor.doorLeaveHitbox.SetActive(true); } _exitDoorFullyCharged = true; } else { property.SetValue(value, num3 + "%"); } } } } if (num > 0) { if ((Object)(object)_leaveDoor.greenIcon != (Object)null) { _leaveDoor.greenIcon.SetActive(false); } if ((Object)(object)_leaveDoor.redIcon != (Object)null) { _leaveDoor.redIcon.SetActive(false); } } } private void HandleCrankSyncPacket(PacketReader reader) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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) bool flag = reader.GetBool(); float num = reader.GetFloat(); Quaternion remoteCrankRotTarget = ReadQuaternion(reader); _remoteCrankHasBattery = flag; _remoteCrankChargeTarget = num; _remoteCrankRotTarget = remoteCrankRotTarget; if (flag && _remoteCrankChargeDisplay < 0.01f) { _remoteCrankChargeDisplay = num; } if (!flag) { _remoteCrankChargeTarget = 0f; _remoteCrankChargeDisplay = 0f; } } private void HandleDeathGhostPacket(int peerId, PacketReader reader) { //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) bool flag = reader.GetBool(); int num = reader.GetInt(); Plugin.Log.LogInfo((object)$"[Death] Received death packet from peer {peerId}: isGhost={flag}, deathType={num}"); if (_remotePlayers.TryGetValue(peerId, out var value)) { value.SetGhostState(flag); Plugin.Log.LogInfo((object)$"[Death] Set peer {peerId} ghost state to {flag}"); } if (flag && _remoteBatteryStates.ContainsKey(peerId)) { _remoteBatteryStates[peerId].LocationID = 0; _remoteBatteryStates[peerId].Charge = 0f; _remoteBatteryStates[peerId].LeftHolding = false; _remoteBatteryStates[peerId].RightHolding = false; _remoteBatteryStates[peerId].InBackpack = false; Plugin.LogDebug($"[Death] Cleared battery state for dead peer {peerId}"); } if (flag) { List list = new List(); foreach (KeyValuePair interactionLock in _interactionLocks) { if (interactionLock.Value == peerId) { list.Add(interactionLock.Key); } } foreach (string item in list) { _interactionLocks.Remove(item); _lockTimestamps.Remove(item); } if (list.Count > 0) { Plugin.LogDebug($"[Death] Cleared {list.Count} interaction locks from dead peer {peerId}"); } } _remoteCrankHasBattery = false; Scene activeScene; if (_localIsGhost && flag) { bool flag2 = false; foreach (KeyValuePair remotePlayer in _remotePlayers) { if (!remotePlayer.Value.IsGhost) { flag2 = true; break; } } if (!flag2) { activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name.Contains("Night")) { Plugin.Log.LogInfo((object)"[Ghost] All players dead - scheduling Home load"); _pendingHomeLoad = true; _pendingHomeLoadTimer = 3f; return; } } } Plugin.LogDebug($"[Death] Partner death handled - LocalGhost={_localIsGhost}, RemoteGhost={flag}"); activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!flag || _localIsGhost || !name.Contains("Night")) { return; } MPManager.Instance?.Spectate?.StartSending(); if (_steam.IsHost) { return; } _hostDiedInLevel = true; Plugin.Log.LogInfo((object)"[Client] Host died - taking over monster control!"); FindMonsters(); if ((Object)(object)_sparky != (Object)null) { NavMeshAgent component = ((Component)_sparky).GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = true; } } if ((Object)(object)_jeff != (Object)null) { NavMeshAgent component2 = ((Component)_jeff).GetComponent(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = true; } } if ((Object)(object)_smile != (Object)null) { NavMeshAgent component3 = ((Component)_smile).GetComponent(); if ((Object)(object)component3 != (Object)null) { ((Behaviour)component3).enabled = true; } } if ((Object)(object)_henry != (Object)null) { NavMeshAgent component4 = ((Component)_henry).GetComponent(); if ((Object)(object)component4 != (Object)null) { ((Behaviour)component4).enabled = true; } } if ((Object)(object)_harold != (Object)null) { NavMeshAgent component5 = ((Component)_harold).GetComponent(); if ((Object)(object)component5 != (Object)null) { ((Behaviour)component5).enabled = true; } } } private void SendToAllPeers(bool reliable = true) { if (_steam != null && _steam.IsRunning) { byte[] bytes = _writer.GetBytes(); _steam.SendToAll(bytes, reliable); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { Plugin.Log.LogInfo((object)$"OnSceneLoaded: {((Scene)(ref scene)).name}, IsGhost={_localIsGhost}"); MinimapFriendPatch.Cleanup(); ClearAllLocks(); _spawnPointCaptured = false; _hostDiedInLevel = false; if (_localIsGhost && _pendingGhostTeleport && ((Scene)(ref scene)).name.Contains("Night")) { Plugin.Log.LogInfo((object)"[Ghost] Scene loaded as ghost, preparing teleport"); HandleGhostSceneLoaded(); } if (((Scene)(ref scene)).name.Equals("Home", StringComparison.OrdinalIgnoreCase)) { if (_localIsGhost) { Plugin.Log.LogInfo((object)"[Ghost] Entered Home, resetting ghost state"); ResetGhostState(); } _pendingPuzzleInit = null; } if (((Scene)(ref scene)).name.Equals("Home", StringComparison.OrdinalIgnoreCase) || !((Scene)(ref scene)).name.Contains("Night")) { MPManager.Instance?.Spectate?.StopSending(); MPManager.Instance?.Spectate?.StopReceiving(); } if (((Scene)(ref scene)).name == _lastScene && !IsLoadingFromSync) { if (_steam == null || !_steam.IsRunning || _connectedPeerIds.Count <= 0) { return; } foreach (RemotePlayer value in _remotePlayers.Values) { value.Destroy(); } _remotePlayers.Clear(); _recreateRemotePlayersNextFrame = true; return; } Plugin.Log.LogInfo((object)("Scene loaded: " + ((Scene)(ref scene)).name)); IsLoadingFromSync = false; _lastScene = ((Scene)(ref scene)).name; foreach (RemotePlayer value2 in _remotePlayers.Values) { value2.Destroy(); } _remotePlayers.Clear(); _remoteBatteryStates.Clear(); _puzzleMaster = null; _puzzleInitSent = false; _puzzleInitStartWaited = false; _puzzleInitApplied = false; _puzzleInitReapplyTimer = 0f; _puzzleInitCompletedStates = null; _puzzleInitActiveStates = null; _completedPuzzleIDs.Clear(); ClearAllLocks(); if (_steam != null && _steam != null && _steam.IsRunning) { _recreateRemotePlayersNextFrame = true; } } public void Cleanup() { SceneManager.sceneLoaded -= OnSceneLoaded; ClearRemotePlayers(); ClearAllLocks(); ResetGhostState(); _connectedPeerIds.Clear(); _remoteBatteryStates.Clear(); _pendingPuzzleInit = null; Plugin.Log.LogInfo((object)"PlayerSync cleaned up"); } public void Update() { //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) _updateCount++; if (_steam == null) { return; } UpdateCrankInterpolation(); IsDyingThisFrame = false; if (_steam.IsRunning && _steam.IsConnected && Time.realtimeSinceStartup - _lastPingTime > 2f) { _lastPingTime = Time.realtimeSinceStartup; SendPingToAll(); } if (_pendingHomeLoad) { _pendingHomeLoadTimer -= Time.deltaTime; if (_pendingHomeLoadTimer <= 0f) { _pendingHomeLoad = false; bool flag = true; foreach (KeyValuePair remotePlayer in _remotePlayers) { if (!remotePlayer.Value.IsGhost) { flag = false; break; } } if (flag && _localIsGhost) { Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name.Contains("Night")) { Plugin.Log.LogInfo((object)"[Ghost] EXECUTING DEFERRED HOME LOAD - all players dead"); ResetGhostState(); IsLoadingFromSync = true; SceneManager.LoadScene("Home"); return; } } } } if (_pendingSceneLoad != null) { if (_sceneLoadDelayTimer > 0f) { _sceneLoadDelayTimer -= Time.deltaTime; return; } string pendingSceneLoad = _pendingSceneLoad; _pendingSceneLoad = null; _sceneLoadDelayTimer = 0f; Plugin.Log.LogInfo((object)("[Client] EXECUTING DEFERRED SCENE LOAD: " + pendingSceneLoad)); try { TriggerClientFade(); SceneManager.LoadScene(pendingSceneLoad); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Client] SceneManager.LoadScene FAILED: {arg}"); } } if (_recreateRemotePlayersNextFrame) { _recreateRemotePlayersNextFrame = false; RecreateRemotePlayers(); } UpdateGhostTeleport(); if (_localIsGhost && _spawnPointCaptured && _ghostTeleportDelay <= 0f) { Camera val = _mainCamera ?? Camera.main; if ((Object)(object)val != (Object)null && ((Component)val).transform.position.y < -5f) { Plugin.Log.LogInfo((object)$"[Ghost] Fell through map (Y={((Component)_mainCamera).transform.position.y:F1}), re-teleporting to spawn"); TeleportLocalPlayer(_levelSpawnPoint); } } if (_pendingGhostPuzzleRestore) { _ghostPuzzleRestoreFrames--; if (_ghostPuzzleRestoreFrames <= 0) { _pendingGhostPuzzleRestore = false; RestoreGhostPuzzleState(); } } foreach (RemotePlayer value in _remotePlayers.Values) { value.UpdateInterpolation(); } if (!_steam.IsRunning || (!_steam.IsHost && !_steam.IsConnected && !_steam.IsInLobby)) { return; } if (!_spawnPointCaptured && _updateCount > 10) { CaptureSpawnPoint(); } if (_updateCount % 300 == 0) { Plugin.LogDebug($"PlayerSync.Update: remotePlayers={_remotePlayers.Count}, ping={AveragePing:F0}ms"); } if ((Object)(object)_gorillaPlayer == (Object)null && (Object)(object)_handControl == (Object)null && (Object)(object)_mainCamera == (Object)null && (Object)(object)_ovrHead == (Object)null) { _gorillaPlayer = Player.Instance; _handControl = Object.FindObjectOfType(); _moveController = Object.FindObjectOfType(); _mainCamera = Camera.main; MonoBehaviour[] array = Object.FindObjectsOfType(); foreach (MonoBehaviour val2 in array) { Type type = ((object)val2).GetType(); if (type.Name == "OVRCameraRig") { object? obj = type.GetProperty("centerEyeAnchor")?.GetValue(val2); Transform val3 = (Transform)((obj is Transform) ? obj : null); object? obj2 = type.GetProperty("leftHandAnchor")?.GetValue(val2); Transform val4 = (Transform)((obj2 is Transform) ? obj2 : null); object? obj3 = type.GetProperty("rightHandAnchor")?.GetValue(val2); Transform val5 = (Transform)((obj3 is Transform) ? obj3 : null); if ((Object)(object)val3 != (Object)null && (Object)(object)val4 != (Object)null && (Object)(object)val5 != (Object)null) { _ovrHead = val3; _ovrLeftHand = val4; _ovrRightHand = val5; } break; } } if ((Object)(object)_ovrLeftHand == (Object)null || (Object)(object)_ovrRightHand == (Object)null) { BackpackControl val6 = Object.FindObjectOfType(); if ((Object)(object)val6 != (Object)null) { if ((Object)(object)val6.leftHand != (Object)null) { _ovrLeftHand = val6.leftHand.transform; } if ((Object)(object)val6.rightHand != (Object)null) { _ovrRightHand = val6.rightHand.transform; } if ((Object)(object)val6.cam != (Object)null && (Object)(object)_ovrHead == (Object)null) { _ovrHead = val6.cam.transform; } } } if ((Object)(object)_ovrLeftHand == (Object)null || (Object)(object)_ovrRightHand == (Object)null) { ActionBasedController[] array2 = Object.FindObjectsOfType(); foreach (ActionBasedController val7 in array2) { string text = ((Object)((Component)val7).gameObject).name.ToLower(); if (text.Contains("left") && (Object)(object)_ovrLeftHand == (Object)null) { _ovrLeftHand = ((Component)val7).transform; } else if (text.Contains("right") && (Object)(object)_ovrRightHand == (Object)null) { _ovrRightHand = ((Component)val7).transform; } } } if ((Object)(object)_ovrLeftHand == (Object)null || (Object)(object)_ovrRightHand == (Object)null) { crankControl val8 = Object.FindObjectOfType(); if ((Object)(object)val8 != (Object)null) { if ((Object)(object)val8.handLeft != (Object)null && (Object)(object)_ovrLeftHand == (Object)null) { _ovrLeftHand = val8.handLeft.transform; } if ((Object)(object)val8.handRight != (Object)null && (Object)(object)_ovrRightHand == (Object)null) { _ovrRightHand = val8.handRight.transform; } } } Plugin.Log.LogInfo((object)("[Tracking] Head=" + (((Object)(object)_ovrHead != (Object)null) ? ((Object)_ovrHead).name : "NONE") + ", LHand=" + (((Object)(object)_ovrLeftHand != (Object)null) ? ((Object)_ovrLeftHand).name : "NONE") + ", RHand=" + (((Object)(object)_ovrRightHand != (Object)null) ? ((Object)_ovrRightHand).name : "NONE"))); if ((Object)(object)_gorillaPlayer != (Object)null) { _playerTransform = ((Component)_gorillaPlayer).transform; } } if (!((Object)(object)_gorillaPlayer == (Object)null) || !((Object)(object)_handControl == (Object)null) || !((Object)(object)_mainCamera == (Object)null)) { if (Time.time - _lastSyncTime >= _syncInterval) { SendPositionUpdate(); _lastSyncTime = Time.time; } CheckFlashlightState(); CheckSceneChange(); CheckCalendarChange(); CheckTvSync(); CheckPaintingSync(); CheckMonsterSync(); CheckBatterySync(); CheckCrankSync(); CheckPuzzleInitSync(); if (_pendingPuzzleInit != null && !_steam.IsHost) { TryApplyPuzzleInit(); } ReapplyPuzzleIndicators(); CheckExitDoorSync(); CheckEndSceneSync(); CheckEarCoveringSync(); CleanupExpiredLocks(); } } private void CheckCalendarChange() { if (_steam.IsHost) { int nightSelected = calenderControl.nightSelected; if (nightSelected != _lastNightSelected) { _lastNightSelected = nightSelected; SendNightSelected(nightSelected); } } } private void CheckTvSync() { if (!_steam.IsHost || _localIsGhost || Time.time - _lastTvSyncTime < _tvSyncInterval) { return; } _lastTvSyncTime = Time.time; if ((Object)(object)_tvVideoPlayer == (Object)null) { tvControl val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && (Object)(object)val.TVVP != (Object)null) { _tvVideoPlayer = val.TVVP; } else { MoviePlayerSample val2 = Object.FindObjectOfType(); if ((Object)(object)val2 != (Object)null) { _tvVideoPlayer = ((Component)val2).GetComponent(); if ((Object)(object)_tvVideoPlayer != (Object)null) { Plugin.LogDebug("Found TV VideoPlayer"); } } } } if ((Object)(object)_tvVideoPlayer != (Object)null && _tvVideoPlayer.isPlaying) { SendTvSync(_tvVideoPlayer.frame); } } private void SendTvSync(long frame) { _writer.Reset(); _writer.Put((byte)5); _writer.Put(frame); SendToAllPeers(); } private void CheckPaintingSync() { if (_steam.IsHost && !_localIsGhost && !(Time.time - _lastPaintingSyncTime < _paintingSyncInterval)) { _lastPaintingSyncTime = Time.time; if ((Object)(object)_paintingControl == (Object)null) { _paintingControl = Object.FindObjectOfType(); } if ((Object)(object)_paintingControl != (Object)null) { SendPaintingSync(); } } } private void SendPaintingSync() { _writer.Reset(); _writer.Put((byte)6); _writer.Put(_paintingControl.intpaintingTall1); _writer.Put(_paintingControl.intpaintingTall2); _writer.Put(_paintingControl.intpaintingTall3); _writer.Put(_paintingControl.intpaintingSquare1); _writer.Put(_paintingControl.intpaintingSquare2); _writer.Put(_paintingControl.intpaintingSquare3); SendToAllPeers(); } private void SendNightSelected(int night) { _writer.Reset(); _writer.Put((byte)4); _writer.Put(night); SendToAllPeers(); Plugin.Log.LogInfo((object)$"[Host] Sent night selection: {night}"); } private void CheckSceneChange() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!(name != _lastScene)) { return; } Plugin.Log.LogInfo((object)("Scene changed: " + _lastScene + " -> " + name)); _ = _lastScene; _lastScene = name; if (_steam.IsHost && !IsLoadingFromSync) { SendSceneChange(name); } _gorillaPlayer = null; _handControl = null; _mainCamera = null; _moveController = null; _ovrHead = null; _ovrLeftHand = null; _ovrRightHand = null; _localFlashlight = null; _localFlashlightControl = null; _tvVideoPlayer = null; _paintingControl = null; _sparky = null; _jeff = null; _smile = null; _henry = null; _harold = null; _clown = null; _puzzleMaster = null; _puzzleInitSent = false; _puzzleInitStartWaited = false; _puzzleInitApplied = false; _puzzleInitReapplyTimer = 0f; _puzzleInitCompletedStates = null; _puzzleInitActiveStates = null; _completedPuzzleIDs.Clear(); _leaveDoor = null; _lastDoorLeaveTimer = 0; _exitDoorFullyCharged = false; _crankControl = null; _lastCrankCharge = -1f; _lastCrankHasBattery = false; if (_steam == null || !_steam.IsRunning || _connectedPeerIds.Count <= 0) { return; } foreach (RemotePlayer value in _remotePlayers.Values) { value.Destroy(); } _remotePlayers.Clear(); _remoteBatteryStates.Clear(); ClearAllLocks(); _recreateRemotePlayersNextFrame = true; } private bool AnyRemotePlayerValid() { foreach (RemotePlayer value in _remotePlayers.Values) { if ((Object)(object)value.Head != (Object)null) { return true; } } return false; } private void ClearRemotePlayers() { foreach (RemotePlayer value in _remotePlayers.Values) { value.Destroy(); } _remotePlayers.Clear(); } private void RecreateRemotePlayers() { if (_steam == null || _steam == null) { return; } foreach (int connectedPeerId in _connectedPeerIds) { if (!_remotePlayers.ContainsKey(connectedPeerId)) { RemotePlayer value = new RemotePlayer(connectedPeerId); _remotePlayers[connectedPeerId] = value; } else { _remotePlayers[connectedPeerId].TryUpgradeVisuals(); } } Plugin.Log.LogInfo((object)$"Remote players recreated: {_remotePlayers.Count}"); } public void SendSceneChange(string sceneName) { _writer.Reset(); _writer.Put((byte)3); _writer.Put(sceneName); _writer.Put(calenderControl.nightSelected); SendToAllPeers(); Plugin.Log.LogInfo((object)$"Sent scene change: {sceneName}, night={calenderControl.nightSelected}"); } private void CheckFlashlightState() { bool flag = false; bool flag2 = false; if ((Object)(object)_localFlashlightControl == (Object)null) { _localFlashlightControl = Object.FindObjectOfType(); } if ((Object)(object)_localFlashlightControl != (Object)null && (Object)(object)_localFlashlightControl.flashlight != (Object)null) { flag = _localFlashlightControl.flashlight.activeSelf; flag2 = true; } if (!flag2) { if ((Object)(object)_localFlashlight == (Object)null) { _localFlashlight = Object.FindObjectOfType(); } if ((Object)(object)_localFlashlight != (Object)null && (Object)(object)_localFlashlight.spotlight != (Object)null) { flag = ((Behaviour)_localFlashlight.spotlight).enabled; flag2 = true; } } if (flag2 && flag != _lastFlashlightState) { _lastFlashlightState = flag; SendFlashlightState(flag); } } private void SendFlashlightState(bool isOn) { _writer.Reset(); _writer.Put((byte)2); _writer.Put(isOn); SendToAllPeers(); } private void CaptureHandPoses(ref float leftGrip, ref float leftTrigger, ref float rightGrip, ref float rightTrigger) { //IL_0027: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_008e: Unknown result type (might be due to invalid IL or missing references) BackpackControl val = Object.FindObjectOfType(); if (!((Object)(object)val != (Object)null)) { return; } InputActionProperty val2; if ((Object)(object)val.controllerLeft != (Object)null) { try { val2 = val.controllerLeft.selectActionValue; leftGrip = ((InputActionProperty)(ref val2)).action.ReadValue(); val2 = val.controllerLeft.uiPressActionValue; leftTrigger = ((InputActionProperty)(ref val2)).action.ReadValue(); } catch (Exception) { } } if ((Object)(object)val.controllerRight != (Object)null) { try { val2 = val.controllerRight.selectActionValue; rightGrip = ((InputActionProperty)(ref val2)).action.ReadValue(); val2 = val.controllerRight.uiPressActionValue; rightTrigger = ((InputActionProperty)(ref val2)).action.ReadValue(); } catch (Exception) { } } } private void SendPositionUpdate() { //IL_0081: 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_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) //IL_0099: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00be: 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) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: 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_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0341: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03d7: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: 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_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) _writer.Reset(); _writer.Put((byte)1); bool value = (Object)(object)_moveController != (Object)null && _moveController.debugSwitch; _sendCount++; string text = "none"; Vector3 position; Quaternion rotation; Vector3 v; Quaternion q; Vector3 v2; Quaternion q2; if ((Object)(object)_ovrHead != (Object)null && (Object)(object)_ovrLeftHand != (Object)null && (Object)(object)_ovrRightHand != (Object)null) { text = "OVR/BackpackControl"; position = _ovrHead.position; rotation = _ovrHead.rotation; v = _ovrLeftHand.position; q = _ovrLeftHand.rotation; v2 = _ovrRightHand.position; q2 = _ovrRightHand.rotation; } else if ((Object)(object)_gorillaPlayer != (Object)null && (Object)(object)_gorillaPlayer.leftHandTransform != (Object)null && (Object)(object)_gorillaPlayer.rightHandTransform != (Object)null) { text = "GorillaPlayer"; position = ((Component)_gorillaPlayer.headCollider).transform.position; rotation = ((Component)_gorillaPlayer.headCollider).transform.rotation; v = _gorillaPlayer.leftHandTransform.position; q = _gorillaPlayer.leftHandTransform.rotation; v2 = _gorillaPlayer.rightHandTransform.position; q2 = _gorillaPlayer.rightHandTransform.rotation; } else if ((Object)(object)_handControl != (Object)null && (Object)(object)_handControl.LTarget != (Object)null && (Object)(object)_handControl.RTarget != (Object)null) { text = "HandControl"; position = _handControl.Head.transform.position; rotation = _handControl.Head.transform.rotation; v = _handControl.LTarget.transform.position; q = _handControl.LTarget.transform.rotation; v2 = _handControl.RTarget.transform.position; q2 = _handControl.RTarget.transform.rotation; } else { if (!((Object)(object)_mainCamera != (Object)null)) { return; } text = "MainCamera-NoHands"; position = ((Component)_mainCamera).transform.position; rotation = ((Component)_mainCamera).transform.rotation; v = position + ((Component)_mainCamera).transform.right * -0.3f + ((Component)_mainCamera).transform.forward * 0.2f; q = rotation; v2 = position + ((Component)_mainCamera).transform.right * 0.3f + ((Component)_mainCamera).transform.forward * 0.2f; q2 = rotation; } Vector3 v3 = position - Vector3.up * 0.5f; Quaternion q3 = Quaternion.Euler(0f, ((Quaternion)(ref rotation)).eulerAngles.y, 0f); float leftGrip = 0f; float leftTrigger = 0f; float rightGrip = 0f; float rightTrigger = 0f; CaptureHandPoses(ref leftGrip, ref leftTrigger, ref rightGrip, ref rightTrigger); _writer.Put(value); WriteVector3(_writer, v3); WriteQuaternion(_writer, q3); WriteVector3(_writer, position); WriteQuaternion(_writer, rotation); WriteVector3(_writer, v); WriteQuaternion(_writer, q); WriteVector3(_writer, v2); WriteQuaternion(_writer, q2); _writer.Put(leftGrip); _writer.Put(leftTrigger); _writer.Put(rightGrip); _writer.Put(rightTrigger); if (_sendCount % 100 == 1) { Plugin.LogDebug("[Send] src=" + text); } SendToAllPeers(reliable: false); } private void WriteVector3(PacketWriter writer, Vector3 v) { //IL_0001: 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_0019: Unknown result type (might be due to invalid IL or missing references) writer.Put(v.x); writer.Put(v.y); writer.Put(v.z); } private void WriteQuaternion(PacketWriter writer, Quaternion q) { //IL_0001: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) writer.Put(q.x); writer.Put(q.y); writer.Put(q.z); writer.Put(q.w); } private Vector3 ReadVector3(PacketReader reader) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); } private Quaternion ReadQuaternion(PacketReader reader) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(reader.GetFloat(), reader.GetFloat(), reader.GetFloat(), reader.GetFloat()); } private void SendInteractionLock(string interactionId, bool locked) { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)17); _writer.Put(interactionId); _writer.Put(locked); SendToAllPeers(); } } private void TriggerClientFade() { try { Type type = Type.GetType("fadeControl, Assembly-CSharp"); if (type == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { type = assemblies[i].GetType("fadeControl"); if (type != null) { break; } } } if (!(type != null)) { return; } Object val = Object.FindObjectOfType(type); if (val != (Object)null) { MethodInfo method = type.GetMethod("fadeOut", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { method.Invoke(val, null); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Client] Could not trigger fade: " + ex.Message)); } } private void HandleTvSync(PacketReader reader) { long num = reader.GetLong(); if (_steam.IsHost) { return; } if ((Object)(object)_tvVideoPlayer == (Object)null) { tvControl val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && (Object)(object)val.TVVP != (Object)null) { _tvVideoPlayer = val.TVVP; } else { MoviePlayerSample val2 = Object.FindObjectOfType(); if ((Object)(object)val2 != (Object)null) { _tvVideoPlayer = ((Component)val2).GetComponent(); } } } if ((Object)(object)_tvVideoPlayer != (Object)null && Math.Abs(_tvVideoPlayer.frame - num) > 30) { _tvVideoPlayer.frame = num; } } private void HandlePaintingSync(PacketReader reader) { if (_steam.IsHost) { return; } int num = reader.GetInt(); int num2 = reader.GetInt(); int num3 = reader.GetInt(); int num4 = reader.GetInt(); int num5 = reader.GetInt(); int num6 = reader.GetInt(); if ((Object)(object)_paintingControl == (Object)null) { _paintingControl = Object.FindObjectOfType(); } if ((Object)(object)_paintingControl != (Object)null) { bool flag = false; if (_paintingControl.intpaintingTall1 != num) { _paintingControl.intpaintingTall1 = num; _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall1, num, false, true); flag = true; } if (_paintingControl.intpaintingTall2 != num2) { _paintingControl.intpaintingTall2 = num2; _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall2, num2, false, true); flag = true; } if (_paintingControl.intpaintingTall3 != num3) { _paintingControl.intpaintingTall3 = num3; _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall3, num3, false, true); flag = true; } if (_paintingControl.intpaintingSquare1 != num4) { _paintingControl.intpaintingSquare1 = num4; _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare1, num4, false, false); flag = true; } if (_paintingControl.intpaintingSquare2 != num5) { _paintingControl.intpaintingSquare2 = num5; _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare2, num5, false, false); flag = true; } if (_paintingControl.intpaintingSquare3 != num6) { _paintingControl.intpaintingSquare3 = num6; _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare3, num6, false, false); flag = true; } if (flag) { Plugin.LogDebug("[Client] Paintings synced from host"); } } } public void SendPaintingFlash(int paintingId) { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)7); _writer.Put(paintingId); SendToAllPeers(); } } public void SendPaintingEntityState(paintingControl pc) { if (_steam != null && _steam.IsRunning && ShouldControlMonsters) { Type? typeFromHandle = typeof(paintingControl); bool value = (bool)typeFromHandle.GetField("boolpaintingTall1", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(pc); bool value2 = (bool)typeFromHandle.GetField("boolpaintingTall2", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(pc); bool value3 = (bool)typeFromHandle.GetField("boolpaintingTall3", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(pc); bool value4 = (bool)typeFromHandle.GetField("boolpaintingSquare1", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(pc); bool value5 = (bool)typeFromHandle.GetField("boolpaintingSquare2", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(pc); bool value6 = (bool)typeFromHandle.GetField("boolpaintingSquare3", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(pc); _writer.Reset(); _writer.Put((byte)11); _writer.Put(value); _writer.Put(value2); _writer.Put(value3); _writer.Put(value4); _writer.Put(value5); _writer.Put(value6); _writer.Put(pc.intpaintingTall1); _writer.Put(pc.intpaintingTall2); _writer.Put(pc.intpaintingTall3); _writer.Put(pc.intpaintingSquare1); _writer.Put(pc.intpaintingSquare2); _writer.Put(pc.intpaintingSquare3); SendToAllPeers(); } } private void HandlePaintingEntity(PacketReader reader) { bool flag = reader.GetBool(); bool flag2 = reader.GetBool(); bool flag3 = reader.GetBool(); bool flag4 = reader.GetBool(); bool flag5 = reader.GetBool(); bool flag6 = reader.GetBool(); int num = reader.GetInt(); int num2 = reader.GetInt(); int num3 = reader.GetInt(); int num4 = reader.GetInt(); int num5 = reader.GetInt(); int num6 = reader.GetInt(); if ((Object)(object)_paintingControl == (Object)null) { _paintingControl = Object.FindObjectOfType(); } if ((Object)(object)_paintingControl != (Object)null) { Type? typeFromHandle = typeof(paintingControl); typeFromHandle.GetField("boolpaintingTall1", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_paintingControl, flag); typeFromHandle.GetField("boolpaintingTall2", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_paintingControl, flag2); typeFromHandle.GetField("boolpaintingTall3", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_paintingControl, flag3); typeFromHandle.GetField("boolpaintingSquare1", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_paintingControl, flag4); typeFromHandle.GetField("boolpaintingSquare2", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_paintingControl, flag5); typeFromHandle.GetField("boolpaintingSquare3", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(_paintingControl, flag6); _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall1, num, flag, true); _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall2, num2, flag2, true); _paintingControl.setPaintingMatSquare(_paintingControl.paintingTall3, num3, flag3, true); _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare1, num4, flag4, false); _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare2, num5, flag5, false); _paintingControl.setPaintingMatSquare(_paintingControl.paintingSquare3, num6, flag6, false); } } public List GetRemotePlayerPositions() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (RemotePlayer value in _remotePlayers.Values) { if ((Object)(object)value.Head != (Object)null) { list.Add(value.Head.transform.position); } } return list; } public Vector3? GetRemotePlayerPosition(int peerId) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (_remotePlayers.TryGetValue(peerId, out var value) && (Object)(object)value.Head != (Object)null) { return value.Head.transform.position; } return null; } public List GetRemotePlayerPositionsNonGhost() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (RemotePlayer value in _remotePlayers.Values) { if ((Object)(object)value.Head != (Object)null && !value.IsGhost) { list.Add(value.Head.transform.position); } } return list; } private void CheckMonsterSync() { if (ShouldControlMonsters && !_localIsGhost && !(Time.time - _lastMonsterSyncTime < _monsterSyncInterval)) { _lastMonsterSyncTime = Time.time; FindMonsters(); CheckSyncedMonsterKills(); SendMonsterSync(); } } private void FindMonsters() { if ((Object)(object)_sparky == (Object)null) { _sparky = Object.FindObjectOfType(); } if ((Object)(object)_jeff == (Object)null) { _jeff = Object.FindObjectOfType(); } if ((Object)(object)_smile == (Object)null) { _smile = Object.FindObjectOfType(); } if ((Object)(object)_henry == (Object)null) { _henry = Object.FindObjectOfType(); } if ((Object)(object)_harold == (Object)null) { _harold = Object.FindObjectOfType(); } if ((Object)(object)_clown == (Object)null) { _clown = Object.FindObjectOfType(); } } private void SendMonsterSync() { //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) _writer.Reset(); _writer.Put((byte)8); bool flag = (Object)(object)_sparky != (Object)null; _writer.Put(flag); if (flag) { FieldInfo field = typeof(sparkyBrain).GetField("currentState", BindingFlags.Instance | BindingFlags.NonPublic); int value = ((!(field != null)) ? 1 : ((int)field.GetValue(_sparky))); _writer.Put(value); } bool flag2 = (Object)(object)_jeff != (Object)null; _writer.Put(flag2); if (flag2) { _writer.Put((Object)(object)_jeff.jeffBody != (Object)null && _jeff.jeffBody.activeSelf); FieldInfo field2 = typeof(jeffBrain).GetField("currentState", BindingFlags.Instance | BindingFlags.NonPublic); int value2 = ((!(field2 != null)) ? 1 : ((int)field2.GetValue(_jeff))); _writer.Put(value2); FieldInfo field3 = typeof(jeffBrain).GetField("totalFlashes", BindingFlags.Instance | BindingFlags.NonPublic); int value3 = ((field3 != null) ? ((int)field3.GetValue(_jeff)) : 0); _writer.Put(value3); } _writer.Put(value: false); bool flag3 = (Object)(object)_henry != (Object)null; _writer.Put(flag3); if (flag3) { WriteVector3(_writer, ((Component)_henry).transform.position); WriteQuaternion(_writer, ((Component)_henry).transform.rotation); FieldInfo field4 = typeof(henryBrain).GetField("resetSwitch", BindingFlags.Instance | BindingFlags.NonPublic); bool value4 = field4 != null && (bool)field4.GetValue(_henry); _writer.Put(value4); FieldInfo field5 = typeof(henryBrain).GetField("chaseSwitch", BindingFlags.Instance | BindingFlags.NonPublic); bool value5 = field5 != null && (bool)field5.GetValue(_henry); _writer.Put(value5); } bool flag4 = (Object)(object)_harold != (Object)null; _writer.Put(flag4); if (flag4) { WriteVector3(_writer, ((Component)_harold).transform.position); WriteQuaternion(_writer, ((Component)_harold).transform.rotation); } bool flag5 = (Object)(object)_clown != (Object)null; _writer.Put(flag5); if (flag5) { int value6 = -1; if ((Object)(object)_clown.clown1 != (Object)null && _clown.clown1.activeSelf) { value6 = 0; } else if ((Object)(object)_clown.clown2 != (Object)null && _clown.clown2.activeSelf) { value6 = 1; } else if ((Object)(object)_clown.clown3 != (Object)null && _clown.clown3.activeSelf) { value6 = 2; } else if ((Object)(object)_clown.clown4 != (Object)null && _clown.clown4.activeSelf) { value6 = 3; } else if ((Object)(object)_clown.clown5 != (Object)null && _clown.clown5.activeSelf) { value6 = 4; } else if ((Object)(object)_clown.clown6 != (Object)null && _clown.clown6.activeSelf) { value6 = 5; } else if ((Object)(object)_clown.clown7 != (Object)null && _clown.clown7.activeSelf) { value6 = 6; } _writer.Put(value6); } SendToAllPeers(reliable: false); } private void CheckSyncedMonsterKills() { //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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) if (Time.time - _lastTargetedKillTime < 1f || _remotePlayers.Count == 0) { return; } if ((Object)(object)_henry != (Object)null) { FieldInfo field = typeof(henryBrain).GetField("resetSwitch", BindingFlags.Instance | BindingFlags.NonPublic); if (!(field != null) || !(bool)field.GetValue(_henry)) { foreach (KeyValuePair remotePlayer in _remotePlayers) { if (!remotePlayer.Value.IsGhost && !((Object)(object)remotePlayer.Value.Head == (Object)null)) { float num = Vector3.Distance(remotePlayer.Value.Head.transform.position, ((Component)_henry).transform.position); if (num < 0.6f) { Plugin.Log.LogInfo((object)$"[Host] Henry near peer {remotePlayer.Key} (dist={num:F2}) - sending targeted kill"); SendTargetedKillTo(remotePlayer.Key, 4); _lastTargetedKillTime = Time.time; return; } } } } } if (!((Object)(object)_harold != (Object)null)) { return; } foreach (KeyValuePair remotePlayer2 in _remotePlayers) { if (!remotePlayer2.Value.IsGhost && !((Object)(object)remotePlayer2.Value.Head == (Object)null)) { float num2 = Vector3.Distance(remotePlayer2.Value.Head.transform.position, ((Component)_harold).transform.position); if (num2 < 0.6f) { Plugin.Log.LogInfo((object)$"[Host] Harold near peer {remotePlayer2.Key} (dist={num2:F2}) - sending targeted kill"); SendTargetedKillTo(remotePlayer2.Key, 2); _lastTargetedKillTime = Time.time; break; } } } } private void SendTargetedKillTo(int peerId, int deathType) { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)42); _writer.Put(deathType); byte[] bytes = _writer.GetBytes(); _steam.SendTo(peerId, bytes); } } private void HandleTargetedKill(PacketReader reader) { int num = reader.GetInt(); if (_localIsGhost) { return; } Plugin.Log.LogInfo((object)$"[Client] Received targeted kill from host: deathType={num}"); jumpscareController val = Object.FindObjectOfType(); if (!((Object)(object)val == (Object)null)) { switch (num) { case 2: val.onDeathHarold(); break; case 4: val.onDeathHenry(); break; } } } public void SendJeffFlash() { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)9); SendToAllPeers(); } } private void CheckBatterySync() { if (Time.time - _lastBatterySyncTime < _batterySyncInterval) { return; } _lastBatterySyncTime = Time.time; if (_localIsGhost) { return; } BackpackControl val = Object.FindObjectOfType(); bool flag = false; bool flag2 = false; if ((Object)(object)val != (Object)null) { FieldInfo field = typeof(BackpackControl).GetField("leftHoldingBattery", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = typeof(BackpackControl).GetField("rightHoldingBattery", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { flag = (bool)field.GetValue(val); } if (field2 != null) { flag2 = (bool)field2.GetValue(val); } } bool num = BackpackControl.batteryLocationID != _lastBatteryLocationID; bool flag3 = BackpackControl.batteryIsInBackpack != _lastBatteryInBackpack; bool flag4 = Mathf.Abs(BackpackControl.batteryCharge - _lastBatteryCharge) > 2f; bool flag5 = flag != _lastLeftHolding || flag2 != _lastRightHolding; if (num || flag3 || flag4 || flag5) { _lastBatteryLocationID = BackpackControl.batteryLocationID; _lastBatteryInBackpack = BackpackControl.batteryIsInBackpack; _lastBatteryCharge = BackpackControl.batteryCharge; _lastLeftHolding = flag; _lastRightHolding = flag2; SendBatterySync(); } } private void SendBatterySync() { _writer.Reset(); _writer.Put((byte)10); _writer.Put(BackpackControl.batteryCharge); _writer.Put(BackpackControl.batteryLocationID); _writer.Put(BackpackControl.batteryIsInBackpack); BackpackControl val = Object.FindObjectOfType(); bool value = false; bool value2 = false; if ((Object)(object)val != (Object)null) { FieldInfo field = typeof(BackpackControl).GetField("leftHoldingBattery", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = typeof(BackpackControl).GetField("rightHoldingBattery", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { value = (bool)field.GetValue(val); } if (field2 != null) { value2 = (bool)field2.GetValue(val); } } _writer.Put(value); _writer.Put(value2); SendToAllPeers(); } private void HandleBatterySync(int peerId, PacketReader reader) { float num = reader.GetFloat(); int num2 = reader.GetInt(); bool flag = reader.GetBool(); bool flag2 = reader.GetBool(); bool flag3 = reader.GetBool(); Plugin.LogDebug($"[Battery] Peer {peerId}: charge={num:F1}, loc={num2}, backpack={flag}, L={flag2}, R={flag3}"); if (!_remotePlayers.TryGetValue(peerId, out var value) || !value.IsGhost) { if (!_remoteBatteryStates.ContainsKey(peerId)) { _remoteBatteryStates[peerId] = new RemoteBatteryState(); } _remoteBatteryStates[peerId].Charge = num; _remoteBatteryStates[peerId].LocationID = num2; _remoteBatteryStates[peerId].InBackpack = flag; _remoteBatteryStates[peerId].LeftHolding = flag2; _remoteBatteryStates[peerId].RightHolding = flag3; value?.SetBatteryState(flag2, flag3); } } public RemoteBatteryState GetRemoteBatteryState(int peerId) { if (_remoteBatteryStates.TryGetValue(peerId, out var value)) { return value; } return null; } public float GetRemoteBatteryCharge() { using (Dictionary.ValueCollection.Enumerator enumerator = _remoteBatteryStates.Values.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current.Charge; } } return -1f; } public int GetRemoteBatteryLocationID() { using (Dictionary.ValueCollection.Enumerator enumerator = _remoteBatteryStates.Values.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current.LocationID; } } return 0; } public bool IsRemoteBatteryAtExit() { foreach (RemoteBatteryState value in _remoteBatteryStates.Values) { if (value.LocationID == 100 && value.Charge >= 0.1f) { return true; } } return false; } public RemoteBatteryState GetFirstRemoteBatteryState() { using (Dictionary.ValueCollection.Enumerator enumerator = _remoteBatteryStates.Values.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } return null; } public RemoteBatteryState GetRemoteBatteryAtLocation(int locationID) { foreach (KeyValuePair remoteBatteryState in _remoteBatteryStates) { if ((!_remotePlayers.TryGetValue(remoteBatteryState.Key, out var value) || !value.IsGhost) && remoteBatteryState.Value.LocationID == locationID) { return remoteBatteryState.Value; } } return null; } private void CheckCrankSync() { //IL_004b: 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_00a8: 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_00e2: 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_00eb: Unknown result type (might be due to invalid IL or missing references) if (_localIsGhost) { return; } if ((Object)(object)_crankControl == (Object)null) { _crankControl = Object.FindObjectOfType(); } if ((Object)(object)_crankControl == (Object)null) { return; } bool flag = BackpackControl.batteryLocationID == 1; float batteryCharge = BackpackControl.batteryCharge; Quaternion rotation = ((Component)_crankControl).transform.rotation; bool flag2 = crankControl.holdTimer > 0; float num = (flag2 ? 0.05f : _crankSyncInterval); if (!(Time.time - _lastCrankSyncTime < num)) { _lastCrankSyncTime = Time.time; bool flag3 = flag && Mathf.Abs(batteryCharge - _lastCrankCharge) > 0.1f; bool flag4 = flag2 && Quaternion.Angle(_lastCrankRotation, rotation) > 2f; if (flag != _lastCrankHasBattery || flag3 || flag4) { _lastCrankHasBattery = flag; _lastCrankCharge = batteryCharge; _lastCrankRotation = rotation; SendCrankSync(flag, batteryCharge, rotation); } } } private void SendCrankSync(bool hasBattery, float charge, Quaternion rotation) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) _writer.Reset(); _writer.Put((byte)19); _writer.Put(hasBattery); _writer.Put(charge); WriteQuaternion(_writer, rotation); SendToAllPeers(); } public void UpdateCrankInterpolation() { //IL_0024: 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_003a: 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) _remoteCrankChargeDisplay = Mathf.Lerp(_remoteCrankChargeDisplay, _remoteCrankChargeTarget, Time.deltaTime * 15f); _remoteCrankRotDisplay = Quaternion.Slerp(_remoteCrankRotDisplay, _remoteCrankRotTarget, Time.deltaTime * 15f); } private void CheckPuzzleInitSync() { if (_steam.IsHost && !_localIsGhost && !_pendingGhostPuzzleRestore) { if ((Object)(object)_puzzleMaster == (Object)null) { _puzzleMaster = Object.FindObjectOfType(); } if (!((Object)(object)_puzzleMaster == (Object)null) && !_puzzleInitSent) { SendPuzzleInit(); _puzzleInitSent = true; _lastTotalCompletedPuzzles = PuzzleMaster.totalCompletedPuzzles; } } } private void SendPuzzleInit() { if ((Object)(object)_puzzleMaster == (Object)null) { return; } _writer.Reset(); _writer.Put((byte)12); Type typeFromHandle = typeof(PuzzleMaster); Type typeFromHandle2 = typeof(PuzzleController); bool[] array = new bool[9]; int[] array2 = new int[9]; bool[] array3 = new bool[9]; PuzzleController[] array4 = (PuzzleController[])(object)new PuzzleController[9] { _puzzleMaster.pCon1, _puzzleMaster.pCon2, _puzzleMaster.pCon3, _puzzleMaster.pCon4, _puzzleMaster.pCon5, _puzzleMaster.pCon6, _puzzleMaster.pCon7, _puzzleMaster.pCon8, _puzzleMaster.pCon9 }; string[] array5 = new string[9] { "ps1", "ps2", "ps3", "ps4", "ps5", "ps6", "ps7", "ps8", "ps9" }; for (int i = 0; i < 9; i++) { FieldInfo field = typeFromHandle.GetField(array5[i], BindingFlags.Instance | BindingFlags.NonPublic); array[i] = (bool)field.GetValue(_puzzleMaster); if ((Object)(object)array4[i] != (Object)null) { FieldInfo field2 = typeFromHandle2.GetField("puzzlePresetID", BindingFlags.Instance | BindingFlags.NonPublic); array2[i] = (int)field2.GetValue(array4[i]); FieldInfo field3 = typeFromHandle2.GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic); array3[i] = (bool)field3.GetValue(array4[i]); } } for (int j = 0; j < 9; j++) { _writer.Put(array[j]); _writer.Put(array2[j]); _writer.Put(array3[j]); if ((Object)(object)array4[j] != (Object)null && array4[j].cubeList != null) { _writer.Put(array4[j].cubeList.Length); PuzzleBlock[] cubeList = array4[j].cubeList; foreach (PuzzleBlock val in cubeList) { _writer.Put(val.blockIDValue); } } else { _writer.Put(0); } } _writer.Put(PuzzleMaster.totalCompletedPuzzles); _writer.Put(PuzzleMaster.requiredPuzzles); SendToAllPeers(); Plugin.Log.LogInfo((object)$"[Host] Sent puzzle init: required={PuzzleMaster.requiredPuzzles}, completed={PuzzleMaster.totalCompletedPuzzles}"); } private void SavePuzzleStateForGhost() { if ((Object)(object)_puzzleMaster == (Object)null) { _puzzleMaster = Object.FindObjectOfType(); } if ((Object)(object)_puzzleMaster == (Object)null) { Plugin.Log.LogWarning((object)"[Ghost] Can't save puzzle state - PuzzleMaster not found"); return; } Type typeFromHandle = typeof(PuzzleMaster); Type typeFromHandle2 = typeof(PuzzleController); bool[] array = new bool[9]; int[] array2 = new int[9]; bool[] array3 = new bool[9]; int[][] array4 = new int[9][]; PuzzleController[] array5 = (PuzzleController[])(object)new PuzzleController[9] { _puzzleMaster.pCon1, _puzzleMaster.pCon2, _puzzleMaster.pCon3, _puzzleMaster.pCon4, _puzzleMaster.pCon5, _puzzleMaster.pCon6, _puzzleMaster.pCon7, _puzzleMaster.pCon8, _puzzleMaster.pCon9 }; string[] array6 = new string[9] { "ps1", "ps2", "ps3", "ps4", "ps5", "ps6", "ps7", "ps8", "ps9" }; for (int i = 0; i < 9; i++) { FieldInfo field = typeFromHandle.GetField(array6[i], BindingFlags.Instance | BindingFlags.NonPublic); array[i] = (bool)field.GetValue(_puzzleMaster); if ((Object)(object)array5[i] != (Object)null) { FieldInfo field2 = typeFromHandle2.GetField("puzzlePresetID", BindingFlags.Instance | BindingFlags.NonPublic); array2[i] = (int)field2.GetValue(array5[i]); FieldInfo field3 = typeFromHandle2.GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic); array3[i] = (bool)field3.GetValue(array5[i]); if (array5[i].cubeList != null) { array4[i] = new int[array5[i].cubeList.Length]; for (int j = 0; j < array5[i].cubeList.Length; j++) { array4[i][j] = array5[i].cubeList[j].blockIDValue; } } else { array4[i] = new int[0]; } } else { array4[i] = new int[0]; } } _savedGhostPuzzleState = new PendingPuzzleInit { ActiveStates = array, PresetIDs = array2, CompletedStates = array3, BlockStates = array4, TotalCompleted = PuzzleMaster.totalCompletedPuzzles, RequiredPuzzles = PuzzleMaster.requiredPuzzles }; Plugin.Log.LogInfo((object)$"[Ghost] Saved puzzle state: completed={PuzzleMaster.totalCompletedPuzzles}, required={PuzzleMaster.requiredPuzzles}"); } public void RestoreGhostPuzzleState() { if (_savedGhostPuzzleState == null) { return; } if ((Object)(object)_puzzleMaster == (Object)null) { _puzzleMaster = Object.FindObjectOfType(); } if ((Object)(object)_puzzleMaster == (Object)null) { Plugin.Log.LogWarning((object)"[Ghost] Can't restore puzzle state - PuzzleMaster not found yet"); return; } Type typeFromHandle = typeof(PuzzleMaster); Type typeFromHandle2 = typeof(PuzzleController); PendingPuzzleInit savedGhostPuzzleState = _savedGhostPuzzleState; PuzzleController[] array = (PuzzleController[])(object)new PuzzleController[9] { _puzzleMaster.pCon1, _puzzleMaster.pCon2, _puzzleMaster.pCon3, _puzzleMaster.pCon4, _puzzleMaster.pCon5, _puzzleMaster.pCon6, _puzzleMaster.pCon7, _puzzleMaster.pCon8, _puzzleMaster.pCon9 }; string[] array2 = new string[9] { "ps1", "ps2", "ps3", "ps4", "ps5", "ps6", "ps7", "ps8", "ps9" }; for (int i = 0; i < 9; i++) { typeFromHandle.GetField(array2[i], BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_puzzleMaster, savedGhostPuzzleState.ActiveStates[i]); if (!((Object)(object)array[i] != (Object)null)) { continue; } typeFromHandle2.GetField("puzzlePresetID", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(array[i], savedGhostPuzzleState.PresetIDs[i]); typeFromHandle2.GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(array[i], savedGhostPuzzleState.CompletedStates[i]); if (savedGhostPuzzleState.CompletedStates[i] || !savedGhostPuzzleState.ActiveStates[i]) { if ((Object)(object)array[i].fanspin != (Object)null) { array[i].fanspin.isOn = true; } GameObject thisMapIndicator = array[i].thisMapIndicator; if (thisMapIndicator != null) { thisMapIndicator.SetActive(true); } } else { GameObject thisMapIndicator2 = array[i].thisMapIndicator; if (thisMapIndicator2 != null) { thisMapIndicator2.SetActive(false); } } if (savedGhostPuzzleState.BlockStates[i] != null && array[i].cubeList != null) { int num = Mathf.Min(savedGhostPuzzleState.BlockStates[i].Length, array[i].cubeList.Length); for (int j = 0; j < num; j++) { array[i].cubeList[j].setThisID(savedGhostPuzzleState.BlockStates[i][j]); } } } PuzzleMaster.totalCompletedPuzzles = savedGhostPuzzleState.TotalCompleted; PuzzleMaster.requiredPuzzles = savedGhostPuzzleState.RequiredPuzzles; _lastTotalCompletedPuzzles = savedGhostPuzzleState.TotalCompleted; _completedPuzzleIDs.Clear(); for (int k = 0; k < 9; k++) { if (savedGhostPuzzleState.CompletedStates[k] && savedGhostPuzzleState.ActiveStates[k] && (Object)(object)array[k] != (Object)null) { _completedPuzzleIDs.Add(array[k].thisPuzzleID); } } Plugin.Log.LogInfo((object)$"[Ghost] Restored puzzle state: completed={savedGhostPuzzleState.TotalCompleted}, required={savedGhostPuzzleState.RequiredPuzzles}"); _puzzleInitCompletedStates = savedGhostPuzzleState.CompletedStates; _puzzleInitActiveStates = savedGhostPuzzleState.ActiveStates; _puzzleInitReapplyTimer = 3f; _puzzleInitApplied = true; _savedGhostPuzzleState = null; } private void HandlePuzzleInit(PacketReader reader) { if (_steam.IsHost) { return; } Plugin.LogDebug("[Client] Received puzzle init"); if (_puzzleInitApplied) { for (int i = 0; i < 9; i++) { reader.GetBool(); reader.GetInt(); reader.GetBool(); int num = reader.GetInt(); for (int j = 0; j < num; j++) { reader.GetInt(); } } reader.GetInt(); reader.GetInt(); Plugin.LogDebug("[Client] Ignoring duplicate puzzle init (already applied)"); return; } bool[] array = new bool[9]; int[] array2 = new int[9]; bool[] array3 = new bool[9]; int[][] array4 = new int[9][]; for (int k = 0; k < 9; k++) { array[k] = reader.GetBool(); array2[k] = reader.GetInt(); array3[k] = reader.GetBool(); int num2 = reader.GetInt(); array4[k] = new int[num2]; for (int l = 0; l < num2; l++) { array4[k][l] = reader.GetInt(); } } int totalCompleted = reader.GetInt(); int requiredPuzzles = reader.GetInt(); _pendingPuzzleInit = new PendingPuzzleInit { ActiveStates = array, PresetIDs = array2, CompletedStates = array3, BlockStates = array4, TotalCompleted = totalCompleted, RequiredPuzzles = requiredPuzzles }; _puzzleInitStartWaited = false; TryApplyPuzzleInit(); } private void TryApplyPuzzleInit() { if (_pendingPuzzleInit == null) { return; } if ((Object)(object)_puzzleMaster == (Object)null) { _puzzleMaster = Object.FindObjectOfType(); } if ((Object)(object)_puzzleMaster == (Object)null) { Plugin.Log.LogWarning((object)"[Client] PuzzleMaster not found, will retry later"); return; } if (!_puzzleInitStartWaited) { _puzzleInitStartWaited = true; return; } Type typeFromHandle = typeof(PuzzleMaster); Type typeFromHandle2 = typeof(PuzzleController); PuzzleController[] array = (PuzzleController[])(object)new PuzzleController[9] { _puzzleMaster.pCon1, _puzzleMaster.pCon2, _puzzleMaster.pCon3, _puzzleMaster.pCon4, _puzzleMaster.pCon5, _puzzleMaster.pCon6, _puzzleMaster.pCon7, _puzzleMaster.pCon8, _puzzleMaster.pCon9 }; string[] array2 = new string[9] { "ps1", "ps2", "ps3", "ps4", "ps5", "ps6", "ps7", "ps8", "ps9" }; _isReceivingPuzzleBlock = true; for (int i = 0; i < 9; i++) { bool flag = _pendingPuzzleInit.ActiveStates[i]; int num = _pendingPuzzleInit.PresetIDs[i]; bool flag2 = _pendingPuzzleInit.CompletedStates[i]; typeFromHandle.GetField(array2[i], BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_puzzleMaster, flag); if (!((Object)(object)array[i] != (Object)null)) { continue; } typeFromHandle2.GetField("puzzlePresetID", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(array[i], num); FieldInfo field = typeFromHandle2.GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic); bool flag3 = (Object)(object)array[i] != (Object)null && _completedPuzzleIDs.Contains(array[i].thisPuzzleID); if (!flag3) { field?.SetValue(array[i], flag2); } if (flag2 || flag3) { if ((Object)(object)array[i].fanspin != (Object)null) { array[i].fanspin.isOn = true; } GameObject thisMapIndicator = array[i].thisMapIndicator; if (thisMapIndicator != null) { thisMapIndicator.SetActive(true); } } else if (!flag) { field?.SetValue(array[i], true); if ((Object)(object)array[i].fanspin != (Object)null) { array[i].fanspin.isOn = true; } GameObject thisMapIndicator2 = array[i].thisMapIndicator; if (thisMapIndicator2 != null) { thisMapIndicator2.SetActive(true); } } else { field?.SetValue(array[i], false); if ((Object)(object)array[i].fanspin != (Object)null) { array[i].fanspin.isOn = false; } GameObject thisMapIndicator3 = array[i].thisMapIndicator; if (thisMapIndicator3 != null) { thisMapIndicator3.SetActive(false); } } int[] array3 = _pendingPuzzleInit.BlockStates[i]; if (array3 != null && array[i].cubeList != null) { int num2 = Mathf.Min(array3.Length, array[i].cubeList.Length); for (int j = 0; j < num2; j++) { array[i].cubeList[j].setThisID(array3[j]); } } } _isReceivingPuzzleBlock = false; if (_pendingPuzzleInit.TotalCompleted > PuzzleMaster.totalCompletedPuzzles) { PuzzleMaster.totalCompletedPuzzles = _pendingPuzzleInit.TotalCompleted; } PuzzleMaster.requiredPuzzles = _pendingPuzzleInit.RequiredPuzzles; for (int k = 0; k < 9; k++) { if (_pendingPuzzleInit.CompletedStates[k] && (Object)(object)array[k] != (Object)null) { _completedPuzzleIDs.Add(array[k].thisPuzzleID); } } Plugin.LogDebug($"[Client] Applied puzzle init: completed={PuzzleMaster.totalCompletedPuzzles}, required={PuzzleMaster.requiredPuzzles}"); foreach (int completedPuzzleID in _completedPuzzleIDs) { for (int l = 0; l < 9; l++) { if ((Object)(object)array[l] != (Object)null && array[l].thisPuzzleID == completedPuzzleID) { FieldInfo field2 = typeFromHandle2.GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic); if (field2 != null) { field2.SetValue(array[l], true); } if ((Object)(object)array[l].fanspin != (Object)null) { array[l].fanspin.isOn = true; } GameObject thisMapIndicator4 = array[l].thisMapIndicator; if (thisMapIndicator4 != null) { thisMapIndicator4.SetActive(true); } break; } } } _puzzleInitCompletedStates = _pendingPuzzleInit.CompletedStates; _puzzleInitActiveStates = _pendingPuzzleInit.ActiveStates; _pendingPuzzleInit = null; _puzzleInitApplied = true; _puzzleInitReapplyTimer = 3f; } private void ReapplyPuzzleIndicators() { if (_puzzleInitReapplyTimer <= 0f) { return; } _puzzleInitReapplyTimer -= Time.deltaTime; if ((Object)(object)_puzzleMaster == (Object)null || _puzzleInitCompletedStates == null || _puzzleInitActiveStates == null) { return; } FieldInfo field = typeof(PuzzleController).GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic); PuzzleController[] array = (PuzzleController[])(object)new PuzzleController[9] { _puzzleMaster.pCon1, _puzzleMaster.pCon2, _puzzleMaster.pCon3, _puzzleMaster.pCon4, _puzzleMaster.pCon5, _puzzleMaster.pCon6, _puzzleMaster.pCon7, _puzzleMaster.pCon8, _puzzleMaster.pCon9 }; for (int i = 0; i < 9; i++) { if ((Object)(object)array[i] == (Object)null || (!_puzzleInitCompletedStates[i] && _puzzleInitActiveStates[i])) { continue; } bool num = field != null && (bool)field.GetValue(array[i]); bool flag = (Object)(object)array[i].thisMapIndicator != (Object)null && array[i].thisMapIndicator.activeSelf; if (!num || !flag) { if (field != null) { field.SetValue(array[i], true); } if ((Object)(object)array[i].fanspin != (Object)null) { array[i].fanspin.isOn = true; } GameObject thisMapIndicator = array[i].thisMapIndicator; if (thisMapIndicator != null) { thisMapIndicator.SetActive(true); } Plugin.LogDebug($"[PuzzleInit] Re-applied completed state for puzzle index {i} (something reset it)"); } } } private void CheckExitDoorSync() { if (_localIsGhost || Time.time - _lastDoorSyncTime < 0.1f) { return; } _lastDoorSyncTime = Time.time; if (BackpackControl.batteryLocationID != 100 || BackpackControl.batteryCharge < 0.1f) { if (_lastDoorLeaveTimer > 0 && !_exitDoorFullyCharged) { _lastDoorLeaveTimer = 0; SendExitDoorProgress(0, 100); } return; } if ((Object)(object)_leaveDoor == (Object)null) { _leaveDoor = Object.FindObjectOfType(); } if (!((Object)(object)_leaveDoor != (Object)null)) { return; } FieldInfo field = typeof(LeaveDoorControl).GetField("doorLeaveTimer", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { int num = (int)field.GetValue(_leaveDoor); if (num >= _leaveDoor.doorLeaveRequiredTime && _leaveDoor.doorLeaveRequiredTime > 0) { _exitDoorFullyCharged = true; } if (num != _lastDoorLeaveTimer) { _lastDoorLeaveTimer = num; SendExitDoorProgress(num, _leaveDoor.doorLeaveRequiredTime); } } } private void SendExitDoorProgress(int timer, int requiredTime) { _writer.Reset(); _writer.Put((byte)18); _writer.Put(timer); _writer.Put(requiredTime); SendToAllPeers(); } public void MarkPuzzleCompleted(int puzzleID) { _completedPuzzleIDs.Add(puzzleID); Plugin.LogDebug($"[Puzzle] Marked puzzle {puzzleID} as completed locally"); } public bool IsPuzzleCompleted(int puzzleID) { return _completedPuzzleIDs.Contains(puzzleID); } public void SendPuzzleComplete(int puzzleID) { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)13); _writer.Put(puzzleID); _writer.Put(PuzzleMaster.totalCompletedPuzzles); SendToAllPeers(); Plugin.LogDebug($"[Puzzle] Sent puzzle complete: puzzleID={puzzleID}, total={PuzzleMaster.totalCompletedPuzzles}"); } } private void HandlePuzzleComplete(PacketReader reader) { int num = reader.GetInt(); int num2 = reader.GetInt(); Plugin.LogDebug($"[Puzzle] Received puzzle complete: puzzleID={num}, remoteTotal={num2}, localTotal={PuzzleMaster.totalCompletedPuzzles}"); if (_completedPuzzleIDs.Contains(num)) { Plugin.LogDebug($"[Puzzle] Puzzle {num} already marked complete, skipping"); return; } if (num == BackpackControl.batteryLocationID && BackpackControl.batteryCharge > 0f) { Plugin.LogDebug($"[Puzzle] Local battery is in puzzle {num} — skipping remote completion (local player is solving it)"); PuzzleMaster.totalCompletedPuzzles++; _completedPuzzleIDs.Add(num); return; } _completedPuzzleIDs.Add(num); PuzzleMaster.totalCompletedPuzzles++; Plugin.LogDebug($"[Puzzle] Incremented local total to {PuzzleMaster.totalCompletedPuzzles}"); if ((Object)(object)_puzzleMaster == (Object)null) { _puzzleMaster = Object.FindObjectOfType(); } if (!((Object)(object)_puzzleMaster != (Object)null)) { return; } PuzzleController[] array = (PuzzleController[])(object)new PuzzleController[9] { _puzzleMaster.pCon1, _puzzleMaster.pCon2, _puzzleMaster.pCon3, _puzzleMaster.pCon4, _puzzleMaster.pCon5, _puzzleMaster.pCon6, _puzzleMaster.pCon7, _puzzleMaster.pCon8, _puzzleMaster.pCon9 }; foreach (PuzzleController val in array) { if ((Object)(object)val != (Object)null && val.thisPuzzleID == num) { Type? typeFromHandle = typeof(PuzzleController); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; typeFromHandle.GetField("puzzleHasCompleted", bindingAttr).SetValue(val, true); FieldInfo field = typeFromHandle.GetField("totalCompletions", bindingAttr); if (field != null) { field.SetValue(val, 0); } for (int j = 0; j < val.cubeList.Length; j++) { val.cubeList[j].setThisID(0); } if ((Object)(object)val.fanspin != (Object)null) { val.fanspin.isOn = true; } GameObject thisMapIndicator = val.thisMapIndicator; if (thisMapIndicator != null) { thisMapIndicator.SetActive(true); } AudioSource winAudio = val.winAudio; if (winAudio != null) { winAudio.Play(); } Plugin.LogDebug($"Marked puzzle {num} as complete (board cleared)"); break; } } } public void SendPuzzleBlock(int puzzleID, int blockNumber, int blockIDValue) { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)14); _writer.Put(puzzleID); _writer.Put(blockNumber); _writer.Put(blockIDValue); SendToAllPeers(); } } private void HandlePuzzleBlock(PacketReader reader) { int num = reader.GetInt(); int num2 = reader.GetInt(); int thisID = reader.GetInt(); if ((Object)(object)_puzzleMaster == (Object)null) { _puzzleMaster = Object.FindObjectOfType(); } if ((Object)(object)_puzzleMaster == (Object)null) { return; } PuzzleController[] array = (PuzzleController[])(object)new PuzzleController[9] { _puzzleMaster.pCon1, _puzzleMaster.pCon2, _puzzleMaster.pCon3, _puzzleMaster.pCon4, _puzzleMaster.pCon5, _puzzleMaster.pCon6, _puzzleMaster.pCon7, _puzzleMaster.pCon8, _puzzleMaster.pCon9 }; foreach (PuzzleController val in array) { if ((Object)(object)val != (Object)null && val.thisPuzzleID == num) { if (num2 >= 0 && num2 < val.cubeList.Length) { _isReceivingPuzzleBlock = true; val.cubeList[num2].setThisID(thisID); _isReceivingPuzzleBlock = false; } break; } } } public void SendClownHonk() { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)15); SendToAllPeers(); } } private void HandleClownHonk(PacketReader reader) { clownNose val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && (Object)(object)val.honkSound != (Object)null) { _isReceivingHonk = true; val.honkSound.Play(); _isReceivingHonk = false; } } public void SendClownState(clownRandom clown) { if (_steam != null && _steam.IsRunning && ShouldControlMonsters) { _writer.Reset(); _writer.Put((byte)25); _writer.Put(clown.clown1.activeSelf); _writer.Put(clown.clown2.activeSelf); _writer.Put(clown.clown3.activeSelf); _writer.Put(clown.clown4.activeSelf); _writer.Put(clown.clown5.activeSelf); _writer.Put(clown.clown6.activeSelf); _writer.Put(clown.clown7.activeSelf); SendToAllPeers(); Plugin.Log.LogInfo((object)"[Controller] Sent clown state"); } } public void SendClownAttack() { if (_steam != null && _steam.IsRunning && ShouldControlMonsters) { _writer.Reset(); _writer.Put((byte)26); SendToAllPeers(); } } private void HandleClownState(PacketReader reader) { bool active = reader.GetBool(); bool active2 = reader.GetBool(); bool active3 = reader.GetBool(); bool active4 = reader.GetBool(); bool active5 = reader.GetBool(); bool active6 = reader.GetBool(); bool active7 = reader.GetBool(); if ((Object)(object)_clownRandom == (Object)null) { _clownRandom = Object.FindObjectOfType(); } if ((Object)(object)_clownRandom != (Object)null) { _clownRandom.clown1.SetActive(active); _clownRandom.clown2.SetActive(active2); _clownRandom.clown3.SetActive(active3); _clownRandom.clown4.SetActive(active4); _clownRandom.clown5.SetActive(active5); _clownRandom.clown6.SetActive(active6); _clownRandom.clown7.SetActive(active7); } } private void HandleClownAttack(PacketReader reader) { if ((Object)(object)_clownRandom == (Object)null) { _clownRandom = Object.FindObjectOfType(); } if ((Object)(object)_clownRandom != (Object)null) { bool flag = false; if ((Object)(object)_clownRandom.c1R != (Object)null && _clownRandom.c1R.isVisible) { flag = true; } if ((Object)(object)_clownRandom.c2R != (Object)null && _clownRandom.c2R.isVisible) { flag = true; } if ((Object)(object)_clownRandom.c3R != (Object)null && _clownRandom.c3R.isVisible) { flag = true; } if ((Object)(object)_clownRandom.c4R != (Object)null && _clownRandom.c4R.isVisible) { flag = true; } if ((Object)(object)_clownRandom.c5R != (Object)null && _clownRandom.c5R.isVisible) { flag = true; } if ((Object)(object)_clownRandom.c6R != (Object)null && _clownRandom.c6R.isVisible) { flag = true; } if ((Object)(object)_clownRandom.c7R != (Object)null && _clownRandom.c7R.isVisible) { flag = true; } if (flag) { Plugin.LogDebug("[Client] Ignoring clown attack - client can see the clown"); return; } _clownRandom.clown1.SetActive(false); _clownRandom.clown2.SetActive(false); _clownRandom.clown3.SetActive(false); _clownRandom.clown4.SetActive(false); _clownRandom.clown5.SetActive(false); _clownRandom.clown6.SetActive(false); _clownRandom.clown7.SetActive(false); typeof(clownRandom).GetField("clownAttackingSwitch", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(_clownRandom, true); } } public void SendSmileTrigger(Vector3 position) { } private void HandleSmileTrigger(PacketReader reader) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) ReadVector3(reader); } public void SendVentSound(Vector3 position, int soundIndex) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)16); WriteVector3(_writer, position); _writer.Put(soundIndex); SendToAllPeers(); } } public void SendCrawlSound(Vector3 position, bool inMainRoom) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)16); WriteVector3(_writer, position); _writer.Put(2); _writer.Put(inMainRoom); SendToAllPeers(); } } private void HandleVentSound(PacketReader reader) { //IL_0002: 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_0053: 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_0046: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ReadVector3(reader); int num = reader.GetInt(); _isReceivingVentSound = true; switch (num) { case 0: { ventSoundPlayer val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && (Object)(object)val.soundPrefab != (Object)null) { Object.Instantiate(val.soundPrefab).transform.position = position; } break; } case 1: PlayRemoteCrawlSound(position, inMainRoom: false); break; case 2: { bool inMainRoom = reader.GetBool(); PlayRemoteCrawlSound(position, inMainRoom); break; } } _isReceivingVentSound = false; } private void PlayRemoteCrawlSound(Vector3 position, bool inMainRoom) { //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_008b: 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_00ee: Expected O, but got Unknown crawlSoundContrl val = Object.FindObjectOfType(); if (!((Object)(object)val == (Object)null)) { AudioSource val2; if (inMainRoom) { val2 = val.w1; } else { AudioSource[] array = (AudioSource[])(object)new AudioSource[4] { val.m1, val.m2, val.m3, val.m4 }; val2 = array[Random.Range(0, array.Length)]; } if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.clip == (Object)null)) { float pitch = Random.Range(0.8f, 1.2f); GameObject val3 = new GameObject("RemoteCrawlSound"); val3.transform.position = position; AudioSource obj = val3.AddComponent(); obj.clip = val2.clip; obj.pitch = pitch; obj.spatialBlend = 1f; obj.volume = val2.volume; obj.maxDistance = 10f; obj.rolloffMode = (AudioRolloffMode)1; obj.Play(); Object.Destroy((Object)val3, val2.clip.length + 0.1f); } } } private void HandleFlashlightUpdate(int peerId, PacketReader reader) { bool flashlightState = reader.GetBool(); if (_remotePlayers.TryGetValue(peerId, out var value)) { value.SetFlashlightState(flashlightState); } } private void HandlePositionUpdate(int peerId, PacketReader reader) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_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_0056: 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_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_0071: 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_00c4: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_00d0: 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) if (!_remotePlayers.TryGetValue(peerId, out var value)) { Plugin.Log.LogWarning((object)$"No remote player for peer {peerId}"); return; } bool isStanding = reader.GetBool(); Vector3 bodyPos = ReadVector3(reader); Quaternion bodyRot = ReadQuaternion(reader); Vector3 val = ReadVector3(reader); Quaternion headRot = ReadQuaternion(reader); Vector3 leftHandPos = ReadVector3(reader); Quaternion leftHandRot = ReadQuaternion(reader); Vector3 rightHandPos = ReadVector3(reader); Quaternion rightHandRot = ReadQuaternion(reader); float leftGrip = reader.GetFloat(); float leftTrigger = reader.GetFloat(); float rightGrip = reader.GetFloat(); float rightTrigger = reader.GetFloat(); if (_updateCount % 150 == 0) { Plugin.LogDebug($"[Recv] peer {peerId}: head={val}"); } value.SetTargets(isStanding, bodyPos, bodyRot, val, headRot, leftHandPos, leftHandRot, rightHandPos, rightHandRot, leftGrip, leftTrigger, rightGrip, rightTrigger); } public void CaptureSpawnPoint() { //IL_003a: 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_0056: Unknown result type (might be due to invalid IL or missing references) if (!_spawnPointCaptured) { MoveTypeController val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && val.debugSwitch && (Object)(object)val.playerObj != (Object)null) { _levelSpawnPoint = val.playerObj.transform.position; _spawnPointCaptured = true; Plugin.Log.LogInfo((object)$"[Ghost] Captured spawn point from playerObj (standing): {_levelSpawnPoint}"); } } } public void SendDeathGhost(bool isGhost, int deathType) { Plugin.Log.LogInfo((object)$"[Death] SendDeathGhost called: isGhost={isGhost}, deathType={deathType}, steamRunning={_steam?.IsRunning}, localIsGhost={_localIsGhost}, debugForce={DebugForceGhostOnDeath}"); if (_steam == null || !_steam.IsRunning) { Plugin.Log.LogInfo((object)"[Death] Steam not running, skipping send"); return; } if (isGhost && _localIsGhost) { Plugin.Log.LogInfo((object)"[Death] Already a ghost, skipping duplicate death notification"); return; } _writer.Reset(); _writer.Put((byte)20); _writer.Put(isGhost); _writer.Put(deathType); SendToAllPeers(); Plugin.Log.LogInfo((object)$"[Death] Sent death notification: deathType={deathType}"); if (isGhost) { IsDyingThisFrame = true; BecomeGhost(); } } public void BecomeGhost() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; Plugin.Log.LogInfo((object)$"[Ghost] BecomeGhost: scene={name}, alreadyGhost={_localIsGhost}, debugForce={DebugForceGhostOnDeath}, remotePlayers={_remotePlayers.Count}"); if (!name.Contains("Night")) { Plugin.Log.LogInfo((object)"[Ghost] Not in Night scene, aborting"); return; } bool flag = DebugForceGhostOnDeath; if (!flag) { foreach (KeyValuePair remotePlayer in _remotePlayers) { if (!remotePlayer.Value.IsGhost) { flag = true; break; } } } Plugin.Log.LogInfo((object)$"[Ghost] partnerAlive={flag}"); if (!flag) { Plugin.Log.LogInfo((object)"[Ghost] No alive partners - all players dead, going to Home"); ResetGhostState(); return; } Plugin.Log.LogInfo((object)("[Ghost] Partner alive - becoming ghost in " + name)); _localIsGhost = true; DebugForceGhostOnDeath = false; BackpackControl.batteryLocationID = 0; BackpackControl.batteryCharge = 0f; BackpackControl.batteryIsInBackpack = false; IsGhostSceneReload = true; } public void ReloadSceneAsGhost() { if (string.IsNullOrEmpty(_ghostSceneToReload)) { Plugin.Log.LogWarning((object)"[Ghost] ReloadSceneAsGhost called but no scene to reload!"); return; } SavePuzzleStateForGhost(); Plugin.Log.LogInfo((object)("[Ghost] ReloadSceneAsGhost - Loading: " + _ghostSceneToReload)); IsLoadingFromSync = true; SceneManager.LoadScene(_ghostSceneToReload); } public void HandleGhostSceneLoaded() { if (_localIsGhost && _pendingGhostTeleport) { _pendingGhostTeleport = false; IsGhostSceneReload = false; Plugin.Log.LogInfo((object)"[Ghost] Scene loaded as ghost, teleporting to partner"); _writer.Reset(); _writer.Put((byte)20); _writer.Put(value: true); _writer.Put(0); SendToAllPeers(); CreateGhostVision(); _pendingGhostPuzzleRestore = true; _ghostPuzzleRestoreFrames = 2; _ghostTeleportDelay = 1f; } } private void CreateGhostVision() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_ghostVisionLight != (Object)null) { Object.Destroy((Object)(object)_ghostVisionLight); _ghostVisionLight = null; } Camera val = Camera.main; if ((Object)(object)val == (Object)null) { val = Object.FindObjectOfType(); } if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"[Ghost] No camera found for ghost vision"); return; } _ghostVisionLight = new GameObject("GhostVision"); _ghostVisionLight.transform.SetParent(((Component)val).transform); _ghostVisionLight.transform.localPosition = Vector3.zero; Light obj = _ghostVisionLight.AddComponent(); obj.type = (LightType)2; obj.color = new Color(0.6f, 0.6f, 0.8f); obj.intensity = 0.8f; obj.range = 6f; obj.shadows = (LightShadows)0; Plugin.Log.LogInfo((object)"[Ghost] Created ghost vision light"); } private void DestroyGhostVision() { if ((Object)(object)_ghostVisionLight != (Object)null) { Object.Destroy((Object)(object)_ghostVisionLight); _ghostVisionLight = null; } } public void TeleportToPartner() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)$"[Ghost] TeleportToPartner called, setting delay=2.5s, spawnCaptured={_spawnPointCaptured}, spawnPoint={_levelSpawnPoint}"); _ghostTeleportDelay = 2.5f; _pendingGhostTeleport = false; CreateGhostVision(); _writer.Reset(); _writer.Put((byte)20); _writer.Put(value: true); _writer.Put(0); SendToAllPeers(); } private void UpdateGhostTeleport() { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) if (_ghostTeleportDelay <= 0f) { return; } _ghostTeleportDelay -= Time.deltaTime; if (_ghostTeleportDelay > 0f) { return; } MoveTypeController val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && val.debugSwitch) { Plugin.Log.LogInfo((object)"[Ghost] Player was standing when died — no teleport needed"); return; } if ((Object)(object)val != (Object)null) { val.debugSwitch = true; if ((Object)(object)val.ccol != (Object)null) { val.ccol.height = val.standingHeight; } if ((Object)(object)val.cmpb != (Object)null) { val.cmpb.moveSpeed = val.standingSpeed; } } Player val2 = _gorillaPlayer ?? Player.Instance ?? Object.FindObjectOfType(); if ((Object)(object)val2 != (Object)null) { val2.maxArmLength = 0f; } Vector3 val3 = _levelSpawnPoint; if (!_spawnPointCaptured) { foreach (KeyValuePair remotePlayer in _remotePlayers) { if ((Object)(object)remotePlayer.Value.Head != (Object)null) { val3 = remotePlayer.Value.Head.transform.position; break; } } } val3.y = 0f; Plugin.Log.LogInfo((object)string.Format("[Ghost] Teleporting to {0} at floor level: {1}", _spawnPointCaptured ? "spawn" : "partner", val3)); TeleportLocalPlayer(val3); } private void TeleportLocalPlayer(Vector3 position) { //IL_008c: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) Player val = _gorillaPlayer ?? Player.Instance; if ((Object)(object)val == (Object)null) { val = Object.FindObjectOfType(); } if ((Object)(object)val != (Object)null) { object? obj = typeof(Player).GetField("playerRigidBody", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(val); Rigidbody val2 = (Rigidbody)((obj is Rigidbody) ? obj : null); if ((Object)(object)val2 != (Object)null) { val2.velocity = Vector3.zero; val2.angularVelocity = Vector3.zero; val2.position = position; ((Component)val2).transform.position = position; } ((Component)val).transform.position = position; FieldInfo field = typeof(Player).GetField("lastLeftHandPosition", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = typeof(Player).GetField("lastRightHandPosition", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field3 = typeof(Player).GetField("lastHeadPosition", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (Object)(object)val.leftHandTransform != (Object)null) { field.SetValue(val, val.leftHandTransform.position); } if (field2 != null && (Object)(object)val.rightHandTransform != (Object)null) { field2.SetValue(val, val.rightHandTransform.position); } FieldInfo field4 = typeof(Player).GetField("headCollider", BindingFlags.Instance | BindingFlags.Public); if (field3 != null && field4 != null) { object? value = field4.GetValue(val); SphereCollider val3 = (SphereCollider)((value is SphereCollider) ? value : null); if ((Object)(object)val3 != (Object)null) { field3.SetValue(val, ((Component)val3).transform.position); } } _gorillaPlayer = val; Plugin.Log.LogInfo((object)$"[Ghost] Teleported GorillaPlayer to {position}, rb.position={((val2 != null) ? new Vector3?(val2.position) : ((Vector3?)null))}"); } else { Plugin.Log.LogWarning((object)"[Ghost] GorillaPlayer not found even via FindObjectOfType!"); } } public void ResetGhostState() { bool localIsGhost = _localIsGhost; _localIsGhost = false; _pendingGhostTeleport = false; _pendingHomeLoad = false; _pendingHomeLoadTimer = 0f; _ghostSceneToReload = null; _ghostTeleportDelay = 0f; _pendingGhostPuzzleRestore = false; _ghostPuzzleRestoreFrames = 0; _savedGhostPuzzleState = null; IsGhostSceneReload = false; DestroyGhostVision(); if (localIsGhost && _steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)20); _writer.Put(value: false); _writer.Put(0); SendToAllPeers(); } Plugin.Log.LogInfo((object)"[Ghost] Ghost state reset"); } private void SendVersionCheck(int peerId) { _writer.Reset(); _writer.Put((byte)21); _writer.Put("1.2.6"); _steam.SendTo(peerId, _writer.GetBytes()); Plugin.Log.LogInfo((object)string.Format("Sent version {0} to peer {1}", "1.2.6", peerId)); } private void HandleVersionCheck(int peerId, PacketReader reader) { string text = reader.GetString(); _peerVersions[peerId] = text; Plugin.Log.LogInfo((object)string.Format("Peer {0} version: {1} (ours: {2})", peerId, text, "1.2.6")); if (text != "1.2.6") { Plugin.Log.LogWarning((object)string.Format("VERSION MISMATCH! Peer {0} has {1}, we have {2}", peerId, text, "1.2.6")); this.OnVersionMismatch?.Invoke(peerId, text); } } private void SendPingToAll() { float realtimeSinceStartup = Time.realtimeSinceStartup; foreach (int connectedPeerId in _connectedPeerIds) { _pendingPings[connectedPeerId] = realtimeSinceStartup; _writer.Reset(); _writer.Put((byte)22); _writer.Put(realtimeSinceStartup); _steam.SendTo(connectedPeerId, _writer.GetBytes(), reliable: false); } } private void HandlePing(int peerId, PacketReader reader) { float value = reader.GetFloat(); _writer.Reset(); _writer.Put((byte)23); _writer.Put(value); _steam.SendTo(peerId, _writer.GetBytes(), reliable: false); } private void HandlePong(int peerId, PacketReader reader) { float num = reader.GetFloat(); float value = (Time.realtimeSinceStartup - num) * 1000f; _peerPings[peerId] = value; _pendingPings.Remove(peerId); } private void HandleHostMigration(int peerId, PacketReader reader) { if (reader.GetBool()) { Plugin.Log.LogInfo((object)"HOST MIGRATION: We are now the host!"); _steam.BecomeHost(); this.OnBecameHost?.Invoke(); } } public void TransferHost(int newHostPeerId) { if (_steam.IsHost) { _writer.Reset(); _writer.Put((byte)24); _writer.Put(value: true); _steam.SendTo(newHostPeerId, _writer.GetBytes()); Plugin.Log.LogInfo((object)$"Transferred host to peer {newHostPeerId}"); } } public int GetFirstConnectedPeer() { using (HashSet.Enumerator enumerator = _connectedPeerIds.GetEnumerator()) { if (enumerator.MoveNext()) { return enumerator.Current; } } return -1; } public void SendVentDoorTrigger(int ventId, Vector3 position) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)28); _writer.Put(ventId); WriteVector3(_writer, position); SendToAllPeers(); } } private void HandleVentDoor(PacketReader reader) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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) reader.GetInt(); Vector3 val = ReadVector3(reader); VentAnimControl[] array = Object.FindObjectsOfType(); VentAnimControl val2 = null; float num = float.MaxValue; VentAnimControl[] array2 = array; foreach (VentAnimControl val3 in array2) { float num2 = Vector3.Distance(((Component)val3).transform.position, val); if (num2 < num) { num = num2; val2 = val3; } } if ((Object)(object)val2 != (Object)null && num < 2f) { FieldInfo field = typeof(VentAnimControl).GetField("timer1", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(val2, 10); } } } public void SendPaintingDeath() { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)29); SendToAllPeers(); Plugin.Log.LogInfo((object)"[Host] Sent painting death to all players"); } } private void HandlePaintingDeath() { Plugin.Log.LogInfo((object)"[Client] Received painting death event"); MoveTypeController val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null || !val.isInMainRoom) { return; } paintingControl val2 = Object.FindObjectOfType(); if (!((Object)(object)val2 != (Object)null)) { return; } FieldInfo field = typeof(paintingControl).GetField("jsc", BindingFlags.Instance | BindingFlags.Public); if (!(field != null)) { return; } object? value = field.GetValue(val2); jumpscareController val3 = (jumpscareController)((value is jumpscareController) ? value : null); if ((Object)(object)val3 != (Object)null) { switch (Random.Range(0, 5)) { case 0: val3.onDeathHarold(); break; case 1: val3.onDeathHenry(); break; case 2: val3.onDeathSmiley(); break; case 3: val3.onDeathSparky(); break; case 4: val3.onDeathJeff(); break; } } } public void CheckEndSceneSync() { if (_steam == null || !_steam.IsRunning || !_steam.IsHost) { return; } if ((Object)(object)_endControl == (Object)null) { _endControl = Object.FindObjectOfType(); } if (!((Object)(object)_endControl == (Object)null)) { bool num = _endControl.imageID != _lastEndImageID; bool flag = Mathf.Abs(_endControl.barFill1 - _lastEndBarFill) > 50f; if (num || flag) { _lastEndImageID = _endControl.imageID; _lastEndBarFill = _endControl.barFill1; SendEndProgress(_endControl.imageID, _endControl.barFill1); } } } private void SendEndProgress(int imageID, float barFill) { _writer.Reset(); _writer.Put((byte)40); _writer.Put(imageID); _writer.Put(barFill); SendToAllPeers(); } private void HandleEndProgress(PacketReader reader) { int imageID = reader.GetInt(); float barFill = reader.GetFloat(); if ((Object)(object)_endControl == (Object)null) { _endControl = Object.FindObjectOfType(); } if ((Object)(object)_endControl != (Object)null) { _endControl.imageID = imageID; _endControl.barFill1 = barFill; } } private void CheckEarCoveringSync() { if (_steam != null && _steam.IsRunning && !_localIsGhost) { bool isCoveringEars = earMaster.isCoveringEars; if (isCoveringEars != _lastEarCoveringState) { _lastEarCoveringState = isCoveringEars; SendEarCovering(isCoveringEars); } } } private void SendEarCovering(bool isCovering) { _writer.Reset(); _writer.Put((byte)41); _writer.Put(isCovering); SendToAllPeers(); } private void HandleEarCovering(PacketReader reader) { _remotePlayerCoveringEars = reader.GetBool(); } } public class RemotePlayer { private bool _usingRealHands; private float _handUpgradeRetryTime; private int _handUpgradeRetryCount; private const int MAX_HAND_UPGRADE_RETRIES = 100; private const float HAND_UPGRADE_RETRY_INTERVAL = 0.1f; private bool _isGhost; private float _targetLeftGrip; private float _targetLeftTrigger; private float _targetRightGrip; private float _targetRightTrigger; private Animator _leftHandAnimator; private Animator _rightHandAnimator; private int _animParamFlex = -1; private int _animParamPinch = -1; private bool _isStanding; private Vector3 _targetHeadPos; private Quaternion _targetHeadRot; private Vector3 _targetLeftHandPos; private Quaternion _targetLeftHandRot; private Vector3 _targetRightHandPos; private Quaternion _targetRightHandRot; private float _lerpSpeed = 25f; private bool _hasReceivedData; private int _logCounter; public int PeerId { get; } public GameObject Head { get; private set; } public GameObject FaceIndicator { get; private set; } public GameObject LeftHand { get; private set; } public GameObject RightHand { get; private set; } public Light Flashlight { get; private set; } public GameObject FlashlightCone { get; private set; } public GameObject LeftBattery { get; private set; } public GameObject RightBattery { get; private set; } public bool IsGhost => _isGhost; public RemotePlayer(int peerId) { PeerId = peerId; CreateVisual(); } private void CreateVisual() { //IL_0113: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0251: 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_01df: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Plugin.HelmetPrefab != (Object)null) { Head = Object.Instantiate(Plugin.HelmetPrefab); ((Object)Head).name = $"RemotePlayer_{PeerId}_Head"; Head.transform.localScale = new Vector3(0.15f, 0.15f, 0.15f); Collider[] componentsInChildren = Head.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } MonoBehaviour[] componentsInChildren2 = Head.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.Destroy((Object)(object)componentsInChildren2[i]); } Renderer[] componentsInChildren3 = Head.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren3.Length; i++) { componentsInChildren3[i].enabled = true; } } else { Head = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)Head).name = $"RemotePlayer_{PeerId}_Head"; Head.transform.localScale = new Vector3(0.25f, 0.25f, 0.25f); Object.Destroy((Object)(object)Head.GetComponent()); SetColor(Head, Color.yellow); } SphereCollider obj = Head.AddComponent(); obj.radius = 0.5f; ((Collider)obj).isTrigger = true; if ((Object)(object)Plugin.HelmetPrefab == (Object)null) { FaceIndicator = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)FaceIndicator).name = "FaceIndicator"; FaceIndicator.transform.SetParent(Head.transform); FaceIndicator.transform.localPosition = new Vector3(0f, 0f, 0.5f); FaceIndicator.transform.localScale = new Vector3(0.3f, 0.3f, 0.5f); Object.Destroy((Object)(object)FaceIndicator.GetComponent()); SetColor(FaceIndicator, Color.white); } SetupHandVisuals(); SetupFlashlight(); SetupBatteryVisuals(); Head.transform.position = Vector3.zero; LeftHand.transform.position = Vector3.zero; RightHand.transform.position = Vector3.zero; } private void SetupHandVisuals() { GameObject val = null; GameObject val2 = null; Transform[] array = Object.FindObjectsOfType(true); foreach (Transform val3 in array) { if (((Object)val3).name == "CustomHandLeft" && (Object)(object)val == (Object)null) { Transform parent = val3.parent; val = ((!((Object)(object)parent != (Object)null) || !((Object)parent).name.ToLower().Contains("offsettu")) ? ((Component)val3).gameObject : ((Component)parent).gameObject); } else if (((Object)val3).name == "CustomHandRight" && (Object)(object)val2 == (Object)null) { Transform parent2 = val3.parent; val2 = ((!((Object)(object)parent2 != (Object)null) || !((Object)parent2).name.ToLower().Contains("offsettu")) ? ((Component)val3).gameObject : ((Component)parent2).gameObject); } } if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null) { bool num = (Object)(object)val.GetComponentInChildren(true) != (Object)null; bool flag = (Object)(object)val2.GetComponentInChildren(true) != (Object)null; if (num && flag) { LeftHand = CloneHandMesh(val, $"RemotePlayer_{PeerId}_LeftHand"); RightHand = CloneHandMesh(val2, $"RemotePlayer_{PeerId}_RightHand"); _usingRealHands = true; TryFindHandAnimators(); return; } } CreateFallbackHands(); } private void LogHandHierarchyDetailed(Transform parent, int depth) { } private GameObject FindChildByNameContains(Transform parent, string nameContains) { Transform[] componentsInChildren = ((Component)parent).GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (((Object)val).name.ToLower().Contains(nameContains.ToLower())) { return ((Component)val).gameObject; } } return null; } private GameObject FindHandMeshInChildren(Transform parent, string handSide) { Renderer[] componentsInChildren = ((Component)parent).GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { string text = ((Object)((Component)val).gameObject).name.ToLower(); if (!text.Contains("ui") && !text.Contains("canvas") && !text.Contains("text") && (text.Contains("hand") || text.Contains("palm") || text.Contains("glove") || val is SkinnedMeshRenderer)) { return ((Component)val).gameObject; } } componentsInChildren = ((Component)parent).GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { if (val2 is SkinnedMeshRenderer || (Object)(object)((Component)val2).GetComponent() != (Object)null) { return ((Component)val2).gameObject; } } return null; } private GameObject CloneHandMesh(GameObject source, string newName) { //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: 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_0183: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate(source); ((Object)val).name = newName; Hand component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } Collider[] componentsInChildren2 = val.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.Destroy((Object)(object)componentsInChildren2[i]); } Transform[] componentsInChildren3 = val.GetComponentsInChildren(true); foreach (Transform val2 in componentsInChildren3) { string text = ((Object)val2).name.ToLower(); if (text.Contains("indicator") || text.Contains("tipindicator") || text.Contains("fingertip") || text.Contains("tip_") || text.Contains("collidervisual") || text.Contains("grabvolume") || text.Contains("leftcollider") || text.Contains("rightcollider") || text.Contains("coll_hands")) { ((Component)val2).gameObject.SetActive(false); } if (text.Contains("battery") || text.Contains("energycell")) { Object.Destroy((Object)(object)((Component)val2).gameObject); } if (text == "canvas") { Object.Destroy((Object)(object)((Component)val2).gameObject); } } Renderer[] componentsInChildren4 = val.GetComponentsInChildren(true); foreach (Renderer val3 in componentsInChildren4) { Bounds bounds = val3.bounds; Vector3 size = ((Bounds)(ref bounds)).size; if (((Vector3)(ref size)).magnitude < 0.05f) { string text2 = ((Object)((Component)val3).gameObject).name.ToLower(); if (!text2.Contains("hand") && !text2.Contains("geom")) { val3.enabled = false; } } } componentsInChildren4 = val.GetComponentsInChildren(); foreach (Renderer val4 in componentsInChildren4) { if (((Component)val4).gameObject.activeInHierarchy) { val4.enabled = true; } } return val; } private void CreateFallbackHands() { //IL_0046: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) LeftHand = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)LeftHand).name = $"RemotePlayer_{PeerId}_LeftHand"; LeftHand.transform.localScale = new Vector3(0.08f, 0.08f, 0.08f); Object.Destroy((Object)(object)LeftHand.GetComponent()); SetColor(LeftHand, new Color(0f, 0.8f, 1f, 0.8f)); RightHand = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)RightHand).name = $"RemotePlayer_{PeerId}_RightHand"; RightHand.transform.localScale = new Vector3(0.08f, 0.08f, 0.08f); Object.Destroy((Object)(object)RightHand.GetComponent()); SetColor(RightHand, new Color(0f, 0.8f, 1f, 0.8f)); _usingRealHands = false; } private void SetupFlashlight() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0046: 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) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Flashlight"); val.transform.SetParent(Head.transform); val.transform.localPosition = new Vector3(0f, 0f, 0.15f); val.transform.localRotation = Quaternion.identity; Flashlight = val.AddComponent(); Flashlight.type = (LightType)0; Flashlight.color = new Color(1f, 0.95f, 0.8f); Flashlight.intensity = 0.8f; Flashlight.range = 8f; Flashlight.spotAngle = 35f; Flashlight.innerSpotAngle = 20f; Flashlight.shadows = (LightShadows)0; ((Behaviour)Flashlight).enabled = false; FlashlightCone = GameObject.CreatePrimitive((PrimitiveType)2); ((Object)FlashlightCone).name = "FlashlightCone"; FlashlightCone.transform.SetParent(Head.transform); FlashlightCone.transform.localPosition = new Vector3(0f, 0f, 0.4f); FlashlightCone.transform.localRotation = Quaternion.Euler(90f, 0f, 0f); FlashlightCone.transform.localScale = new Vector3(0.15f, 0.25f, 0.15f); Object.Destroy((Object)(object)FlashlightCone.GetComponent()); SetColor(FlashlightCone, new Color(1f, 1f, 0.7f, 0.15f)); FlashlightCone.SetActive(false); } private void SetupBatteryVisuals() { //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) BackpackControl val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && (Object)(object)val.batteryLeft != (Object)null && (Object)(object)val.batteryRight != (Object)null) { LeftBattery = Object.Instantiate(val.batteryLeft); ((Object)LeftBattery).name = $"RemotePlayer_{PeerId}_LeftBattery"; LeftBattery.transform.SetParent(LeftHand.transform, false); Collider[] componentsInChildren = LeftBattery.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } MonoBehaviour[] componentsInChildren2 = LeftBattery.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.Destroy((Object)(object)componentsInChildren2[i]); } RightBattery = Object.Instantiate(val.batteryRight); ((Object)RightBattery).name = $"RemotePlayer_{PeerId}_RightBattery"; RightBattery.transform.SetParent(RightHand.transform, false); componentsInChildren = RightBattery.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } componentsInChildren2 = RightBattery.GetComponentsInChildren(); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.Destroy((Object)(object)componentsInChildren2[i]); } LeftBattery.SetActive(false); RightBattery.SetActive(false); Renderer[] componentsInChildren3 = LeftBattery.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { componentsInChildren3[i].enabled = false; } } else { LeftBattery = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)LeftBattery).name = $"RemotePlayer_{PeerId}_LeftBattery"; LeftBattery.transform.SetParent(LeftHand.transform); LeftBattery.transform.localPosition = new Vector3(0f, 0f, 0.05f); LeftBattery.transform.localScale = new Vector3(0.04f, 0.06f, 0.08f); Object.Destroy((Object)(object)LeftBattery.GetComponent()); SetColor(LeftBattery, new Color(1f, 0.5f, 0f)); LeftBattery.SetActive(false); RightBattery = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)RightBattery).name = $"RemotePlayer_{PeerId}_RightBattery"; RightBattery.transform.SetParent(RightHand.transform); RightBattery.transform.localPosition = new Vector3(0f, 0f, 0.05f); RightBattery.transform.localScale = new Vector3(0.04f, 0.06f, 0.08f); Object.Destroy((Object)(object)RightBattery.GetComponent()); SetColor(RightBattery, new Color(1f, 0.5f, 0f)); RightBattery.SetActive(false); } } public void TryUpgradeVisuals() { _handUpgradeRetryCount = 0; _handUpgradeRetryTime = Time.time; TryUpgradeHandVisuals(); } private void TryHandUpgradeWithRetry() { if (!_usingRealHands && _handUpgradeRetryCount < 100 && !(Time.time - _handUpgradeRetryTime < 0.1f)) { _handUpgradeRetryTime = Time.time; _handUpgradeRetryCount++; TryUpgradeHandVisuals(); } } private void TryFindHandAnimators() { if ((Object)(object)LeftHand != (Object)null && (Object)(object)_leftHandAnimator == (Object)null) { _leftHandAnimator = LeftHand.GetComponentInChildren(); } if ((Object)(object)RightHand != (Object)null && (Object)(object)_rightHandAnimator == (Object)null) { _rightHandAnimator = RightHand.GetComponentInChildren(); } if (((Object)(object)_leftHandAnimator != (Object)null || (Object)(object)_rightHandAnimator != (Object)null) && _animParamFlex == -1) { _animParamFlex = Animator.StringToHash("Flex"); _animParamPinch = Animator.StringToHash("Pinch"); } } private void TryUpgradeHandVisuals() { //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0435: Unknown result type (might be due to invalid IL or missing references) //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_0459: Unknown result type (might be due to invalid IL or missing references) if (_usingRealHands) { return; } GameObject val = null; GameObject val2 = null; Transform[] array = Object.FindObjectsOfType(true); Transform[] array2 = array; foreach (Transform val3 in array2) { if (((Object)val3).name == "CustomHandLeft" && (Object)(object)val == (Object)null) { Transform parent = val3.parent; val = ((!((Object)(object)parent != (Object)null) || !((Object)parent).name.ToLower().Contains("offsettu")) ? ((Component)val3).gameObject : ((Component)parent).gameObject); } else if (((Object)val3).name == "CustomHandRight" && (Object)(object)val2 == (Object)null) { Transform parent2 = val3.parent; val2 = ((!((Object)(object)parent2 != (Object)null) || !((Object)parent2).name.ToLower().Contains("offsettu")) ? ((Component)val3).gameObject : ((Component)parent2).gameObject); } } if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { array2 = array; foreach (Transform val4 in array2) { string text = ((Object)val4).name.ToLower(); if (text.Contains("l_hand_skeletal") && (Object)(object)val == (Object)null) { val = ((Component)val4).gameObject; } else if (text.Contains("r_hand_skeletal") && (Object)(object)val2 == (Object)null) { val2 = ((Component)val4).gameObject; } } } if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { BackpackControl val5 = Object.FindObjectOfType(); if ((Object)(object)val5 != (Object)null) { if ((Object)(object)val == (Object)null && (Object)(object)val5.leftHand != (Object)null) { Transform val6 = val5.leftHand.transform.Find("offsettu"); if ((Object)(object)val6 != (Object)null) { val = ((Component)val6).gameObject; } else { SkinnedMeshRenderer componentInChildren = val5.leftHand.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { val = ((Component)componentInChildren).gameObject; } } } if ((Object)(object)val2 == (Object)null && (Object)(object)val5.rightHand != (Object)null) { Transform val7 = val5.rightHand.transform.Find("offsettu"); if ((Object)(object)val7 != (Object)null) { val2 = ((Component)val7).gameObject; } else { SkinnedMeshRenderer componentInChildren2 = val5.rightHand.GetComponentInChildren(true); if ((Object)(object)componentInChildren2 != (Object)null) { val2 = ((Component)componentInChildren2).gameObject; } } } } } if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { HandAnim[] array3 = Object.FindObjectsOfType(true); foreach (HandAnim val8 in array3) { string text2 = ((Object)((Component)val8).gameObject).name.ToLower(); if ((text2.Contains("left") || text2.Contains("l_")) && (Object)(object)val == (Object)null) { val = ((Component)val8).gameObject; } else if ((text2.Contains("right") || text2.Contains("r_")) && (Object)(object)val2 == (Object)null) { val2 = ((Component)val8).gameObject; } } } if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } bool num = (Object)(object)val.GetComponentInChildren(true) != (Object)null; bool flag = (Object)(object)val2.GetComponentInChildren(true) != (Object)null; if (num && flag) { Vector3 position = LeftHand.transform.position; Quaternion rotation = LeftHand.transform.rotation; Vector3 position2 = RightHand.transform.position; Quaternion rotation2 = RightHand.transform.rotation; if ((Object)(object)LeftBattery != (Object)null) { LeftBattery.transform.SetParent((Transform)null); } if ((Object)(object)RightBattery != (Object)null) { RightBattery.transform.SetParent((Transform)null); } Object.Destroy((Object)(object)LeftHand); Object.Destroy((Object)(object)RightHand); LeftHand = CloneHandMesh(val, $"RemotePlayer_{PeerId}_LeftHand"); RightHand = CloneHandMesh(val2, $"RemotePlayer_{PeerId}_RightHand"); LeftHand.transform.position = position; LeftHand.transform.rotation = rotation; RightHand.transform.position = position2; RightHand.transform.rotation = rotation2; if ((Object)(object)LeftBattery != (Object)null) { LeftBattery.transform.SetParent(LeftHand.transform); } if ((Object)(object)RightBattery != (Object)null) { RightBattery.transform.SetParent(RightHand.transform); } _usingRealHands = true; TryFindHandAnimators(); } } private void SetColor(GameObject obj, Color color) { //IL_0080: 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) //IL_006c: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) Renderer component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Shader val = Shader.Find("Standard"); if ((Object)(object)val == (Object)null) { val = Shader.Find("Unlit/Color"); } if ((Object)(object)val == (Object)null) { val = Shader.Find("Diffuse"); } if ((Object)(object)val == (Object)null) { val = Shader.Find("Mobile/Diffuse"); } if ((Object)(object)val != (Object)null) { component.material = new Material(val); component.material.color = color; } else { component.material.color = color; } component.enabled = true; } } public void SetTargets(bool isStanding, Vector3 bodyPos, Quaternion bodyRot, Vector3 headPos, Quaternion headRot, Vector3 leftHandPos, Quaternion leftHandRot, Vector3 rightHandPos, Quaternion rightHandRot, float leftGrip, float leftTrigger, float rightGrip, float rightTrigger) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0028: 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_0030: 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_0071: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: 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) _isStanding = isStanding; _targetHeadPos = headPos; _targetHeadRot = headRot; _targetLeftHandPos = leftHandPos; _targetLeftHandRot = leftHandRot; _targetRightHandPos = rightHandPos; _targetRightHandRot = rightHandRot; _targetLeftGrip = leftGrip; _targetLeftTrigger = leftTrigger; _targetRightGrip = rightGrip; _targetRightTrigger = rightTrigger; if (!_hasReceivedData) { _hasReceivedData = true; Head.transform.position = headPos; Head.transform.rotation = headRot; LeftHand.transform.position = leftHandPos; LeftHand.transform.rotation = leftHandRot; RightHand.transform.position = rightHandPos; RightHand.transform.rotation = rightHandRot; } } public void UpdateInterpolation() { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_0110: 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_013c: 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) //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) if (!((Object)(object)Head == (Object)null) && _hasReceivedData) { _logCounter++; TryHandUpgradeWithRetry(); if (_animParamFlex == -1 && _usingRealHands) { TryFindHandAnimators(); } if ((Object)(object)_leftHandAnimator != (Object)null && _animParamFlex != -1) { _leftHandAnimator.SetFloat(_animParamFlex, _targetLeftGrip); _leftHandAnimator.SetFloat(_animParamPinch, _targetLeftTrigger); } if ((Object)(object)_rightHandAnimator != (Object)null && _animParamFlex != -1) { _rightHandAnimator.SetFloat(_animParamFlex, _targetRightGrip); _rightHandAnimator.SetFloat(_animParamPinch, _targetRightTrigger); } float num = Mathf.Clamp01(Time.deltaTime * _lerpSpeed); if (Vector3.Distance(Head.transform.position, _targetHeadPos) > 0.5f) { Head.transform.position = _targetHeadPos; Head.transform.rotation = _targetHeadRot; LeftHand.transform.position = _targetLeftHandPos; LeftHand.transform.rotation = _targetLeftHandRot; RightHand.transform.position = _targetRightHandPos; RightHand.transform.rotation = _targetRightHandRot; } else { Head.transform.position = Vector3.Lerp(Head.transform.position, _targetHeadPos, num); Head.transform.rotation = Quaternion.Slerp(Head.transform.rotation, _targetHeadRot, num); float num2 = Mathf.Clamp01(Time.deltaTime * _lerpSpeed * 1.5f); LeftHand.transform.position = Vector3.Lerp(LeftHand.transform.position, _targetLeftHandPos, num2); LeftHand.transform.rotation = Quaternion.Slerp(LeftHand.transform.rotation, _targetLeftHandRot, num2); RightHand.transform.position = Vector3.Lerp(RightHand.transform.position, _targetRightHandPos, num2); RightHand.transform.rotation = Quaternion.Slerp(RightHand.transform.rotation, _targetRightHandRot, num2); } } } public void SetFlashlightState(bool isOn) { if ((Object)(object)Flashlight != (Object)null) { ((Behaviour)Flashlight).enabled = isOn; } if ((Object)(object)FlashlightCone != (Object)null) { FlashlightCone.SetActive(isOn); } } public void SetBatteryState(bool leftHolding, bool rightHolding) { if ((Object)(object)LeftBattery != (Object)null) { LeftBattery.SetActive(leftHolding); Renderer[] componentsInChildren = LeftBattery.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = leftHolding; } } if ((Object)(object)RightBattery != (Object)null) { RightBattery.SetActive(rightHolding); Renderer[] componentsInChildren = RightBattery.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = rightHolding; } } } public void SetGhostState(bool isGhost) { _isGhost = isGhost; float alpha = (isGhost ? 0.4f : 1f); SetObjectTransparency(Head, alpha); SetObjectTransparency(FaceIndicator, alpha); SetObjectTransparency(LeftHand, alpha); SetObjectTransparency(RightHand, alpha); } private void SetObjectTransparency(GameObject obj, float alpha) { //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_0047: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)obj == (Object)null) { return; } Renderer[] componentsInChildren = obj.GetComponentsInChildren(); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val.material != (Object)null) { Color color = val.material.color; color.a = alpha; val.material.color = color; if (alpha < 1f) { val.material.SetFloat("_Mode", 3f); val.material.SetInt("_SrcBlend", 5); val.material.SetInt("_DstBlend", 10); val.material.SetInt("_ZWrite", 0); val.material.DisableKeyword("_ALPHATEST_ON"); val.material.EnableKeyword("_ALPHABLEND_ON"); val.material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); val.material.renderQueue = 3000; } else { val.material.SetFloat("_Mode", 0f); val.material.SetInt("_SrcBlend", 1); val.material.SetInt("_DstBlend", 0); val.material.SetInt("_ZWrite", 1); val.material.DisableKeyword("_ALPHATEST_ON"); val.material.DisableKeyword("_ALPHABLEND_ON"); val.material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); val.material.renderQueue = -1; } } } } public void Destroy() { if ((Object)(object)Head != (Object)null) { Object.Destroy((Object)(object)Head); } if ((Object)(object)FaceIndicator != (Object)null) { Object.Destroy((Object)(object)FaceIndicator); } if ((Object)(object)LeftHand != (Object)null) { Object.Destroy((Object)(object)LeftHand); } if ((Object)(object)RightHand != (Object)null) { Object.Destroy((Object)(object)RightHand); } if ((Object)(object)LeftBattery != (Object)null) { Object.Destroy((Object)(object)LeftBattery); } if ((Object)(object)RightBattery != (Object)null) { Object.Destroy((Object)(object)RightBattery); } } } public class SpectateSystem { public const byte PACKET_SPECTATE_FRAME = 30; public const byte PACKET_SPECTATE_START = 31; public const byte PACKET_SPECTATE_STOP = 32; private const int CAPTURE_WIDTH = 320; private const int CAPTURE_HEIGHT = 180; private const float CAPTURE_INTERVAL = 0.25f; private const int JPEG_QUALITY = 60; private RenderTexture _captureRT; private Texture2D _captureTex; private Camera _captureCamera; private float _lastCaptureTime; private bool _isSending; private Texture2D _receiveTex; private MoviePlayerSample _tvPlayer; private Renderer _tvRenderer; private bool _isReceiving; private bool _tvWasPlaying; private INetworkTransport _steam; private PacketWriter _writer = new PacketWriter(65536); public bool IsSending => _isSending; public bool IsReceiving => _isReceiving; public void Initialize(INetworkTransport steam) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown _steam = steam; _captureRT = new RenderTexture(320, 180, 16); _captureRT.Create(); _captureTex = new Texture2D(320, 180, (TextureFormat)3, false); _receiveTex = new Texture2D(2, 2, (TextureFormat)3, false); } public void Update() { if (_steam != null && _steam.IsRunning && _isSending) { UpdateSender(); } } public void StartSending() { if (!_isSending) { _captureCamera = Camera.main; if ((Object)(object)_captureCamera == (Object)null) { _captureCamera = Object.FindObjectOfType(); } if ((Object)(object)_captureCamera == (Object)null) { Plugin.Log.LogWarning((object)"[Spectate] No camera found, can't send frames"); return; } _isSending = true; _lastCaptureTime = 0f; SendSpectateStart(); } } public void StopSending() { if (_isSending) { _isSending = false; _captureCamera = null; SendSpectateStop(); } } public void StartReceiving() { if (_isReceiving) { return; } _tvPlayer = Object.FindObjectOfType(); if ((Object)(object)_tvPlayer == (Object)null) { Plugin.Log.LogWarning((object)"[Spectate] No MoviePlayerSample found in scene"); return; } _tvRenderer = ((Component)_tvPlayer).GetComponent(); if ((Object)(object)_tvRenderer == (Object)null) { Plugin.Log.LogWarning((object)"[Spectate] No Renderer on TV"); return; } _tvWasPlaying = _tvPlayer.IsPlaying; _tvPlayer.Stop(); if ((Object)(object)_tvRenderer.material != (Object)null) { _tvRenderer.material.mainTexture = (Texture)(object)_receiveTex; } _isReceiving = true; } public void StopReceiving() { if (_isReceiving) { _isReceiving = false; if ((Object)(object)_tvPlayer != (Object)null && _tvWasPlaying) { _tvPlayer.Play(); } _tvPlayer = null; _tvRenderer = null; } } private void UpdateSender() { //IL_0094: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_captureCamera == (Object)null) && _isSending && !(Time.realtimeSinceStartup - _lastCaptureTime < 0.25f)) { _lastCaptureTime = Time.realtimeSinceStartup; RenderTexture targetTexture = _captureCamera.targetTexture; _captureCamera.targetTexture = _captureRT; _captureCamera.Render(); _captureCamera.targetTexture = targetTexture; RenderTexture active = RenderTexture.active; RenderTexture.active = _captureRT; _captureTex.ReadPixels(new Rect(0f, 0f, 320f, 180f), 0, 0); _captureTex.Apply(); RenderTexture.active = active; byte[] array = ImageConversion.EncodeToJPG(_captureTex, 60); if (array != null && array.Length != 0) { SendFrame(array); } } } private void SendFrame(byte[] jpegData) { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)30); _writer.Put(jpegData.Length); for (int i = 0; i < jpegData.Length; i++) { _writer.Put(jpegData[i]); } _steam.SendToAll(_writer.GetBytes(), reliable: false); } } private void SendSpectateStart() { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)31); _steam.SendToAll(_writer.GetBytes()); } } private void SendSpectateStop() { if (_steam != null && _steam.IsRunning) { _writer.Reset(); _writer.Put((byte)32); _steam.SendToAll(_writer.GetBytes()); } } public void OnPacketReceived(byte packetType, PacketReader reader) { switch (packetType) { case 30: HandleFrame(reader); break; case 31: HandleSpectateStart(); break; case 32: HandleSpectateStop(); break; } } private void HandleFrame(PacketReader reader) { if (!_isReceiving) { return; } int num = reader.GetInt(); if (num > 0 && num <= 102400) { byte[] array = new byte[num]; for (int i = 0; i < num; i++) { array[i] = reader.GetByte(); } ImageConversion.LoadImage(_receiveTex, array); if ((Object)(object)_tvRenderer != (Object)null && (Object)(object)_tvRenderer.material != (Object)null) { _tvRenderer.material.mainTexture = (Texture)(object)_receiveTex; } } } private void HandleSpectateStart() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name.Equals("Home", StringComparison.OrdinalIgnoreCase)) { StartReceiving(); } } private void HandleSpectateStop() { StopReceiving(); } public void Cleanup() { StopSending(); StopReceiving(); if ((Object)(object)_captureRT != (Object)null) { _captureRT.Release(); Object.Destroy((Object)(object)_captureRT); _captureRT = null; } if ((Object)(object)_captureTex != (Object)null) { Object.Destroy((Object)(object)_captureTex); _captureTex = null; } if ((Object)(object)_receiveTex != (Object)null) { Object.Destroy((Object)(object)_receiveTex); _receiveTex = null; } } } public class VoiceChat { public const byte PACKET_VOICE = 200; private bool _isRecording; private INetworkTransport _steam; private Dictionary _voicePlayers = new Dictionary(); private int _steamSampleRate; private PacketWriter _writer = new PacketWriter(24576); public bool Enabled { get; set; } = true; public bool PushToTalk { get; set; } public float MaxDistance { get; set; } = 30f; public float MinDistance { get; set; } = 2f; public void Initialize(INetworkTransport steam) { _steam = steam; } private void EnsureSteamReady() { if (_steamSampleRate != 0 || !SteamClient.IsValid) { return; } try { _steamSampleRate = (int)SteamUser.OptimalSampleRate; if (_steamSampleRate == 0) { _steamSampleRate = 24000; } Plugin.Log.LogInfo((object)$"[Voice] Steam rate={_steamSampleRate}, Unity rate={AudioSettings.outputSampleRate}"); } catch (Exception ex) { Plugin.Log.LogError((object)("[Voice] Sample rate error: " + ex.Message)); _steamSampleRate = 24000; } } public void Update() { if (Enabled && _steam != null && _steam.IsConnected) { EnsureSteamReady(); if (_steamSampleRate != 0) { UpdateRecording(); UpdateVoicePlayerPositions(); } } } private void UpdateRecording() { bool flag = !PushToTalk; if (flag && !_isRecording) { SteamUser.VoiceRecord = true; _isRecording = true; } else if (!flag && _isRecording) { SteamUser.VoiceRecord = false; _isRecording = false; } if (!_isRecording || !SteamUser.HasVoiceData) { return; } byte[] array = SteamUser.ReadVoiceDataBytes(); if (array != null && array.Length != 0) { _writer.Reset(); _writer.Put((byte)200); _writer.Put(array.Length); for (int i = 0; i < array.Length; i++) { _writer.Put(array[i]); } _steam.SendToAll(_writer.GetBytes(), reliable: false); } } public void OnVoiceDataReceived(int peerId, PacketReader reader) { if (!Enabled) { return; } EnsureSteamReady(); int num = reader.GetInt(); if (num <= 0 || num > 20000) { return; } byte[] array = new byte[num]; for (int i = 0; i < num; i++) { array[i] = reader.GetByte(); } using MemoryStream memoryStream = new MemoryStream(); if (SteamUser.DecompressVoice(array, (Stream)memoryStream) > 0) { byte[] pcm = memoryStream.ToArray(); if (!_voicePlayers.TryGetValue(peerId, out var value)) { value = CreateVoicePlayer(peerId); _voicePlayers[peerId] = value; } value?.QueueAudio(pcm, _steamSampleRate); } } private VoicePlayer CreateVoicePlayer(int peerId) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown GameObject val = new GameObject($"VoicePlayer_{peerId}"); Object.DontDestroyOnLoad((Object)val); VoicePlayer voicePlayer = val.AddComponent(); voicePlayer.PeerId = peerId; voicePlayer.MinDistance = MinDistance; voicePlayer.MaxDistance = MaxDistance; return voicePlayer; } private void UpdateVoicePlayerPositions() { //IL_008f: Unknown result type (might be due to invalid IL or missing references) PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync == null) { return; } List list = null; foreach (KeyValuePair voicePlayer in _voicePlayers) { if ((Object)(object)voicePlayer.Value == (Object)null || (Object)(object)((Component)voicePlayer.Value).gameObject == (Object)null) { if (list == null) { list = new List(); } list.Add(voicePlayer.Key); } else { Vector3? remotePlayerPosition = playerSync.GetRemotePlayerPosition(voicePlayer.Key); if (remotePlayerPosition.HasValue) { ((Component)voicePlayer.Value).transform.position = remotePlayerPosition.Value; } } } if (list == null) { return; } foreach (int item in list) { _voicePlayers.Remove(item); } } public void OnPeerDisconnected(int peerId) { if (_voicePlayers.TryGetValue(peerId, out var value)) { if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } _voicePlayers.Remove(peerId); } } public void Cleanup() { if (_isRecording) { SteamUser.VoiceRecord = false; _isRecording = false; } foreach (VoicePlayer value in _voicePlayers.Values) { if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)value).gameObject); } } _voicePlayers.Clear(); } } public class VoicePlayer : MonoBehaviour { private AudioSource _audioSource; private float[] _ringBuffer; private volatile int _writePos; private volatile int _readPos; private volatile int _samplesAvailable; private readonly object _lock = new object(); private int _bufferSize; private int _outputRate; private float _resampleStep; private float _resampleFrac; private const int JITTER_MS = 20; private int _jitterSamples; private bool _playing; private float _silenceTimer; public int PeerId { get; set; } public float MinDistance { get; set; } = 2f; public float MaxDistance { get; set; } = 30f; private void Awake() { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown _outputRate = AudioSettings.outputSampleRate; _bufferSize = _outputRate * 2; _ringBuffer = new float[_bufferSize]; _jitterSamples = _outputRate * 20 / 1000; _audioSource = ((Component)this).gameObject.AddComponent(); _audioSource.spatialBlend = 1f; _audioSource.rolloffMode = (AudioRolloffMode)1; _audioSource.minDistance = MinDistance; _audioSource.maxDistance = MaxDistance; _audioSource.loop = true; _audioSource.playOnAwake = false; _audioSource.volume = 1f; _audioSource.dopplerLevel = 0f; _audioSource.priority = 0; AudioClip clip = AudioClip.Create($"Voice_{PeerId}", 1024, 1, _outputRate, true, new PCMReaderCallback(OnAudioRead)); _audioSource.clip = clip; _audioSource.Play(); } public void QueueAudio(byte[] pcm, int inputRate) { if (pcm == null || pcm.Length < 2) { return; } _resampleStep = (float)inputRate / (float)_outputRate; int num = pcm.Length / 2; float[] array = new float[num]; for (int i = 0; i < num; i++) { short num2 = (short)(pcm[i * 2] | (pcm[i * 2 + 1] << 8)); array[i] = (float)num2 / 32768f; } lock (_lock) { while (_resampleFrac < (float)(num - 1)) { int num3 = (int)_resampleFrac; float num4 = _resampleFrac - (float)num3; float num5 = ((num3 + 1 >= num) ? array[num3] : (array[num3] * (1f - num4) + array[num3 + 1] * num4)); _ringBuffer[_writePos] = num5; _writePos = (_writePos + 1) % _bufferSize; if (_samplesAvailable < _bufferSize) { _samplesAvailable++; } _resampleFrac += _resampleStep; } _resampleFrac -= (int)_resampleFrac; if (_resampleFrac < 0f) { _resampleFrac = 0f; } } _silenceTimer = 0f; } private void OnAudioRead(float[] data) { lock (_lock) { if (!_playing) { if (_samplesAvailable < _jitterSamples) { Array.Clear(data, 0, data.Length); return; } _playing = true; } for (int i = 0; i < data.Length; i++) { if (_samplesAvailable > 0) { data[i] = _ringBuffer[_readPos]; _readPos = (_readPos + 1) % _bufferSize; _samplesAvailable--; } else { data[i] = 0f; _playing = false; } } } } private void Update() { if ((Object)(object)_audioSource != (Object)null) { _audioSource.minDistance = MinDistance; _audioSource.maxDistance = MaxDistance; } _silenceTimer += Time.deltaTime; if (_silenceTimer > 1f) { lock (_lock) { _samplesAvailable = 0; _readPos = _writePos; _playing = false; } } } private void OnDestroy() { if ((Object)(object)_audioSource != (Object)null) { _audioSource.Stop(); if ((Object)(object)_audioSource.clip != (Object)null) { Object.Destroy((Object)(object)_audioSource.clip); } } } } public interface INetworkTransport { bool IsRunning { get; } bool IsHost { get; } bool IsConnected { get; } bool IsInLobby { get; } bool IsJoining { get; } bool IsLobbyLocked { get; } int ConnectedPeerCount { get; } int Ping { get; } event Action OnPeerConnected; event Action OnPeerDisconnected; event Action OnDataReceived; event Action OnLobbyCreated; event Action OnLobbyJoined; event Action OnJoinFailed; event Action OnLobbyLeft; event Action OnPlayerJoined; event Action OnPlayerLeft; event Action OnVersionMismatch; event Action OnBecameHost; bool Initialize(); void Shutdown(); void Update(); void StartHost(int port = 0); void Connect(string address, int port); void Disconnect(); void LockLobby(); void UnlockLobby(); void JoinLobby(string lobbyId); void ProcessPendingJoin(); void BecomeHost(); void SendToAll(byte[] data, bool reliable = true); void SendTo(int peerId, byte[] data, bool reliable = true); string GetLobbyId(); string GetPlayerName(); List GetConnectedPlayerNames(); void InviteFriends(); } public class PacketWriter { private byte[] _buffer; private int _position; public int Length => _position; public PacketWriter(int initialCapacity = 256) { _buffer = new byte[initialCapacity]; _position = 0; } public void Reset() { _position = 0; } public byte[] GetBytes() { byte[] array = new byte[_position]; Array.Copy(_buffer, array, _position); return array; } private void EnsureCapacity(int additionalBytes) { if (_position + additionalBytes > _buffer.Length) { byte[] array = new byte[Math.Max(_buffer.Length * 2, _position + additionalBytes)]; Array.Copy(_buffer, array, _position); _buffer = array; } } public void Put(byte value) { EnsureCapacity(1); _buffer[_position++] = value; } public void Put(bool value) { Put(value ? ((byte)1) : ((byte)0)); } public void Put(int value) { EnsureCapacity(4); _buffer[_position++] = (byte)value; _buffer[_position++] = (byte)(value >> 8); _buffer[_position++] = (byte)(value >> 16); _buffer[_position++] = (byte)(value >> 24); } public void Put(long value) { byte[] bytes = BitConverter.GetBytes(value); EnsureCapacity(8); Array.Copy(bytes, 0, _buffer, _position, 8); _position += 8; } public void Put(float value) { byte[] bytes = BitConverter.GetBytes(value); EnsureCapacity(4); Array.Copy(bytes, 0, _buffer, _position, 4); _position += 4; } public void Put(double value) { byte[] bytes = BitConverter.GetBytes(value); EnsureCapacity(8); Array.Copy(bytes, 0, _buffer, _position, 8); _position += 8; } public void Put(string value) { if (string.IsNullOrEmpty(value)) { Put(0); return; } byte[] bytes = Encoding.UTF8.GetBytes(value); Put(bytes.Length); EnsureCapacity(bytes.Length); Array.Copy(bytes, 0, _buffer, _position, bytes.Length); _position += bytes.Length; } } public class PacketReader { private byte[] _buffer; private int _position; private int _length; public int AvailableBytes => _length - _position; public PacketReader(byte[] data) { _buffer = data; _position = 0; _length = data.Length; } public byte GetByte() { return _buffer[_position++]; } public bool GetBool() { return GetByte() != 0; } public int GetInt() { int result = _buffer[_position] | (_buffer[_position + 1] << 8) | (_buffer[_position + 2] << 16) | (_buffer[_position + 3] << 24); _position += 4; return result; } public long GetLong() { long result = BitConverter.ToInt64(_buffer, _position); _position += 8; return result; } public float GetFloat() { float result = BitConverter.ToSingle(_buffer, _position); _position += 4; return result; } public double GetDouble() { double result = BitConverter.ToDouble(_buffer, _position); _position += 8; return result; } public string GetString() { int num = GetInt(); if (num == 0) { return string.Empty; } string result = Encoding.UTF8.GetString(_buffer, _position, num); _position += num; return result; } } public class SteamTransport : INetworkTransport { public class FriendGameInfo { public Friend Friend; public string Name; public SteamId SteamId; public bool IsInGame; public bool IsJoinable; public ulong LobbyId; public string Status; } private float _lastPingTime; private float _pingInterval = 2f; private Dictionary _pingSentTimes = new Dictionary(); private Dictionary _peerPings = new Dictionary(); private const byte TRANSPORT_PING = 250; private const byte TRANSPORT_PONG = 251; private const byte TRANSPORT_VERSION_CHECK = 252; private const byte TRANSPORT_VERSION_MISMATCH = 253; private const byte TRANSPORT_HOST_MIGRATION = 254; private Dictionary _steamIdToPeerId = new Dictionary(); private Dictionary _peerIdToSteamId = new Dictionary(); private HashSet _connectedPeers = new HashSet(); private int _nextPeerId = 1; private bool _initialized; private const uint CRAWLSPACE2_APP_ID = 2258670u; private HashSet _versionVerifiedPeers = new HashSet(); private int _sendFailCount; private ulong _pendingJoinLobbyId; public bool IsRunning { get; private set; } public bool IsHost { get; private set; } public bool IsJoining { get; private set; } public bool IsConnected => _connectedPeers.Count > 0; public int ConnectedPeerCount => _connectedPeers.Count; public bool IsLobbyLocked { get; private set; } public Lobby CurrentLobby { get; private set; } public bool IsInLobby { get { //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_0009: Unknown result type (might be due to invalid IL or missing references) Lobby currentLobby = CurrentLobby; return ((Lobby)(ref currentLobby)).Id.Value != 0; } } public int Ping { get; private set; } public event Action OnPeerConnected; public event Action OnPeerDisconnected; public event Action OnDataReceived; public event Action OnVersionMismatch; public event Action OnBecameHost; event Action INetworkTransport.OnLobbyCreated { add { _onLobbyCreatedString += value; } remove { _onLobbyCreatedString -= value; } } event Action INetworkTransport.OnLobbyJoined { add { _onLobbyJoinedString += value; } remove { _onLobbyJoinedString -= value; } } event Action INetworkTransport.OnPlayerJoined { add { _onPlayerJoinedString += value; } remove { _onPlayerJoinedString -= value; } } event Action INetworkTransport.OnPlayerLeft { add { _onPlayerLeftString += value; } remove { _onPlayerLeftString -= value; } } private event Action _onLobbyCreatedString; private event Action _onLobbyJoinedString; private event Action _onPlayerJoinedString; private event Action _onPlayerLeftString; public event Action OnLobbyCreated; public event Action OnLobbyJoined; public event Action OnJoinFailed; public event Action OnLobbyLeft; public event Action OnPlayerJoined; public event Action OnPlayerLeft; public bool Initialize() { if (_initialized) { return true; } try { if (!SteamClient.IsValid) { uint steamAppId = GetSteamAppId(); Plugin.Log.LogInfo((object)$"Initializing Steam with App ID: {steamAppId}"); try { SteamClient.Init(steamAppId, false); } catch (Exception ex) { Plugin.Log.LogError((object)("SteamClient.Init failed: " + ex.Message)); Plugin.Log.LogInfo((object)"Make sure Steam is running and you own the game!"); return false; } } if (!SteamClient.IsValid) { Plugin.Log.LogError((object)"Steam client is not valid after init attempt"); return false; } SteamMatchmaking.OnLobbyCreated += HandleLobbyCreated; SteamMatchmaking.OnLobbyEntered += HandleLobbyEntered; SteamMatchmaking.OnLobbyMemberJoined += HandleLobbyMemberJoined; SteamMatchmaking.OnLobbyMemberLeave += HandleLobbyMemberLeave; SteamFriends.OnGameLobbyJoinRequested += HandleGameLobbyJoinRequested; SteamFriends.OnGameRichPresenceJoinRequested += HandleRichPresenceJoinRequested; SteamNetworking.OnP2PSessionRequest = (Action)Delegate.Combine(SteamNetworking.OnP2PSessionRequest, new Action(HandleP2PSessionRequest)); _initialized = true; CheckCommandLineJoin(); return true; } catch (Exception ex2) { Plugin.Log.LogError((object)("Failed to initialize Steam: " + ex2.Message)); Plugin.Log.LogError((object)"Ensure steam_api64.dll is in the game folder and Steam is running"); return false; } } private uint GetSteamAppId() { string path = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))), "steam_appid.txt"); try { if (File.Exists(path) && uint.TryParse(File.ReadAllText(path).Trim(), out var result)) { Plugin.Log.LogInfo((object)$"Found steam_appid.txt with ID: {result}"); return result; } } catch { } try { File.WriteAllText(path, 2258670u.ToString()); Plugin.Log.LogInfo((object)$"Created steam_appid.txt with App ID: {2258670u}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not create steam_appid.txt: " + ex.Message)); } return 2258670u; } public void StartHost(int port = 0) { if (Initialize()) { IsHost = true; IsRunning = true; CreateLobbyAsync(); } } public void Connect(string address, int port) { if (Initialize()) { IsHost = false; IsRunning = true; if (ulong.TryParse(address, out var result)) { JoinLobbyAsync(result); } else { Plugin.Log.LogWarning((object)"Steam transport: Use lobby ID or join via Steam friends"); } } } public void Disconnect() { LeaveLobby(); } public void Shutdown() { LeaveLobby(); ClearRichPresence(); _connectedPeers.Clear(); _steamIdToPeerId.Clear(); _peerIdToSteamId.Clear(); IsRunning = false; IsHost = false; IsJoining = false; } public void Update() { if (!_initialized) { return; } SteamClient.RunCallbacks(); ProcessPendingJoin(); if (IsRunning) { ReceiveP2PMessages(); if (IsConnected && Time.realtimeSinceStartup - _lastPingTime > _pingInterval) { _lastPingTime = Time.realtimeSinceStartup; SendPing(); Plugin.LogDebug($"P2P status: {_connectedPeers.Count} peers, Ping={Ping}ms"); } } } private void SendPing() { //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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) PacketWriter packetWriter = new PacketWriter(16); packetWriter.Put((byte)250); packetWriter.Put(Time.realtimeSinceStartup); foreach (SteamId connectedPeer in _connectedPeers) { _pingSentTimes[connectedPeer] = Time.realtimeSinceStartup; SendToSteamId(connectedPeer, packetWriter.GetBytes(), reliable: false); } } private void HandlePing(SteamId from, float sentTime) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) PacketWriter packetWriter = new PacketWriter(16); packetWriter.Put((byte)251); packetWriter.Put(sentTime); SendToSteamId(from, packetWriter.GetBytes(), reliable: false); } private void HandlePong(SteamId from, float originalSentTime) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) int value = (int)((Time.realtimeSinceStartup - originalSentTime) * 1000f / 2f); _peerPings[from] = value; int num = 0; foreach (int value2 in _peerPings.Values) { if (value2 > num) { num = value2; } } Ping = num; } private void SendVersionCheck(SteamId target) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) PacketWriter packetWriter = new PacketWriter(64); packetWriter.Put((byte)252); packetWriter.Put("1.2.6"); SendToSteamId(target, packetWriter.GetBytes(), reliable: true); } private void HandleVersionCheck(SteamId from, string remoteVersion) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0070: Unknown result type (might be due to invalid IL or missing references) if (_versionVerifiedPeers.Contains(from)) { return; } _versionVerifiedPeers.Add(from); if (remoteVersion != "1.2.6") { Plugin.Log.LogWarning((object)("Version mismatch! Local: 1.2.6, Remote: " + remoteVersion)); PacketWriter packetWriter = new PacketWriter(64); packetWriter.Put((byte)253); packetWriter.Put("1.2.6"); SendToSteamId(from, packetWriter.GetBytes(), reliable: true); if (_steamIdToPeerId.TryGetValue(from, out var _)) { this.OnVersionMismatch?.Invoke("Player has v" + remoteVersion + " (you have v1.2.6)"); } } } private void HandleVersionMismatch(SteamId from, string hostVersion) { this.OnVersionMismatch?.Invoke("Version mismatch! Host has v" + hostVersion + ", you have v1.2.6"); } public void LockLobby() { //IL_0019: 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) if (IsInLobby && IsHost) { IsLobbyLocked = true; Lobby currentLobby = CurrentLobby; ((Lobby)(ref currentLobby)).SetJoinable(false); } } public void UnlockLobby() { //IL_0019: 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) if (IsInLobby && IsHost) { IsLobbyLocked = false; Lobby currentLobby = CurrentLobby; ((Lobby)(ref currentLobby)).SetJoinable(true); } } private async void CreateLobbyAsync(int maxPlayers = 10) { try { Lobby? val = await SteamMatchmaking.CreateLobbyAsync(maxPlayers); if (val.HasValue) { CurrentLobby = val.Value; Lobby currentLobby = CurrentLobby; ((Lobby)(ref currentLobby)).SetData("game", "Crawlspace2MP"); currentLobby = CurrentLobby; ((Lobby)(ref currentLobby)).SetData("version", "1.2.6"); currentLobby = CurrentLobby; ((Lobby)(ref currentLobby)).SetPublic(); currentLobby = CurrentLobby; ((Lobby)(ref currentLobby)).SetJoinable(true); currentLobby = CurrentLobby; ((Lobby)(ref currentLobby)).SetGameServer(SteamClient.SteamId); UpdateRichPresence(); this.OnLobbyCreated?.Invoke(CurrentLobby); Action action = this._onLobbyCreatedString; if (action != null) { currentLobby = CurrentLobby; action(((Lobby)(ref currentLobby)).Id.Value.ToString()); } } else { IsRunning = false; IsHost = false; Plugin.Log.LogWarning((object)"Lobby creation returned null"); } } catch (Exception arg) { IsRunning = false; IsHost = false; Plugin.Log.LogError((object)$"Failed to create lobby: {arg}"); } } private async void JoinLobbyAsync(ulong lobbyId) { IsJoining = true; try { Plugin.Log.LogInfo((object)$"Joining lobby {lobbyId}..."); Lobby lobby = new Lobby(SteamId.op_Implicit(lobbyId)); Plugin.Log.LogInfo((object)$"Refreshing lobby data for {lobbyId}..."); bool flag = ((Lobby)(ref lobby)).Refresh(); Plugin.Log.LogInfo((object)$"Lobby refresh result: {flag}"); await Task.Delay(500); Task joinTask = ((Lobby)(ref lobby)).Join(); Task timeoutTask = Task.Delay(15000); if (await Task.WhenAny(new Task[2] { joinTask, timeoutTask }) == timeoutTask) { IsRunning = false; IsHost = false; IsJoining = false; Plugin.Log.LogWarning((object)$"Join lobby timed out for {lobbyId}"); this.OnJoinFailed?.Invoke("Connection timed out - check if host is still in lobby"); return; } RoomEnter val = await joinTask; Plugin.Log.LogInfo((object)$"Lobby.Join() result: {val}"); if ((int)val == 1) { CurrentLobby = lobby; IsJoining = false; Plugin.Log.LogInfo((object)$"Joined lobby successfully! ID: {((Lobby)(ref lobby)).Id}"); await Task.Delay(100); if (_connectedPeers.Count == 0) { Plugin.Log.LogInfo((object)"No peers yet after join, HandleLobbyEntered should add them..."); foreach (Friend member in ((Lobby)(ref lobby)).Members) { Friend current = member; if (SteamId.op_Implicit(current.Id) != SteamId.op_Implicit(SteamClient.SteamId) && !_connectedPeers.Contains(current.Id)) { Plugin.Log.LogInfo((object)$"Manually adding peer: {((Friend)(ref current)).Name} ({current.Id})"); SteamNetworking.AcceptP2PSessionWithUser(current.Id); AcceptPeer(current.Id); } } } UpdateRichPresence(); this.OnLobbyJoined?.Invoke(lobby); this._onLobbyJoinedString?.Invoke(((Lobby)(ref lobby)).Id.Value.ToString()); } else { IsRunning = false; IsHost = false; IsJoining = false; string text = (val - 2) switch { 0 => "Lobby doesn't exist - host may have left", 1 => "Not allowed to join - lobby may be full or locked", 2 => "Lobby is full", 3 => "Steam error - try again", 4 => "You are banned from this lobby", 5 => "Limited user account", 6 => "Clan disabled", 7 => "Community ban", 8 => "Host has blocked you", 9 => "You have blocked the host", _ => $"Join failed: {val}", }; Plugin.Log.LogWarning((object)$"Join failed: {val} - {text}"); this.OnJoinFailed?.Invoke(text); } } catch (Exception ex) { IsRunning = false; IsHost = false; IsJoining = false; Plugin.Log.LogError((object)$"Failed to join lobby: {ex}"); this.OnJoinFailed?.Invoke("Error: " + ex.Message); } } public void JoinLobby(SteamId lobbyId) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (Initialize()) { IsHost = false; IsRunning = true; JoinLobbyAsync(SteamId.op_Implicit(lobbyId)); } } public void JoinLobby(string lobbyId) { //IL_000d: 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) if (ulong.TryParse(lobbyId, out var result)) { JoinLobby(new SteamId { Value = result }); } } private void LeaveLobby() { //IL_0019: 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_0043: 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_0054: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { return; } foreach (SteamId connectedPeer in _connectedPeers) { SteamNetworking.CloseP2PSessionWithUser(connectedPeer); } Lobby val = CurrentLobby; ((Lobby)(ref val)).Leave(); val = (CurrentLobby = default(Lobby)); _connectedPeers.Clear(); this.OnLobbyLeft?.Invoke(); } public void SendToAll(byte[] data, bool reliable = true) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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) foreach (SteamId connectedPeer in _connectedPeers) { SendToSteamId(connectedPeer, data, reliable); } } public void SendTo(int peerId, byte[] data, bool reliable = true) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (_peerIdToSteamId.TryGetValue(peerId, out var value)) { SendToSteamId(value, data, reliable); } } private void SendToSteamId(SteamId target, byte[] data, bool reliable) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) P2PSend val = (P2PSend)(reliable ? 2 : 0); if (!SteamNetworking.SendP2PPacket(target, data, data.Length, 0, val) && data.Length != 0 && data[0] != 1) { _sendFailCount++; if (_sendFailCount <= 3 || _sendFailCount % 100 == 0) { Plugin.Log.LogWarning((object)($"Failed to send P2P packet to {target}, type={data[0]}, reliable={reliable}" + ((_sendFailCount > 3) ? $" (x{_sendFailCount})" : ""))); } } } private void ReceiveP2PMessages() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //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_002d: 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_0041: 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_0125: 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_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_0107: 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) int num = 0; while (SteamNetworking.IsP2PPacketAvailable(0)) { P2Packet? val = SteamNetworking.ReadP2PPacket(0); if (!val.HasValue) { continue; } num++; SteamId steamId = val.Value.SteamId; byte[] data = val.Value.Data; if (num <= 3) { Plugin.LogDebug(string.Format("Received P2P packet from {0}, size={1}, type={2}", steamId, data.Length, (data.Length != 0) ? data[0].ToString() : "empty")); } if (!_connectedPeers.Contains(steamId) && IsLobbyMember(steamId)) { AcceptPeer(steamId); } if (data.Length != 0) { byte b = data[0]; if (b >= 250) { PacketReader packetReader = new PacketReader(data); packetReader.GetByte(); switch (b) { case 250: HandlePing(steamId, packetReader.GetFloat()); break; case 251: HandlePong(steamId, packetReader.GetFloat()); break; case 252: HandleVersionCheck(steamId, packetReader.GetString()); break; case 253: HandleVersionMismatch(steamId, packetReader.GetString()); break; case 254: HandleHostMigration(steamId); break; } continue; } } if (_steamIdToPeerId.TryGetValue(steamId, out var value)) { PacketReader arg = new PacketReader(data); this.OnDataReceived?.Invoke(value, arg); } } } private void HandleHostMigration(SteamId newHostId) { //IL_0000: 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) if (SteamId.op_Implicit(newHostId) == SteamId.op_Implicit(SteamClient.SteamId)) { IsHost = true; this.OnBecameHost?.Invoke(); } } private bool IsLobbyMember(SteamId id) { //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_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_0030: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { return false; } Lobby currentLobby = CurrentLobby; foreach (Friend member in ((Lobby)(ref currentLobby)).Members) { if (SteamId.op_Implicit(member.Id) == SteamId.op_Implicit(id)) { return true; } } return false; } private void AcceptPeer(SteamId steamId) { //IL_0000: 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_002c: 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) //IL_0048: Unknown result type (might be due to invalid IL or missing references) SteamNetworking.AcceptP2PSessionWithUser(steamId); _connectedPeers.Add(steamId); int num = _nextPeerId++; _steamIdToPeerId[steamId] = num; _peerIdToSteamId[num] = steamId; _sendFailCount = 0; SendVersionCheck(steamId); this.OnPeerConnected?.Invoke(num); } private void RemovePeer(SteamId steamId) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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) if (_steamIdToPeerId.TryGetValue(steamId, out var value)) { _steamIdToPeerId.Remove(steamId); _peerIdToSteamId.Remove(value); _connectedPeers.Remove(steamId); _peerPings.Remove(steamId); _pingSentTimes.Remove(steamId); _versionVerifiedPeers.Remove(steamId); SteamNetworking.CloseP2PSessionWithUser(steamId); this.OnPeerDisconnected?.Invoke(value); } } private void HandleP2PSessionRequest(SteamId steamId) { //IL_0001: 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_0016: 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) if (IsLobbyMember(steamId)) { SteamNetworking.AcceptP2PSessionWithUser(steamId); if (!_connectedPeers.Contains(steamId)) { AcceptPeer(steamId); } } } private void HandleLobbyCreated(Result result, Lobby lobby) { } private void HandleLobbyEntered(Lobby lobby) { //IL_000c: 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_0027: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0102: 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_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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) //IL_013f: 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_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) ManualLogSource log = Plugin.Log; object arg = ((Lobby)(ref lobby)).Id; Friend owner = ((Lobby)(ref lobby)).Owner; log.LogInfo((object)$"Entered lobby: {arg}, Owner: {((Friend)(ref owner)).Name} ({((Lobby)(ref lobby)).Owner.Id})"); Plugin.Log.LogInfo((object)$"We are: {SteamClient.Name} ({SteamClient.SteamId}), IsHost={IsHost}"); CurrentLobby = lobby; int num = 0; foreach (Friend member in ((Lobby)(ref lobby)).Members) { Friend current = member; num++; Plugin.Log.LogInfo((object)$" Lobby member {num}: {((Friend)(ref current)).Name} ({current.Id})"); } Plugin.Log.LogInfo((object)$"Total lobby members: {num}"); foreach (Friend member2 in ((Lobby)(ref lobby)).Members) { Friend current2 = member2; if (SteamId.op_Implicit(current2.Id) != SteamId.op_Implicit(SteamClient.SteamId) && !_connectedPeers.Contains(current2.Id)) { Plugin.Log.LogInfo((object)$"Adding peer for existing lobby member: {((Friend)(ref current2)).Name} ({current2.Id})"); SteamNetworking.AcceptP2PSessionWithUser(current2.Id); AcceptPeer(current2.Id); } } Plugin.Log.LogInfo((object)$"After HandleLobbyEntered: connectedPeers={_connectedPeers.Count}, IsConnected={IsConnected}"); UpdateRichPresence(); this.OnLobbyJoined?.Invoke(lobby); this._onLobbyJoinedString?.Invoke(((Lobby)(ref lobby)).Id.Value.ToString()); } private void HandleLobbyMemberJoined(Lobby lobby, Friend friend) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) //IL_002a: 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_005f: 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) if (SteamId.op_Implicit(friend.Id) != SteamId.op_Implicit(SteamClient.SteamId)) { Plugin.Log.LogInfo((object)$"Player joined lobby: {((Friend)(ref friend)).Name} ({friend.Id})"); SteamNetworking.AcceptP2PSessionWithUser(friend.Id); if (!_connectedPeers.Contains(friend.Id)) { AcceptPeer(friend.Id); } UpdateRichPresence(); this.OnPlayerJoined?.Invoke(friend); this._onPlayerJoinedString?.Invoke(((Friend)(ref friend)).Name); } } private void HandleLobbyMemberLeave(Lobby lobby, Friend friend) { //IL_001b: 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_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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)("Player left lobby: " + ((Friend)(ref friend)).Name)); bool num = SteamId.op_Implicit(friend.Id) == SteamId.op_Implicit(((Lobby)(ref lobby)).Owner.Id); this.OnPlayerLeft?.Invoke(friend); this._onPlayerLeftString?.Invoke(((Friend)(ref friend)).Name); RemovePeer(friend.Id); UpdateRichPresence(); if (num && IsInLobby && !IsHost) { CheckHostMigration(); } } private async void CheckHostMigration() { await Task.Delay(500); if (IsInLobby) { Lobby currentLobby = CurrentLobby; if (SteamId.op_Implicit(((Lobby)(ref currentLobby)).Owner.Id) == SteamId.op_Implicit(SteamClient.SteamId) && !IsHost) { Plugin.Log.LogInfo((object)"Host migration: We are now the lobby owner!"); IsHost = true; PacketWriter packetWriter = new PacketWriter(16); packetWriter.Put((byte)254); SendToAll(packetWriter.GetBytes()); this.OnBecameHost?.Invoke(); } } } private void HandleGameLobbyJoinRequested(Lobby lobby, SteamId friendId) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_00dd: 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) Plugin.Log.LogInfo((object)$"=== STEAM JOIN REQUESTED === lobby={((Lobby)(ref lobby)).Id}, friend={friendId}"); if (((Lobby)(ref lobby)).Id.Value == 0L) { Plugin.Log.LogWarning((object)"Invalid lobby ID in join request"); this.OnJoinFailed?.Invoke("Invalid lobby - try using lobby code instead"); return; } if (IsRunning || IsInLobby || IsJoining) { Plugin.Log.LogWarning((object)$"Already in session (Running={IsRunning}, InLobby={IsInLobby}, Joining={IsJoining})"); this.OnJoinFailed?.Invoke("Already in a session - disconnect first"); return; } IsRunning = true; IsHost = false; IsJoining = true; Plugin.Log.LogInfo((object)$"Joining lobby {((Lobby)(ref lobby)).Id} via Steam overlay..."); JoinLobbyAsync(SteamId.op_Implicit(((Lobby)(ref lobby)).Id)); } private void HandleRichPresenceJoinRequested(Friend friend, string connectString) { Plugin.Log.LogInfo((object)("Rich presence join requested from " + ((Friend)(ref friend)).Name + ": " + connectString)); if (string.IsNullOrEmpty(connectString)) { Plugin.Log.LogWarning((object)"Empty connect string in rich presence join"); return; } string[] array = connectString.Split(new char[1] { ' ' }); if (array.Length >= 2 && array[0] == "+connect_lobby") { if (ulong.TryParse(array[1], out var result)) { if (IsRunning || IsInLobby || IsJoining) { Plugin.Log.LogWarning((object)"Already in a session, ignoring rich presence join"); this.OnJoinFailed?.Invoke("Already in a session - disconnect first"); return; } IsRunning = true; IsHost = false; IsJoining = true; Plugin.Log.LogInfo((object)$"Joining lobby {result} via rich presence..."); JoinLobbyAsync(result); } else { Plugin.Log.LogWarning((object)("Invalid lobby ID in connect string: " + array[1])); } } else { Plugin.Log.LogWarning((object)("Unknown connect string format: " + connectString)); } } private void CheckCommandLineJoin() { string[] commandLineArgs = Environment.GetCommandLineArgs(); Plugin.Log.LogInfo((object)("Command line args: " + string.Join(" ", commandLineArgs))); for (int i = 0; i < commandLineArgs.Length; i++) { if (commandLineArgs[i] == "+connect_lobby" && i + 1 < commandLineArgs.Length && ulong.TryParse(commandLineArgs[i + 1], out var result)) { Plugin.Log.LogInfo((object)$"Found lobby ID in command line: {result}"); _pendingJoinLobbyId = result; } } } public void ProcessPendingJoin() { if (_pendingJoinLobbyId != 0L && !IsRunning && !IsInLobby && !IsJoining) { ulong pendingJoinLobbyId = _pendingJoinLobbyId; _pendingJoinLobbyId = 0uL; Plugin.Log.LogInfo((object)$"Processing pending join for lobby {pendingJoinLobbyId}"); IsRunning = true; IsHost = false; IsJoining = true; JoinLobbyAsync(pendingJoinLobbyId); } } public string GetLobbyId() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_001c: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { return ""; } Lobby currentLobby = CurrentLobby; return ((Lobby)(ref currentLobby)).Id.Value.ToString(); } public void BecomeHost() { IsHost = true; Plugin.Log.LogInfo((object)"BecomeHost called - we are now the host"); } public void InviteFriends() { //IL_0019: 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_0021: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { Plugin.Log.LogWarning((object)"Cannot invite friends - not in a lobby"); return; } Lobby currentLobby = CurrentLobby; SteamFriends.OpenGameInviteOverlay(((Lobby)(ref currentLobby)).Id); Plugin.Log.LogInfo((object)"Opened Steam invite overlay"); } public string GetPlayerName() { if (!SteamClient.IsValid) { return "Unknown"; } return SteamClient.Name; } public List GetConnectedPlayerNames() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_002c: 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_002e: 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) List list = new List(); if (!IsInLobby) { return list; } Lobby currentLobby = CurrentLobby; foreach (Friend member in ((Lobby)(ref currentLobby)).Members) { Friend current = member; if (SteamId.op_Implicit(current.Id) != SteamId.op_Implicit(SteamClient.SteamId)) { list.Add(((Friend)(ref current)).Name); } } return list; } public void UpdateRichPresence() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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) //IL_0067: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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) if (!_initialized) { return; } try { if (IsInLobby) { int num = _connectedPeers.Count + 1; string arg = (IsHost ? "Hosting" : "In"); SteamFriends.SetRichPresence("status", $"{arg} Co-op ({num}P)"); Lobby currentLobby = CurrentLobby; SteamFriends.SetRichPresence("steam_player_group", ((object)((Lobby)(ref currentLobby)).Id/*cast due to .constrained prefix*/).ToString()); SteamFriends.SetRichPresence("steam_player_group_size", num.ToString()); currentLobby = CurrentLobby; SteamFriends.SetRichPresence("connect", $"+connect_lobby {((Lobby)(ref currentLobby)).Id}"); ManualLogSource log = Plugin.Log; object arg2 = num; currentLobby = CurrentLobby; log.LogInfo((object)$"Rich presence: {arg} Co-op ({arg2}P), connect=+connect_lobby {((Lobby)(ref currentLobby)).Id}"); } else { ClearRichPresence(); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to update rich presence: " + ex.Message)); } } public void ClearRichPresence() { if (!_initialized) { return; } try { SteamFriends.ClearRichPresence(); } catch { } } public bool InviteFriend(SteamId friendId) { //IL_001a: 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_0022: 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) if (!IsInLobby) { Plugin.Log.LogWarning((object)"Cannot invite friend - not in a lobby"); return false; } Lobby currentLobby = CurrentLobby; bool flag = ((Lobby)(ref currentLobby)).InviteFriend(friendId); Plugin.Log.LogInfo((object)string.Format("Invited {0} to lobby: {1}", friendId, flag ? "success" : "failed")); return flag; } public List GetAllOnlineFriends() { //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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_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) List list = new List(); if (!_initialized) { return list; } try { foreach (Friend friend in SteamFriends.GetFriends()) { Friend current = friend; if (!((Friend)(ref current)).IsOnline) { continue; } FriendGameInfo friendGameInfo = new FriendGameInfo { Friend = current, Name = ((Friend)(ref current)).Name, SteamId = current.Id, IsInGame = ((Friend)(ref current)).IsPlayingThisGame, IsJoinable = false, LobbyId = 0uL, Status = (((Friend)(ref current)).IsPlayingThisGame ? "Playing Crawlspace 2" : "Online") }; if (((Friend)(ref current)).IsPlayingThisGame) { string richPresence = ((Friend)(ref current)).GetRichPresence("connect"); if (!string.IsNullOrEmpty(richPresence) && richPresence.StartsWith("+connect_lobby")) { string[] array = richPresence.Split(new char[1] { ' ' }); if (array.Length >= 2 && ulong.TryParse(array[1], out var result)) { friendGameInfo.IsJoinable = true; friendGameInfo.LobbyId = result; friendGameInfo.Status = "In Lobby - Joinable"; } } string richPresence2 = ((Friend)(ref current)).GetRichPresence("status"); if (!string.IsNullOrEmpty(richPresence2)) { friendGameInfo.Status = richPresence2; } } list.Add(friendGameInfo); } list.Sort(delegate(FriendGameInfo a, FriendGameInfo b) { if (a.IsInGame != b.IsInGame) { return b.IsInGame.CompareTo(a.IsInGame); } return (a.IsJoinable != b.IsJoinable) ? b.IsJoinable.CompareTo(a.IsJoinable) : string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); }); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Error getting friends list: " + ex.Message)); } return list; } public List GetFriendsPlayingGame() { //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_004f: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if (!_initialized) { return list; } try { foreach (Friend friend in SteamFriends.GetFriends()) { Friend current = friend; if ((!((Friend)(ref current)).IsOnline && !((Friend)(ref current)).IsPlayingThisGame) || !((Friend)(ref current)).IsPlayingThisGame) { continue; } FriendGameInfo friendGameInfo = new FriendGameInfo { Friend = current, Name = ((Friend)(ref current)).Name, SteamId = current.Id, IsInGame = true, IsJoinable = false, LobbyId = 0uL, Status = "Playing" }; string richPresence = ((Friend)(ref current)).GetRichPresence("connect"); if (!string.IsNullOrEmpty(richPresence) && richPresence.StartsWith("+connect_lobby")) { string[] array = richPresence.Split(new char[1] { ' ' }); if (array.Length >= 2 && ulong.TryParse(array[1], out var result)) { friendGameInfo.IsJoinable = true; friendGameInfo.LobbyId = result; friendGameInfo.Status = "In Lobby - Joinable"; } } string richPresence2 = ((Friend)(ref current)).GetRichPresence("status"); if (!string.IsNullOrEmpty(richPresence2)) { friendGameInfo.Status = richPresence2; } list.Add(friendGameInfo); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Error getting friends list: " + ex.Message)); } return list; } public void JoinFriendGame(ulong lobbyId) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (lobbyId == 0L) { Plugin.Log.LogWarning((object)"Cannot join friend - no lobby ID"); return; } Plugin.Log.LogInfo((object)$"Joining friend's lobby: {lobbyId}"); JoinLobby(SteamId.op_Implicit(lobbyId)); } } [BepInPlugin("com.crawlspace2.multiplayer", "Crawlspace2MP", "1.2.6")] public class Plugin : BaseUnityPlugin { public static ManualLogSource Log { get; private set; } public static GameObject HelmetPrefab { get; private set; } public static bool VerboseLogging { get; private set; } public static void LogDebug(string message) { if (VerboseLogging) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } } } private void Awake() { //IL_0066: 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_007a: 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_0087: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("Logging", "VerboseLogging", false, "Enable detailed diagnostic logging. Turn this on when reporting bugs, then send BepInEx/LogOutput.log").Value; Log.LogInfo((object)"Crawlspace 2 Multiplayer v1.2.6 loading..."); if (VerboseLogging) { Log.LogInfo((object)"[Config] Verbose logging ENABLED"); } ValidateReflection(); LoadCustomAssets(); new Harmony("com.crawlspace2.multiplayer").PatchAll(); GameObject val = new GameObject("Crawlspace2MP_Manager"); val.AddComponent(); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; Log.LogInfo((object)"Multiplayer mod loaded!"); } private void ValidateReflection() { List list = new List(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; if (typeof(henryBrain).GetField("resetSwitch", bindingAttr) == null) { list.Add("henryBrain.resetSwitch"); } if (typeof(henryBrain).GetField("chaseSwitch", bindingAttr) == null) { list.Add("henryBrain.chaseSwitch"); } if (typeof(sparkyBrain).GetField("currentState", bindingAttr) == null) { list.Add("sparkyBrain.currentState"); } if (typeof(jeffBrain).GetField("currentState", bindingAttr) == null) { list.Add("jeffBrain.currentState"); } if (typeof(jeffBrain).GetField("totalFlashes", bindingAttr) == null) { list.Add("jeffBrain.totalFlashes"); } if (typeof(SmileBrain).GetField("isChasing", bindingAttr) == null) { list.Add("SmileBrain.isChasing"); } if (typeof(SmileBrain).GetField("chaseTime", bindingAttr) == null) { list.Add("SmileBrain.chaseTime"); } if (typeof(clownRandom).GetField("clownAttackingSwitch", bindingAttr) == null) { list.Add("clownRandom.clownAttackingSwitch"); } if (typeof(clownRandom).GetField("clownKillTimer", bindingAttr) == null) { list.Add("clownRandom.clownKillTimer"); } if (typeof(PuzzleController).GetField("puzzleHasCompleted", bindingAttr) == null) { list.Add("PuzzleController.puzzleHasCompleted"); } if (typeof(PuzzleController).GetField("puzzlePresetID", bindingAttr) == null) { list.Add("PuzzleController.puzzlePresetID"); } if (typeof(PuzzleController).GetField("timer2", bindingAttr) == null) { list.Add("PuzzleController.timer2"); } if (typeof(PuzzleController).GetField("timer", bindingAttr) == null) { list.Add("PuzzleController.timer"); } if (typeof(PuzzleController).GetField("handColorID", bindingAttr) == null) { list.Add("PuzzleController.handColorID"); } if (typeof(PuzzleController).GetField("originCubeID", bindingAttr) == null) { list.Add("PuzzleController.originCubeID"); } if (typeof(paintingControl).GetField("boolpaintingTall1", bindingAttr) == null) { list.Add("paintingControl.boolpaintingTall1"); } if (typeof(paintingControl).GetField("boolpaintingTall2", bindingAttr) == null) { list.Add("paintingControl.boolpaintingTall2"); } if (typeof(paintingControl).GetField("boolpaintingTall3", bindingAttr) == null) { list.Add("paintingControl.boolpaintingTall3"); } if (typeof(paintingControl).GetField("boolpaintingSquare1", bindingAttr) == null) { list.Add("paintingControl.boolpaintingSquare1"); } if (typeof(paintingControl).GetField("boolpaintingSquare2", bindingAttr) == null) { list.Add("paintingControl.boolpaintingSquare2"); } if (typeof(paintingControl).GetField("boolpaintingSquare3", bindingAttr) == null) { list.Add("paintingControl.boolpaintingSquare3"); } if (typeof(PuzzleBlock).GetField("blockIDValue") == null) { list.Add("PuzzleBlock.blockIDValue"); } if (typeof(BackpackControl).GetField("batteryLocationID") == null) { list.Add("BackpackControl.batteryLocationID (static)"); } if (typeof(BackpackControl).GetField("batteryCharge") == null) { list.Add("BackpackControl.batteryCharge (static)"); } if (typeof(PuzzleMaster).GetField("totalCompletedPuzzles") == null) { list.Add("PuzzleMaster.totalCompletedPuzzles (static)"); } if (typeof(earMaster).GetField("isCoveringEars") == null) { list.Add("earMaster.isCoveringEars (static)"); } if (typeof(MinimapControl).GetField("timer", bindingAttr) == null) { list.Add("MinimapControl.timer"); } if (typeof(MinimapControl).GetField("haroldTimer", bindingAttr) == null) { list.Add("MinimapControl.haroldTimer"); } if (list.Count > 0) { Log.LogError((object)"╔═══════════════════════════════════════════════════════════════╗"); Log.LogError((object)$"║ CRITICAL: Reflection validation failed for {list.Count,2} field(s) ║"); Log.LogError((object)"╠═══════════════════════════════════════════════════════════════╣"); foreach (string item in list) { Log.LogError((object)$"║ ✗ {item,-57} ║"); } Log.LogError((object)"╠═══════════════════════════════════════════════════════════════╣"); Log.LogError((object)"║ The game may have updated. Multiplayer features WILL break. ║"); Log.LogError((object)"║ Please report this to the mod developer. ║"); Log.LogError((object)"╚═══════════════════════════════════════════════════════════════╝"); } else { Log.LogInfo((object)"✓ Reflection validation passed - all fields found"); } } private void LoadCustomAssets() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) string text = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "mpassets"); if (!File.Exists(text)) { return; } try { AssetBundle val = AssetBundle.LoadFromFile(text); if (!((Object)(object)val == (Object)null)) { GameObject val2 = val.LoadAsset("assets/sm_kaska_lp.fbx"); GameObject val3 = val.LoadAsset("assets/vr.dae"); if ((Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null) { HelmetPrefab = val2; GameObject obj = Object.Instantiate(val3); obj.transform.SetParent(val2.transform, false); obj.transform.localPosition = new Vector3(0f, -0.11f, 0.211f); obj.transform.localRotation = Quaternion.identity; ((Object)obj).name = "VR_Visor"; Log.LogInfo((object)"✓ Loaded helmet with visor (SM_Kaska_LP + VR)"); } else if ((Object)(object)val2 != (Object)null) { HelmetPrefab = val2; Log.LogWarning((object)"Loaded helmet only (visor missing from asset bundle)"); } else if ((Object)(object)val3 != (Object)null) { HelmetPrefab = val3; Log.LogWarning((object)"Loaded visor only (helmet missing from asset bundle)"); } else { Log.LogError((object)"Failed to load helmet or visor from asset bundle - remote players will use fallback visuals"); } } } catch (Exception ex) { Log.LogError((object)("Error loading custom assets: " + ex.Message)); } } } public class MPManager : MonoBehaviour { private string _statusMessage = "Initializing Steam..."; private string _connectedPlayerName = ""; private bool _steamInitialized; private float _copiedTime = -10f; private bool _uiHidden; private GameObject _tutorialSign; private string _lastLobbyId = ""; public static MPManager Instance { get; private set; } public PlayerSync PlayerSync { get; private set; } public SteamTransport Steam { get; private set; } public VoiceChat VoiceChat { get; private set; } public SpectateSystem Spectate { get; private set; } public MultiplayerUI WorldUI { get; private set; } public bool IsHost => Steam?.IsHost ?? false; public bool IsConnected => Steam?.IsConnected ?? false; public bool IsRunning => Steam?.IsRunning ?? false; public bool IsJoining => Steam?.IsJoining ?? false; public string LastLobbyId => _lastLobbyId; public string StatusMessage { get { return _statusMessage; } set { _statusMessage = value; } } private void Awake() { Instance = this; Steam = new SteamTransport(); PlayerSync = new PlayerSync(); PlayerSync.Initialize(Steam); VoiceChat = new VoiceChat(); VoiceChat.Initialize(Steam); Spectate = new SpectateSystem(); Spectate.Initialize(Steam); Steam.OnLobbyCreated += OnSteamLobbyCreated; Steam.OnLobbyJoined += OnSteamLobbyJoined; Steam.OnJoinFailed += OnJoinFailed; Steam.OnPeerConnected += OnPeerConnected; Steam.OnPeerDisconnected += OnPeerDisconnected; Steam.OnPlayerJoined += OnPlayerJoined; Steam.OnPlayerLeft += OnPlayerLeft; Steam.OnVersionMismatch += OnVersionMismatch; Steam.OnBecameHost += OnBecameHost; SceneManager.sceneLoaded += OnSceneLoadedForLobby; SceneManager.sceneLoaded += OnSceneLoadedForUI; if (Steam.Initialize()) { _steamInitialized = true; _statusMessage = "Welcome, " + SteamClient.Name + "!"; } else { _statusMessage = "Steam init failed! Is Steam running?"; Plugin.Log.LogError((object)"Failed to initialize Steam"); } } private void OnSteamLobbyCreated(Lobby lobby) { //IL_0002: 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) string systemCopyBuffer = ((Lobby)(ref lobby)).Id.Value.ToString(); _statusMessage = "Lobby created! Code auto-copied"; GUIUtility.systemCopyBuffer = systemCopyBuffer; _copiedTime = Time.realtimeSinceStartup; } private void OnSteamLobbyJoined(Lobby lobby) { //IL_0003: 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_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) Friend owner = ((Lobby)(ref lobby)).Owner; _connectedPlayerName = ((Friend)(ref owner)).Name; owner = ((Lobby)(ref lobby)).Owner; _statusMessage = "Joined " + ((Friend)(ref owner)).Name + "'s game!"; } private void OnJoinFailed(string reason) { _statusMessage = reason; } private void OnPlayerJoined(Friend friend) { _connectedPlayerName = ((Friend)(ref friend)).Name; _statusMessage = ((Friend)(ref friend)).Name + " joined!"; } private void OnPlayerLeft(Friend friend) { _statusMessage = ((Friend)(ref friend)).Name + " left"; if (Steam.ConnectedPeerCount == 0) { _connectedPlayerName = ""; _statusMessage = (IsHost ? "Waiting for players..." : "Disconnected"); } } private void OnPeerConnected(int peerId) { } private void OnPeerDisconnected(int peerId) { if (Steam.ConnectedPeerCount == 0) { _connectedPlayerName = ""; _statusMessage = (IsHost ? "Waiting for players..." : "Partner disconnected"); } } private void OnVersionMismatch(string message) { _statusMessage = "⚠\ufe0f " + message; Plugin.Log.LogWarning((object)("Version mismatch: " + message)); } private void OnBecameHost() { _statusMessage = "You are now the host!"; } private void OnSceneLoadedForLobby(Scene scene, LoadSceneMode mode) { if (Steam != null && Steam.IsInLobby) { if (((Scene)(ref scene)).name.Equals("Home", StringComparison.OrdinalIgnoreCase)) { Steam.UnlockLobby(); } else if (((Scene)(ref scene)).name.Contains("Night")) { Steam.LockLobby(); } } } private void OnSceneLoadedForUI(Scene scene, LoadSceneMode mode) { bool num = ((Scene)(ref scene)).name.Equals("Home", StringComparison.OrdinalIgnoreCase); bool num2 = ((Scene)(ref scene)).name.Equals("PreHome", StringComparison.OrdinalIgnoreCase); if ((Object)(object)_tutorialSign != (Object)null) { Object.Destroy((Object)(object)_tutorialSign); _tutorialSign = null; } if (num2) { _tutorialSign = CreateTutorialSign(); } if (num) { if ((Object)(object)WorldUI == (Object)null) { WorldUI = ((Component)this).gameObject.AddComponent(); } WorldUI.Initialize(this); } else { WorldUI?.Hide(); } } private GameObject CreateTutorialSign() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0020: 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_006f: 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_0084: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0125: 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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("MP_TutorialSign"); val.transform.position = new Vector3(-0.2434f, 1.782f, -3.8182f); val.transform.rotation = Quaternion.Euler(0f, 180f, 0f); Canvas obj = val.AddComponent(); obj.renderMode = (RenderMode)2; obj.sortingOrder = 90; RectTransform component = ((Component)obj).GetComponent(); component.sizeDelta = new Vector2(400f, 120f); ((Transform)component).localScale = Vector3.one * 0.001f; GameObject val2 = new GameObject("BG", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent((Transform)(object)component, false); ((Graphic)val2.AddComponent()).color = new Color(0.07f, 0.07f, 0.12f, 0.9f); RectTransform component2 = val2.GetComponent(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; Vector2 offsetMin = (component2.offsetMax = Vector2.zero); component2.offsetMin = offsetMin; Outline obj2 = val2.AddComponent(); ((Shadow)obj2).effectColor = new Color(1f, 0.85f, 0.45f, 0.5f); ((Shadow)obj2).effectDistance = new Vector2(2f, 2f); GameObject val3 = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val3.transform.SetParent((Transform)(object)component, false); RectTransform component3 = val3.GetComponent(); component3.anchorMin = Vector2.zero; component3.anchorMax = Vector2.one; component3.offsetMin = new Vector2(12f, 8f); component3.offsetMax = new Vector2(-12f, -8f); Text obj3 = val3.AddComponent(); Font val4 = Font.CreateDynamicFontFromOSFont("Arial", 16); if ((Object)(object)val4 == (Object)null) { val4 = Resources.GetBuiltinResource("Arial.ttf"); } obj3.font = val4; obj3.fontSize = 22; obj3.alignment = (TextAnchor)4; ((Graphic)obj3).color = new Color(1f, 0.85f, 0.45f, 1f); obj3.text = "You must complete Night 0\nbefore you can play multiplayer!"; return val; } private void Update() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) Keyboard current = Keyboard.current; bool flag = current != null && ((ButtonControl)current.insertKey).wasPressedThisFrame; if (!flag) { try { flag = Input.GetKeyDown((KeyCode)277); } catch { } } if (flag) { _uiHidden = !_uiHidden; } if (Plugin.VerboseLogging && current != null && ((ButtonControl)current.gKey).wasPressedThisFrame) { Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name.Contains("Night") && PlayerSync != null && !PlayerSync.IsLocalGhost) { PlayerSync.DebugForceGhostOnDeath = !PlayerSync.DebugForceGhostOnDeath; Plugin.Log.LogInfo((object)$"[Debug] Ghost on next death: {PlayerSync.DebugForceGhostOnDeath}"); } } Steam?.Update(); PlayerSync?.Update(); VoiceChat?.Update(); Spectate?.Update(); } private void OnGUI() { _ = _uiHidden; } public void HostGame() { StartHosting(); } public void JoinLobby(ulong lobbyId) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!_steamInitialized) { _statusMessage = "Steam not initialized!"; return; } if (Steam.IsRunning || Steam.IsInLobby) { _statusMessage = "Already in a session! Disconnect first."; return; } Steam.JoinLobby(SteamId.op_Implicit(lobbyId)); _statusMessage = "Joining lobby..."; } public void DisconnectFromLobby() { _lastLobbyId = Steam?.GetLobbyId() ?? ""; Disconnect(); } private void StartHosting() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); if (!((Scene)(ref activeScene)).name.Equals("Home", StringComparison.OrdinalIgnoreCase)) { _statusMessage = "Return to Home to host!"; return; } if (!_steamInitialized) { _statusMessage = "Steam not initialized!"; return; } if (Steam.IsRunning || Steam.IsInLobby || Steam.IsJoining) { if (Steam.IsHost) { _statusMessage = "Already hosting!"; } else { _statusMessage = "Already in a session! Disconnect first."; } return; } try { Steam.StartHost(); _statusMessage = "Creating lobby..."; Plugin.Log.LogInfo((object)"Starting Steam host..."); } catch (Exception ex) { _statusMessage = "Host failed: " + ex.Message; Plugin.Log.LogError((object)$"Failed to start host: {ex}"); } } private void Disconnect() { try { Spectate?.Cleanup(); VoiceChat?.Cleanup(); PlayerSync?.Cleanup(); Steam?.Shutdown(); _statusMessage = "Disconnected"; Plugin.Log.LogInfo((object)"Disconnected"); } catch (Exception arg) { Plugin.Log.LogError((object)$"Disconnect error: {arg}"); _statusMessage = "Disconnected"; } } private void OnDestroy() { Spectate?.Cleanup(); VoiceChat?.Cleanup(); PlayerSync?.Cleanup(); Steam?.Shutdown(); } } public static class PluginInfo { public const string PLUGIN_GUID = "com.crawlspace2.multiplayer"; public const string PLUGIN_NAME = "Crawlspace2MP"; public const string PLUGIN_VERSION = "1.2.6"; } [HarmonyPatch(typeof(calenderControl), "increaseValue")] public class CalendarIncreasePatch { private static bool Prefix() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_003d: Unknown result type (might be due to invalid IL or missing references) SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport != null && steamTransport.IsInLobby && steamTransport.IsConnected) { Lobby currentLobby = steamTransport.CurrentLobby; if (SteamId.op_Implicit(((Lobby)(ref currentLobby)).Owner.Id) != SteamId.op_Implicit(SteamClient.SteamId)) { Plugin.LogDebug("[Client] Calendar blocked - only host can select night"); return false; } } return true; } } [HarmonyPatch(typeof(calenderControl), "decreaseValue")] public class CalendarDecreasePatch { private static bool Prefix() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_003d: Unknown result type (might be due to invalid IL or missing references) SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport != null && steamTransport.IsInLobby && steamTransport.IsConnected) { Lobby currentLobby = steamTransport.CurrentLobby; if (SteamId.op_Implicit(((Lobby)(ref currentLobby)).Owner.Id) != SteamId.op_Implicit(SteamClient.SteamId)) { return false; } } return true; } } [HarmonyPatch(typeof(doorSceneChange), "OnTriggerEnter")] public class DoorPatch { private static bool Prefix(doorSceneChange __instance, Collider other) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0037: Unknown result type (might be due to invalid IL or missing references) SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport == null || !steamTransport.IsInLobby) { return true; } Lobby currentLobby = steamTransport.CurrentLobby; bool flag = SteamId.op_Implicit(((Lobby)(ref currentLobby)).Owner.Id) == SteamId.op_Implicit(SteamClient.SteamId); if (steamTransport.IsConnected && !flag) { return false; } if (flag && steamTransport.IsRunning) { if (calenderControl.nightSelected == 0) { return true; } __instance.loadSelectedNight(); string scenename = __instance.scenename; if (!string.IsNullOrEmpty(scenename)) { Plugin.Log.LogInfo((object)("[Host] Door entered, sending scene change: " + scenename)); PlayerSync.IsLoadingFromSync = true; MPManager.Instance.PlayerSync.SendSceneChange(scenename); } else { Plugin.Log.LogWarning((object)"[Host] doorSceneChange.scenename is empty after loadSelectedNight()"); } } return true; } } [HarmonyPatch(typeof(sceneLeave), "OnTriggerEnter")] public class SceneLeavePatch { private static bool Prefix(sceneLeave __instance, Collider other) { //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_0087: 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) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport == null || !steamTransport.IsInLobby) { return true; } if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } if (MPManager.Instance?.PlayerSync != null) { foreach (Vector3 item in MPManager.Instance.PlayerSync.GetRemotePlayerPositionsNonGhost()) { if (!(item.x > -6.8f) || !(item.x < 0.5f) || !(item.z > -4.3f) || !(item.z < 3.1f)) { Plugin.Log.LogInfo((object)"[Exit] Blocked - alive remote player not in main room yet"); return false; } } } Lobby currentLobby = steamTransport.CurrentLobby; bool flag = SteamId.op_Implicit(((Lobby)(ref currentLobby)).Owner.Id) == SteamId.op_Implicit(SteamClient.SteamId); if (steamTransport.IsRunning) { __instance.loadSelectedNight(); string text = typeof(sceneLeave).GetField("scenename", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance) as string; if (!string.IsNullOrEmpty(text)) { Plugin.Log.LogInfo((object)("[" + (flag ? "Host" : "Client") + "] Scene exit, sending scene change: " + text)); PlayerSync.IsLoadingFromSync = true; MPManager.Instance.PlayerSync.SendSceneChange(text); } else { Plugin.Log.LogWarning((object)"[Exit] sceneLeave.scenename is empty after loadSelectedNight()"); } } return true; } } [HarmonyPatch(typeof(StartMenu), "LoadScene")] public class StartMenuPatch { private static bool Prefix() { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsConnected && !MPManager.Instance.Steam.IsHost) { return false; } return true; } } [HarmonyPatch(typeof(paintingControl), "onPaintingFlash")] public class PaintingFlashPatch { private static void Postfix(int paintingID) { MPManager instance = MPManager.Instance; if ((instance == null || instance.PlayerSync?.IsReceivingPaintingFlash != true) && (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost)) { MPManager.Instance?.PlayerSync?.SendPaintingFlash(paintingID); } } } [HarmonyPatch(typeof(paintingControl), "setEntityPainting")] public class PaintingEntitySpawnPatch { private static bool Prefix() { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.PlayerSync.ShouldControlMonsters) { return false; } return true; } private static void Postfix(paintingControl __instance) { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.Steam != null && MPManager.Instance.Steam.IsRunning && MPManager.Instance.PlayerSync.ShouldControlMonsters) { MPManager.Instance.PlayerSync?.SendPaintingEntityState(__instance); } } } [HarmonyPatch(typeof(paintingControl), "timerControl")] public class PaintingTimerPatch { private static bool Prefix(paintingControl __instance) { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(paintingControl), "killPlayer")] public class PaintingKillSyncPatch { private static void Postfix(paintingControl __instance) { } } [HarmonyPatch(typeof(paintingControl), "paintingTriggerTimer")] public class PaintingTriggerPatch { private static bool Prefix() { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.PlayerSync.ShouldControlMonsters) { return false; } return true; } } [HarmonyPatch(typeof(clownRandom), "randomizerV2")] public class ClownRandomizerPatch { private static bool Prefix() { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.PlayerSync.ShouldControlMonsters) { return false; } return true; } private static void Postfix(clownRandom __instance) { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && MPManager.Instance.PlayerSync.ShouldControlMonsters) { MPManager.Instance.PlayerSync?.SendClownState(__instance); } } } [HarmonyPatch(typeof(clownRandom), "checkVisableClown")] public class ClownVisibilityPatch { private static bool Prefix() { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.PlayerSync.ShouldControlMonsters) { return false; } return true; } } [HarmonyPatch(typeof(clownRandom), "FixedUpdate")] public class ClownUpdatePatch { private static bool Prefix(clownRandom __instance) { //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning || MPManager.Instance.PlayerSync.ShouldControlMonsters) { return true; } FieldInfo field = typeof(clownRandom).GetField("clownAttackingSwitch", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (bool)field.GetValue(__instance)) { FieldInfo field2 = typeof(clownRandom).GetField("clownKillTimer", BindingFlags.Instance | BindingFlags.NonPublic); int num = ((field2 != null) ? ((int)field2.GetValue(__instance)) : 0); num++; field2?.SetValue(__instance, num); if (num >= __instance.clownTimeToKill) { __instance.JSC.onDeathClown(); field2?.SetValue(__instance, 0); } if (!(__instance.player.transform.position.x > -6.4f) || !(__instance.player.transform.position.x < 0.1f) || !(__instance.player.transform.position.z > -3.9f) || !(__instance.player.transform.position.z < 2.7f)) { field2?.SetValue(__instance, 0); field?.SetValue(__instance, false); } } return false; } } [HarmonyPatch(typeof(clownRandom), "clownStartAttack")] public class ClownAttackPatch { private static void Postfix(clownRandom __instance) { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && MPManager.Instance.PlayerSync.ShouldControlMonsters) { MPManager.Instance.PlayerSync?.SendClownAttack(); } } } [HarmonyPatch(typeof(jeffBrain), "onFlash")] public class JeffFlashPatch { private static void Postfix() { MPManager instance = MPManager.Instance; if ((instance == null || instance.PlayerSync?.IsReceivingJeffFlash != true) && (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost)) { MPManager.Instance?.PlayerSync?.SendJeffFlash(); } } } public static class MultiplayerTargeting { public static Vector3 GetClosestPlayerPosition(Vector3 monsterPos, GameObject localPlayer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_001c: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_00ac: 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_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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) //IL_0073: 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) Vector3 result = monsterPos; float num = float.MaxValue; if ((Object)(object)localPlayer != (Object)null) { Vector3 position = localPlayer.transform.position; float num2 = Vector3.Distance(monsterPos, position); if (num2 < num) { num = num2; result = position; } } if (MPManager.Instance?.PlayerSync != null) { foreach (Vector3 item in MPManager.Instance.PlayerSync.GetRemotePlayerPositionsNonGhost()) { float num3 = Vector3.Distance(monsterPos, item); if (num3 < num) { num = num3; result = item; } } } if (num == float.MaxValue && (Object)(object)localPlayer != (Object)null) { return localPlayer.transform.position; } return result; } public static float GetClosestPlayerDistance(Vector3 monsterPos, GameObject localPlayer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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) Vector3 closestPlayerPosition = GetClosestPlayerPosition(monsterPos, localPlayer); return Vector3.Distance(monsterPos, closestPlayerPosition); } } [HarmonyPatch(typeof(sparkyBrain), "huntMode")] public class SparkyHuntPatch { private static bool Prefix(sparkyBrain __instance) { return true; } } [HarmonyPatch(typeof(sparkyBrain), "wanderMode")] public class SparkyWanderPatch { private static bool Prefix() { return true; } } [HarmonyPatch(typeof(jeffBrain), "huntMode")] public class JeffHuntPatch { private static bool Prefix(jeffBrain __instance) { return true; } } [HarmonyPatch(typeof(jeffBrain), "wanderMode")] public class JeffWanderPatch { private static bool Prefix() { return true; } } [HarmonyPatch(typeof(henryBrain), "moveToPlayer")] public class HenryMovePatch { private static bool Prefix(henryBrain __instance) { //IL_006b: 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_0077: 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_008e: 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) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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_011c: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return true; } if (!MPManager.Instance.PlayerSync.ShouldControlMonsters) { return false; } if (MPManager.Instance.PlayerSync.IsLocalGhost) { List remotePlayerPositionsNonGhost = MPManager.Instance.PlayerSync.GetRemotePlayerPositionsNonGhost(); if (remotePlayerPositionsNonGhost.Count > 0) { Vector3 val = remotePlayerPositionsNonGhost[0]; float num = Vector3.Distance(((Component)__instance).transform.position, val); for (int i = 1; i < remotePlayerPositionsNonGhost.Count; i++) { float num2 = Vector3.Distance(((Component)__instance).transform.position, remotePlayerPositionsNonGhost[i]); if (num2 < num) { num = num2; val = remotePlayerPositionsNonGhost[i]; } } FieldInfo field = typeof(henryBrain).GetField("resetSwitch", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (bool)field.GetValue(__instance)) { __instance.agent.speed = __instance.henrySpeed * 15f; if (Vector3.Distance(((Component)__instance).transform.position, __instance.agent.destination) < 0.5f && num > 5f) { field?.SetValue(__instance, false); } } else { __instance.agent.SetDestination(val); } } return false; } return true; } } [HarmonyPatch(typeof(henryBrain), "outOfBoundCheck")] public class HenryBoundsCheckPatch { private static bool Prefix(henryBrain __instance) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_008e: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.PlayerSync.ShouldControlMonsters) { return false; } PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && MPManager.Instance.Steam.IsRunning) { Vector3 position = __instance.player.transform.position; bool flag = position.x > -6.8f && position.x < 0.5f && position.z > -4.3f && position.z < 3.1f; List remotePlayerPositionsNonGhost = playerSync.GetRemotePlayerPositionsNonGhost(); bool flag2 = false; foreach (Vector3 item in remotePlayerPositionsNonGhost) { if (!(item.x > -6.8f) || !(item.x < 0.5f) || !(item.z > -4.3f) || !(item.z < 3.1f)) { flag2 = true; break; } } if (flag && !flag2) { FieldInfo field = typeof(henryBrain).GetField("cooldownTimer", BindingFlags.Instance | BindingFlags.NonPublic); int num = ((field != null) ? ((int)field.GetValue(__instance)) : 0); if (num > 50) { field?.SetValue(__instance, 0); __instance.setPosRandom(); return false; } field?.SetValue(__instance, num + 1); } return false; } return true; } } [HarmonyPatch(typeof(SmileBrain), "moveToPlayer")] public class SmileMovePatch { private static bool Prefix(SmileBrain __instance) { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(sparkyBrain), "playerDistFuncStateControl")] public class SparkyClientPatch { private static bool Prefix(sparkyBrain __instance) { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(jeffBrain), "playerDistFuncStateControl")] public class JeffClientPatch { private static bool Prefix(jeffBrain __instance) { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(henryBrain), "playerDistFuncStateControl")] public class HenryClientPatch { private static bool Prefix(henryBrain __instance) { //IL_005e: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return true; } PlayerSync playerSync = MPManager.Instance.PlayerSync; if (playerSync == null) { return true; } if (!playerSync.ShouldControlMonsters) { return false; } if (playerSync.IsLocalGhost) { List remotePlayerPositionsNonGhost = playerSync.GetRemotePlayerPositionsNonGhost(); if (remotePlayerPositionsNonGhost.Count == 0) { return false; } Vector3 val = remotePlayerPositionsNonGhost[0]; float num = Vector3.Distance(((Component)__instance).transform.position, val); for (int i = 1; i < remotePlayerPositionsNonGhost.Count; i++) { float num2 = Vector3.Distance(((Component)__instance).transform.position, remotePlayerPositionsNonGhost[i]); if (num2 < num) { num = num2; val = remotePlayerPositionsNonGhost[i]; } } BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; FieldInfo field = typeof(henryBrain).GetField("resetSwitch", bindingAttr); bool flag = field != null && (bool)field.GetValue(__instance); typeof(henryBrain).GetField("playerdist", bindingAttr)?.SetValue(__instance, num); if ((!(num < 0.5f) || flag) && num < 0.5f && flag) { __instance.setRandomPos(); } if (num < 2.5f) { __instance.agent.speed = __instance.henrySpeed * 1.4f; typeof(henryBrain).GetField("chaseSwitch", bindingAttr)?.SetValue(__instance, true); } else { __instance.agent.speed = __instance.henrySpeed; FieldInfo field2 = typeof(henryBrain).GetField("chaseSwitch", bindingAttr); if (field2 != null && (bool)field2.GetValue(__instance)) { __instance.setRandomPos(); field2?.SetValue(__instance, false); } } return false; } return true; } } [HarmonyPatch(typeof(mapEnBrain), "playerDistFunc")] public class HaroldClientPatch { private static bool Prefix(mapEnBrain __instance) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return true; } PlayerSync playerSync = MPManager.Instance.PlayerSync; if (playerSync == null) { return true; } if (playerSync.IsLocalGhost) { List remotePlayerPositionsNonGhost = playerSync.GetRemotePlayerPositionsNonGhost(); if (remotePlayerPositionsNonGhost.Count > 0) { float num = float.MaxValue; foreach (Vector3 item in remotePlayerPositionsNonGhost) { float num2 = Vector3.Distance(item, ((Component)__instance).transform.position); if (num2 < num) { num = num2; } } if (num < __instance.minimapViewDist) { __instance.mmc.setMapIconPosEnemy(); } } return false; } if (!playerSync.ShouldControlMonsters) { if ((Object)(object)__instance.player != (Object)null) { float num3 = Vector3.Distance(__instance.player.transform.position, ((Component)__instance).transform.position); if (num3 < __instance.minimapViewDist) { __instance.mmc.setMapIconPosEnemy(); if (num3 < __instance.minimapViewDist / 2f) { __instance.hapticTriggerFunc(); } } } return false; } return true; } } [HarmonyPatch(typeof(mapEnBrain), "wanderMode")] public class HaroldWanderPatch { private static bool Prefix(mapEnBrain __instance) { //IL_00d1: 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) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning) { if (!MPManager.Instance.PlayerSync.ShouldControlMonsters) { return false; } __instance.agent.speed = __instance.baseSpeed; float num = BackpackControl.batteryCharge; bool flag = !MPManager.Instance.PlayerSync.IsLocalGhost; if (MPManager.Instance.PlayerSync.IsLocalGhost) { num = MPManager.Instance.PlayerSync.GetRemoteBatteryCharge(); flag = MPManager.Instance.PlayerSync.GetRemotePlayerPositionsNonGhost().Count > 0; } if (!flag) { typeof(mapEnBrain).GetField("playerLock", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, false); if (Vector3.Distance(((Component)__instance).transform.position, __instance.agent.destination) < 0.1f) { __instance.setRandomPos(); } return false; } FieldInfo field = typeof(mapEnBrain).GetField("playerLock", BindingFlags.Instance | BindingFlags.NonPublic); bool flag2 = field != null && (bool)field.GetValue(__instance); if (num <= 0.2f) { field?.SetValue(__instance, true); } else if (flag2) { field?.SetValue(__instance, false); __instance.setRandomPos(); } if (Vector3.Distance(((Component)__instance).transform.position, __instance.agent.destination) < 0.1f) { __instance.setRandomPos(); } return false; } return true; } } [HarmonyPatch(typeof(SmileBrain), "playerDistFuncStateControl")] public class SmileClientPatch { private static bool Prefix(SmileBrain __instance) { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(EnemyDifMaster), "multiAttackTrigger")] public class EnemyDifMasterMultiAttackPatch { private static bool Prefix() { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(EnemyDifMaster), "attackTrigger")] public class EnemyDifMasterAttackPatch { private static bool Prefix() { if (MPManager.Instance?.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(sparkyBrain), "triggerAttack")] public class SparkyTriggerSyncPatch { private static void Postfix(sparkyBrain __instance) { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && MPManager.Instance.PlayerSync != null) { _ = MPManager.Instance.PlayerSync.ShouldControlMonsters; } } } [HarmonyPatch(typeof(jeffBrain), "triggerAttack")] public class JeffTriggerSyncPatch { private static void Postfix(jeffBrain __instance) { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && MPManager.Instance.PlayerSync != null) { _ = MPManager.Instance.PlayerSync.ShouldControlMonsters; } } } [HarmonyPatch(typeof(PuzzleMaster), "Start")] public class PuzzleMasterStartPatch { private static bool Prefix(PuzzleMaster __instance) { if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return true; } if (MPManager.Instance.Steam.IsHost && MPManager.Instance.PlayerSync != null && MPManager.Instance.PlayerSync.IsLocalGhost) { PuzzleMaster.totalCompletedPuzzles = 0; PuzzleMaster.requiredPuzzles = __instance.totalPuzzlesThisNight; PuzzleController[] array = (PuzzleController[])(object)new PuzzleController[9] { __instance.pCon1, __instance.pCon2, __instance.pCon3, __instance.pCon4, __instance.pCon5, __instance.pCon6, __instance.pCon7, __instance.pCon8, __instance.pCon9 }; foreach (PuzzleController val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.thisMapIndicator != (Object)null) { val.thisMapIndicator.SetActive(false); } } Plugin.Log.LogInfo((object)"[Ghost Host] Skipping PuzzleMaster.Start() random init - will restore saved state"); return false; } return true; } } [HarmonyPatch(typeof(PuzzleController), "Start")] public class PuzzleControllerStartPatch { private static void Prefix(PuzzleController __instance, out bool __state) { __state = false; if (MPManager.Instance?.PlayerSync == null) { return; } SteamTransport steam = MPManager.Instance.Steam; if (steam != null && steam.IsRunning) { bool num = !steam.IsHost; bool flag = steam.IsHost && MPManager.Instance.PlayerSync.IsLocalGhost; if (num || flag) { FieldInfo field = typeof(PuzzleController).GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic); __state = field != null && (bool)field.GetValue(__instance); } } } private static void Postfix(PuzzleController __instance, bool __state) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; FieldInfo field = typeof(PuzzleController).GetField("requiredCompletions", bindingAttr); if (field != null && (int)field.GetValue(__instance) <= 0) { field.SetValue(__instance, 4); } if (__state) { typeof(PuzzleController).GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(__instance, true); if ((Object)(object)__instance.fanspin != (Object)null) { __instance.fanspin.isOn = true; } GameObject thisMapIndicator = __instance.thisMapIndicator; if (thisMapIndicator != null) { thisMapIndicator.SetActive(true); } Plugin.LogDebug($"[PuzzleControllerStartPatch] Restored completed state for puzzle {__instance.thisPuzzleID}"); } } } [HarmonyPatch(typeof(PuzzleController), "setPuzzlePresetID")] public class PuzzlePresetPatch { private static bool Prefix() { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.Steam.IsHost) { return false; } return true; } } [HarmonyPatch(typeof(PuzzleController), "loadPreset")] public class PuzzleLoadPresetPatch { private static void Prefix(PuzzleController __instance, int presetID) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; int num = (int)typeof(PuzzleController).GetField("totalCompletions", bindingAttr).GetValue(__instance); int num2 = (int)typeof(PuzzleController).GetField("requiredCompletions", bindingAttr).GetValue(__instance); Plugin.Log.LogInfo((object)$"[Puzzle] loadPreset: puzzle={__instance.thisPuzzleID}, presetID={presetID}, oldTC={num}, oldRC={num2} -> resetting TC=0"); FieldInfo field = typeof(PuzzleController).GetField("totalCompletions", bindingAttr); if (field != null) { field.SetValue(__instance, 0); } FieldInfo field2 = typeof(PuzzleController).GetField("originCubeID", bindingAttr); if (field2 != null) { field2.SetValue(__instance, -1); } } private static void Postfix(PuzzleController __instance, int presetID) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; FieldInfo field = typeof(PuzzleController).GetField("requiredCompletions", bindingAttr); if (field != null) { int num = (int)field.GetValue(__instance); if (num <= 0) { field.SetValue(__instance, 4); Plugin.Log.LogInfo((object)$"[Puzzle] loadPreset Postfix: requiredCompletions was {num} for preset {presetID}, forced to 4"); } } } } [HarmonyPatch(typeof(PuzzleController), "FixedUpdate")] public class PuzzleControllerUpdatePatch { private static bool Prefix(PuzzleController __instance) { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync == null) { return true; } Type typeFromHandle = typeof(PuzzleController); if (!playerSync.IsLocalGhost && __instance.thisPuzzleID == BackpackControl.batteryLocationID && BackpackControl.batteryCharge > 0f) { FieldInfo field = typeFromHandle.GetField("puzzlePresetID", BindingFlags.Instance | BindingFlags.NonPublic); if (!(field != null) || (int)field.GetValue(__instance) == 0) { FieldInfo field2 = typeFromHandle.GetField("timer2", BindingFlags.Instance | BindingFlags.NonPublic); if (field2 != null && (int)field2.GetValue(__instance) >= 3) { field2.SetValue(__instance, 3); } } return true; } PlayerSync.RemoteBatteryState remoteBatteryAtLocation = playerSync.GetRemoteBatteryAtLocation(__instance.thisPuzzleID); if (remoteBatteryAtLocation != null && remoteBatteryAtLocation.Charge > 0f) { __instance.setMats(1); FieldInfo field3 = typeFromHandle.GetField("timer2", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field4 = typeFromHandle.GetField("puzzleHasCompleted", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field5 = typeFromHandle.GetField("puzzlePresetID", BindingFlags.Instance | BindingFlags.NonPublic); if (field3 != null && field4 != null && field5 != null) { int num = (int)field3.GetValue(__instance); bool flag = (bool)field4.GetValue(__instance); int num2 = (int)field5.GetValue(__instance); num++; if (num == 5 && !flag) { if (num2 > 0) { __instance.loadPreset(num2); } else { num = 4; } } field3.SetValue(__instance, num); } return false; } return true; } } [HarmonyPatch(typeof(PuzzleController), "onComplete")] public class PuzzleOnCompletePatch { private static void Prefix(PuzzleController __instance) { BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; int num = (int)typeof(PuzzleController).GetField("totalCompletions", bindingAttr).GetValue(__instance); int num2 = (int)typeof(PuzzleController).GetField("requiredCompletions", bindingAttr).GetValue(__instance); int num3 = (int)typeof(PuzzleController).GetField("puzzlePresetID", bindingAttr).GetValue(__instance); bool flag = (bool)typeof(PuzzleController).GetField("puzzleHasCompleted", bindingAttr).GetValue(__instance); Plugin.Log.LogInfo((object)$"[Puzzle] onComplete: puzzle={__instance.thisPuzzleID}, totalCompletions={num}+1, requiredCompletions={num2}, presetID={num3}, alreadyCompleted={flag}"); if (num + 1 >= num2) { Plugin.Log.LogInfo((object)$"[Puzzle] >>> WILL TRIGGER onWin! tc={num}+1 >= rc={num2}"); } } } [HarmonyPatch(typeof(PuzzleController), "onWin")] public class PuzzleCompletePatch { private static bool Prefix(PuzzleController __instance) { if (MPManager.Instance?.PlayerSync == null) { return true; } if (MPManager.Instance.PlayerSync.IsPuzzleCompleted(__instance.thisPuzzleID)) { Type? typeFromHandle = typeof(PuzzleController); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic; typeFromHandle.GetField("puzzleHasCompleted", bindingAttr).SetValue(__instance, true); if ((Object)(object)__instance.fanspin != (Object)null) { __instance.fanspin.isOn = true; } if ((Object)(object)__instance.thisMapIndicator != (Object)null) { __instance.thisMapIndicator.SetActive(true); } if ((Object)(object)__instance.winAudio != (Object)null) { __instance.winAudio.Play(); } for (int i = 0; i < __instance.cubeList.Length; i++) { __instance.cubeList[i].setThisID(0); } Plugin.LogDebug($"[Puzzle] onWin for puzzle {__instance.thisPuzzleID} — already counted, skipping counter increment"); return false; } return true; } private static void Postfix(PuzzleController __instance) { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.PlayerSync.IsPuzzleCompleted(__instance.thisPuzzleID)) { MPManager.Instance.PlayerSync.MarkPuzzleCompleted(__instance.thisPuzzleID); MPManager.Instance.PlayerSync.SendPuzzleComplete(__instance.thisPuzzleID); } } } [HarmonyPatch(typeof(clownNose), "checkHonk")] public class ClownHonkPatch { private static void Prefix(clownNose __instance, out bool __state) { __state = (Object)(object)__instance.honkSound != (Object)null && __instance.honkSound.isPlaying; } private static void Postfix(clownNose __instance, bool __state) { MPManager instance = MPManager.Instance; if ((instance == null || instance.PlayerSync?.IsReceivingHonk != true) && (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) && (Object)(object)__instance.honkSound != (Object)null && __instance.honkSound.isPlaying && !__state && MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning) { MPManager.Instance.PlayerSync.SendClownHonk(); } } } [HarmonyPatch(typeof(ventSoundPlayer), "playRandomAudio")] public class VentSoundPatch { private static void Postfix(ventSoundPlayer __instance) { //IL_0084: 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_0094: Unknown result type (might be due to invalid IL or missing references) MPManager instance = MPManager.Instance; if ((instance == null || instance.PlayerSync?.IsReceivingVentSound != true) && (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) && MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning) { Vector3 position = __instance.spawnpos.position; MPManager.Instance.PlayerSync.SendVentSound(position, 0); } } } [HarmonyPatch(typeof(crawlSoundContrl), "playTapSound")] public class CrawlSoundPatch { private static void Postfix(crawlSoundContrl __instance) { //IL_0084: 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_00b1: Unknown result type (might be due to invalid IL or missing references) MPManager instance = MPManager.Instance; if ((instance == null || instance.PlayerSync?.IsReceivingVentSound != true) && (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) && MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning) { Vector3 position = ((Component)__instance).transform.position; bool inMainRoom = (Object)(object)__instance.mtc != (Object)null && __instance.mtc.isInMainRoom; MPManager.Instance.PlayerSync.SendCrawlSound(position, inMainRoom); } } } [HarmonyPatch(typeof(PuzzleBlock), "OnTriggerStay")] public class PuzzleBlockLockPatch { private static bool Prefix(PuzzleBlock __instance) { if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return true; } int thisPuzzleID = __instance.pcontrol.thisPuzzleID; string interactionId = $"puzzle_{thisPuzzleID}"; if (PlayerSync.IsLockedByOther(interactionId)) { return false; } MPManager.Instance.PlayerSync.RefreshLock(interactionId); return true; } } [HarmonyPatch(typeof(PuzzleBlock), "setThisID")] public class PuzzleBlockVisualSyncPatch { private static void Postfix(PuzzleBlock __instance, int input) { if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && !MPManager.Instance.PlayerSync.IsReceivingPuzzleBlock && !((Object)(object)__instance.pcontrol == (Object)null)) { int thisPuzzleID = __instance.pcontrol.thisPuzzleID; bool num = !MPManager.Instance.PlayerSync.IsLocalGhost && thisPuzzleID == BackpackControl.batteryLocationID && BackpackControl.batteryCharge > 0f; PlayerSync.RemoteBatteryState remoteBatteryAtLocation = MPManager.Instance.PlayerSync.GetRemoteBatteryAtLocation(thisPuzzleID); bool flag = remoteBatteryAtLocation != null && remoteBatteryAtLocation.Charge > 0f; if (num || flag) { MPManager.Instance.PlayerSync.SendPuzzleBlock(thisPuzzleID, __instance.blockNumber, input); } } } } [HarmonyPatch(typeof(crankControl), "batteryScreenVisual")] public class CrankVisualSyncPatch { private static bool Prefix(crankControl __instance) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return true; } if (BackpackControl.batteryLocationID == 1) { return true; } if (!MPManager.Instance.PlayerSync.RemoteHasBatteryInCrank) { return true; } float remoteCrankChargeDisplay = MPManager.Instance.PlayerSync.RemoteCrankChargeDisplay; if ((Object)(object)__instance.batteryFill != (Object)null) { __instance.batteryFill.fillAmount = remoteCrankChargeDisplay / 55f; } if ((Object)(object)__instance.batteryIMG != (Object)null) { __instance.batteryIMG.SetActive(true); } ((Component)__instance).transform.rotation = MPManager.Instance.PlayerSync.RemoteCrankRotationDisplay; return false; } } [HarmonyPatch(typeof(batteryZoneCheck), "visualSet")] public class BatteryZoneVisualPatch { private static void Postfix(batteryZoneCheck __instance) { if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning || BackpackControl.batteryLocationID == __instance.thisStationID) { return; } if (MPManager.Instance.PlayerSync?.GetRemoteBatteryAtLocation(__instance.thisStationID) != null) { if ((Object)(object)__instance.thisBatteryVisual != (Object)null) { __instance.thisBatteryVisual.SetActive(true); } } else if ((Object)(object)__instance.thisBatteryVisual != (Object)null && BackpackControl.batteryLocationID != __instance.thisStationID) { __instance.thisBatteryVisual.SetActive(false); } } } [HarmonyPatch(typeof(VentAnimControl), "OnTriggerStay")] public class VentDoorSyncPatch { private static void Postfix(VentAnimControl __instance, Collider other) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.Steam != null && MPManager.Instance.Steam.IsRunning && (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost)) { int instanceID = ((Object)__instance).GetInstanceID(); MPManager.Instance.PlayerSync.SendVentDoorTrigger(instanceID, ((Component)__instance).transform.position); } } } [HarmonyPatch(typeof(paintingControl), "setAllPaintings")] public class PaintingRandomBlockPatch { private static bool Prefix() { SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport != null && steamTransport.IsInLobby && steamTransport.IsConnected && !steamTransport.IsHost) { return false; } return true; } } [HarmonyPatch(typeof(paintingControl), "setEntityPainting")] public class PaintingEntitySyncPatch { private static void Postfix(paintingControl __instance) { SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport != null && steamTransport.IsRunning && steamTransport.IsHost) { MPManager.Instance.PlayerSync.SendPaintingEntityState(__instance); } } } [HarmonyPatch(typeof(BackpackControl), "placeBatteryCheck")] public class BlockBatteryDoublePlacePatch { private static bool Prefix() { if (MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return true; } int batteryZoneIDGlobal = batteryZoneCheck.batteryZoneIDGlobal; if (batteryZoneIDGlobal <= 0) { return true; } if (batteryZoneIDGlobal == 1 || batteryZoneIDGlobal >= 100) { return true; } PlayerSync.RemoteBatteryState remoteBatteryAtLocation = MPManager.Instance.PlayerSync.GetRemoteBatteryAtLocation(batteryZoneIDGlobal); if (remoteBatteryAtLocation != null) { Plugin.LogDebug($"[Battery] Blocked placement at zone {batteryZoneIDGlobal} — remote player has battery there (charge={remoteBatteryAtLocation.Charge:F1})"); return false; } return true; } } [HarmonyPatch(typeof(EndControl), "isLooking")] public class EndControlSyncPatch { private static bool Prefix() { SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport != null && steamTransport.IsRunning && !steamTransport.IsHost) { return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "onDeathClown")] public class DeathClownPatch { private static void Prefix(jumpscareController __instance) { if (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) { FieldInfo field = ((object)__instance).GetType().GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (int)field.GetValue(__instance) == 0) { Plugin.Log.LogInfo((object)"[Death] Clown killed player - sending death notification"); MPManager.Instance?.PlayerSync?.SendDeathGhost(isGhost: true, 1); } } } } [HarmonyPatch(typeof(jumpscareController), "onDeathHarold")] public class DeathHaroldPatch { private static void Prefix(jumpscareController __instance) { if (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) { FieldInfo field = ((object)__instance).GetType().GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (int)field.GetValue(__instance) == 0) { Plugin.Log.LogInfo((object)"[Death] Harold killed player - sending death notification"); MPManager.Instance?.PlayerSync?.SendDeathGhost(isGhost: true, 2); } } } } [HarmonyPatch(typeof(jumpscareController), "onDeathSparky")] public class DeathSparkyPatch { private static void Prefix(jumpscareController __instance) { if (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) { FieldInfo field = ((object)__instance).GetType().GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (int)field.GetValue(__instance) == 0) { Plugin.Log.LogInfo((object)"[Death] Sparky killed player - sending death notification"); MPManager.Instance?.PlayerSync?.SendDeathGhost(isGhost: true, 3); } } } } [HarmonyPatch(typeof(jumpscareController), "onDeathHenry")] public class DeathHenryPatch { private static void Prefix(jumpscareController __instance) { if (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) { FieldInfo field = ((object)__instance).GetType().GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (int)field.GetValue(__instance) == 0) { Plugin.Log.LogInfo((object)"[Death] Henry killed player - sending death notification"); MPManager.Instance?.PlayerSync?.SendDeathGhost(isGhost: true, 4); } } } } [HarmonyPatch(typeof(jumpscareController), "onDeathSmiley")] public class DeathSmileyPatch { private static void Prefix(jumpscareController __instance) { if (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) { FieldInfo field = ((object)__instance).GetType().GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (int)field.GetValue(__instance) == 0) { Plugin.Log.LogInfo((object)"[Death] Smiley killed player - sending death notification"); MPManager.Instance?.PlayerSync?.SendDeathGhost(isGhost: true, 5); } } } } [HarmonyPatch(typeof(jumpscareController), "onDeathJeff")] public class DeathJeffPatch { private static void Prefix(jumpscareController __instance) { if (MPManager.Instance?.PlayerSync == null || !MPManager.Instance.PlayerSync.IsLocalGhost) { FieldInfo field = ((object)__instance).GetType().GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null && (int)field.GetValue(__instance) == 0) { Plugin.Log.LogInfo((object)"[Death] Jeff killed player - sending death notification"); MPManager.Instance?.PlayerSync?.SendDeathGhost(isGhost: true, 6); } } } } [HarmonyPatch(typeof(MinimapControl), "setMapIconPos")] public class MinimapFriendPatch { private static List _friendIndicators = new List(); private static MinimapControl _lastMinimapControl; private static readonly Color[] PLAYER_COLORS = (Color[])(object)new Color[9] { Color.cyan, new Color(1f, 0.5f, 0f), new Color(1f, 1f, 0f), new Color(1f, 0.3f, 1f), new Color(0.5f, 1f, 0.5f), new Color(0.6f, 0.4f, 1f), new Color(1f, 1f, 1f), new Color(0.3f, 0.7f, 1f), new Color(1f, 0.7f, 0.7f) }; public static void Cleanup() { foreach (GameObject friendIndicator in _friendIndicators) { if ((Object)(object)friendIndicator != (Object)null) { Object.Destroy((Object)(object)friendIndicator); } } _friendIndicators.Clear(); _lastMinimapControl = null; } private static void Postfix(MinimapControl __instance) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: 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_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) if (MPManager.Instance?.PlayerSync == null || MPManager.Instance?.Steam == null || !MPManager.Instance.Steam.IsRunning) { return; } if ((Object)(object)__instance.minimap == (Object)null || !__instance.minimap.activeSelf) { foreach (GameObject friendIndicator in _friendIndicators) { if ((Object)(object)friendIndicator != (Object)null) { friendIndicator.SetActive(false); } } return; } if ((Object)(object)_lastMinimapControl != (Object)(object)__instance) { Cleanup(); _lastMinimapControl = __instance; } List remotePlayerPositions = MPManager.Instance.PlayerSync.GetRemotePlayerPositions(); while (_friendIndicators.Count < remotePlayerPositions.Count) { int count = _friendIndicators.Count; GameObject val = Object.Instantiate(__instance.playerIndicator, __instance.playerIndicator.transform.parent); ((Object)val).name = $"FriendIndicator_{count}"; Color val2 = ((count < PLAYER_COLORS.Length) ? PLAYER_COLORS[count] : Color.white); SpriteRenderer component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.color = val2; } Renderer component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null && (Object)(object)component2.material != (Object)null) { component2.material.color = val2; } Component[] components = val.GetComponents(); foreach (Component val3 in components) { PropertyInfo property = ((object)val3).GetType().GetProperty("color"); if (property != null && property.PropertyType == typeof(Color)) { property.SetValue(val3, val2); break; } } _friendIndicators.Add(val); } for (int j = 0; j < _friendIndicators.Count; j++) { if (j < remotePlayerPositions.Count) { Vector3 val4 = remotePlayerPositions[j]; _friendIndicators[j].transform.localPosition = new Vector3(val4.x * 15.4f + __instance.xOffest, val4.z * 15.52f + __instance.yOffset, 0f); _friendIndicators[j].SetActive(true); } else { _friendIndicators[j].SetActive(false); } } } } [HarmonyPatch(typeof(LeaveDoorControl), "leaveCheck")] public class LeaveDoorCheckPatch { private static bool Prefix(LeaveDoorControl __instance) { SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport == null || !steamTransport.IsRunning) { return true; } bool flag = BackpackControl.batteryLocationID == 100 && BackpackControl.batteryCharge >= 0.1f; bool flag2 = MPManager.Instance.PlayerSync.IsRemoteBatteryAtExit(); bool flag3 = false; FieldInfo field = typeof(LeaveDoorControl).GetField("doorLeaveTimer", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { flag3 = (int)field.GetValue(__instance) >= __instance.doorLeaveRequiredTime && __instance.doorLeaveRequiredTime > 0; } if (flag3 && PuzzleMaster.totalCompletedPuzzles == PuzzleMaster.requiredPuzzles) { __instance.setYes(); if ((Object)(object)__instance.doorLeaveHitbox != (Object)null) { __instance.doorLeaveHitbox.SetActive(true); } return false; } if (flag) { return true; } if (flag2) { if (PuzzleMaster.totalCompletedPuzzles != PuzzleMaster.requiredPuzzles) { __instance.setNo(); return false; } __instance.setYes(); return false; } return true; } } [HarmonyPatch(typeof(SceneManager), "LoadScene", new Type[] { typeof(string) })] public class GhostSceneInterceptPatch { private static bool Prefix(ref string sceneName) { Plugin.Log.LogInfo((object)$"[Ghost] LoadScene interceptor: sceneName={sceneName}, IsGhostSceneReload={PlayerSync.IsGhostSceneReload}"); if (!PlayerSync.IsGhostSceneReload) { return true; } PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync == null) { return true; } if (sceneName.Equals("Home", StringComparison.OrdinalIgnoreCase)) { Plugin.Log.LogInfo((object)"[Ghost] Intercepting Home load - staying in scene as ghost (no reload)"); PlayerSync.IsGhostSceneReload = false; jumpscareController val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { if ((Object)(object)val.worldMaster != (Object)null) { val.worldMaster.SetActive(true); } if ((Object)(object)val.leftHand != (Object)null) { val.leftHand.SetActive(true); } if ((Object)(object)val.rightHand != (Object)null) { val.rightHand.SetActive(true); } if ((Object)(object)val.bkgGO != (Object)null) { val.bkgGO.SetActive(false); } if ((Object)(object)val.staticScreen != (Object)null) { val.staticScreen.SetActive(false); } if ((Object)(object)val.sparkyGO != (Object)null) { val.sparkyGO.SetActive(false); } if ((Object)(object)val.haroldGO != (Object)null) { val.haroldGO.SetActive(false); } if ((Object)(object)val.smileyGO != (Object)null) { val.smileyGO.SetActive(false); } if ((Object)(object)val.jeffGO != (Object)null) { val.jeffGO.SetActive(false); } if ((Object)(object)val.henryGO != (Object)null) { val.henryGO.SetActive(false); } if ((Object)(object)val.clownJSDoll != (Object)null) { val.clownJSDoll.SetActive(false); } if ((Object)(object)val.JSSparky != (Object)null) { val.JSSparky.Stop(); } if ((Object)(object)val.JSHarold != (Object)null) { val.JSHarold.Stop(); } if ((Object)(object)val.JSHenry != (Object)null) { val.JSHenry.Stop(); } if ((Object)(object)val.JSJeff != (Object)null) { val.JSJeff.Stop(); } if ((Object)(object)val.JSSmile != (Object)null) { val.JSSmile.Stop(); } typeof(jumpscareController).GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(val, 0); Plugin.Log.LogInfo((object)"[Ghost] World re-enabled, jumpscare cleaned up"); } playerSync.TeleportToPartner(); return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "Start")] public class GhostJumpscareStartPatch { private static void Postfix(jumpscareController __instance) { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost) { Plugin.Log.LogInfo((object)"[Ghost] Re-enabling world after ghost reload"); if ((Object)(object)__instance.worldMaster != (Object)null) { __instance.worldMaster.SetActive(true); } if ((Object)(object)__instance.leftHand != (Object)null) { __instance.leftHand.SetActive(true); } if ((Object)(object)__instance.rightHand != (Object)null) { __instance.rightHand.SetActive(true); } if ((Object)(object)__instance.bkgGO != (Object)null) { __instance.bkgGO.SetActive(false); } if ((Object)(object)__instance.staticScreen != (Object)null) { __instance.staticScreen.SetActive(false); } if ((Object)(object)__instance.sparkyGO != (Object)null) { __instance.sparkyGO.SetActive(false); } if ((Object)(object)__instance.haroldGO != (Object)null) { __instance.haroldGO.SetActive(false); } if ((Object)(object)__instance.smileyGO != (Object)null) { __instance.smileyGO.SetActive(false); } if ((Object)(object)__instance.jeffGO != (Object)null) { __instance.jeffGO.SetActive(false); } if ((Object)(object)__instance.henryGO != (Object)null) { __instance.henryGO.SetActive(false); } if ((Object)(object)__instance.clownJSDoll != (Object)null) { __instance.clownJSDoll.SetActive(false); } FieldInfo field = typeof(jumpscareController).GetField("deathID", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(__instance, 0); } Plugin.Log.LogInfo((object)"[Ghost] World re-enabled after ghost reload"); } } } [HarmonyPatch(typeof(PuzzleBlock), "OnTriggerStay")] public class GhostPuzzleBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(BackpackControl), "grabBattery")] public class GhostBatteryBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(crankControl), "getGeneratedPower")] public class GhostCrankBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost) { return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "onDeathClown")] public class GhostDeathClownBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost && !playerSync.IsDyingThisFrame) { return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "onDeathHarold")] public class GhostDeathHaroldBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost && !playerSync.IsDyingThisFrame) { return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "onDeathSparky")] public class GhostDeathSparkyBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost && !playerSync.IsDyingThisFrame) { return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "onDeathHenry")] public class GhostDeathHenryBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost && !playerSync.IsDyingThisFrame) { return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "onDeathSmiley")] public class GhostDeathSmileyBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost && !playerSync.IsDyingThisFrame) { return false; } return true; } } [HarmonyPatch(typeof(jumpscareController), "onDeathJeff")] public class GhostDeathJeffBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost && !playerSync.IsDyingThisFrame) { return false; } return true; } } [HarmonyPatch(typeof(paintingControl), "killPlayer")] public class GhostPaintingDeathBlockPatch { private static bool Prefix() { PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync != null && playerSync.IsLocalGhost) { Plugin.Log.LogInfo((object)"[Ghost] Blocked painting death - ghosts are immune"); return false; } return true; } } [HarmonyPatch(typeof(MinimapControl), "FixedUpdate")] public class GhostMinimapPatch { private static bool Prefix(MinimapControl __instance) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) PlayerSync playerSync = MPManager.Instance?.PlayerSync; if (playerSync == null || !playerSync.IsLocalGhost) { return true; } FieldInfo field = typeof(MinimapControl).GetField("timer", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null) { return true; } field.SetValue(__instance, 7); if ((Object)(object)__instance.minimap != (Object)null) { __instance.minimap.SetActive(true); } __instance.setMapIconPos(); if ((Object)(object)__instance.minimapRect != (Object)null) { ((Component)__instance.minimapRect).transform.localScale = new Vector3(1f, 1f, 1f); } if ((Object)(object)__instance.enemyIndicator != (Object)null) { FieldInfo field2 = typeof(MinimapControl).GetField("haroldTimer", BindingFlags.Instance | BindingFlags.NonPublic); if (field2 != null) { int num = (int)field2.GetValue(__instance); num--; field2.SetValue(__instance, num); __instance.enemyIndicator.SetActive(num > 0); } } if ((Object)(object)__instance.handmesh != (Object)null && (Object)(object)__instance.handmatTrans != (Object)null) { ((Renderer)__instance.handmesh).material = __instance.handmatTrans; } return false; } } [HarmonyPatch(typeof(pausetest2), "Update")] public class DisablePauseSystemPatch { private static bool Prefix(pausetest2 __instance) { SteamTransport steamTransport = MPManager.Instance?.Steam; if (steamTransport != null && steamTransport.IsInLobby) { Time.timeScale = 1f; AudioListener.pause = false; FieldInfo field = typeof(pausetest2).GetField("bPause", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(__instance, false); } return false; } return true; } } public class MultiplayerUI : MonoBehaviour { private Canvas _canvas; private GameObject _panelRoot; private MPManager _manager; private Text _statusText; private Text _tutorialHint; private Text _playerNameText; private Text _connectedPlayersText; private Text _lobbyCodeText; private Button _hostButton; private Button _disconnectButton; private Button _showCodeButton; private Button _copyCodeButton; private Button _pasteJoinButton; private Button _voiceToggleButton; private Text _micInfoText; private GameObject _notConnectedPanel; private GameObject _connectedPanel; private GameObject _joiningPanel; private GameObject _lobbyCodePanel; private GameObject _friendsPanel; private GameObject _friendsScrollContent; private ScrollRect _friendsScroll; private Text _friendsHeaderText; private List _friendEntries = new List(); private GameObject _inviteFriendsPanel; private GameObject _inviteFriendsContent; private ScrollRect _inviteFriendsScroll; private Text _inviteHeaderText; private List _inviteEntries = new List(); private bool _showLobbyCode; private float _copiedTime = -10f; private float _lastFriendsRefresh; private bool _initialized; private Font _cachedFont; private Dictionary _inviteSentTime = new Dictionary(); private string _lastStatusMsg = ""; private float _statusSetTime; private const float CANVAS_W = 550f; private const float CANVAS_H = 800f; private const float WORLD_SCALE = 0.00092f; private static readonly Color C_BG = new Color(0.07f, 0.07f, 0.12f, 0.92f); private static readonly Color C_HEADER = new Color(0.11f, 0.13f, 0.22f, 1f); private static readonly Color C_BTN = new Color(0.22f, 0.36f, 0.58f, 1f); private static readonly Color C_HOST = new Color(0.16f, 0.48f, 0.26f, 1f); private static readonly Color C_DC = new Color(0.58f, 0.16f, 0.16f, 1f); private static readonly Color C_INVITE = new Color(0.22f, 0.42f, 0.68f, 1f); private static readonly Color C_JOIN = new Color(0.18f, 0.52f, 0.3f, 1f); private static readonly Color C_TEXT = new Color(0.92f, 0.92f, 0.96f, 1f); private static readonly Color C_DIM = new Color(0.5f, 0.5f, 0.55f, 1f); private static readonly Color C_OK = new Color(0.45f, 0.95f, 0.45f, 1f); private static readonly Color C_WARN = new Color(1f, 0.85f, 0.45f, 1f); private static readonly Color C_ERR = new Color(1f, 0.45f, 0.45f, 1f); private static readonly Color C_ROW = new Color(0.12f, 0.13f, 0.2f, 0.85f); private static readonly Color C_ROW_ALT = new Color(0.14f, 0.15f, 0.22f, 0.85f); private static readonly Color C_LINE = new Color(0.28f, 0.32f, 0.45f, 0.35f); private Transform _rightHand; private LineRenderer _laserLine; private GameObject _laserDot; private GameObject _laserObj; private bool _trigDown; private bool _trigWas; private GameObject _hovered; public void Initialize(MPManager manager) { _manager = manager; if (_initialized && (Object)(object)_panelRoot != (Object)null) { _panelRoot.SetActive(true); RefreshUI(); RefreshFriendsList(); } else { CreateUI(); SetupLaser(); RefreshUI(); RefreshFriendsList(); _initialized = true; } } public void Hide() { if ((Object)(object)_panelRoot != (Object)null) { _panelRoot.SetActive(false); } } private void Update() { if (_initialized && !((Object)(object)_panelRoot == (Object)null) && _panelRoot.activeSelf) { if ((Object)(object)_rightHand == (Object)null) { _rightHand = FindHand(); } RefreshUI(); UpdateLaser(); if (Time.realtimeSinceStartup - _lastFriendsRefresh > 2f) { RefreshFriendsList(); } if ((Object)(object)_copyCodeButton != (Object)null) { SetLabel(_copyCodeButton, (Time.realtimeSinceStartup - _copiedTime < 2f) ? "Copied!" : "Copy"); } } } private void CreateUI() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_010d: 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_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_024d: 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_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e4: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Expected O, but got Unknown //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_0472: Unknown result type (might be due to invalid IL or missing references) //IL_0483: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Expected O, but got Unknown //IL_04a0: Unknown result type (might be due to invalid IL or missing references) //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04bb: Expected O, but got Unknown //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_05a1: Unknown result type (might be due to invalid IL or missing references) //IL_05b2: Unknown result type (might be due to invalid IL or missing references) //IL_05bc: Expected O, but got Unknown //IL_05cf: Unknown result type (might be due to invalid IL or missing references) //IL_05e0: Unknown result type (might be due to invalid IL or missing references) //IL_05ea: Expected O, but got Unknown //IL_0610: Unknown result type (might be due to invalid IL or missing references) //IL_064b: Unknown result type (might be due to invalid IL or missing references) //IL_06b9: Unknown result type (might be due to invalid IL or missing references) //IL_06d4: Unknown result type (might be due to invalid IL or missing references) //IL_06e5: Unknown result type (might be due to invalid IL or missing references) //IL_06ef: Expected O, but got Unknown //IL_0703: Unknown result type (might be due to invalid IL or missing references) //IL_0718: Unknown result type (might be due to invalid IL or missing references) //IL_072d: Unknown result type (might be due to invalid IL or missing references) //IL_0742: Unknown result type (might be due to invalid IL or missing references) //IL_0756: Unknown result type (might be due to invalid IL or missing references) //IL_0795: Unknown result type (might be due to invalid IL or missing references) _panelRoot = new GameObject("MP_WorldUI"); _panelRoot.transform.SetParent(((Component)this).transform); _panelRoot.transform.position = new Vector3(-0.2434f, 1.782f, -3.8182f); _panelRoot.transform.rotation = Quaternion.Euler(0f, 180f, 0f); _canvas = _panelRoot.AddComponent(); _canvas.renderMode = (RenderMode)2; _canvas.sortingOrder = 100; RectTransform component = ((Component)_canvas).GetComponent(); component.sizeDelta = new Vector2(550f, 800f); ((Transform)component).localScale = Vector3.one * 0.00092f; _panelRoot.AddComponent(); CanvasScaler obj = _panelRoot.AddComponent(); obj.dynamicPixelsPerUnit = 10f; obj.referencePixelsPerUnit = 100f; GameObject val = MkObj("BG", component); ((Graphic)val.AddComponent()).color = C_BG; Fill(val); Outline obj2 = val.AddComponent(); ((Shadow)obj2).effectColor = new Color(0.3f, 0.4f, 0.6f, 0.5f); ((Shadow)obj2).effectDistance = new Vector2(2f, 2f); GameObject obj3 = MkObj("Header", component); ((Graphic)obj3.AddComponent()).color = C_HEADER; RectTransform component2 = obj3.GetComponent(); component2.anchorMin = new Vector2(0f, 1f); component2.anchorMax = new Vector2(1f, 1f); component2.pivot = new Vector2(0.5f, 1f); component2.sizeDelta = new Vector2(0f, 48f); component2.anchoredPosition = Vector2.zero; _playerNameText = MkText("Name", component2, "", 12, (TextAnchor)4); ((Graphic)_playerNameText).color = new Color(0.65f, 0.8f, 1f); RectTransform component3 = ((Component)_playerNameText).GetComponent(); component3.anchorMin = new Vector2(0f, 0.65f); component3.anchorMax = new Vector2(1f, 1f); Vector2 offsetMin = (component3.offsetMax = Vector2.zero); component3.offsetMin = offsetMin; Text obj4 = MkText("Title", component2, "Crawlspace 2 MP", 20, (TextAnchor)4); ((Graphic)obj4).color = C_TEXT; obj4.fontStyle = (FontStyle)1; RectTransform component4 = ((Component)obj4).GetComponent(); component4.anchorMin = new Vector2(0f, 0f); component4.anchorMax = new Vector2(1f, 0.62f); offsetMin = (component4.offsetMax = Vector2.zero); component4.offsetMin = offsetMin; GameObject obj5 = MkObj("Body", component); RectTransform component5 = obj5.GetComponent(); component5.anchorMin = Vector2.zero; component5.anchorMax = Vector2.one; component5.offsetMin = new Vector2(10f, 16f); component5.offsetMax = new Vector2(-10f, -50f); VerticalLayoutGroup obj6 = obj5.AddComponent(); ((HorizontalOrVerticalLayoutGroup)obj6).spacing = 2f; ((LayoutGroup)obj6).padding = new RectOffset(0, 0, 0, 0); ((LayoutGroup)obj6).childAlignment = (TextAnchor)1; ((HorizontalOrVerticalLayoutGroup)obj6).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj6).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)obj6).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj6).childForceExpandHeight = false; _statusText = AddLabel(component5, "", 12, (TextAnchor)4, 16f); ((Graphic)_statusText).color = C_TEXT; _tutorialHint = AddLabel(component5, "", 12, (TextAnchor)4, 0f); ((Graphic)_tutorialHint).color = C_WARN; ((Component)_tutorialHint).gameObject.SetActive(false); _connectedPlayersText = AddLabel(component5, "", 1, (TextAnchor)4, 0f); ((Component)_connectedPlayersText).gameObject.SetActive(false); ((Graphic)_connectedPlayersText).color = C_OK; _notConnectedPanel = MkPanel(component5, "NotConnected"); RectTransform component6 = _notConnectedPanel.GetComponent(); GameObject val2 = MkObj("HostRow", component6); HorizontalLayoutGroup obj7 = val2.AddComponent(); ((HorizontalOrVerticalLayoutGroup)obj7).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)obj7).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj7).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj7).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj7).childForceExpandHeight = false; val2.AddComponent().preferredHeight = 28f; _hostButton = MkBtn(val2.GetComponent(), "Host Game", C_HOST, 28f, (UnityAction)delegate { if (_manager.IsRunning || _manager.IsJoining) { _manager.DisconnectFromLobby(); } _manager.HostGame(); }); _pasteJoinButton = MkBtn(val2.GetComponent(), "Paste & Join", C_BTN, 28f, (UnityAction)delegate { if (_manager.IsRunning || _manager.IsJoining) { _manager.DisconnectFromLobby(); } string systemCopyBuffer = GUIUtility.systemCopyBuffer; if (!string.IsNullOrEmpty(systemCopyBuffer)) { systemCopyBuffer = systemCopyBuffer.Trim(); if (ulong.TryParse(systemCopyBuffer, out var result) && result != 0) { string text = _manager.Steam?.GetLobbyId() ?? ""; string text2 = _manager.LastLobbyId ?? ""; string text3 = result.ToString(); if (text3 == text || text3 == text2) { _manager.StatusMessage = "Can't join your own lobby"; } else { _manager.Steam?.JoinFriendGame(result); _manager.StatusMessage = "Joining from code..."; } } else { _manager.StatusMessage = "Invalid lobby code"; } } else { _manager.StatusMessage = "Clipboard empty"; } }); AddLine(component6); _friendsHeaderText = AddLabel(component6, "Friends Playing", 11, (TextAnchor)3, 14f); ((Graphic)_friendsHeaderText).color = C_DIM; BuildScrollList(component6, out _friendsPanel, out _friendsScroll, out _friendsScrollContent, 0f); _friendsPanel.GetComponent().flexibleHeight = 1f; _connectedPanel = MkPanel(component5, "Connected"); RectTransform component7 = _connectedPanel.GetComponent(); GameObject val3 = MkObj("ActionRow", component7); HorizontalLayoutGroup obj8 = val3.AddComponent(); ((HorizontalOrVerticalLayoutGroup)obj8).spacing = 4f; ((HorizontalOrVerticalLayoutGroup)obj8).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj8).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj8).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj8).childForceExpandHeight = false; val3.AddComponent().preferredHeight = 24f; _voiceToggleButton = MkBtn(val3.GetComponent(), "Mic: ON", C_BTN, 24f, (UnityAction)delegate { if (_manager.VoiceChat != null) { _manager.VoiceChat.Enabled = !_manager.VoiceChat.Enabled; } }); _disconnectButton = MkBtn(val3.GetComponent(), "Disconnect", C_DC, 24f, (UnityAction)delegate { _manager.DisconnectFromLobby(); }); _micInfoText = AddLabel(component7, "", 9, (TextAnchor)3, 12f); ((Graphic)_micInfoText).color = C_DIM; BuildCodeRow(component7); AddLine(component7); _inviteHeaderText = AddLabel(component7, "Invite Friends", 11, (TextAnchor)3, 14f); ((Graphic)_inviteHeaderText).color = C_DIM; BuildScrollList(component7, out _inviteFriendsPanel, out _inviteFriendsScroll, out _inviteFriendsContent, 0f); _inviteFriendsPanel.GetComponent().flexibleHeight = 1f; _joiningPanel = MkPanel(component5, "Joining"); ((Graphic)AddLabel(_joiningPanel.GetComponent(), "Joining lobby...", 16, (TextAnchor)4, 24f)).color = C_WARN; MkBtn(_joiningPanel.GetComponent(), "Cancel", C_DC, 30f, (UnityAction)delegate { _manager.DisconnectFromLobby(); }); GameObject obj9 = MkObj("Footer", component); RectTransform component8 = obj9.GetComponent(); component8.anchorMin = Vector2.zero; component8.anchorMax = new Vector2(1f, 0f); component8.pivot = new Vector2(0.5f, 0f); component8.sizeDelta = new Vector2(0f, 14f); component8.anchoredPosition = new Vector2(0f, 1f); Text obj10 = obj9.AddComponent(); obj10.font = GetFont(); obj10.fontSize = 10; obj10.alignment = (TextAnchor)4; ((Graphic)obj10).color = new Color(0.35f, 0.35f, 0.4f, 0.7f); obj10.text = "v1.2.6 | Steam Voice"; } private void BuildCodeRow(RectTransform parent) { //IL_006a: 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_0085: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_016d: Unknown result type (might be due to invalid IL or missing references) _lobbyCodePanel = MkObj("CodePanel", parent); HorizontalLayoutGroup obj = _lobbyCodePanel.AddComponent(); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 4f; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; _lobbyCodePanel.AddComponent().preferredHeight = 18f; _showCodeButton = MkBtn(_lobbyCodePanel.GetComponent(), "Show Code", C_BTN, 18f, (UnityAction)delegate { _showLobbyCode = !_showLobbyCode; }); ((Component)_showCodeButton).gameObject.AddComponent().preferredWidth = 80f; _copyCodeButton = MkBtn(_lobbyCodePanel.GetComponent(), "Copy", C_BTN, 18f, (UnityAction)delegate { string text = _manager.Steam?.GetLobbyId() ?? ""; if (!string.IsNullOrEmpty(text)) { GUIUtility.systemCopyBuffer = text; _copiedTime = Time.realtimeSinceStartup; } }); ((Component)_copyCodeButton).gameObject.AddComponent().preferredWidth = 45f; GameObject val = MkObj("CodeText", _lobbyCodePanel.GetComponent()); LayoutElement obj2 = val.AddComponent(); obj2.flexibleWidth = 1f; obj2.preferredHeight = 18f; _lobbyCodeText = val.AddComponent(); _lobbyCodeText.font = GetFont(); _lobbyCodeText.fontSize = 9; _lobbyCodeText.alignment = (TextAnchor)3; ((Graphic)_lobbyCodeText).color = new Color(0.9f, 0.9f, 0.55f); } private void BuildScrollList(RectTransform parent, out GameObject panel, out ScrollRect scroll, out GameObject content, float height) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown panel = MkObj("ScrollPanel", parent); LayoutElement val = panel.AddComponent(); if (height > 0f) { val.preferredHeight = height; } val.flexibleHeight = 0f; panel.AddComponent(); scroll = panel.AddComponent(); scroll.horizontal = false; scroll.vertical = true; scroll.movementType = (MovementType)2; scroll.scrollSensitivity = 30f; content = MkObj("Content", panel.GetComponent()); RectTransform component = content.GetComponent(); component.anchorMin = new Vector2(0f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(0.5f, 1f); component.sizeDelta = new Vector2(0f, 0f); VerticalLayoutGroup obj = content.AddComponent(); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 2f; ((LayoutGroup)obj).padding = new RectOffset(0, 0, 1, 1); ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; content.AddComponent().verticalFit = (FitMode)2; scroll.content = component; } private void SetupLaser() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00d0: 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_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) try { _rightHand = FindHand(); if ((Object)(object)_rightHand == (Object)null) { Plugin.Log.LogWarning((object)"[MPUI] No right hand"); return; } _laserObj = new GameObject("MP_Laser"); _laserObj.transform.SetParent(((Component)this).transform); _laserLine = _laserObj.AddComponent(); _laserLine.startWidth = 0.003f; _laserLine.endWidth = 0.001f; _laserLine.positionCount = 2; _laserLine.useWorldSpace = true; Material val = new Material(Shader.Find("UI/Default") ?? Shader.Find("Sprites/Default")); val.color = new Color(0.3f, 0.6f, 1f, 0.7f); val.renderQueue = 4000; ((Renderer)_laserLine).material = val; _laserLine.startColor = new Color(0.3f, 0.6f, 1f, 0.7f); _laserLine.endColor = new Color(0.3f, 0.6f, 1f, 0.3f); ((Renderer)_laserLine).sortingOrder = 200; _laserDot = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)_laserDot).name = "MP_Dot"; _laserDot.transform.localScale = Vector3.one * 0.005f; _laserDot.transform.SetParent(((Component)this).transform); Collider component = _laserDot.GetComponent(); if (Object.op_Implicit((Object)(object)component)) { Object.Destroy((Object)(object)component); } Material val2 = new Material(Shader.Find("UI/Default") ?? Shader.Find("Sprites/Default")); val2.color = Color.white; val2.renderQueue = 4000; _laserDot.GetComponent().material = val2; _laserDot.SetActive(false); ((Renderer)_laserLine).enabled = false; if ((Object)(object)EventSystem.current == (Object)null) { GameObject val3 = new GameObject("MP_EventSystem"); val3.AddComponent(); val3.AddComponent(); } Plugin.Log.LogInfo((object)("[MPUI] Laser on " + ((Object)_rightHand).name)); } catch (Exception ex) { Plugin.Log.LogError((object)("[MPUI] Laser err: " + ex.Message)); } } private Transform FindHand() { try { MonoBehaviour[] array = Object.FindObjectsOfType(); foreach (MonoBehaviour val in array) { if (((object)val).GetType().Name == "OVRCameraRig") { object? obj = ((object)val).GetType().GetProperty("rightHandAnchor")?.GetValue(val); Transform val2 = (Transform)((obj is Transform) ? obj : null); if ((Object)(object)val2 != (Object)null) { return val2; } } } } catch { } try { BackpackControl val3 = Object.FindObjectOfType(); if ((Object)(object)val3?.rightHand != (Object)null) { return val3.rightHand.transform; } } catch { } try { Transform[] array2 = Object.FindObjectsOfType(); foreach (Transform val4 in array2) { string text = ((Object)val4).name.ToLower(); if (text.Contains("righthand") || text == "righthandanchor" || text.Contains("rightcontroller")) { return val4; } } } catch { } return null; } private void UpdateLaser() { //IL_0050: 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) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_0075: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0105: 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_0120: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_rightHand == (Object)null || (Object)(object)_laserLine == (Object)null) { return; } _trigWas = _trigDown; _trigDown = ReadTrigger(); bool flag = _trigDown && !_trigWas; Vector3 position = _rightHand.position; Vector3 forward = _rightHand.forward; bool flag2 = false; Vector3 val = position + forward * 5f; GameObject val2 = null; if ((Object)(object)_canvas != (Object)null) { RectTransform component = ((Component)_canvas).GetComponent(); Plane val3 = default(Plane); ((Plane)(ref val3))..ctor(((Transform)component).forward, ((Transform)component).position); Ray val4 = default(Ray); ((Ray)(ref val4))..ctor(position, forward); float num = default(float); if (((Plane)(ref val3)).Raycast(val4, ref num) && num > 0f && num < 5f) { val = position + forward * num; Vector3 val5 = ((Transform)component).InverseTransformPoint(val); Vector2 sizeDelta = component.sizeDelta; if (Mathf.Abs(val5.x) <= sizeDelta.x * 0.5f && Mathf.Abs(val5.y) <= sizeDelta.y * 0.5f) { flag2 = true; val2 = FindHitGraphic(val); } } } if (flag2) { ((Renderer)_laserLine).enabled = true; _laserLine.SetPosition(0, position); _laserLine.SetPosition(1, val); _laserDot.SetActive(true); _laserDot.transform.position = val + ((Transform)((Component)_canvas).GetComponent()).forward * 0.001f; DoHover(val2); if (flag && (Object)(object)val2 != (Object)null) { Button componentInParent = val2.GetComponentInParent