using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Xml.Linq; using Entanglement; using Entanglement.Compat; using Entanglement.Compat.Playermodels; using Entanglement.Data; using Entanglement.Exceptions; using Entanglement.Extensions; using Entanglement.Gamemodes; using Entanglement.Gamemodes.BuiltIn; using Entanglement.Managers; using Entanglement.Modularity; using Entanglement.Network; using Entanglement.Objects; using Entanglement.Patching; using Entanglement.Representation; using Entanglement.Sync; using Entanglement.UI; using Entanglement.Voice; using HarmonyLib; using Il2CppSystem; using MelonLoader; using MelonLoader.Preferences; using ModThatIsNotMod; using ModThatIsNotMod.BoneMenu; using PuppetMasta; using Steamworks; using StressLevelZero; using StressLevelZero.AI; using StressLevelZero.Arena; using StressLevelZero.Combat; using StressLevelZero.Data; using StressLevelZero.Interaction; using StressLevelZero.Player; using StressLevelZero.Pool; using StressLevelZero.Props; using StressLevelZero.Props.Weapons; using StressLevelZero.Rig; using StressLevelZero.SFX; using StressLevelZero.Utilities; using StressLevelZero.VRMK; using StressLevelZero.Zones; using TMPro; using UnhollowerBaseLib; using UnhollowerRuntimeLib; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: Guid("490e160d-251d-4ab4-a3bb-f473961ff8a1")] [assembly: AssemblyTitle("Entanglement Redux")] [assembly: AssemblyFileVersion("0.4.0")] [assembly: MelonInfo(typeof(EntanglementMod), "Entanglement Redux", "0.4.0", "willpsdk", null)] [assembly: MelonGame("Stress Level Zero", "BONEWORKS")] [assembly: MelonIncompatibleAssemblies(new string[] { "MultiplayerMod" })] [assembly: MelonPriority(-10000)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("0.4.0.0")] namespace Entanglement { public static class EntangleLogger { public static void Log(string txt, ConsoleColor txt_color = ConsoleColor.White) { ((MelonBase)EntanglementMod.Instance).LoggerInstance.Msg(txt_color, txt); } public static void Log(object obj, ConsoleColor txt_color = ConsoleColor.White) { ((MelonBase)EntanglementMod.Instance).LoggerInstance.Msg(txt_color, obj); } public static void Warn(string txt) { ((MelonBase)EntanglementMod.Instance).LoggerInstance.Warning(txt); } public static void Warn(object obj) { ((MelonBase)EntanglementMod.Instance).LoggerInstance.Warning(obj); } public static void Error(string txt) { ((MelonBase)EntanglementMod.Instance).LoggerInstance.Error(txt); } public static void Error(object obj) { ((MelonBase)EntanglementMod.Instance).LoggerInstance.Error(obj); } } public static class EntangleNotif { public static void PlayerJoin(string username) { Notifications.SendNotification(username + " has joined the server!", 4f); } public static void PlayerLeave(string username) { Notifications.SendNotification(username + " has left the server!", 4f); } public static void PlayerDisconnect(DisconnectReason reason) { Notifications.SendNotification($"You were disconnected for reason {reason}.", 4f); } public static void LobbyStarted() { Notifications.SendNotification("Lobby started!", 4f); } public static void JoinServer(string username) { Notifications.SendNotification("Joined " + username + "'s server!", 4f); } public static void LeftServer() { Notifications.SendNotification("You left the server.", 4f); } public static void InvalidSteam() { Notifications.SendNotification("Failed to initialize the Steam API! Continuing without Entanglement!\nMake sure Steam is running, you are logged in, and the game was launched through Steam.", 4f); } public static void GamemodeStarted(string modeName) { Notifications.SendNotification(modeName + " has started!", 4f); } public static void GamemodeEnded(string modeName) { Notifications.SendNotification(modeName + " round over!", 4f); } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct EntanglementVersion { public const byte versionMajor = 0; public const byte versionMinor = 4; public const short versionPatch = 0; public const byte minVersionMajorSupported = 0; public const byte minVersionMinorSupported = 4; } public class EntanglementMod : MelonMod { public static byte? sceneChange; public static Assembly entanglementAssembly; public static bool hasUnpatched; private static float lastUpdateRealtime; private const float SUSPEND_GAP_SECONDS = 3f; public static EntanglementMod Instance { get; protected set; } public static string VersionString { get; protected set; } static EntanglementMod() { sceneChange = null; hasUnpatched = false; lastUpdateRealtime = 0f; AppDomain.CurrentDomain.AssemblyResolve += (object sender, ResolveEventArgs args) => (new AssemblyName(args.Name).Name == "Steamworks.NET") ? Assembly.Load(EmbeddedResource.LoadFromAssembly(Assembly.GetExecutingAssembly(), "Entanglement.resources.Steamworks.NET.dll")) : null; } public override void OnApplicationStart() { entanglementAssembly = Assembly.GetExecutingAssembly(); Instance = this; VersionString = $"{(byte)0}.{(byte)4}.{(short)0}"; EntangleLogger.Log("Current Entanglement version is " + VersionString); EntangleLogger.Log($"Minimum supported Entanglement version is {(byte)0}.{(byte)4}.*"); VersionChecking.CheckModVersion((MelonMod)(object)this, "https://boneworks.thunderstore.io/package/Entanglement/Entanglement/"); PersistentData.Initialize(); GameSDK.LoadGameSDK(); EntangleLogger.Log("Entanglement Debug Build!", ConsoleColor.Blue); SteamIntegration.Initialize(); if (SteamIntegration.isInvalid) { EntangleNotif.InvalidSteam(); return; } Patcher.Initialize(); NetworkMessage.RegisterHandlersFromAssembly(entanglementAssembly); Client.StartClient(); CustomItemSync.Initialize(); PlayermodelSync.Initialize(); GamemodeHandler.Initialize(); PlayerRepresentation.LoadBundle(); LoadingScreen.LoadBundle(); EntanglementUI.CreateUI(); BanList.PullFromFile(); EntangleLogger.Log("Welcome to the Entanglement Redux Beta!", ConsoleColor.DarkYellow); } public override void OnApplicationLateStart() { if (SteamIntegration.isInvalid) { ((MelonBase)this).HarmonyInstance.UnpatchSelf(); hasUnpatched = true; } else { PlayerDeathManager.Initialize(); } } public override void OnUpdate() { if (SteamIntegration.isInvalid) { if (!hasUnpatched) { ((MelonBase)this).HarmonyInstance.UnpatchSelf(); hasUnpatched = true; } return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (lastUpdateRealtime > 0f && realtimeSinceStartup - lastUpdateRealtime > 3f) { EntangleLogger.Log($"App was suspended for {realtimeSinceStartup - lastUpdateRealtime:F1}s, draining the stale network backlog..."); Node.activeNode?.ClearMessageBuffer(); } lastUpdateRealtime = realtimeSinceStartup; TransformSyncBatcher.Flush(); VoiceManager.Tick(); ModuleHandler.Update(); if (Input.GetKeyDown((KeyCode)115)) { Server.StartServer(); } if (Input.GetKeyDown((KeyCode)107)) { Server.instance?.Shutdown(); } if (Input.GetKeyDown((KeyCode)114)) { if (PlayerRepresentation.debugRepresentation == null) { PlayerRepresentation.debugRepresentation = new PlayerRepresentation("Dummy", 0L); } else { PlayerRepresentation.debugRepresentation.CreateRagdoll(); } } StatsUI.UpdateUI(); EntanglementUI.UpdateUI(); PlayerDeathManager.CheckLethality(); PlayerRepresentation.SyncPlayerReps(); FileTransferManager.Tick(); GamemodeHandler.Tick(); } public override void OnFixedUpdate() { if (!SteamIntegration.isInvalid) { ModuleHandler.FixedUpdate(); PlayerRepresentation.UpdatePlayerReps(); } } public override void OnLateUpdate() { if (!SteamIntegration.isInvalid) { ModuleHandler.LateUpdate(); Client.instance?.Tick(); Server.instance?.Tick(); SteamIntegration.Tick(); } } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { if (SteamIntegration.isInvalid) { return; } Application.backgroundLoadingPriority = (ThreadPriority)1; QualitySettings.asyncUploadTimeSlice = 2; QualitySettings.asyncUploadBufferSize = 4; ModuleHandler.OnSceneWasInitialized(buildIndex, sceneName); SpawnableData.GetData(); PlayerScripts.GetPlayerScripts(); PlayerRepresentation.GetPlayerTransforms(); foreach (PlayerRepresentation value in PlayerRepresentation.representations.Values) { value.RecreateRepresentations(); } Client.instance.currentScene = (byte)buildIndex; if (!LevelChangeAnnouncer.ConsumeAnnounce(buildIndex)) { sceneChange = (byte)buildIndex; } if (SteamIntegration.hasLobby && !Node.isServer && Node.activeNode != null) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ClientReady, new EmptyMessageData()); if (networkMessage != null) { Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); } } SteamIntegration.targetScene = sceneName.ToLower(); SteamIntegration.UpdateActivity(); } public override void BONEWORKS_OnLoadingScreen() { if (!SteamIntegration.isInvalid) { Application.backgroundLoadingPriority = (ThreadPriority)4; QualitySettings.asyncUploadTimeSlice = 8; QualitySettings.asyncUploadBufferSize = 16; ModuleHandler.OnLoadingScreen(); LoadingScreen.OverrideScreen(); ObjectSync.OnCleanup(); ObjectSync.poolPairs.Clear(); SceneEventSync.OnSceneCleanup(); TransformSyncBatcher.Clear(); Server.instance?.replayedUsers.Clear(); FileTransferManager.Clear(); PlayerRepresentation.debugRepresentation = null; } } public override void OnApplicationQuit() { if (!SteamIntegration.isInvalid) { ModuleHandler.OnApplicationQuit(); Node.activeNode.Shutdown(); SteamIntegration.Shutdown(); } } } } namespace Entanglement.Voice { public enum VoiceMode : byte { Proximity, Global } public static class VoiceManager { private class VoicePlayer { public AudioSource source; public AudioClip clip; public int clipSamples; public long written; public long played; public int lastTimeSamples; public float lastReceiveTime; } private struct DelayedVoicePacket { public byte[] data; public int count; public float playAt; } public static bool micEnabled = true; public static VoiceMode mode = VoiceMode.Proximity; public static int proximityRange = 12; public static int outputVolume = 100; private const float baseVoiceGain = 2f; private static bool recording; private static uint sampleRate; private static readonly byte[] compressedBuffer = new byte[8192]; private static readonly byte[] receiveScratch = new byte[8192]; private static readonly byte[] decompressBuffer = new byte[65536]; private static float[] sampleBuffer = new float[32768]; private static readonly Dictionary chunkPool = new Dictionary(); public static float localVoiceTime = -10f; private const float speakingWindow = 0.3f; private static readonly Dictionary players = new Dictionary(); private static readonly HashSet mutedPlayers = new HashSet(); public static bool debugVoiceOnRep = false; private const long debugRepVoiceId = -1337L; private const float debugVoiceDelaySeconds = 10f; private static readonly Queue debugVoiceQueue = new Queue(); public static bool IsLocalSpeaking => micEnabled && Time.time - localVoiceTime < 0.3f; public static bool IsSpeaking(long userId) { VoicePlayer value; return players.TryGetValue(userId, out value) && Time.time - value.lastReceiveTime < 0.3f; } public static bool IsMuted(long userId) { return mutedPlayers.Contains(userId); } public static void SetMuted(long userId, bool muted) { if (muted) { mutedPlayers.Add(userId); if (players.TryGetValue(userId, out var value) && Object.op_Implicit((Object)(object)value.source)) { value.source.Stop(); value.played = value.written; } } else { mutedPlayers.Remove(userId); } } public static void Tick() { //IL_00cd: 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_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: 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_00fd: Unknown result type (might be due to invalid IL or missing references) bool flag = false; flag = debugVoiceOnRep && PlayerRepresentation.debugRepresentation != null; if (!SteamIntegration.hasLobby && !flag) { if (recording) { SteamUser.StopVoiceRecording(); recording = false; } if (players.Count > 0) { Reset(); } return; } if (sampleRate == 0) { sampleRate = SteamUser.GetVoiceOptimalSampleRate(); } if (micEnabled && !recording) { SteamUser.StartVoiceRecording(); recording = true; } else if (!micEnabled && recording) { SteamUser.StopVoiceRecording(); recording = false; } if (recording) { uint num = default(uint); EVoiceResult availableVoice = SteamUser.GetAvailableVoice(ref num); if ((int)availableVoice == 0 && num != 0) { uint num2 = default(uint); availableVoice = SteamUser.GetVoice(true, compressedBuffer, (uint)compressedBuffer.Length, ref num2); if ((int)availableVoice == 0 && num2 != 0) { if (SteamIntegration.hasLobby) { VoiceDataMessageHandler.SendVoice(compressedBuffer, (int)num2); } localVoiceTime = Time.time; QueueDebugVoice(compressedBuffer, (int)num2); } } } ProcessDebugVoice(); foreach (VoicePlayer value in players.Values) { if (Object.op_Implicit((Object)(object)value.source) && value.source.isPlaying) { int timeSamples = value.source.timeSamples; if (timeSamples < value.lastTimeSamples) { value.played += value.clipSamples - value.lastTimeSamples + timeSamples; } else { value.played += timeSamples - value.lastTimeSamples; } value.lastTimeSamples = timeSamples; if (value.played >= value.written) { value.source.Stop(); } } } } public static void ReceiveVoice(long speakerId, byte[] data, int offset, int count) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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) if (count <= 0 || count > receiveScratch.Length || mutedPlayers.Contains(speakerId)) { return; } if (sampleRate == 0) { sampleRate = SteamUser.GetVoiceOptimalSampleRate(); } Buffer.BlockCopy(data, offset, receiveScratch, 0, count); uint num = default(uint); EVoiceResult val = SteamUser.DecompressVoice(receiveScratch, (uint)count, decompressBuffer, (uint)decompressBuffer.Length, ref num, sampleRate); if ((int)val != 0 || num == 0) { return; } VoicePlayer player = GetPlayer(speakerId); if (player != null) { int num2 = (int)num / 2; if (sampleBuffer.Length < num2) { sampleBuffer = new float[num2]; } float num3 = (float)outputVolume / 100f * 2f; for (int i = 0; i < num2; i++) { short num4 = (short)(decompressBuffer[i * 2] | (decompressBuffer[i * 2 + 1] << 8)); sampleBuffer[i] = Mathf.Clamp((float)num4 / 32768f * num3, -1f, 1f); } player.lastReceiveTime = Time.time; WriteSamples(player, num2); } } private static void WriteSamples(VoicePlayer player, int count) { int num = (int)(player.written % player.clipSamples); int num2 = Math.Min(count, player.clipSamples - num); float[] chunk = GetChunk(num2); Array.Copy(sampleBuffer, 0, chunk, 0, num2); player.clip.SetData(Il2CppStructArray.op_Implicit(chunk), num); int num3 = count - num2; if (num3 > 0) { float[] chunk2 = GetChunk(num3); Array.Copy(sampleBuffer, num2, chunk2, 0, num3); player.clip.SetData(Il2CppStructArray.op_Implicit(chunk2), 0); } player.written += count; if (!player.source.isPlaying && player.written - player.played >= sampleRate / 10) { int num4 = (int)(player.played % player.clipSamples); player.source.timeSamples = num4; player.lastTimeSamples = num4; player.source.Play(); } } private static float[] GetChunk(int size) { if (!chunkPool.TryGetValue(size, out var value)) { value = new float[size]; chunkPool[size] = value; } return value; } private static VoicePlayer GetPlayer(long speakerId) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown players.TryGetValue(speakerId, out var value); if (value != null && Object.op_Implicit((Object)(object)value.source)) { return value; } PlayerRepresentation playerRepresentation = ResolveRep(speakerId); if (playerRepresentation == null || (Object)(object)playerRepresentation.repRoot == (Object)null) { return null; } GameObject val = new GameObject($"Voice {speakerId}"); Transform val2 = (Object.op_Implicit((Object)(object)playerRepresentation.repTransforms[0]) ? playerRepresentation.repTransforms[0] : playerRepresentation.repRoot); val.transform.SetParent(val2, false); AudioSource val3 = val.AddComponent(); val3.loop = true; val3.playOnAwake = false; val3.rolloffMode = (AudioRolloffMode)1; val3.dopplerLevel = 0f; int num = (int)sampleRate; AudioClip clip = (val3.clip = AudioClip.Create($"VoiceClip {speakerId}", num, 1, (int)sampleRate, false)); value = new VoicePlayer { source = val3, clip = clip, clipSamples = num }; players[speakerId] = value; ApplySettingsTo(value); return value; } private static PlayerRepresentation ResolveRep(long speakerId) { if (speakerId == -1337) { return PlayerRepresentation.debugRepresentation; } PlayerRepresentation.representations.TryGetValue(speakerId, out var value); return value; } private static void QueueDebugVoice(byte[] compressed, int written) { if (debugVoiceOnRep && PlayerRepresentation.debugRepresentation != null && written > 0) { byte[] array = new byte[written]; Buffer.BlockCopy(compressed, 0, array, 0, written); debugVoiceQueue.Enqueue(new DelayedVoicePacket { data = array, count = written, playAt = Time.time + 10f }); } } private static void ProcessDebugVoice() { if (!debugVoiceOnRep || PlayerRepresentation.debugRepresentation == null) { if (debugVoiceQueue.Count > 0) { debugVoiceQueue.Clear(); } } else { while (debugVoiceQueue.Count > 0 && Time.time >= debugVoiceQueue.Peek().playAt) { DelayedVoicePacket delayedVoicePacket = debugVoiceQueue.Dequeue(); ReceiveVoice(-1337L, delayedVoicePacket.data, 0, delayedVoicePacket.count); } } } public static void ApplySettings() { foreach (VoicePlayer value in players.Values) { ApplySettingsTo(value); } } private static void ApplySettingsTo(VoicePlayer player) { if (Object.op_Implicit((Object)(object)player.source)) { player.source.volume = 1f; if (mode == VoiceMode.Global) { player.source.spatialBlend = 0f; return; } player.source.spatialBlend = 1f; player.source.minDistance = 1f; player.source.maxDistance = Mathf.Max(2f, (float)proximityRange); } } public static void Reset() { foreach (VoicePlayer value in players.Values) { if (Object.op_Implicit((Object)(object)value.source)) { Object.Destroy((Object)(object)((Component)value.source).gameObject); } } players.Clear(); } } } namespace Entanglement.Gamemodes { public enum GamemodeEventType : byte { ReportPlayerKilled = 0, RoundStart = 10, RoundEnd = 11, PlayerKilled = 12, PlayerScored = 13, PlayerEliminated = 14, Custom = 15 } public struct GamemodeState { public string activeModeId; public bool roundActive; public float roundTimeRemaining; } public abstract class EntanglementGamemode { public abstract string Id { get; } public abstract string DisplayName { get; } public virtual Color MenuColor => Color.white; public virtual bool UsesTeams => false; public virtual int TeamCount => 2; public virtual bool EliminationMode => false; public virtual float DefaultRoundSeconds => 300f; public virtual Color GetTeamColor(byte team) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return Color.white; } public virtual void OnModeStart() { } public virtual void OnModeStop() { } public virtual void OnRoundStart() { } public virtual void OnRoundEnd() { } public virtual void HostTick(float deltaTime) { } public virtual void OnPlayerKilled(long killerId, long victimId) { } public virtual void OnPlayerJoined(long userId) { } public virtual void OnPlayerLeft(long userId) { } public virtual void OnStateApplied(GamemodeState state) { } public virtual void OnEventReceived(GamemodeEventType type, long a, long b, int value, string message) { } protected void SetScore(long userId, int score) { GamemodeHandler.SetScore(userId, score); } protected void AddScore(long userId, int delta) { GamemodeHandler.AddScore(userId, delta); } protected void SetTeam(long userId, byte team) { GamemodeHandler.SetTeam(userId, team); } protected void BroadcastEvent(GamemodeEventType type, long a = 0L, long b = 0L, int value = 0, string message = null) { GamemodeHandler.BroadcastEvent(type, a, b, value, message); } protected void StartRound() { GamemodeHandler.StartRound(); } protected void EndRound() { GamemodeHandler.EndRoundInternal(); } } public static class GamemodeHandler { public static readonly Dictionary registeredModes = new Dictionary(); public static float roundDurationOverrideSeconds = 0f; public const int minPlayersToStart = 2; public static readonly Dictionary scores = new Dictionary(); public static readonly Dictionary teams = new Dictionary(); public static readonly HashSet eliminated = new HashSet(); private static long lastAttacker; private static float lastAttackTime = -10f; private const float attackMemorySeconds = 8f; private static float stateBroadcastTimer; private const float stateBroadcastInterval = 2f; public static EntanglementGamemode ActiveMode { get; private set; } public static bool RoundActive { get; private set; } public static float RoundTimeRemaining { get; private set; } public static int PlayerCount => (Node.activeNode?.connectedUsers.Count ?? 0) + 1; public static void RegisterGamemode(EntanglementGamemode mode) { if (mode != null && !string.IsNullOrEmpty(mode.Id)) { registeredModes[mode.Id] = mode; } } public static void Initialize() { RegisterGamemode(new DeathmatchGamemode()); RegisterGamemode(new TeamBattleGamemode()); RegisterGamemode(new LastManStandingGamemode()); PlayerAttackMessageHandler.OnDamageReceived += OnLocalDamageReceived; PlayerDeathManager.OnLocalPlayerDied += OnLocalPlayerDied; } public static bool TryStartMatch(string id, out string reason) { reason = ""; if (!Node.isServer) { reason = "Only the host can start a gamemode."; return false; } if (RoundActive) { reason = "A round is already running. Force stop it first."; return false; } if (PlayerCount < 2) { reason = $"Need at least {2} players to start."; return false; } if (!registeredModes.ContainsKey(id)) { reason = "That gamemode isn't registered."; return false; } StartMode(id); StartRound(); return true; } public static bool StartMode(string id) { if (!Node.isServer) { return false; } if (!registeredModes.TryGetValue(id, out var value)) { return false; } ActiveMode?.OnModeStop(); scores.Clear(); teams.Clear(); eliminated.Clear(); RoundActive = false; ActiveMode = value; ActiveMode.OnModeStart(); BroadcastState(); EntangleLogger.Log("[Gamemode] Started '" + value.DisplayName + "'"); return true; } public static void StopMode() { if (Node.isServer && ActiveMode != null) { if (RoundActive) { RoundActive = false; ActiveMode.OnRoundEnd(); BroadcastEvent(GamemodeEventType.RoundEnd, 0L, 0L); } ActiveMode.OnModeStop(); EntangleLogger.Log("[Gamemode] Stopped '" + ActiveMode.DisplayName + "'"); ActiveMode = null; scores.Clear(); teams.Clear(); eliminated.Clear(); BroadcastState(); } } public static void StartRound() { if (Node.isServer && ActiveMode != null) { RoundActive = true; RoundTimeRemaining = ((roundDurationOverrideSeconds > 0f) ? roundDurationOverrideSeconds : ActiveMode.DefaultRoundSeconds); eliminated.Clear(); ActiveMode.OnRoundStart(); BroadcastEvent(GamemodeEventType.RoundStart, 0L, 0L); BroadcastState(); } } internal static void EndRoundInternal() { if (Node.isServer && ActiveMode != null && RoundActive) { RoundActive = false; ActiveMode.OnRoundEnd(); eliminated.Clear(); BroadcastEvent(GamemodeEventType.RoundEnd, 0L, 0L); BroadcastState(); } } public static void SetRoundTimeRemaining(float seconds) { if (Node.isServer) { RoundTimeRemaining = Mathf.Max(0f, seconds); BroadcastState(); } } public static void AddRoundTime(float deltaSeconds) { SetRoundTimeRemaining(RoundTimeRemaining + deltaSeconds); } public static void SetRoundDuration(float seconds) { if (Node.isServer) { roundDurationOverrideSeconds = Mathf.Max(0f, seconds); } } public static void SetScore(long userId, int score) { if (Node.isServer) { scores[userId] = score; BroadcastEvent(GamemodeEventType.PlayerScored, userId, 0L, score); BroadcastState(); } } public static void AddScore(long userId, int delta) { scores.TryGetValue(userId, out var value); SetScore(userId, value + delta); } public static bool ShouldBlockDamage(long attackerId) { if (ActiveMode == null || !ActiveMode.UsesTeams) { return false; } long currentUserId = SteamIntegration.currentUserId; if (!teams.TryGetValue(attackerId, out var value)) { return false; } if (!teams.TryGetValue(currentUserId, out var value2)) { return false; } return value == value2; } public static void SetTeam(long userId, byte team) { if (Node.isServer) { teams[userId] = team; BroadcastState(); } } public static void BroadcastEvent(GamemodeEventType type, long a = 0L, long b = 0L, int value = 0, string message = null) { if (Node.isServer) { GamemodeEventData data = new GamemodeEventData { type = type, a = a, b = b, value = value, message = (message ?? "") }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.GamemodeEvent, data); if (networkMessage != null) { Node.activeNode?.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); } HandleEventLocally(type, a, b, value, message ?? ""); } } private static void HandleEventLocally(GamemodeEventType type, long a, long b, int value, string message) { switch (type) { case GamemodeEventType.RoundStart: EntangleNotif.GamemodeStarted(ActiveMode?.DisplayName ?? "Gamemode"); break; case GamemodeEventType.RoundEnd: EntangleNotif.GamemodeEnded(ActiveMode?.DisplayName ?? "Gamemode"); break; } ActiveMode?.OnEventReceived(type, a, b, value, message); } public static void Tick() { if (!SteamIntegration.hasLobby || !Node.isServer || ActiveMode == null) { return; } ActiveMode.HostTick(Time.deltaTime); if (RoundActive) { RoundTimeRemaining -= Time.deltaTime; if (RoundTimeRemaining <= 0f) { EndRoundInternal(); } } stateBroadcastTimer += Time.deltaTime; if (stateBroadcastTimer >= 2f) { stateBroadcastTimer = 0f; BroadcastState(); } } private static void BroadcastState() { if (Node.isServer) { GamemodeStateData data = new GamemodeStateData { activeModeId = (ActiveMode?.Id ?? ""), roundActive = RoundActive, roundTimeRemaining = RoundTimeRemaining, scores = new Dictionary(scores), teams = new Dictionary(teams), eliminated = new List(eliminated) }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.GamemodeState, data); if (networkMessage != null) { Node.activeNode?.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); } ApplyState(data); } } internal static void ApplyState(GamemodeStateData data) { if (!Node.isServer) { scores.Clear(); foreach (KeyValuePair score in data.scores) { scores[score.Key] = score.Value; } teams.Clear(); foreach (KeyValuePair team in data.teams) { teams[team.Key] = team.Value; } eliminated.Clear(); foreach (long item in data.eliminated) { eliminated.Add(item); } RoundActive = data.roundActive; RoundTimeRemaining = data.roundTimeRemaining; if (string.IsNullOrEmpty(data.activeModeId)) { ActiveMode = null; } else if (ActiveMode == null || ActiveMode.Id != data.activeModeId) { registeredModes.TryGetValue(data.activeModeId, out var value); ActiveMode = value; } } ApplyVisuals(); ActiveMode?.OnStateApplied(new GamemodeState { activeModeId = data.activeModeId, roundActive = data.roundActive, roundTimeRemaining = data.roundTimeRemaining }); } private static void ApplyVisuals() { //IL_008b: 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) foreach (KeyValuePair representation in PlayerRepresentation.representations) { PlayerRepresentation value = representation.Value; if (value != null) { value.SetEliminated(eliminated.Contains(representation.Key)); if (ActiveMode != null && ActiveMode.UsesTeams && teams.TryGetValue(representation.Key, out var value2)) { value.SetNameColor(ActiveMode.GetTeamColor(value2)); } else { value.SetNameColor(Color.white); } } } } internal static void ApplyEvent(long sender, GamemodeEventData data) { if (Node.isServer && data.type == GamemodeEventType.ReportPlayerKilled) { ProcessDeath(data.a, sender); } else if (!Node.isServer) { HandleEventLocally(data.type, data.a, data.b, data.value, data.message); } } private static void ProcessDeath(long killerId, long victimId) { if (ActiveMode != null) { ActiveMode.OnPlayerKilled(killerId, victimId); BroadcastEvent(GamemodeEventType.PlayerKilled, killerId, victimId); if (RoundActive && ActiveMode.EliminationMode && eliminated.Add(victimId)) { BroadcastEvent(GamemodeEventType.PlayerEliminated, victimId, 0L); BroadcastState(); } } } private static void OnLocalDamageReceived(long attacker, float damage) { lastAttacker = attacker; lastAttackTime = Time.time; } private static void OnLocalPlayerDied() { if (ActiveMode == null) { return; } long num = ((Time.time - lastAttackTime <= 8f) ? lastAttacker : 0); long currentUserId = SteamIntegration.currentUserId; if (Node.isServer) { ProcessDeath(num, currentUserId); return; } GamemodeEventData data = new GamemodeEventData { type = GamemodeEventType.ReportPlayerKilled, a = num, b = 0L, value = 0, message = "" }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.GamemodeEvent, data); if (networkMessage != null) { Node.activeNode?.SendMessage(SteamIntegration.lobbyOwnerId, NetworkChannel.Reliable, networkMessage.GetBytes()); } } public static void Clear() { ActiveMode = null; RoundActive = false; scores.Clear(); teams.Clear(); eliminated.Clear(); } } } namespace Entanglement.Gamemodes.BuiltIn { public class DeathmatchGamemode : EntanglementGamemode { public override string Id => "deathmatch"; public override string DisplayName => "Deathmatch"; public override Color MenuColor => Color.red; public override float DefaultRoundSeconds => 600f; public override void OnPlayerKilled(long killerId, long victimId) { if (killerId != victimId && killerId != 0) { AddScore(killerId, 1); } } public override void OnRoundEnd() { EntangleLogger.Log("[Deathmatch] Round over"); } } public class TeamBattleGamemode : EntanglementGamemode { public const int scoreToWin = 25; private static readonly Color[] teamColors = (Color[])(object)new Color[4] { new Color(1f, 0.35f, 0.3f), new Color(0.35f, 0.55f, 1f), new Color(0.4f, 1f, 0.4f), new Color(1f, 0.9f, 0.3f) }; private byte nextTeam; public override string Id => "team_battle"; public override string DisplayName => "Team Battle"; public override Color MenuColor => Color.blue; public override bool UsesTeams => true; public override int TeamCount => 2; public override float DefaultRoundSeconds => 600f; public override Color GetTeamColor(byte team) { //IL_0017: 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) return (team < teamColors.Length) ? teamColors[team] : Color.white; } public override void OnModeStart() { nextTeam = 0; SetTeam(SteamIntegration.currentUserId, nextTeam); nextTeam = (byte)((nextTeam + 1) % TeamCount); if (Node.activeNode == null) { return; } foreach (long connectedUser in Node.activeNode.connectedUsers) { SetTeam(connectedUser, nextTeam); nextTeam = (byte)((nextTeam + 1) % TeamCount); } } public override void OnPlayerJoined(long userId) { SetTeam(userId, nextTeam); nextTeam = (byte)((nextTeam + 1) % TeamCount); } public override void OnPlayerKilled(long killerId, long victimId) { if (killerId == victimId || killerId == 0 || !GamemodeHandler.teams.TryGetValue(killerId, out var value)) { return; } int num = 0; foreach (KeyValuePair score in GamemodeHandler.scores) { if (GamemodeHandler.teams.TryGetValue(score.Key, out var value2) && value2 == value) { num += score.Value; } } AddScore(killerId, 1); if (num + 1 >= 25) { EndRound(); } } public override void OnRoundEnd() { EntangleLogger.Log("[Team Battle] Round over"); } } public class LastManStandingGamemode : EntanglementGamemode { private const int survivalBonus = 5; public override string Id => "last_man_standing"; public override string DisplayName => "Last Man Standing"; public override Color MenuColor => new Color(1f, 0.55f, 0f); public override float DefaultRoundSeconds => 300f; public override bool EliminationMode => true; private static IEnumerable AllPlayers() { yield return SteamIntegration.currentUserId; if (Node.activeNode == null) { yield break; } foreach (long connectedUser in Node.activeNode.connectedUsers) { yield return connectedUser; } } public override void OnPlayerKilled(long killerId, long victimId) { if (killerId != victimId && killerId != 0) { AddScore(killerId, 1); } int num = 0; int num2 = 0; long userId = 0L; foreach (long item in AllPlayers()) { num++; if (item != victimId && !GamemodeHandler.eliminated.Contains(item)) { num2++; userId = item; } } if (num > 1 && num2 <= 1) { if (num2 == 1) { AddScore(userId, 5); } EndRound(); } } public override void OnRoundEnd() { EntangleLogger.Log("[Last Man Standing] Round over"); } } } namespace Entanglement.UI { public static class BanlistUI { public static MenuCategory banCategory; private const string refreshText = "Refresh"; public static void CreateUI(MenuCategory category) { //IL_0007: 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) banCategory = category.CreateSubCategory("Banned Users", Color.white); banCategory.CreateFunctionElement("Refresh", Color.white, (Action)Refresh); } public static void ClearPlayers() { List list = new List(); foreach (MenuElement element in banCategory.elements) { if (element.displayText != "Refresh") { list.Add(element.displayText); } } foreach (string item in list) { banCategory.RemoveElement(item); } } public static void Refresh() { ClearPlayers(); foreach (Tuple bannedUser in BanList.bannedUsers) { AddUser(bannedUser.Item1, bannedUser.Item2); } UpdateMenu(); } public static void UpdateMenu() { MenuManager.OpenCategory(banCategory); } public static void AddUser(long userId, string userName) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) MenuCategory val = banCategory.CreateSubCategory(userName, Color.white); val.CreateFunctionElement("Unban", Color.red, (Action)delegate { BanList.UnbanUser(userId, userName); Refresh(); }); } } public static class ClientUI { public static void CreateUI(MenuCategory category) { //IL_0007: 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_0029: Unknown result type (might be due to invalid IL or missing references) MenuCategory val = category.CreateSubCategory("Client Menu", Color.white); MenuCategory val2 = val.CreateSubCategory("Client Settings", Color.white); val2.CreateBoolElement("NameTags", Color.white, true, (Action)delegate(bool value) { Client.nameTagsVisible = value; }); } } public static class DebugUI { public static void CreateUI(MenuCategory category) { //IL_0007: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) MenuCategory val = category.CreateSubCategory("Debug", Color.red); val.CreateFunctionElement("Create Debug Representation", Color.white, (Action)delegate { PlayerRepresentation.debugRepresentation?.DeleteRepresentations(); PlayerRepresentation.debugRepresentation = new PlayerRepresentation("Dummy", 0L); }); val.CreateFunctionElement("Remove Debug Representation", Color.white, (Action)delegate { PlayerRepresentation.debugRepresentation?.DeleteRepresentations(); PlayerRepresentation.debugRepresentation = null; VoiceManager.debugVoiceOnRep = false; }); val.CreateBoolElement("Voice Chat Debug On Rep", Color.white, false, (Action)delegate(bool value) { if (value && PlayerRepresentation.debugRepresentation == null) { Notifications.SendNotification("Spawn a debug representation first.", 3f); VoiceManager.debugVoiceOnRep = false; } else { VoiceManager.debugVoiceOnRep = value; if (value) { Notifications.SendNotification("Speak - you'll hear it back from the dummy in 10s.", 4f); } } }); } } public static class LoadingScreen { public static AssetBundle assetBundle; public static void LoadBundle() { assetBundle = EmebeddedAssetBundle.LoadFromAssembly(EntanglementMod.entanglementAssembly, "Entanglement.resources.logo.eres"); } public static void OverrideScreen() { Texture2D texture = assetBundle.LoadAsset("entanglement.png"); GameObject val = GameObject.Find("Canvas/RawImage (1)"); val.GetComponent().texture = (Texture)(object)texture; } } public static class LobbiesUI { private static MenuCategory lobbiesCategory; private static CallResult lobbyListResult; private const string refreshText = "Refresh"; public static void CreateUI(MenuCategory category) { //IL_0007: 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) lobbiesCategory = category.CreateSubCategory("Public Lobbies", Color.white); lobbiesCategory.CreateFunctionElement("Refresh", Color.white, (Action)Refresh); lobbyListResult = CallResult.Create((APIDispatchDelegate)OnSteamLobbySearch); } public static void Refresh() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) ClearMenuItems(); SteamMatchmaking.AddRequestLobbyListStringFilter("entanglement", "true", (ELobbyComparison)0); SteamMatchmaking.AddRequestLobbyListDistanceFilter((ELobbyDistanceFilter)3); lobbyListResult.Set(SteamMatchmaking.RequestLobbyList(), (APIDispatchDelegate)null); UpdateMenu(); } public static void OnSteamLobbySearch(LobbyMatchList_t result, bool bIOFailure) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //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_0056: 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) if (bIOFailure) { EntangleLogger.Log("Failed to search for Public Lobbies!"); return; } int nLobbiesMatching = (int)result.m_nLobbiesMatching; EntangleLogger.Log(string.Format("Searched for {0} Public Lobb{1}.", nLobbiesMatching, (nLobbiesMatching == 1) ? "y" : "ies")); for (int i = 0; i < nLobbiesMatching; i++) { CSteamID lobbyByIndex = SteamMatchmaking.GetLobbyByIndex(i); EntangleLogger.Log($"Found Lobby with id {lobbyByIndex.m_SteamID}."); AddLobby(lobbyByIndex); } } public static void ClearMenuItems() { List list = new List(); foreach (MenuElement element in lobbiesCategory.elements) { if (element.displayText != "Refresh") { list.Add(element.displayText); } } foreach (string item in list) { lobbiesCategory.RemoveElement(item); } } public static void AddLobby(CSteamID lobbyId) { //IL_0006: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) EntangleLogger.Log($"Trying to add lobby with id {lobbyId.m_SteamID}."); string text = SteamMatchmaking.GetLobbyData(lobbyId, "host_name"); string lobbyData = SteamMatchmaking.GetLobbyData(lobbyId, "scene"); if (string.IsNullOrEmpty(text)) { text = "Unknown"; } int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(lobbyId); int lobbyMemberLimit = SteamMatchmaking.GetLobbyMemberLimit(lobbyId); string text2 = $"{text}'s Game ({numLobbyMembers}/{lobbyMemberLimit})"; if (!string.IsNullOrEmpty(lobbyData)) { text2 = text2 + " - " + lobbyData; } CreateLobbyItem(text2, lobbyId); } public static void CreateLobbyItem(string name, CSteamID lobbyId) { //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_0014: Unknown result type (might be due to invalid IL or missing references) lobbiesCategory.CreateFunctionElement(name, Color.white, (Action)delegate { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (SteamIntegration.hasLobby) { EntangleLogger.Error("Already in a server!"); } else { Client.instance.JoinLobby(lobbyId); } }); UpdateMenu(); } public static void UpdateMenu() { MenuManager.OpenCategory(lobbiesCategory); } } public static class ServerUI { private static MenuCategory playersCategory; private const string refreshText = "Refresh"; public static void CreateUI(MenuCategory category) { //IL_0007: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_00d8: 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_0122: 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_0189: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) MenuCategory val = category.CreateSubCategory("Server Menu", Color.white); val.CreateFunctionElement("Start Server", Color.white, (Action)delegate { Server.StartServer(); }); val.CreateFunctionElement("Stop Server", Color.white, (Action)delegate { if (Server.instance != null) { Server.instance.Shutdown(); } }); val.CreateFunctionElement("Disconnect", Color.white, (Action)delegate { if (Node.activeNode is Client client) { client.DisconnectFromServer(); } }); val.CreateFunctionElement("Invite Friends", Color.white, (Action)delegate { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (SteamIntegration.hasLobby) { SteamFriends.ActivateGameOverlayInviteDialog(SteamIntegration.lobby); } else { EntangleLogger.Error("You aren't in a server!"); } }); MenuCategory val2 = val.CreateSubCategory("Server Settings", Color.white); val2.CreateIntElement("Max Players", Color.white, 8, (Action)delegate(int value) { Server.maxPlayers = (byte)value; Server.instance?.UpdateLobbyConfig(); }, 1, 1, 250, true); val2.CreateBoolElement("Locked", Color.white, false, (Action)delegate(bool value) { Server.isLocked = value; Server.instance?.UpdateLobbyConfig(); }); val2.CreateEnumElement("Visibility", Color.white, (Enum)ServerVisibility.Private, (Action)delegate(Enum value) { if (value is ServerVisibility visibility) { Server.visibility = visibility; Server.instance?.UpdateLobbyConfig(); } }); playersCategory = val.CreateSubCategory("Players", Color.white); playersCategory.CreateFunctionElement("Refresh", Color.white, (Action)Refresh); } public static void ClearPlayers() { List list = new List(); foreach (MenuElement element in playersCategory.elements) { if (element.displayText != "Refresh") { list.Add(element.displayText); } } foreach (string item in list) { playersCategory.RemoveElement(item); } } public static void Refresh() { //IL_001c: 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_0032: Unknown result type (might be due to invalid IL or missing references) ClearPlayers(); if (!SteamIntegration.hasLobby) { UpdateMenu(); return; } int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(SteamIntegration.lobby); for (int i = 0; i < numLobbyMembers; i++) { long steamID = (long)SteamMatchmaking.GetLobbyMemberByIndex(SteamIntegration.lobby, i).m_SteamID; if (steamID != SteamIntegration.currentUserId) { AddUser(steamID, SteamIntegration.GetUserName(steamID)); } } UpdateMenu(); } public static void UpdateMenu() { MenuManager.OpenCategory(playersCategory); } public static void AddUser(long userId, string userName) { //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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) string playerName = userName; Color val = Color.white; if (userId == SteamIntegration.lobbyOwnerId) { playerName += " (Host)"; val = Color.yellow; } MenuCategory val2 = playersCategory.CreateSubCategory(playerName, val); if (SteamIntegration.isHost) { val2.CreateFunctionElement("Kick", Color.red, (Action)delegate { if (SteamIntegration.isHost) { Server.instance?.KickUser(userId, playerName); Refresh(); } }); val2.CreateFunctionElement("Ban", Color.red, (Action)delegate { if (SteamIntegration.isHost) { BanList.BanUser(userId, userName); Server.instance.KickUser(userId, playerName, DisconnectReason.Banned); Refresh(); } }); val2.CreateFunctionElement("Teleport To", Color.yellow, (Action)delegate { Server.instance?.TeleportTo(userId); }); } val2.CreateFunctionElement("View Steam Profile", Color.white, (Action)delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) SteamFriends.ActivateGameOverlayToUser("steamid", new CSteamID((ulong)userId)); }); } } public static class StatsUI { public static IntElement downElem; public static IntElement upElem; public static void CreateUI(MenuCategory category) { //IL_0007: 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_0037: Unknown result type (might be due to invalid IL or missing references) MenuCategory val = category.CreateSubCategory("Net Stats", Color.white); val.CreateIntElement("Bytes Down", Color.white, 0, (Action)null, 1, int.MinValue, int.MaxValue, false); val.CreateIntElement("Bytes Up", Color.white, 0, (Action)null, 1, int.MinValue, int.MaxValue, false); MenuElement obj = val.elements[0]; downElem = (IntElement)(object)((obj is IntElement) ? obj : null); MenuElement obj2 = val.elements[1]; upElem = (IntElement)(object)((obj2 is IntElement) ? obj2 : null); } public static void UpdateUI() { ((GenericElement)(object)downElem).SetValue((int)Node.activeNode.recievedByteCount); Node.activeNode.recievedByteCount = 0u; ((GenericElement)(object)upElem).SetValue((int)Node.activeNode.sentByteCount); Node.activeNode.sentByteCount = 0u; } } public static class EntanglementUI { private static MenuCategory rootCategory; private static MenuElement suicideElement; private static bool lastInServer; public static void CreateUI() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) rootCategory = MenuManager.CreateCategory("Entanglement Redux", Color.white); ServerUI.CreateUI(rootCategory); ClientUI.CreateUI(rootCategory); BanlistUI.CreateUI(rootCategory); LobbiesUI.CreateUI(rootCategory); VoiceUI.CreateUI(rootCategory); SyncUI.CreateUI(rootCategory); GamemodeUI.CreateUI(rootCategory); StatsUI.CreateUI(rootCategory); DebugUI.CreateUI(rootCategory); rootCategory.CreateFunctionElement("Suicide", Color.red, (Action)Suicide); suicideElement = rootCategory.elements[rootCategory.elements.Count - 1]; } public static void UpdateUI() { bool hasLobby = SteamIntegration.hasLobby; if (hasLobby != lastInServer) { lastInServer = hasLobby; MoveSuicideButton(hasLobby); } } private static void MoveSuicideButton(bool toTop) { if (rootCategory != null && suicideElement != null) { rootCategory.elements.Remove(suicideElement); if (toTop) { rootCategory.elements.Insert(0, suicideElement); } else { rootCategory.elements.Add(suicideElement); } if (GetActiveCategory() == rootCategory) { MenuManager.OpenCategory(rootCategory); } } } private static MenuCategory GetActiveCategory() { object? obj = typeof(MenuManager).GetField("activeCategory", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); return (MenuCategory)((obj is MenuCategory) ? obj : null); } private static void Suicide() { if (!SteamIntegration.hasLobby) { Notifications.SendNotification("You need to be in a server to do that.", 3f); } else { PlayerDeathManager.Suicide(); } } } public static class VoiceUI { private static MenuCategory muteCategory; private const string refreshText = "Refresh"; public static void CreateUI(MenuCategory category) { //IL_0007: 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_0049: 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_00b6: 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_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) MenuCategory val = category.CreateSubCategory("Voice Settings", Color.cyan); val.CreateBoolElement("Voice Chat", Color.white, true, (Action)delegate(bool value) { VoiceManager.micEnabled = value; }); val.CreateEnumElement("Mode", Color.white, (Enum)VoiceMode.Proximity, (Action)delegate(Enum value) { if (value is VoiceMode) { VoiceManager.mode = (VoiceMode)(object)value; VoiceManager.ApplySettings(); } }); val.CreateIntElement("Proximity Range", Color.white, 12, (Action)delegate(int value) { VoiceManager.proximityRange = value; VoiceManager.ApplySettings(); }, 2, 2, 100, true); val.CreateIntElement("Volume %", Color.white, 100, (Action)delegate(int value) { VoiceManager.outputVolume = value; VoiceManager.ApplySettings(); }, 10, 0, 200, true); muteCategory = val.CreateSubCategory("Mute Players", Color.red); muteCategory.CreateFunctionElement("Refresh", Color.white, (Action)RefreshMuteList); val.CreateFunctionElement("How to change mic", Color.yellow, (Action)delegate { Notifications.SendNotification("Voice uses your Steam mic.\nChange it in Steam: Settings > Voice > Voice Input Device.\nIn VR, open the Steam overlay (not SteamVR) to reach Steam Settings.", 10f); }); } private static void ClearMuteList() { List list = new List(); foreach (MenuElement element in muteCategory.elements) { if (element.displayText != "Refresh") { list.Add(element.displayText); } } foreach (string item in list) { muteCategory.RemoveElement(item); } } private static void RefreshMuteList() { //IL_0014: 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_0031: 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) ClearMuteList(); if (SteamIntegration.hasLobby) { int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(SteamIntegration.lobby); for (int i = 0; i < numLobbyMembers; i++) { long userId = (long)SteamMatchmaking.GetLobbyMemberByIndex(SteamIntegration.lobby, i).m_SteamID; if (userId != SteamIntegration.currentUserId) { muteCategory.CreateBoolElement("Mute " + SteamIntegration.GetUserName(userId), Color.white, VoiceManager.IsMuted(userId), (Action)delegate(bool value) { VoiceManager.SetMuted(userId, value); }); } } } MenuManager.OpenCategory(muteCategory); } } public static class SyncUI { public static void CreateUI(MenuCategory category) { //IL_0007: 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_0052: 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) MenuCategory val = category.CreateSubCategory("File Sync", Color.green); val.CreateBoolElement("Sync Custom Items", Color.white, SyncPrefs.itemSyncEnabled.Value, (Action)delegate(bool value) { SyncPrefs.itemSyncEnabled.Value = value; }); val.CreateBoolElement("Sync Playermodels", Color.white, SyncPrefs.playermodelSyncEnabled.Value, (Action)delegate(bool value) { SyncPrefs.playermodelSyncEnabled.Value = value; }); val.CreateIntElement("Max File Size (MB)", Color.white, SyncPrefs.maxSyncSizeKB.Value / 1024, (Action)delegate(int value) { SyncPrefs.maxSyncSizeKB.Value = value * 1024; }, 10, 1, 500, true); } } public static class GamemodeUI { private static MenuCategory scoresCategory; private const string refreshText = "Refresh Scores"; public static void CreateUI(MenuCategory category) { //IL_0007: 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_006a: 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_00d3: 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_011e: 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_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: 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) MenuCategory val = category.CreateSubCategory("Gamemodes", Color.magenta); MenuCategory val2 = val.CreateSubCategory("Host Controls", Color.yellow); foreach (EntanglementGamemode mode in GamemodeHandler.registeredModes.Values) { val2.CreateFunctionElement("Play: " + mode.DisplayName, mode.MenuColor, (Action)delegate { if (Node.isServer && !GamemodeHandler.TryStartMatch(mode.Id, out var reason)) { Notifications.SendNotification(reason, 4f); } }); } val2.CreateFunctionElement("Force Stop Gamemode", Color.red, (Action)delegate { if (Node.isServer) { GamemodeHandler.StopMode(); } }); MenuCategory val3 = val2.CreateSubCategory("Round Timer", Color.cyan); val3.CreateIntElement("Default Length (s, 0 = mode default)", Color.white, 0, (Action)delegate(int value) { if (Node.isServer) { GamemodeHandler.SetRoundDuration(value); } }, 30, 0, 3600, true); val3.CreateIntElement("Set Time Left (s)", Color.white, 0, (Action)delegate(int value) { if (Node.isServer) { if (!GamemodeHandler.RoundActive) { Notifications.SendNotification("No round is running.", 3f); } else { GamemodeHandler.SetRoundTimeRemaining(value); Notifications.SendNotification($"Time left: {(int)GamemodeHandler.RoundTimeRemaining}s", 3f); } } }, 30, 0, 3600, true); val3.CreateFunctionElement("Add 60 seconds", Color.green, (Action)delegate { AdjustTime(60f); }); val3.CreateFunctionElement("Remove 60 seconds", Color.red, (Action)delegate { AdjustTime(-60f); }); scoresCategory = val.CreateSubCategory("Scores", Color.white); scoresCategory.CreateFunctionElement("Refresh Scores", Color.white, (Action)RefreshScores); } private static void AdjustTime(float delta) { if (Node.isServer) { if (!GamemodeHandler.RoundActive) { Notifications.SendNotification("No round is running.", 3f); return; } GamemodeHandler.AddRoundTime(delta); Notifications.SendNotification($"Time left: {(int)GamemodeHandler.RoundTimeRemaining}s", 3f); } } private static void ClearScores() { List list = new List(); foreach (MenuElement element in scoresCategory.elements) { if (element.displayText != "Refresh Scores") { list.Add(element.displayText); } } foreach (string item in list) { scoresCategory.RemoveElement(item); } } private static void RefreshScores() { //IL_0071: 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_00c3: 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) ClearScores(); if (GamemodeHandler.ActiveMode == null) { scoresCategory.CreateFunctionElement("No gamemode active", Color.grey, (Action)delegate { }); MenuManager.OpenCategory(scoresCategory); return; } scoresCategory.CreateFunctionElement("Mode: " + GamemodeHandler.ActiveMode.DisplayName, Color.white, (Action)delegate { }); scoresCategory.CreateFunctionElement(GamemodeHandler.RoundActive ? $"Round time left: {(int)GamemodeHandler.RoundTimeRemaining}s" : "No round active", Color.white, (Action)delegate { }); foreach (KeyValuePair score in GamemodeHandler.scores) { string userName = SteamIntegration.GetUserName(score.Key); string arg = (GamemodeHandler.eliminated.Contains(score.Key) ? " (eliminated)" : ""); scoresCategory.CreateFunctionElement($"{userName}: {score.Value}{arg}", Color.white, (Action)delegate { }); } MenuManager.OpenCategory(scoresCategory); } } } namespace Entanglement.Representation { public class PlayerRepresentation { public static float legJitter = 10f; public static Dictionary representations = new Dictionary(); public static Transform[] syncedPoints = (Transform[])(object)new Transform[3]; public static Transform syncedRoot; public Transform[] repTransforms = (Transform[])(object)new Transform[3]; public Transform repRoot; public GameObject repFord; public Material repHologram; public GameObject repCanvas; public Canvas repCanvasComponent; public Transform repCanvasTransform; public TextMeshProUGUI repNameText; public Transform repGeo; public Transform repSHJnt; public Collider[] colliders = (Collider[])(object)new Collider[0]; private Renderer[] cachedRenderers; public SLZ_Body repBody; public SLZ_Body ragdollBody; public CharacterAnimationManager repAnimationManager; public GunSFX repGunSFX; public GunSFX repBalloonSFX; public GunSFX repStabSFX; public GravGunSFX repPowerPunchSFX; public Animator repAnimator; public Animator skinAnimator; public Animator activeAnimator; public GameObject currentSkinObject; public AssetBundle currentSkinBundle; public string currentSkinPath; public bool isCustomSkinned; public Vector3 repInputVel = Vector3.zero; public Vector3 repSavedVel = Vector3.zero; public Vector3 prevRepRootPos = Vector3.zero; public string playerName; public long playerId; public bool isGrounded; public bool hasNetTarget = false; public Vector3 netRootPosition; public Vector3 netRootVelocity; public float netReceiveTime; public Vector3[] netPositions = (Vector3[])(object)new Vector3[3]; public Quaternion[] netRotations = (Quaternion[])(object)new Quaternion[3]; public Vector3[] netLimbVelocities = (Vector3[])(object)new Vector3[3]; public const float repFollowSharpness = 35f; public const float repLimbSharpness = 60f; public const float repExtrapolationLimit = 0.2f; public const float repSnapDistance = 2f; public const float repMaxPredictedSpeed = 25f; public static PlayerRepresentation debugRepresentation; public static float debugLoopbackHz = 18f; private static readonly long debugLoopbackFakeOwner = 1L; private static TransformSyncable debugLoopSyncable; private static GameObject debugLoopProxy; private static GameObject debugLoopSource; private static float debugLoopTimer; public static AssetBundle playerRepBundle; private bool wasTalking; private Color baseNameColor = Color.white; public bool IsEliminated { get; private set; } public void SetEliminated(bool eliminated) { if (IsEliminated == eliminated) { return; } IsEliminated = eliminated; if (!Object.op_Implicit((Object)(object)repRoot)) { return; } if (cachedRenderers == null) { cachedRenderers = Il2CppArrayBase.op_Implicit(((Component)repRoot).GetComponentsInChildren(true)); } Renderer[] array = cachedRenderers; foreach (Renderer val in array) { if (Object.op_Implicit((Object)(object)val)) { val.enabled = !eliminated; } } if (Object.op_Implicit((Object)(object)repCanvas)) { repCanvas.SetActive(!eliminated); } } private static void UpdateDebugHeldLoopback() { //IL_017c: 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_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0197: 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_01a6: 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_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: 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) GameObject val = null; if (debugRepresentation != null) { if (Object.op_Implicit((Object)(object)PlayerScripts.playerRightHand) && Object.op_Implicit((Object)(object)PlayerScripts.playerRightHand.m_CurrentAttachedObject)) { val = ((Component)PlayerScripts.playerRightHand.m_CurrentAttachedObject.transform.GetJointedRoot()).gameObject; } else if (Object.op_Implicit((Object)(object)PlayerScripts.playerLeftHand) && Object.op_Implicit((Object)(object)PlayerScripts.playerLeftHand.m_CurrentAttachedObject)) { val = ((Component)PlayerScripts.playerLeftHand.m_CurrentAttachedObject.transform.GetJointedRoot()).gameObject; } } if ((Object)(object)val != (Object)(object)debugLoopSource || ((Object)(object)val != (Object)null && (Object)(object)debugLoopSyncable == (Object)null)) { DestroyDebugLoopback(); debugLoopSource = val; if (Object.op_Implicit((Object)(object)val)) { CreateDebugLoopback(val); } } if (!Object.op_Implicit((Object)(object)val) || (Object)(object)debugLoopSyncable == (Object)null) { return; } float num = ((debugLoopbackHz > 0f) ? (1f / debugLoopbackHz) : 0f); debugLoopTimer += Time.deltaTime; if (debugLoopTimer < num) { return; } debugLoopTimer = 0f; Rigidbody val2 = val.GetComponent(); if (!Object.op_Implicit((Object)(object)val2)) { val2 = val.GetComponentInChildren(); } Vector3 velocity = (Object.op_Implicit((Object)(object)val2) ? val2.velocity : Vector3.zero); Vector3 angularVelocity = (Object.op_Implicit((Object)(object)val2) ? val2.angularVelocity : Vector3.zero); SimplifiedTransform simplifiedTransform = new SimplifiedTransform(val.transform.position + Vector3.forward, val.transform.rotation); try { debugLoopSyncable.ApplyTransform(simplifiedTransform, velocity, angularVelocity); } catch { DestroyDebugLoopback(); } } private static void CreateDebugLoopback(GameObject held) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_014d: 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_0157: 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: Expected O, but got Unknown //IL_00c8: 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_00f8: Unknown result type (might be due to invalid IL or missing references) try { debugLoopProxy = new GameObject("DebugHeldLoopback " + ((Object)held).name); debugLoopProxy.transform.position = held.transform.position; debugLoopProxy.transform.rotation = held.transform.rotation; foreach (MeshFilter componentsInChild in held.GetComponentsInChildren(false)) { MeshRenderer component = ((Component)componentsInChild).GetComponent(); if (!((Object)(object)componentsInChild.sharedMesh == (Object)null) && !((Object)(object)component == (Object)null)) { GameObject val = new GameObject("mesh"); val.transform.SetParent(debugLoopProxy.transform, false); val.transform.position = ((Component)componentsInChild).transform.position; val.transform.rotation = ((Component)componentsInChild).transform.rotation; val.transform.localScale = ((Component)componentsInChild).transform.lossyScale; val.AddComponent().sharedMesh = componentsInChild.sharedMesh; ((Renderer)val.AddComponent()).sharedMaterials = ((Renderer)component).sharedMaterials; } } Transform transform = debugLoopProxy.transform; transform.position += Vector3.forward; Rigidbody val2 = debugLoopProxy.AddComponent(); val2.useGravity = false; debugLoopSyncable = TransformSyncable.CreateSync(debugLoopbackFakeOwner, val2) as TransformSyncable; if ((Object)(object)debugLoopSyncable == (Object)null) { DestroyDebugLoopback(); return; } debugLoopSyncable.isValid = true; debugLoopSyncable.EnqueueOwner(debugLoopbackFakeOwner); } catch { DestroyDebugLoopback(); } } private static void DestroyDebugLoopback() { if ((Object)(object)debugLoopSyncable != (Object)null) { try { if (Object.op_Implicit((Object)(object)debugLoopProxy)) { TransformSyncable.cache.Remove(debugLoopProxy); } debugLoopSyncable.Cleanup(); } catch { } debugLoopSyncable = null; } if (Object.op_Implicit((Object)(object)debugLoopProxy)) { Object.Destroy((Object)(object)debugLoopProxy); debugLoopProxy = null; } debugLoopSource = null; debugLoopTimer = 0f; } public static void LoadBundle() { playerRepBundle = EmebeddedAssetBundle.LoadFromAssembly(EntanglementMod.entanglementAssembly, "Entanglement.resources.playerrep.eres"); if ((Object)(object)playerRepBundle == (Object)null) { throw new NullReferenceException("playerRepBundle is null! Did you forget to compile the player bundle into the dll?"); } } public PlayerRepresentation(string playerName, long playerId) { //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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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) this.playerName = playerName; this.playerId = playerId; RecreateRepresentations(); } public void DeleteRepresentations() { Object.Destroy((Object)(object)repFord); Object.Destroy((Object)(object)repCanvas); if (Object.op_Implicit((Object)(object)currentSkinObject)) { Object.Destroy((Object)(object)currentSkinObject); } } public void RecreateRepresentations() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0047: 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) try { repCanvas = new GameObject("RepCanvas"); repCanvasComponent = repCanvas.AddComponent(); repCanvasComponent.renderMode = (RenderMode)2; repCanvasTransform = repCanvas.transform; repCanvasTransform.localScale = Vector3.one / 200f; repNameText = repCanvas.AddComponent(); ((TMP_Text)repNameText).alignment = (TextAlignmentOptions)4098; ((TMP_Text)repNameText).enableAutoSizing = true; ((TMP_Text)repNameText).text = playerName; repHologram = Object.Instantiate(playerRepBundle.LoadAsset("PlayerHolographic")); repFord = Object.Instantiate(playerRepBundle.LoadAsset("PlayerRep")); ((Object)repFord).name = $"PlayerRep.{playerId}"; repRoot = repFord.transform; repGunSFX = ((Component)repRoot.Find("GunSFX")).GetComponent(); repBalloonSFX = ((Component)repRoot.Find("BalloonSFX")).GetComponent(); repStabSFX = ((Component)repRoot.Find("StabSFX")).GetComponent(); repPowerPunchSFX = ((Component)repRoot.Find("PuncherSFX")).GetComponent(); Transform val = repRoot.Find("Body"); repBody = ((Component)val).GetComponent(); repBody.OnStart(); ragdollBody = ((Component)repRoot.Find("Ragdoll")).GetComponent(); Transform val2 = repRoot.Find("Brett@neutral"); repAnimator = ((Component)val2).GetComponent(); repAnimator.runtimeAnimatorController = PlayerScripts.playerAnimatorController; activeAnimator = repAnimator; repAnimationManager = ((Component)val2).GetComponent(); repGeo = val2.Find("geoGrp"); repSHJnt = val2.Find("SHJntGrp"); repTransforms[0] = repRoot.Find("Head"); repTransforms[1] = repRoot.Find("Hand (left)"); repTransforms[2] = repRoot.Find("Hand (right)"); colliders = Il2CppArrayBase.op_Implicit(((Component)repRoot).GetComponentsInChildren()); if (isCustomSkinned && currentSkinPath != null) { PlayerSkinLoader.ApplyPlayermodel(this, currentSkinPath); } } catch { EntangleLogger.Error($"Error caught creating rep from user {playerId}"); } } public void CreateRagdoll() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_00ea: 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) if (!Object.op_Implicit((Object)(object)activeAnimator)) { return; } GameObject val = new GameObject($"Ragdoll {playerId}"); GameObject val2 = Object.Instantiate(((Component)ragdollBody).gameObject); val2.transform.parent = val.transform; Collider[] array = Il2CppArrayBase.op_Implicit(val2.GetComponentsInChildren(true)); Collider[] array2 = array; foreach (Collider val3 in array2) { Collider[] array3 = array; foreach (Collider val4 in array3) { if (!((Object)(object)val3 == (Object)(object)val4)) { Physics.IgnoreCollision(val3, val4, true); } } } val2.gameObject.SetActive(true); foreach (Rigidbody componentsInChild in val2.GetComponentsInChildren(true)) { componentsInChild.velocity = repSavedVel; componentsInChild.angularVelocity = Vector3.zero; } CopyBone(((Component)repBody).transform, val2.transform); CopyBones(repBody.references, val2.GetComponent().references); val2.gameObject.AddComponent(); if (Node.isServer && SteamIntegration.hasLobby) { MelonCoroutines.Start(SyncRagdollBones(val2)); } } private static IEnumerator SyncRagdollBones(GameObject ragdoll) { yield return (object)new WaitForSeconds(0.5f); if (!Object.op_Implicit((Object)(object)ragdoll) || !SteamIntegration.hasLobby || !Node.isServer) { yield break; } foreach (Rigidbody rb in ragdoll.GetComponentsInChildren(true)) { if (Object.op_Implicit((Object)(object)rb) && !rb.isKinematic && !Object.op_Implicit((Object)(object)TransformSyncable.cache.Get(((Component)rb).gameObject))) { SyncUtilities.UpdateBodyAttached(rb, null, -1, -1f); SyncUtilities.UpdateBodyDetached(rb); } } } public void CopyBones(References from, References to) { CopyBone(from.skull, to.skull); CopyBone(from.c4Vertebra, to.c4Vertebra); CopyBone(from.t1Offset, to.t1Offset); CopyBone(from.t7Vertebra, to.t7Vertebra); CopyBone(from.l1Vertebra, to.l1Vertebra); CopyBone(from.l3Vertebra, to.l3Vertebra); CopyBone(from.sacrum, to.sacrum); CopyBone(from.leftHip, to.leftHip); CopyBone(from.leftKnee, to.leftKnee); CopyBone(from.leftAnkle, to.leftAnkle); CopyBone(from.rightHip, to.rightHip); CopyBone(from.rightKnee, to.rightKnee); CopyBone(from.rightAnkle, to.rightAnkle); CopyBone(from.leftShoulder, to.leftShoulder); CopyBone(from.leftElbow, to.leftElbow); CopyBone(from.leftWrist, to.leftWrist); CopyBone(from.rightShoulder, to.rightShoulder); CopyBone(from.rightElbow, to.rightElbow); CopyBone(from.rightWrist, to.rightWrist); } public void CopyBone(Transform from, Transform to) { //IL_0003: 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) to.position = from.position; to.rotation = from.rotation; } public void SetNetTargets(Vector3 rootPosition, Vector3[] positions, Quaternion[] rotations) { //IL_00a1: 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_002e: 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_0035: 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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_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_0073: 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_0082: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_010f: 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) float time = Time.time; if (hasNetTarget) { float num = Mathf.Clamp(time - netReceiveTime, 0.008f, 0.5f); netRootVelocity = Vector3.ClampMagnitude((rootPosition - netRootPosition) / num, 25f); for (int i = 0; i < netPositions.Length; i++) { netLimbVelocities[i] = Vector3.ClampMagnitude((positions[i] - netPositions[i]) / num, 25f); } } else { netRootVelocity = Vector3.zero; for (int j = 0; j < netLimbVelocities.Length; j++) { netLimbVelocities[j] = Vector3.zero; } } netRootPosition = rootPosition; netReceiveTime = time; for (int k = 0; k < netPositions.Length; k++) { netPositions[k] = positions[k]; netRotations[k] = rotations[k]; } hasNetTarget = true; } public void ApplyNetSmoothing(float dt) { //IL_003d: 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_0049: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00bc: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_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_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_0156: Unknown result type (might be due to invalid IL or missing references) if (!hasNetTarget || !Object.op_Implicit((Object)(object)repRoot)) { return; } float num = Mathf.Min(Time.time - netReceiveTime, 0.2f); Vector3 val = netRootPosition + netRootVelocity * num; float num2 = 1f - Mathf.Exp(-35f * dt); float num3 = 1f - Mathf.Exp(-60f * dt); Vector3 val2 = repRoot.position - val; if (((Vector3)(ref val2)).sqrMagnitude > 4f) { num2 = (num3 = 1f); } repRoot.position = Vector3.Lerp(repRoot.position, val, num2); for (int i = 0; i < repTransforms.Length; i++) { if (Object.op_Implicit((Object)(object)repTransforms[i])) { repTransforms[i].position = Vector3.Lerp(repTransforms[i].position, netPositions[i] + netLimbVelocities[i] * num, num3); repTransforms[i].rotation = Quaternion.Slerp(repTransforms[i].rotation, netRotations[i], num3); } } UpdateNametagPosition(); UpdateTalkingIndicator(); } public void UpdateNametagPosition() { //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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)repCanvasTransform) && !((Object)(object)repTransforms[0] == (Object)null)) { repCanvasTransform.position = repTransforms[0].position + Vector3.up * 0.4f; if (Object.op_Implicit((Object)(object)Camera.current)) { repCanvasTransform.rotation = Quaternion.LookRotation(Vector3.Normalize(repCanvasTransform.position - ((Component)Camera.current).transform.position), Vector3.up); } } } public void SetNameColor(Color color) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: 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) baseNameColor = color; if (!wasTalking && Object.op_Implicit((Object)(object)repNameText)) { ((TMP_Text)repNameText).color = baseNameColor; } } private void UpdateTalkingIndicator() { //IL_007e: 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) if (Object.op_Implicit((Object)(object)repNameText)) { bool flag = VoiceManager.IsSpeaking(playerId); if (flag != wasTalking) { wasTalking = flag; ((TMP_Text)repNameText).text = (flag ? ("● " + playerName) : playerName); ((TMP_Text)repNameText).color = (Color)(flag ? new Color(0.4f, 1f, 0.5f) : baseNameColor); } } } public void SaveVelocity() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_001c: 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_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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_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) Vector3 position = repRoot.position; float fixedDeltaTime = Time.fixedDeltaTime; repSavedVel = Vector3.Slerp(repInputVel, PhysicsData.GetVelocity(position, prevRepRootPos, fixedDeltaTime), fixedDeltaTime * legJitter); if (isGrounded) { repInputVel = repSavedVel; } else { repInputVel = Vector3.zero; } prevRepRootPos = position; } public void UpdateIK() { //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) try { if ((!Object.op_Implicit((Object)(object)currentSkinBundle) || !Object.op_Implicit((Object)(object)currentSkinObject)) && isCustomSkinned) { PlayerSkinLoader.ApplyPlayermodel(this, currentSkinPath); } if (Object.op_Implicit((Object)(object)activeAnimator)) { activeAnimator.Update(Time.fixedDeltaTime); repAnimationManager.OnLateUpdate(); SaveVelocity(); repBody.FullBodyUpdate(repInputVel, Vector3.zero); repBody.ArtToBlender.UpdateBlender(); } } catch { } } public void UpdatePose(Handedness hand, int index) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) Il2CppStringArray playerHandPoses = PlayerScripts.playerHandPoses; if (((Il2CppArrayBase)(object)playerHandPoses).Count >= index + 1) { UpdatePose(hand, ((Il2CppArrayBase)(object)playerHandPoses)[index]); } } public void UpdatePose(Handedness hand, string pose) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) CharacterAnimationManager obj = repAnimationManager; if (obj != null) { obj.SetHandPose(hand, pose); } } public void UpdatePoseRadius(Handedness hand, float radius) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) CharacterAnimationManager obj = repAnimationManager; if (obj != null) { obj.SetCylinderRadius(hand, radius); } } public void UpdateFingers(Handedness hand, float indexCurl = 1f, float middleCurl = 1f, float ringCurl = 1f, float pinkyCurl = 1f, float thumbCurl = 1f) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) repAnimationManager.ApplyFingerCurl(hand, 1f - thumbCurl, 1f - indexCurl, 1f - middleCurl, 1f - ringCurl, 1f - pinkyCurl); } public void UpdateFingers(Handedness hand, SimplifiedHand handData) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) UpdateFingers(hand, handData.indexCurl, handData.middleCurl, handData.ringCurl, handData.pinkyCurl, handData.thumbCurl); } public void IgnoreCollision(Rigidbody otherBody, bool ignore) { Collider[] array = Il2CppArrayBase.op_Implicit(((Component)otherBody).GetComponentsInChildren()); Collider[] array2 = colliders; foreach (Collider val in array2) { Collider[] array3 = array; foreach (Collider val2 in array3) { Physics.IgnoreCollision(val, val2, ignore); } } } public static void GetPlayerTransforms() { GameObject val = GameObject.Find("[RigManager (Default Brett)]/[SkeletonRig (GameWorld Brett)]"); if (Object.op_Implicit((Object)(object)val)) { syncedRoot = val.transform; syncedPoints[0] = syncedRoot.Find("Head"); syncedPoints[1] = syncedRoot.Find("Hand (left)"); syncedPoints[2] = syncedRoot.Find("Hand (right)"); } } public static PlayerRepSyncData GetPlayerSyncData() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_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) Transform[] array = syncedPoints; foreach (Transform val in array) { if ((Object)(object)val == (Object)null) { return null; } } PlayerRepSyncData playerRepSyncData = new PlayerRepSyncData(); playerRepSyncData.userId = SteamIntegration.currentUserId; for (int j = 0; j < playerRepSyncData.simplifiedTransforms.Length; j++) { playerRepSyncData.simplifiedTransforms[j].position = syncedPoints[j].position; playerRepSyncData.simplifiedTransforms[j].rotation = SimplifiedQuaternion.SimplifyQuat(syncedPoints[j].rotation); } playerRepSyncData.rootPosition = syncedRoot.position; playerRepSyncData.isGrounded = PlayerScripts.playerGrounder.isGrounded; playerRepSyncData.simplifiedLeftHand = new SimplifiedHand(PlayerScripts.playerLeftHand.fingerCurl); playerRepSyncData.simplifiedRightHand = new SimplifiedHand(PlayerScripts.playerRightHand.fingerCurl); try { if (debugRepresentation != null) { for (int k = 0; k < playerRepSyncData.simplifiedTransforms.Length; k++) { playerRepSyncData.simplifiedTransforms[k].Apply(debugRepresentation.repTransforms[k]); Transform obj = debugRepresentation.repTransforms[k]; obj.position += Vector3.forward; } debugRepresentation.repRoot.position = syncedRoot.position + Vector3.forward; debugRepresentation.isGrounded = playerRepSyncData.isGrounded; debugRepresentation.UpdateFingers((Handedness)1, playerRepSyncData.simplifiedLeftHand); debugRepresentation.UpdateFingers((Handedness)2, playerRepSyncData.simplifiedRightHand); debugRepresentation.UpdateNametagPosition(); } UpdateDebugHeldLoopback(); } catch { } return playerRepSyncData; } public static void SyncPlayerReps() { if (SteamIntegration.hasLobby) { PlayerRepSyncData playerSyncData = GetPlayerSyncData(); if (playerSyncData != null) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.PlayerRepSync, playerSyncData); Node.activeNode.BroadcastMessage(NetworkChannel.Unreliable, networkMessage.GetBytes()); } else { GetPlayerTransforms(); } } else if ((debugRepresentation != null || (Object)(object)debugLoopProxy != (Object)null) && GetPlayerSyncData() == null) { GetPlayerTransforms(); } } public static void UpdatePlayerReps() { //IL_0063: 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_0078: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)syncedRoot)) { return; } foreach (PlayerRepresentation value in representations.Values) { if (value == null || !Object.op_Implicit((Object)(object)value.repRoot)) { continue; } value.ApplyNetSmoothing(Time.fixedDeltaTime); Vector3 val = syncedRoot.position - value.repRoot.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (!(sqrMagnitude < 1000000f)) { continue; } value.UpdateIK(); Transform obj = value.repCanvasTransform; if (obj != null) { GameObject gameObject = ((Component)obj).gameObject; if (gameObject != null) { gameObject.SetActive(Client.nameTagsVisible && !value.IsEliminated); } } } try { if (debugRepresentation != null) { debugRepresentation.UpdateIK(); } } catch { } } } } namespace Entanglement.Patching { public static class Patcher { public static void Initialize() { OptionalAssemblyPatch.AttemptPatches(); } public static void Patch(MethodBase method, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { ((MelonBase)EntanglementMod.Instance).HarmonyInstance.Patch(method, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } [HarmonyPatch(typeof(Prop_Health), "DESTROYED")] public static class PropHealthPatch { public static bool Prefix(Prop_Health __instance) { if (!Object.op_Implicit((Object)(object)__instance.impactSFX)) { return false; } return true; } public static void Postfix(Prop_Health __instance) { if (SteamIntegration.hasLobby) { TransformSyncable transformSyncable = TransformSyncable.DestructCache.Get(((Component)__instance).gameObject); if (Object.op_Implicit((Object)(object)transformSyncable) && transformSyncable.IsOwner()) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ObjectDestroy, new ObjectDestroyMessageData { objectId = transformSyncable.objectId }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } } } } [HarmonyPatch(typeof(ObjectDestructable), "TakeDamage")] public static class DestructablePatch { public static void Postfix(ObjectDestructable __instance, Vector3 normal, float damage, bool crit = false, AttackType attackType = (AttackType)0) { if (SteamIntegration.hasLobby && __instance._isDead) { TransformSyncable transformSyncable = TransformSyncable.DestructCache.Get(((Component)__instance).gameObject); if (Object.op_Implicit((Object)(object)transformSyncable) && transformSyncable.IsOwner()) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ObjectDestroy, new ObjectDestroyMessageData { objectId = transformSyncable.objectId }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } } } } public static class FantasyArena_Settings { public static bool m_invalidSettings; public static void SendEnemyCount(bool isLow) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.FantasyCount, new FantasyEnemyCountMessageData { isLow = isLow }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } public static void SendDifficulty(byte difficulty) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.FantasyDiff, new FantasyDifficultyMessageData { difficulty = difficulty }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } } [HarmonyPatch(typeof(UIHapticHoverArena), "OnPointerEnter")] public static class ChallengePatch { public static void Postfix(UIHapticHoverArena __instance, PointerEventData eventData) { if (Object.op_Implicit((Object)(object)__instance.arenaUIControl) && Object.op_Implicit((Object)(object)__instance.challenge) && (Object)(object)__instance.arenaUIControl.activeChallenge == (Object)(object)__instance.challenge) { Arena_Challenge challenge = __instance.challenge; EntangleLogger.Log("Set challenge to " + ((Object)challenge).name + "!"); byte index = (byte)((IEnumerable)Arena_GameManager.instance.masterChallengeList.ToArray()).ToList().FindIndex((Arena_Challenge o) => (Object)(object)o == (Object)(object)challenge); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.FantasyChal, new FantasyChallengeMessageData { index = index }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } } } [HarmonyPatch(typeof(Control_UI_Arena), "SetEasyDifficulty")] public static class EasyDifficultyPatch { public static void Postfix() { if (!FantasyArena_Settings.m_invalidSettings) { EntangleLogger.Log("Set easy difficulty!"); FantasyArena_Settings.SendDifficulty(0); } FantasyArena_Settings.m_invalidSettings = false; } } [HarmonyPatch(typeof(Control_UI_Arena), "SetMediumDifficulty")] public static class MediumDifficultyPatch { public static void Postfix() { if (!FantasyArena_Settings.m_invalidSettings) { EntangleLogger.Log("Set medium difficulty!"); FantasyArena_Settings.SendDifficulty(1); } FantasyArena_Settings.m_invalidSettings = false; } } [HarmonyPatch(typeof(Control_UI_Arena), "SetHardDifficulty")] public static class HardDifficultyPatch { public static void Postfix() { if (!FantasyArena_Settings.m_invalidSettings) { EntangleLogger.Log("Set hard difficulty!"); FantasyArena_Settings.SendDifficulty(2); } FantasyArena_Settings.m_invalidSettings = false; } } [HarmonyPatch(typeof(Control_UI_Arena), "ToggleEnemyCount")] public static class ToggleEnemyCountPatch { public static void Postfix(Control_UI_Arena __instance) { bool isLowEnemyCount = __instance.arenaStats.arenaDataPlayer.playerStats.isLowEnemyCount; EntangleLogger.Log("Toggling Enemy Count to " + (isLowEnemyCount ? "LOW" : "HIGH") + "!"); FantasyArena_Settings.SendEnemyCount(isLowEnemyCount); } } [HarmonyPatch(typeof(Control_UI_Arena), "OnLoadSaveFile")] public static class LoadSaveFilePatch { public static void Postfix(Control_UI_Arena __instance) { if (Server.instance != null) { bool isLowEnemyCount = __instance.arenaStats.arenaDataPlayer.playerStats.isLowEnemyCount; EntangleLogger.Log("Loading saved enemy count of " + (isLowEnemyCount ? "LOW" : "HIGH") + "!"); FantasyArena_Settings.SendEnemyCount(isLowEnemyCount); } } } [HarmonyPatch(typeof(PowerPuncher), "OnCollisionEnter")] public class GadgetPatches { public static void Prefix(PowerPuncher __instance, Collision collision) { //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_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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00e2: 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_00f4: 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_00fb: 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_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0132: 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_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) if (!SteamIntegration.hasLobby || !Object.op_Implicit((Object)(object)collision.rigidbody)) { return; } Transform root = collision.gameObject.transform.root; string name = ((Object)root).name; if (!name.Contains("PlayerRep")) { return; } string[] array = name.Split(new char[1] { '.' }); if (array.Length < 2) { throw new IndexOutOfRangeException(); } long userId = long.Parse(array[1]); ContactPoint contact = collision.GetContact(0); if (!(((Object)((ContactPoint)(ref contact)).thisCollider).name != "col_mainBody (1)")) { Vector3 relativeVelocity = collision.relativeVelocity; float num = Vector3.Dot(((Vector3)(ref relativeVelocity)).normalized, ((Component)__instance).transform.TransformDirection(__instance.forward)); num = Mathf.Min(0f, num); Vector3 val = collision.relativeVelocity * num * __instance._triggerStartTime; val = Vector3.ClampMagnitude(val, 30f) * 7f; if (!(val == Vector3.zero)) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.PowerPunch, new PowerPunchMessageData { force = val, localPosition = PlayerRepresentation.syncedRoot.InverseTransformPosition(((Component)__instance).transform.position) }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.SendMessage(userId, NetworkChannel.Attack, bytes); } } } } [HarmonyPatch(typeof(GameControl), "RELOADLEVEL")] public static class ReloadLevelPatch { public static bool Prefix() { if (SteamIntegration.hasLobby) { return false; } return true; } } public static class LevelChangeAnnouncer { public static bool levelAnnounced = false; public static int lastAnnouncedIndex = -1; public static void Announce(int sceneBuildIndex) { if (SteamIntegration.hasLobby && Node.isServer && (!levelAnnounced || lastAnnouncedIndex != sceneBuildIndex)) { LevelChangeMessageData data = new LevelChangeMessageData { sceneIndex = (byte)sceneBuildIndex, sceneReload = (sceneBuildIndex == BoneworksSceneManager.currentSceneIndex) }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.LevelChange, data); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); levelAnnounced = true; lastAnnouncedIndex = sceneBuildIndex; EntangleLogger.Log($"Announced level change to scene {sceneBuildIndex} early, clients now load in parallel!"); } } public static bool ConsumeAnnounce(int buildIndex) { bool result = levelAnnounced && lastAnnouncedIndex == buildIndex; levelAnnounced = false; lastAnnouncedIndex = -1; return result; } public static int ResolveSceneIndex(string sceneName) { for (int i = 0; i < SceneManager.sceneCountInBuildSettings; i++) { string scenePathByBuildIndex = SceneUtility.GetScenePathByBuildIndex(i); if (!string.IsNullOrEmpty(scenePathByBuildIndex) && scenePathByBuildIndex.EndsWith("/" + sceneName + ".unity", StringComparison.OrdinalIgnoreCase)) { return i; } } return -1; } } [HarmonyPatch(typeof(BoneworksSceneManager), "LoadScene", new Type[] { typeof(int) })] public static class LoadSceneIndexPatch { public static void Prefix(int sceneBuildIndex) { LevelChangeAnnouncer.Announce(sceneBuildIndex); } } [HarmonyPatch(typeof(BoneworksSceneManager), "LoadScene", new Type[] { typeof(string) })] public static class LoadSceneNamePatch { public static void Prefix(string sceneName) { int num = LevelChangeAnnouncer.ResolveSceneIndex(sceneName); if (num >= 0) { LevelChangeAnnouncer.Announce(num); } } } [HarmonyPatch(typeof(ForcePullGrip), "OnFarHandHoverUpdate")] public class ForcePullPatch { public static void Prefix(ForcePullGrip __instance, ref bool __state, Hand hand) { __state = __instance.pullCoroutine != null; } public static void Postfix(ForcePullGrip __instance, ref bool __state, Hand hand) { if (__instance.pullCoroutine != null && !__state) { ObjectSync.OnGripAttached(((Component)__instance).gameObject); } } } [HarmonyPatch(typeof(ForcePullGrip), "CancelPull")] public class ForceCancelPatch { public static void Postfix(ForcePullGrip __instance, Hand hand) { ObjectSync.OnForcePullCancelled(((Component)__instance).gameObject); } } [HarmonyPatch(typeof(Gun), "OnFire")] public class GunShotPatch { public static void Prefix(Gun __instance) { if (SteamIntegration.hasLobby) { BulletObject chamberedCartridge = __instance.chamberedCartridge; Transform firePointTransform = __instance.firePointTransform; if (Object.op_Implicit((Object)(object)firePointTransform) && Object.op_Implicit((Object)(object)chamberedCartridge)) { GunShotMessageData data = new GunShotMessageData { userId = SteamIntegration.currentUserId, bulletObject = chamberedCartridge, bulletTransform = new SimplifiedTransform(firePointTransform) }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.GunShot, data); Node.activeNode.BroadcastMessage(NetworkChannel.Attack, networkMessage.GetBytes()); } } } } [HarmonyPatch(typeof(BalloonGun), "OnFire")] public class BalloonShotPatch { public static void Prefix(BalloonGun __instance) { //IL_0039: 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) if (SteamIntegration.hasLobby) { Transform firePointTransform = ((Gun)__instance).firePointTransform; if (Object.op_Implicit((Object)(object)firePointTransform)) { BalloonShotMessageData data = new BalloonShotMessageData { userId = SteamIntegration.currentUserId, balloonColor = __instance.currentColor, balloonTransform = new SimplifiedTransform(firePointTransform) }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.BalloonShot, data); Node.activeNode.BroadcastMessage(NetworkChannel.Attack, networkMessage.GetBytes()); } } } } public static class Magazine_Settings { public static bool InGun(this MagazinePlug plug) { if (!Object.op_Implicit((Object)(object)plug)) { return false; } Socket lastSocket = ((AlignPlug)plug)._lastSocket; if (Object.op_Implicit((Object)(object)lastSocket) && (Object)(object)lastSocket.LockedPlug == (Object)(object)plug) { return true; } return false; } public static bool EnteringOrInside(this MagazinePlug plug) { return ((AlignPlug)plug)._isEnterTransition || plug.InGun(); } public static void ForceEject(this MagazinePlug plug) { try { ((AlignPlug)plug).EjectPlug(); ((AlignPlug)plug).ClearFromSocket(); } catch { } ((Component)plug.magazine).gameObject.SetActive(true); ((Component)plug.magazine).transform.parent = null; ((AlignPlug)plug)._isEnterTransition = false; ((AlignPlug)plug)._isExitTransition = false; ((AlignPlug)plug)._isExitComplete = true; } } [HarmonyPatch(typeof(MagazinePlug), "OnPlugExitComplete")] public static class PlugExitPatch { public static void Postfix(MagazinePlug __instance) { TransformSyncable orAdd = TransformSyncable.cache.GetOrAdd(((Component)__instance.magazine).gameObject); if (!Object.op_Implicit((Object)(object)orAdd) || !orAdd.IsOwner()) { EntangleLogger.Log("Not owner of mag or not synced!"); return; } MagazineSocket val = ((Il2CppObjectBase)((AlignPlug)__instance)._lastSocket).Cast(); Gun componentInParent = ((Component)val).GetComponentInParent(); if (!Object.op_Implicit((Object)(object)componentInParent)) { EntangleLogger.Log("No gun found!"); return; } TransformSyncable orAdd2 = TransformSyncable.cache.GetOrAdd(((Component)componentInParent).gameObject); if (!Object.op_Implicit((Object)(object)orAdd2) || !orAdd2.IsOwner()) { EntangleLogger.Log("Not owner of gun or not synced!"); return; } MagazinePlugMessageData magazinePlugMessageData = new MagazinePlugMessageData { magId = orAdd.objectId, gunId = orAdd2.objectId, isInsert = false }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.MagazinePlug, magazinePlugMessageData); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); EntangleLogger.Log($"Magazine exited from {((Object)__instance).name}! Magazine id is {magazinePlugMessageData.magId} and gun id is {magazinePlugMessageData.gunId}."); } } [HarmonyPatch(typeof(MagazinePlug), "OnPlugInsertComplete")] public static class PlugEnterPatch { public static void Postfix(MagazinePlug __instance) { TransformSyncable orAdd = TransformSyncable.cache.GetOrAdd(((Component)__instance.magazine).gameObject); if (!Object.op_Implicit((Object)(object)orAdd) || !orAdd.IsOwner()) { EntangleLogger.Log("Not owner of mag or not synced!"); return; } MagazineSocket val = ((Il2CppObjectBase)((AlignPlug)__instance)._lastSocket).Cast(); Gun componentInParent = ((Component)val).GetComponentInParent(); if (!Object.op_Implicit((Object)(object)componentInParent)) { EntangleLogger.Log("No gun found!"); return; } TransformSyncable orAdd2 = TransformSyncable.cache.GetOrAdd(((Component)componentInParent).gameObject); if (!Object.op_Implicit((Object)(object)orAdd2) || !orAdd2.IsOwner()) { EntangleLogger.Log("Not owner of gun or not synced!"); return; } MagazinePlugMessageData magazinePlugMessageData = new MagazinePlugMessageData { magId = orAdd.objectId, gunId = orAdd2.objectId, isInsert = true }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.MagazinePlug, magazinePlugMessageData); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); EntangleLogger.Log($"Magazine inserted into {((Object)__instance).name}! Magazine id is {magazinePlugMessageData.magId} and gun id is {magazinePlugMessageData.gunId}."); } } [HarmonyPatch(typeof(StabPoint), "SpawnStab")] public class StabPatch { public static void Postfix(StabPoint __instance, Transform tran, Collision c, float stabForce, ImpactProperties surfaceProperties) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) if (!SteamIntegration.hasLobby) { return; } try { if (Object.op_Implicit((Object)(object)__instance.rb)) { TransformSyncable transformSyncable = TransformSyncable.cache.Get(((Component)__instance.rb).gameObject); if (Object.op_Implicit((Object)(object)transformSyncable) && !transformSyncable.IsOwner()) { return; } } Transform root = ((Component)surfaceProperties).transform.root; string name = ((Object)root).name; if (name.Contains("PlayerRep")) { string[] array = name.Split(new char[1] { '.' }); if (array.Length < 2) { throw new IndexOutOfRangeException(); } long userId = long.Parse(array[1]); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.PlayerAttack, new PlayerAttackMessageData { attackType = (AttackType)32, attackDamage = __instance.damage * ((ImpactPropertiesVariables)surfaceProperties).FireResistance }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.SendMessage(userId, NetworkChannel.Attack, bytes); } } catch { } } } public static class Pool_Settings { public static List GetAllPoolees(this Pool pool) { if (!ObjectSync.poolPairs.TryGetValue(pool, out var value)) { value = new List(); ObjectSync.poolPairs.Add(pool, value); } return value; } public static float GetRelativeSpawnTime(this Poolee poolee) { if (!Object.op_Implicit((Object)(object)poolee.pool)) { return -1f; } float num = (((Component)poolee).gameObject.activeInHierarchy ? poolee.timeSpawned : 0f); return (float)poolee.pool._timeOfLastSpawn - num; } public static Poolee GetAccuratePoolee(this Pool pool, int index, float relativeTime = -1f) { List allPoolees = pool.GetAllPoolees(); if (allPoolees.Count <= 0) { return null; } Poolee result = null; if (relativeTime < 0f) { return allPoolees[Math.Min(index, allPoolees.Count() - 1)]; } int num = -1; float num2 = -1f; for (int i = 0; i < allPoolees.Count(); i++) { Poolee val = allPoolees[i]; float relativeSpawnTime = val.GetRelativeSpawnTime(); if (!(Mathf.Abs(relativeSpawnTime - relativeTime) > Mathf.Abs(num2 - relativeTime)) && Math.Abs(i - index) <= Math.Abs(num - index)) { num = i; num2 = relativeSpawnTime; result = val; } } return result; } } [HarmonyPatch(typeof(Poolee), "OnCleanup")] public static class CleanupPatch { public static void Prefix(Poolee __instance, ref SimplifiedTransform __state) { __state = new SimplifiedTransform(((Component)__instance).transform); } public static void Postfix(Poolee __instance, ref SimplifiedTransform __state) { __state.Apply(((Component)__instance).transform); } } [HarmonyPatch(typeof(Pool), "InstantiatePoolee")] public static class InstantiatePatch { public static bool Prefix(Pool __instance) { if (!Object.op_Implicit((Object)(object)__instance.Prefab)) { return false; } return true; } public static void Postfix(Pool __instance, Poolee __result, Vector3 position, Quaternion rotation) { if (__instance.IsBlacklisted()) { return; } try { if (!__instance._pooledObjects.Contains(__result)) { return; } if (!ObjectSync.poolPairs.TryGetValue(__instance, out var value)) { value = new List(); if (ObjectSync.poolPairs.ContainsKey(__instance)) { ObjectSync.poolPairs[__instance] = value; } else { ObjectSync.poolPairs.Add(__instance, value); } } if (!value.Contains(__result)) { value.Add(__result); __result.onSpawnDelegate = ((Il2CppObjectBase)Delegate.Combine((Delegate)(object)__result.onSpawnDelegate, (Delegate)(object)Action.op_Implicit((Action)delegate(GameObject go) { OnSpawn(go, __instance); }))).Cast>(); } } catch { } } public static void OnSpawn(GameObject spawnedObject, Pool pool) { if (!SteamIntegration.hasLobby || SpawnManager.SpawnOverride) { return; } PooleeSyncable pooleeSyncable = PooleeSyncable._Cache.Get(spawnedObject); if (Object.op_Implicit((Object)(object)pooleeSyncable)) { if (Node.isServer) { pooleeSyncable.SetOwner(SteamIntegration.currentUserId); SpawnTransferMessageData data = new SpawnTransferMessageData { spawnId = pooleeSyncable.id, transform = new SimplifiedTransform(spawnedObject.transform) }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.SpawnTransfer, data); Node.activeNode.BroadcastMessage(NetworkChannel.Object, networkMessage.GetBytes()); } } else if (Node.isServer) { MelonCoroutines.Start(OnSpawnHost(spawnedObject, pool)); } else { MelonCoroutines.Start(OnSpawnClient(spawnedObject)); } } public static IEnumerator OnSpawnClient(GameObject spawnedObject) { if (SceneLoader.loading) { while (SceneLoader.loading) { yield return null; } } for (int i = 0; i < 2; i++) { yield return null; if (Object.op_Implicit((Object)(object)spawnedObject)) { spawnedObject.SetActive(false); } } } public static IEnumerator OnSpawnHost(GameObject spawnedObject, Pool pool) { if (SceneLoader.loading) { while (SceneLoader.loading) { yield return null; } if (!spawnedObject.activeInHierarchy) { yield break; } } Rigidbody[] rbs = Il2CppArrayBase.op_Implicit(spawnedObject.GetComponentsInChildren()); ushort id = ObjectSync.GetNextObjectIdBlock(rbs.Length); byte rbCount = (byte)rbs.Length; for (ushort i = 0; i < rbs.Length; i++) { Rigidbody rb = rbs[i]; GameObject go = ((Component)rb).gameObject; ushort thisId = (ushort)(i + id); TransformSyncable existingSync = TransformSyncable.cache.GetOrAdd(go); if (Object.op_Implicit((Object)(object)existingSync)) { ObjectSync.MoveSyncable(existingSync, thisId); existingSync.ClearOwner(); existingSync.TrySetStale(SteamIntegration.lobbyOwnerId); } else { TransformSyncable.CreateSync(SteamIntegration.lobbyOwnerId, ComponentCacheExtensions.m_RigidbodyCache.GetOrAdd(go), thisId); } ObjectSync.lastId = thisId; } if (rbCount == 0) { ObjectSync.lastId = id; } NetworkMessage clientMessage = NetworkMessage.CreateMessage(data: new SpawnClientMessageData { rbCount = rbCount, spawnId = id, title = SpawnManager.GetPoolTitle(pool), transform = new SimplifiedTransform(spawnedObject.transform) }, type: BuiltInMessageType.SpawnClient); Node.activeNode.BroadcastMessage(NetworkChannel.Object, clientMessage.GetBytes()); PooleeSyncable pooleeSyncable = spawnedObject.AddComponent(); pooleeSyncable.id = id; pooleeSyncable.transforms = Il2CppArrayBase.op_Implicit(spawnedObject.GetComponentsInChildren(true)); } } [HarmonyPatch(typeof(ButtonToggle), "Update")] public static class ButtonTogglePatch { public static Dictionary pressedStates = new Dictionary(); public static void Postfix(ButtonToggle __instance) { if (!SteamIntegration.hasLobby) { return; } bool isPressed = __instance._isPressed; int instanceID = ((Object)__instance).GetInstanceID(); if (!pressedStates.TryGetValue(instanceID, out var value)) { pressedStates[instanceID] = isPressed; } else if (isPressed != value) { pressedStates[instanceID] = isPressed; SceneEventType type = ((!isPressed) ? SceneEventType.ButtonDepress : SceneEventType.ButtonPress); if (SceneEventSync.MarkEvent(SceneEventSync.EventKey(instanceID, type))) { SceneEventSync.SendEvent(type, ((Component)__instance).transform, 0); } } } } [HarmonyPatch(typeof(KeyReciever), "OnMagazineLocked")] public static class KeyLockPatch { public static void Postfix(KeyReciever __instance) { if (SteamIntegration.hasLobby && SceneEventSync.MarkEvent(SceneEventSync.EventKey(((Object)__instance).GetInstanceID(), SceneEventType.KeyLock))) { SceneEventSync.SendEvent(SceneEventType.KeyLock, ((Component)__instance).transform, 0); } } } [HarmonyPatch(typeof(KeyReciever), "OnMagazineUnlocked")] public static class KeyUnlockPatch { public static void Postfix(KeyReciever __instance) { if (SteamIntegration.hasLobby && SceneEventSync.MarkEvent(SceneEventSync.EventKey(((Object)__instance).GetInstanceID(), SceneEventType.KeyUnlock))) { SceneEventSync.SendEvent(SceneEventType.KeyUnlock, ((Component)__instance).transform, 0); } } } [HarmonyPatch(typeof(PullDevice), "OnGripAttachedUpdate")] public static class PullDevicePatch { public static Dictionary pulledStates = new Dictionary(); public static void Postfix(PullDevice __instance) { if (!SteamIntegration.hasLobby) { return; } bool isPulled = __instance._isPulled; int instanceID = ((Object)__instance).GetInstanceID(); if (!pulledStates.TryGetValue(instanceID, out var value)) { pulledStates[instanceID] = isPulled; } else if (isPulled != value) { pulledStates[instanceID] = isPulled; if (isPulled && SceneEventSync.MarkEvent(SceneEventSync.EventKey(instanceID, SceneEventType.PullDevicePull))) { SceneEventSync.SendEvent(SceneEventType.PullDevicePull, ((Component)__instance).transform, 0); } } } } [HarmonyPatch(typeof(AIBrain), "OnDeath")] public static class AIBrainDeathPatch { public static bool isRemoteDeath; public static void Postfix(AIBrain __instance) { if (SteamIntegration.hasLobby && !isRemoteDeath && SceneEventSync.MarkEvent(SceneEventSync.EventKey(((Object)__instance).GetInstanceID(), SceneEventType.NpcDeath))) { PooleeSyncable pooleeSyncable = SceneEventSync.FindPooleeSyncable(((Component)__instance).transform); SceneEventSync.SendEvent(SceneEventType.NpcDeath, ((Component)__instance).transform, (ushort)(Object.op_Implicit((Object)(object)pooleeSyncable) ? pooleeSyncable.id : 0)); } } } public static class AIBrainDespawnPatch { public static bool isRemoteDespawn; } [HarmonyPatch(typeof(Control_MonoMat), "InsertMagazine")] public static class MonoMatInsertPatch { public static bool isRemoteInsert; public static void Postfix(Control_MonoMat __instance, Magazine magazine) { if (!SteamIntegration.hasLobby || isRemoteInsert) { return; } ushort num = 0; if (Object.op_Implicit((Object)(object)magazine)) { TransformSyncable transformSyncable = TransformSyncable.cache.Get(((Component)magazine).gameObject); if (Object.op_Implicit((Object)(object)transformSyncable) && transformSyncable.isValid) { num = transformSyncable.objectId; } } if (num == 0) { EntangleLogger.Warn("A magazine was inserted into a MonoMat but wasn't synced, other players won't see the deposit!"); } SceneEventSync.SendEvent(SceneEventType.MonoMatInsert, ((Component)__instance).transform, num); } } [HarmonyPatch(typeof(SpawnGun), "OnFire")] public class SpawnFirePatch { public static void Postfix(SpawnGun __instance) { if (SteamIntegration.hasLobby) { MelonCoroutines.Start(SpawnGunFire(__instance)); } } public static IEnumerator SpawnGunFire(SpawnGun __instance) { SpawnableObject spawnable = __instance._selectedSpawnable; if ((int)__instance._selectedMode != 0 || !Object.op_Implicit((Object)(object)spawnable)) { yield break; } yield return null; yield return null; Pool objPool = PoolManager.GetPool(spawnable.title); if (Object.op_Implicit((Object)(object)objPool)) { Poolee lastSpawn = objPool._lastSpawn; if (Object.op_Implicit((Object)(object)lastSpawn) && !Node.isServer) { Transform objTransform = ((Component)lastSpawn).transform; Vector3 position = objTransform.position; Quaternion rotation = objTransform.rotation; NetworkMessage message = NetworkMessage.CreateMessage(data: new SpawnRequestMessageData { title = spawnable.title, transform = new SimplifiedTransform(position, rotation) }, type: BuiltInMessageType.SpawnRequest); Node.activeNode.BroadcastMessage(NetworkChannel.Object, message.GetBytes()); ((Component)lastSpawn).gameObject.SetActive(false); } } } } [HarmonyPatch(typeof(Player_Health), "TAKEDAMAGE")] public static class PlayerDamagePatch { public static bool Prefix(Player_Health __instance, float damage, bool crit) { if (!__instance.alive) { return false; } return true; } } [HarmonyPatch(typeof(Hand), "AttachObject")] public static class GripAttachPatch { public static void Prefix(Hand __instance, GameObject objectToAttach) { ObjectSync.OnGripAttached(objectToAttach); } } [HarmonyPatch(typeof(Hand), "DetachObject")] public static class GripDetachPatch { public static void Prefix(Hand __instance, GameObject objectToDetach, bool restoreOriginalParent = true) { try { ObjectSync.OnGripDetached(__instance); } catch { EntangleLogger.Warn("Caught exception while detaching grip!"); } } } [HarmonyPatch(typeof(HandSFX), "PunchAttack")] public static class PunchPatch { public static void Postfix(HandSFX __instance, Collision c, float impulse, float relVelSqr) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (!SteamIntegration.hasLobby) { return; } Transform root = c.gameObject.transform.root; string name = ((Object)root).name; if (name.Contains("PlayerRep")) { string[] array = name.Split(new char[1] { '.' }); if (array.Length < 2) { throw new IndexOutOfRangeException(); } long userId = long.Parse(array[1]); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.PlayerAttack, new PlayerAttackMessageData { attackType = (AttackType)2, attackDamage = (int)(ushort)(impulse / 5f) }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.SendMessage(userId, NetworkChannel.Attack, bytes); } else { ObjectSync.OnBodyPunched(c.gameObject); } } } [HarmonyPatch(typeof(SkeletonHand), "SetHandPose")] public static class PosePatch { public static int prevLeftPose; public static int prevRightPose; public static void Postfix(SkeletonHand __instance, string handPoseName) { //IL_0039: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)__instance.GetCharacterAnimationManager())) { return; } int num = ((Il2CppArrayBase)(object)PlayerScripts.playerHandPoses).IndexOf(handPoseName); if (num <= -1) { return; } Handedness handedness = __instance.handedness; bool flag = true; if ((int)handedness == 1) { flag = prevLeftPose != num; prevLeftPose = num; } else { flag = prevRightPose != num; prevRightPose = num; } if (flag) { HandPoseChangeMessageData handPoseChangeMessageData = new HandPoseChangeMessageData(); handPoseChangeMessageData.userId = SteamIntegration.currentUserId; handPoseChangeMessageData.hand = handedness; handPoseChangeMessageData.poseIndex = (ushort)num; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.HandPose, handPoseChangeMessageData); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); if (PlayerRepresentation.debugRepresentation != null) { PlayerRepresentation.debugRepresentation.UpdatePose(handPoseChangeMessageData.hand, handPoseChangeMessageData.poseIndex); } } } } [HarmonyPatch(typeof(SkeletonHand), "SetCylinderRadius")] public static class GripRadiusPatch { public static float prevLeftRadius; public static float prevRightRadius; public static void Postfix(SkeletonHand __instance, float radius) { //IL_001b: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_007b: 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_00bc: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)__instance.GetCharacterAnimationManager())) { return; } Handedness handedness = __instance.handedness; Handedness val = handedness; Handedness val2 = val; if ((int)val2 != 1) { if ((int)val2 == 2) { if (radius == prevRightRadius) { return; } prevRightRadius = radius; } } else { if (radius == prevLeftRadius) { return; } prevLeftRadius = radius; } GripRadiusMessageData gripRadiusMessageData = new GripRadiusMessageData(); gripRadiusMessageData.userId = SteamIntegration.currentUserId; gripRadiusMessageData.hand = handedness; gripRadiusMessageData.radius = radius; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.GripRadius, gripRadiusMessageData); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); if (PlayerRepresentation.debugRepresentation != null) { PlayerRepresentation.debugRepresentation.UpdatePoseRadius(gripRadiusMessageData.hand, gripRadiusMessageData.radius); } } } [HarmonyPatch(typeof(HandWeaponSlotReciever), "MakeStatic")] public static class WeaponInsertPatch { public static void Prefix(this HandWeaponSlotReciever __instance) { TransformSyncable transformSyncable; if (SteamIntegration.hasLobby && Object.op_Implicit((Object)(object)(transformSyncable = TransformSyncable.cache.Get(((Component)__instance.m_WeaponHost.rb).gameObject)))) { TransformCollisionMessageData data = new TransformCollisionMessageData { objectId = transformSyncable.objectId, enabled = false }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.TransformCollision, data); Node.activeNode.BroadcastMessage(NetworkChannel.Object, networkMessage.GetBytes()); } } } [HarmonyPatch(typeof(HandWeaponSlotReciever), "MakeDynamic")] public static class WeaponExitPatch { public static void Prefix(this HandWeaponSlotReciever __instance) { TransformSyncable transformSyncable; if (SteamIntegration.hasLobby && Object.op_Implicit((Object)(object)(transformSyncable = TransformSyncable.cache.Get(((Component)__instance.m_WeaponHost.rb).gameObject)))) { TransformCollisionMessageData data = new TransformCollisionMessageData { objectId = transformSyncable.objectId, enabled = true }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.TransformCollision, data); Node.activeNode.BroadcastMessage(NetworkChannel.Object, networkMessage.GetBytes()); } } } public static class ZombieMode_Settings { public static bool m_invalidSettings; public unsafe static void SetDifficulty(this Zombie_GameControl __instance, Difficulty dif) { //IL_0002: 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_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_0040: 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_0042: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected I4, but got Unknown __instance.difficulty = dif; __instance.gamePageDiffText.text = ((object)(*(Difficulty*)(&dif))/*cast due to .constrained prefix*/).ToString(); __instance.diffText.text = $"DIFFICULTY: {dif}"; Difficulty val = dif; Difficulty val2 = val; string text = (int)val2 switch { 1 => __instance.medDesc, 2 => __instance.hardDesc, 3 => __instance.harderDesc, 4 => __instance.hardestDesc, _ => __instance.easyDesc, }; __instance.diffDescriptionText.text = text; } } [HarmonyPatch(typeof(Zombie_GameControl), "SetGameMode")] public class GameModePatch { public static void Postfix(Zombie_GameControl __instance, int mode) { if (!ZombieMode_Settings.m_invalidSettings) { EntangleLogger.Log($"Setting ZWH GameMode to {mode}!"); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ZombieMode, new ZombieModeMessageData { mode = (byte)mode }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } ZombieMode_Settings.m_invalidSettings = false; } } [HarmonyPatch(typeof(Zombie_GameControl), "ToggleLoadout")] public class ToggleLoadoutPatch { public static void Postfix(Zombie_GameControl __instance, int loadIndex) { if (!ZombieMode_Settings.m_invalidSettings) { EntangleLogger.Log($"Switching to loadout {loadIndex}!"); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ZombieLoadout, new ZombieLoadoutMessageData { loadIndex = (byte)loadIndex }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } ZombieMode_Settings.m_invalidSettings = false; } } [HarmonyPatch(typeof(Zombie_GameControl), "ToggleDifficulty")] public class ToggleDifficultyPatch { public static void Postfix(Zombie_GameControl __instance) { //IL_0007: 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_002f: Unknown result type (might be due to invalid IL or missing references) EntangleLogger.Log($"Switched to difficulty {__instance.difficulty}"); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ZombieDiff, new ZombieDifficultyMessageData { difficulty = __instance.difficulty }); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } } [HarmonyPatch(typeof(Zombie_GameControl), "StartSelectedMode")] public class ZombieStartPatch { public static void Postfix(Zombie_GameControl __instance) { if (!ZombieMode_Settings.m_invalidSettings) { EntangleLogger.Log("Starting Zombie Warehouse!"); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ZombieStart, new EmptyMessageData()); byte[] bytes = networkMessage.GetBytes(); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, bytes); } } } public static class ZoneTrackingUtilities { public static Dictionary zoneCount = new Dictionary((IEqualityComparer?)new UnityComparer()); public static Dictionary triggerCount = new Dictionary((IEqualityComparer?)new UnityComparer()); public static void Increment(SceneZone zone) { if (!zoneCount.ContainsKey(zone)) { zoneCount.Add(zone, 0); } zoneCount[zone]++; } public static void Decrement(SceneZone zone) { if (!zoneCount.ContainsKey(zone)) { zoneCount.Add(zone, 0); } zoneCount[zone]--; zoneCount[zone] = Mathf.Clamp(zoneCount[zone], 0, int.MaxValue); } public static bool CanEnter(SceneZone zone) { if (!zoneCount.ContainsKey(zone)) { return false; } return zoneCount[zone] <= 1; } public static bool CanExit(SceneZone zone) { if (!zoneCount.ContainsKey(zone)) { return false; } return zoneCount[zone] <= 0; } public static void Increment(PlayerTrigger trigger) { if (!triggerCount.ContainsKey(trigger)) { triggerCount.Add(trigger, 0); } triggerCount[trigger]++; } public static void Decrement(PlayerTrigger trigger) { if (!triggerCount.ContainsKey(trigger)) { triggerCount.Add(trigger, 0); } triggerCount[trigger]--; triggerCount[trigger] = Mathf.Clamp(triggerCount[trigger], 0, int.MaxValue); } public static bool CanEnter(PlayerTrigger trigger) { if (!triggerCount.ContainsKey(trigger)) { return false; } return triggerCount[trigger] <= 1; } public static bool CanExit(PlayerTrigger trigger) { if (!triggerCount.ContainsKey(trigger)) { return false; } return triggerCount[trigger] <= 0; } public static bool IsRemoteRep(Collider other) { Transform val = (Object.op_Implicit((Object)(object)other) ? ((Component)other).transform.root : null); return Object.op_Implicit((Object)(object)val) && ((Object)val).name.Contains("PlayerRep"); } } [HarmonyPatch(typeof(SceneZone), "OnTriggerEnter")] public static class ZoneEnterPatch { public static bool Prefix(SceneZone __instance, Collider other) { if (!SteamIntegration.hasLobby) { return true; } if (ZoneTrackingUtilities.IsRemoteRep(other)) { return false; } if (((Component)other).CompareTag("Player")) { ZoneTrackingUtilities.Increment(__instance); bool flag = ZoneTrackingUtilities.CanEnter(__instance); EntangleLogger.Log($"Entering SceneZone {((Object)__instance).name} with number {ZoneTrackingUtilities.zoneCount[__instance]} and result {flag}"); return flag; } return true; } } [HarmonyPatch(typeof(SceneZone), "OnTriggerExit")] public static class ZoneExitPatch { public static bool Prefix(SceneZone __instance, Collider other) { if (!SteamIntegration.hasLobby) { return true; } if (ZoneTrackingUtilities.IsRemoteRep(other)) { return false; } if (((Component)other).CompareTag("Player")) { ZoneTrackingUtilities.Decrement(__instance); bool flag = ZoneTrackingUtilities.CanExit(__instance); EntangleLogger.Log($"Exiting SceneZone {((Object)__instance).name} with number {ZoneTrackingUtilities.zoneCount[__instance]} and result {flag}"); return flag; } return true; } } [HarmonyPatch(typeof(PlayerTrigger), "OnTriggerEnter")] public static class PlayerTriggerEnterPatch { public static bool Prefix(PlayerTrigger __instance, Collider other) { if (!SteamIntegration.hasLobby) { return true; } if (ZoneTrackingUtilities.IsRemoteRep(other)) { return false; } if (((Component)other).CompareTag("Player")) { ZoneTrackingUtilities.Increment(__instance); bool flag = ZoneTrackingUtilities.CanEnter(__instance); EntangleLogger.Log($"Entering PlayerTrigger {((Object)__instance).name} with number {ZoneTrackingUtilities.triggerCount[__instance]} and result {flag}"); return flag; } return true; } } [HarmonyPatch(typeof(PlayerTrigger), "OnTriggerExit")] public static class PlayerTriggerExitPatch { public static bool Prefix(PlayerTrigger __instance, Collider other) { if (!SteamIntegration.hasLobby) { return true; } if (ZoneTrackingUtilities.IsRemoteRep(other)) { return false; } if (((Component)other).CompareTag("Player")) { ZoneTrackingUtilities.Decrement(__instance); bool flag = ZoneTrackingUtilities.CanExit(__instance); EntangleLogger.Log($"Exiting PlayerTrigger {((Object)__instance).name} with number {ZoneTrackingUtilities.triggerCount[__instance]} and result {flag}"); return flag; } return true; } } } namespace Entanglement.Objects { public static class ObjectBlacklist { private static string[] blacklistedObjects = new string[1] { "[RigManager (Default Brett)]" }; public static bool IsBlacklisted(this GameObject obj) { for (int i = 0; i < blacklistedObjects.Length; i++) { if (obj.transform.InHierarchyOf(blacklistedObjects[i])) { return true; } } return false; } } public static class ObjectSync { public static Dictionary> poolPairs = new Dictionary>((IEqualityComparer?)new UnityComparer()); public static Dictionary syncedObjects = new Dictionary(new UnityComparer()); public static List queuedSyncs = new List(); public static ushort lastId = 0; public static void OnCleanup() { try { RemoveObjects(); } catch { } lastId = 0; } public static void RemoveObjects() { foreach (Syncable value in syncedObjects.Values) { try { value.Cleanup(); } catch { } } syncedObjects.Clear(); queuedSyncs.Clear(); TransformSyncable.cache = new CustomComponentCache(); TransformSyncable.DestructCache = new CustomComponentCache(); } public static void MoveSyncable(Syncable syncable, ushort newId) { syncedObjects.Remove(syncable.objectId); syncedObjects.Remove(newId); syncedObjects.Add(newId, syncable); syncable.objectId = newId; } public static void RegisterSyncable(Syncable syncable, ushort objectId) { if (syncedObjects.ContainsKey(objectId)) { if ((Object)(object)syncedObjects[objectId] != (Object)(object)syncable) { syncedObjects[objectId].Cleanup(); } syncedObjects.Remove(objectId); } syncedObjects.Add(objectId, syncable); lastId = objectId; } public static ushort GetNextObjectId() { ushort num = lastId; do { lastId++; if (lastId == 0) { lastId = 1; } if (!syncedObjects.ContainsKey(lastId)) { return lastId; } } while (lastId != num); return lastId; } public static ushort GetNextObjectIdBlock(int count) { if (count <= 1) { return GetNextObjectId(); } ushort num = lastId; do { lastId++; if (lastId == 0) { lastId = 1; } bool flag = true; for (int i = 0; i < count; i++) { ushort num2 = (ushort)(lastId + i); if (num2 == 0 || syncedObjects.ContainsKey(num2)) { flag = false; break; } } if (flag) { return lastId; } } while (lastId != num); return lastId; } public static ushort QueueSyncable(Syncable syncable) { if (queuedSyncs.Has(syncable)) { int index = queuedSyncs.FindIndex((Syncable o) => (Object)(object)o == (Object)(object)syncable); queuedSyncs.RemoveAt(index); } queuedSyncs.Add(syncable); return (ushort)queuedSyncs.IndexOf(syncable); } public static bool TryGetSyncable(ushort id, out Syncable syncable) { return syncedObjects.TryGetValue(id, out syncable); } public static void GetPooleeData(Transform obj, out Rigidbody[] rigidbodies, out string overrideRootName, out short spawnIndex, out float spawnTime) { Transform root = ((Component)obj).transform.root; overrideRootName = null; spawnIndex = -1; spawnTime = -1f; rigidbodies = null; Magazine val = Magazine.Cache.Get(((Component)root).gameObject); if (Object.op_Implicit((Object)(object)val)) { SpawnableObject spawnableObject = val.magazineData.spawnableObject; if (Object.op_Implicit((Object)(object)spawnableObject)) { spawnTime = 0f; spawnIndex = 0; overrideRootName = spawnableObject.title; rigidbodies = root.GetChildBodies(); } return; } Poolee objPoolee = Poolee.Cache.Get(((Component)root).gameObject); if (Object.op_Implicit((Object)(object)objPoolee)) { Pool pool = objPoolee.pool; if (Object.op_Implicit((Object)(object)pool)) { List allPoolees = pool.GetAllPoolees(); spawnIndex = (short)allPoolees.FindIndex((Poolee o) => (Object)(object)o == (Object)(object)objPoolee); spawnTime = objPoolee.GetRelativeSpawnTime(); overrideRootName = ((Object)pool).name.Remove(0, 7); } rigidbodies = ((Component)objPoolee).transform.GetChildBodies(); } else { rigidbodies = ((Component)obj).transform.GetJointedBodies(); } } public static bool CheckForInstantiation(GameObject prefab, string poolName) { string text = poolName.ToLower(); string text2 = text; if (text2 == "nimbus gun" || text2 == "utility gun") { return true; } Magazine component = prefab.GetComponent(); if (Object.op_Implicit((Object)(object)component)) { return true; } return false; } public static void OnGripAttached(GameObject grip) { if (SteamIntegration.hasLobby) { MelonCoroutines.Start(OnGripValid(grip)); } } public static IEnumerator OnGripValid(GameObject grip) { yield return null; yield return null; if (Object.op_Implicit((Object)(object)grip) && grip.activeInHierarchy && !grip.IsBlacklisted()) { Rigidbody[] rigidbodies = null; GetPooleeData(grip.transform, out rigidbodies, out var overrideRootName, out var spawnIndex, out var spawnTime); object[] obj = new object[5] { ((Object)grip).name, null, null, null, null }; Rigidbody[] array = rigidbodies; obj[1] = ((array != null) ? array.Length : 0); obj[2] = overrideRootName; obj[3] = spawnIndex; obj[4] = spawnTime; EntangleLogger.Log(string.Format("[ObjectSync] Grip '{0}': bodies={1}, root='{2}', spawnIndex={3}, spawnTime={4:F2}", obj)); for (int i = 0; i < rigidbodies.Length; i++) { SyncUtilities.UpdateBodyAttached(rigidbodies[i], overrideRootName, spawnIndex, spawnTime); } } } public static void OnBodyPunched(GameObject hit) { if (SteamIntegration.hasLobby && Object.op_Implicit((Object)(object)hit) && !hit.IsBlacklisted()) { MelonCoroutines.Start(OnPunchValid(hit)); } } public static IEnumerator OnPunchValid(GameObject hit) { yield return null; if (!Object.op_Implicit((Object)(object)hit) || !hit.activeInHierarchy) { yield break; } Transform hitRoot = hit.transform.root; if (Object.op_Implicit((Object)(object)hitRoot) && Object.op_Implicit((Object)(object)((Component)hitRoot).GetComponentInChildren(true))) { yield break; } GetPooleeData(hit.transform, out var rigidbodies, out var overrideRootName, out var spawnIndex, out var spawnTime); if (rigidbodies == null) { yield break; } foreach (Rigidbody rb in rigidbodies) { if (Object.op_Implicit((Object)(object)rb) && !rb.isKinematic && !Object.op_Implicit((Object)(object)TransformSyncable.cache.Get(((Component)rb).gameObject))) { SyncUtilities.UpdateBodyAttached(rb, overrideRootName, spawnIndex, spawnTime); SyncUtilities.UpdateBodyDetached(rb); } } } public static void OnGripDetached(Hand __instance) { if (!SteamIntegration.hasLobby) { return; } GameObject currentAttachedObject = __instance.m_CurrentAttachedObject; if (!Object.op_Implicit((Object)(object)currentAttachedObject) || currentAttachedObject.IsBlacklisted()) { return; } Rigidbody[] jointedBodies = currentAttachedObject.transform.GetJointedBodies(); Rigidbody heldObject = __instance.otherHand.GetHeldObject(); if (!Object.op_Implicit((Object)(object)heldObject) || !jointedBodies.Has(heldObject)) { for (int i = 0; i < jointedBodies.Length; i++) { SyncUtilities.UpdateBodyDetached(jointedBodies[i]); } } } public static void OnForcePullCancelled(GameObject grip) { if (SteamIntegration.hasLobby && !grip.IsBlacklisted()) { Rigidbody[] jointedBodies = grip.transform.GetJointedBodies(); for (int i = 0; i < jointedBodies.Length; i++) { SyncUtilities.UpdateBodyDetached(jointedBodies[i]); } } } } [RegisterTypeInIl2Cpp] public class PooleeSyncable : MonoBehaviour { public static CustomComponentCache _Cache = new CustomComponentCache(); public static Dictionary _PooleeLookup = new Dictionary(new UnityComparer()); public Poolee Poolee; public ushort id; public TransformSyncable[] transforms; private PuppetMaster puppetMaster; private bool searchedPuppet; private float nextMuscleCheck; public PooleeSyncable(IntPtr intPtr) : base(intPtr) { } public void Awake() { Poolee = ((Component)this).GetComponent(); _Cache.Add(((Component)this).gameObject, this); } public void Start() { _PooleeLookup[id] = this; } public void OnDestroy() { _Cache.Remove(((Component)this).gameObject); if (_PooleeLookup.TryGetValue(id, out var value) && (Object)(object)value == (Object)(object)this) { _PooleeLookup.Remove(id); } } public void OnSpawn(long ownerId, SimplifiedTransform simplifiedTransform) { MelonCoroutines.Start(CoOnSpawn(ownerId, simplifiedTransform)); } public void SetOwner(long ownerId) { TransformSyncable[] array = transforms; foreach (TransformSyncable transformSyncable in array) { transformSyncable.ForceOwner(ownerId, checkForMag: false); } } public void FixedUpdate() { if (transforms == null || transforms.Length == 0 || Time.time < nextMuscleCheck) { return; } nextMuscleCheck = Time.time + 0.5f; if (!searchedPuppet) { searchedPuppet = true; puppetMaster = ((Component)this).GetComponentInChildren(true); } if (!Object.op_Implicit((Object)(object)puppetMaster)) { return; } bool flag = false; TransformSyncable[] array = transforms; foreach (TransformSyncable transformSyncable in array) { if (Object.op_Implicit((Object)(object)transformSyncable) && transformSyncable.IsOwner()) { flag = true; break; } } puppetMaster.muscleWeight = (flag ? 1f : 0f); } public IEnumerator CoOnSpawn(long ownerId, SimplifiedTransform simplifiedTransform) { ((Component)this).gameObject.SetActive(false); yield return null; simplifiedTransform.Apply(((Component)this).transform); ((Component)this).gameObject.SetActive(true); SetOwner(ownerId); } } public static class SpawnManager { internal static bool SpawnOverride = false; private static string[] BlacklistedPools = new string[3] { "ProjectilePool", "AudioPlayer", "Utility Gun" }; public static string GetPoolTitle(Pool pool) { return ((Object)pool).name.Remove(0, 7); } public static bool IsBlacklisted(this Pool pool) { string poolTitle = GetPoolTitle(pool); for (int i = 0; i < BlacklistedPools.Length; i++) { if (poolTitle == BlacklistedPools[i] || !Object.op_Implicit((Object)(object)pool.Prefab) || !Object.op_Implicit((Object)(object)pool.Prefab.GetComponentInChildren()) || pool.Prefab.HasBlacklistedComponent()) { return true; } } return false; } public static bool HasBlacklistedComponent(this GameObject prefab) { return Object.op_Implicit((Object)(object)prefab.GetComponentInChildren()); } } [RegisterTypeInIl2Cpp] public abstract class Syncable : MonoBehaviour { public List ownerQueue = new List(); public long staleOwner = 0L; public long lastOwner = 0L; public ushort objectId = 0; public bool isValid = false; public Syncable(IntPtr intPtr) : base(intPtr) { } public virtual void RemoveFromQueue(ushort id) { if (!isValid) { objectId = id; isValid = true; ObjectSync.RegisterSyncable(this, id); EntangleLogger.Log($"Recieved ID from host of {id} on object {((Object)((Component)this).gameObject).name}!"); } } public virtual bool ShouldSync() { return true; } public abstract void SyncUpdate(); protected virtual void FixedUpdate() { if (isValid && IsOwner() && ShouldSync()) { SyncUpdate(); } } protected abstract void UpdateOwner(bool checkForMag = true); public virtual void EnqueueOwner(long owner) { if (!ownerQueue.Contains(owner)) { ownerQueue.Add(owner); } UpdateStale(); UpdateOwner(); } public virtual void DequeueOwner(long owner) { if (ownerQueue.Contains(owner)) { ownerQueue.Remove(owner); } UpdateStale(); UpdateOwner(); } public virtual void ClearOwner() { ownerQueue.Clear(); lastOwner = staleOwner; staleOwner = 0L; } public virtual void TrySetStale(long owner) { lastOwner = staleOwner; if (ownerQueue.Count == 0) { staleOwner = owner; } else { EnqueueOwner(owner); } UpdateOwner(); } public virtual void ForceOwner(long owner, bool checkForMag = true) { lastOwner = staleOwner; ownerQueue.Clear(); staleOwner = owner; UpdateOwner(checkForMag); } public virtual void SendEnqueue() { } public virtual void SendDequeue() { } public virtual void Cleanup() { Object.Destroy((Object)(object)this); } public void UpdateStale() { lastOwner = staleOwner; if (ownerQueue.Count > 0) { staleOwner = ownerQueue[0]; } } public bool IsOwner() { return staleOwner == SteamIntegration.currentUserId; } } [RegisterTypeInIl2Cpp] public class TransformSyncable : Syncable { public enum GripEventType : byte { AttachedEvent, DetachEvent, PrimaryButtonEventDown, PrimaryButtonEvent, PrimaryButtonEventUp } public static CustomComponentCache DestructCache = new CustomComponentCache(); public Prop_Health _CachedHealth; public ObjectDestructable _CachedDestructable; public float objectHealth = 0f; public GripEvents[] events; private bool ignoreThisFrame = false; public static CustomComponentCache cache = new CustomComponentCache(); public Rigidbody rb; public Rigidbody[] _CachedBodies; public Vector3 lastPosition; public Quaternion lastRotation; public Rigidbody targetBody; public GameObject targetGo; public ConfigurableJoint syncJoint; public Gun _CachedGun; public MagazinePlug _CachedPlug; public float startDrag = -1f; public float startAngularDrag = -1f; public const float positionSpring = 5000000f; public const float positionDamper = 100000f; public const float maximumForce = 50000f; public const float linearLimit = 0.005f; public const float extrapolationLimit = 0.25f; public const float followSharpness = 24f; public const float velocityGain = 24f; public const float angularGain = 18f; public const float snapDistance = 2f; public bool hasNetTarget = false; public Vector3 netPosition; public Quaternion netRotation; public Vector3 netVelocity; public Vector3 netAngularVelocity; public float netReceiveTime; public bool isWorldConstrained = false; protected float nextConstraintCheck = 0f; protected RigidbodyInterpolation startInterpolation = (RigidbodyInterpolation)0; protected bool interpolationOverridden = false; protected float timeOfDisable = 0f; private TransformSyncMessageData cachedSyncData; private TransformSyncMessageData cachedRestData; private bool wasSleeping; private float lastRestTime = -10f; private float nextSendTime; private Transform cachedParent; private bool cachedRigAttached; public void SetHealth(float health) { if (Object.op_Implicit((Object)(object)_CachedDestructable)) { _CachedDestructable._health = health; } if (Object.op_Implicit((Object)(object)_CachedHealth)) { _CachedHealth.cur_Health = health; } } public float GetHealth() { if (Object.op_Implicit((Object)(object)_CachedDestructable)) { return _CachedDestructable._health; } if (Object.op_Implicit((Object)(object)_CachedHealth)) { return _CachedHealth.cur_Health; } return 0f; } public void Destruct() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_CachedDestructable)) { _CachedDestructable._health = 0f; _CachedDestructable.TakeDamage(Vector3.up, 10f, true, (AttackType)64); } if (Object.op_Implicit((Object)(object)_CachedHealth)) { _CachedHealth.TIMEDKILL(); } timeOfDisable = Time.realtimeSinceStartup; } public void SetupEvents() { for (byte b = 0; b < events.Length; b++) { GripEvents grip = events[b]; EntangleLogger.Log($"Found event at {b} with name {((Object)grip).name}"); Action action = delegate { AttachedEvent(grip); }; Action action2 = delegate { DetachEvent(grip); }; Action action3 = delegate { PrimaryButtonEventDown(grip); }; Action action4 = delegate { PrimaryButtonEvent(grip); }; Action action5 = delegate { PrimaryButtonEventUp(grip); }; UnityEvent attachedEvent = grip.AttachedEvent; if (attachedEvent != null) { attachedEvent.AddListener(UnityAction.op_Implicit(action)); } UnityEvent detachEvent = grip.DetachEvent; if (detachEvent != null) { detachEvent.AddListener(UnityAction.op_Implicit(action2)); } UnityEvent primaryButtonEventDown = grip.PrimaryButtonEventDown; if (primaryButtonEventDown != null) { primaryButtonEventDown.AddListener(UnityAction.op_Implicit(action3)); } UnityEvent primaryButtonEvent = grip.PrimaryButtonEvent; if (primaryButtonEvent != null) { primaryButtonEvent.AddListener(UnityAction.op_Implicit(action4)); } UnityEvent primaryButtonEventUp = grip.PrimaryButtonEventUp; if (primaryButtonEventUp != null) { primaryButtonEventUp.AddListener(UnityAction.op_Implicit(action5)); } } } public void CallEvent(GripEventType type, byte idx) { EntangleLogger.Log($"Received event of type {type} at index {idx}"); if (events.Length <= idx) { EntangleLogger.Log($"Skipping event out of range (events length: {events.Length}, index: {idx})"); return; } GripEvents val = events[idx]; ignoreThisFrame = true; try { switch (type) { default: { UnityEvent attachedEvent = val.AttachedEvent; if (attachedEvent != null) { attachedEvent.Invoke(); } break; } case GripEventType.DetachEvent: { UnityEvent detachEvent = val.DetachEvent; if (detachEvent != null) { detachEvent.Invoke(); } break; } case GripEventType.PrimaryButtonEvent: { UnityEvent primaryButtonEvent = val.PrimaryButtonEvent; if (primaryButtonEvent != null) { primaryButtonEvent.Invoke(); } break; } case GripEventType.PrimaryButtonEventDown: { UnityEvent primaryButtonEventDown = val.PrimaryButtonEventDown; if (primaryButtonEventDown != null) { primaryButtonEventDown.Invoke(); } break; } case GripEventType.PrimaryButtonEventUp: { UnityEvent primaryButtonEventUp = val.PrimaryButtonEventUp; if (primaryButtonEventUp != null) { primaryButtonEventUp.Invoke(); } break; } } } catch { } ignoreThisFrame = false; } public void AttachedEvent(GripEvents grip) { SendEvent(GripEventType.AttachedEvent, grip); } public void DetachEvent(GripEvents grip) { SendEvent(GripEventType.DetachEvent, grip); } public void PrimaryButtonEventDown(GripEvents grip) { SendEvent(GripEventType.PrimaryButtonEventDown, grip); } public void PrimaryButtonEvent(GripEvents grip) { SendEvent(GripEventType.PrimaryButtonEvent, grip); } public void PrimaryButtonEventUp(GripEvents grip) { SendEvent(GripEventType.PrimaryButtonEventUp, grip); } public void SendEvent(GripEventType type, GripEvents grip) { if (ignoreThisFrame) { return; } byte b = 0; bool flag = false; for (byte b2 = 0; b2 < events.Length; b2++) { if ((Object)(object)grip == (Object)(object)events[b2]) { b = b2; flag = true; break; } } if (flag) { EntangleLogger.Log($"Sending Grip Event of type {type} and index {b}."); GripEventMessageData data = new GripEventMessageData { objectId = objectId, index = b, type = type }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.GripEvent, data); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); } } public TransformSyncable(IntPtr intPtr) : base(intPtr) { }//IL_0043: Unknown result type (might be due to invalid IL or missing references) public override void Cleanup() { DestroyJoint(); SetRemoteInterpolation(remote: false); if (Object.op_Implicit((Object)(object)targetGo)) { Object.Destroy((Object)(object)targetGo); } base.Cleanup(); } private float InterestInterval() { //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_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_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) if (cachedRigAttached) { return 0f; } Dictionary representations = PlayerRepresentation.representations; if (representations.Count == 0) { return 0f; } Vector3 position = ((Component)this).transform.position; float num = float.MaxValue; foreach (PlayerRepresentation value in representations.Values) { if (value != null && !((Object)(object)value.repRoot == (Object)null)) { Vector3 val = value.repRoot.position - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; } } } if (num < 100f) { return 0f; } if (num < 400f) { return 1f / 30f; } if (num < 900f) { return 1f / 15f; } return 0.125f; } private void SendRestState() { //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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (cachedRestData == null) { cachedRestData = new TransformSyncMessageData { resting = true }; } cachedRestData.objectId = objectId; cachedRestData.simplifiedTransform = new SimplifiedTransform(((Component)this).transform); cachedRestData.velocity = Vector3.zero; cachedRestData.angularVelocity = Vector3.zero; TransformSyncBatcher.EnqueueRest(cachedRestData); } public void ApplyRestState(SimplifiedTransform simplifiedTransform) { //IL_0037: 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_0049: 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) //IL_0059: 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_0082: 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_00c9: 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_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) if (SteamIntegration.currentUserId != staleOwner && (!Object.op_Implicit((Object)(object)_CachedPlug) || !_CachedPlug.EnteringOrInside())) { netPosition = simplifiedTransform.position; netRotation = simplifiedTransform.rotation.ExpandQuat(); netVelocity = Vector3.zero; netAngularVelocity = Vector3.zero; hasNetTarget = false; lastRestTime = Time.time; ((Component)this).transform.position = netPosition; ((Component)this).transform.rotation = netRotation; if (Object.op_Implicit((Object)(object)rb) && !rb.isKinematic) { rb.position = netPosition; rb.rotation = netRotation; rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; rb.Sleep(); } if (Object.op_Implicit((Object)(object)targetBody)) { targetBody.position = netPosition; targetBody.rotation = netRotation; } } } public override void SyncUpdate() { //IL_0098: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00fb: 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) float num = InterestInterval(); if (num > 0f) { if (Time.time < nextSendTime) { return; } nextSendTime = Time.time + num; } if (cachedSyncData == null) { cachedSyncData = new TransformSyncMessageData(); } cachedSyncData.objectId = objectId; cachedSyncData.simplifiedTransform = new SimplifiedTransform(((Component)this).transform); cachedSyncData.velocity = (Object.op_Implicit((Object)(object)rb) ? rb.velocity : Vector3.zero); cachedSyncData.angularVelocity = (Object.op_Implicit((Object)(object)rb) ? rb.angularVelocity : Vector3.zero); TransformSyncBatcher.Enqueue(cachedSyncData); if (Object.op_Implicit((Object)(object)targetGo)) { targetGo.transform.position = ((Component)this).transform.position; targetGo.transform.rotation = ((Component)this).transform.rotation; } UpdateStoredPositions(); } public override bool ShouldSync() { Transform parent = ((Component)this).transform.parent; if ((Object)(object)parent != (Object)(object)cachedParent) { cachedParent = parent; cachedRigAttached = Object.op_Implicit((Object)(object)parent) && Object.op_Implicit((Object)(object)((Component)((Component)this).transform).GetComponentInParent()); } if (cachedRigAttached) { return HasChangedPositions(); } return (!Object.op_Implicit((Object)(object)rb)) ? HasChangedPositions() : (!rb.IsSleeping() && HasChangedPositions()); } public bool HasChangedPositions() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Vector3 val = ((Component)this).transform.position - lastPosition; return ((Vector3)(ref val)).sqrMagnitude > 0.0001f || Quaternion.Angle(((Component)this).transform.rotation, lastRotation) > 0.05f; } protected override void UpdateOwner(bool checkForMag = true) { if (lastOwner == SteamIntegration.currentUserId) { objectHealth = GetHealth(); } if (IsOwner()) { hasNetTarget = false; } if (!IsOwner()) { SetHealth(float.PositiveInfinity); } else { SetHealth(objectHealth); } if (TryGetRigidbody(out var rigidbody)) { if (PlayerRepresentation.representations.ContainsKey(lastOwner)) { PlayerRepresentation.representations[lastOwner].IgnoreCollision(rigidbody, ignore: false); } if (PlayerRepresentation.representations.ContainsKey(staleOwner)) { PlayerRepresentation.representations[staleOwner].IgnoreCollision(rigidbody, ignore: true); } try { if (checkForMag && Object.op_Implicit((Object)(object)_CachedGun)) { MagazineSocket magazineSocket = _CachedGun.magazineSocket; if (Object.op_Implicit((Object)(object)magazineSocket) && Object.op_Implicit((Object)(object)magazineSocket._magazinePlug)) { TransformSyncable orAdd = cache.GetOrAdd(((Component)magazineSocket._magazinePlug.magazine).gameObject); if (Object.op_Implicit((Object)(object)orAdd)) { orAdd.ForceOwner(staleOwner, checkForMag: false); } } } } catch { } } if (Object.op_Implicit((Object)(object)_CachedPlug) && lastOwner != staleOwner && staleOwner == SteamIntegration.currentUserId) { Socket lastSocket = ((AlignPlug)_CachedPlug)._lastSocket; if (_CachedPlug.InGun()) { _CachedPlug.ForceEject(); ((AlignPlug)_CachedPlug).InsertPlug(lastSocket); } } } protected void OnEnable() { //IL_0033: 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_006d: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) DestroyJoint(); if (isValid && hasNetTarget && !IsOwner()) { ((Component)this).transform.position = netPosition; ((Component)this).transform.rotation = netRotation; if (TryGetRigidbody(out var rigidbody) && !rigidbody.isKinematic) { rigidbody.position = netPosition; rigidbody.rotation = netRotation; rigidbody.velocity = netVelocity; rigidbody.angularVelocity = netAngularVelocity; } if (Object.op_Implicit((Object)(object)targetBody)) { targetBody.position = netPosition; targetBody.rotation = netRotation; } } } protected void OnDisable() { timeOfDisable = Time.realtimeSinceStartup; DestroyJoint(); SendDequeue(); } protected void UpdateStoredPositions() { //IL_0008: 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_001e: Unknown result type (might be due to invalid IL or missing references) lastPosition = ((Component)this).transform.position; lastRotation = ((Component)this).transform.rotation; } protected virtual void OnCollisionEnter(Collision collision) { if (!SteamIntegration.hasLobby || ((Component)collision.collider).gameObject.IsBlacklisted() || !Object.op_Implicit((Object)(object)collision.rigidbody) || !IsOwner() || (Object.op_Implicit((Object)(object)rb) && !_CachedBodies.IsHolding())) { return; } Transform transform = ((Component)collision.rigidbody).transform; Rigidbody[] rigidbodies = null; long currentUserId = SteamIntegration.currentUserId; ObjectSync.GetPooleeData(transform, out rigidbodies, out var overrideRootName, out var spawnIndex, out var spawnTime); foreach (Rigidbody val in rigidbodies) { TransformSyncable transformSyncable = cache.Get(((Component)val).gameObject); if (!Object.op_Implicit((Object)(object)transformSyncable) && !val.isKinematic) { ushort? num = null; ushort callbackIndex = 0; if (Server.instance != null) { num = ObjectSync.GetNextObjectId(); } Syncable syncable = CreateSync(currentUserId, val, num); if (Server.instance == null) { callbackIndex = ObjectSync.QueueSyncable(syncable); } TransformCreateMessageData data = new TransformCreateMessageData { ownerId = currentUserId, objectId = (ushort)(num.HasValue ? num.Value : 0), callbackIndex = callbackIndex, objectPath = ((Component)val).transform.GetFullPath(overrideRootName), spawnIndex = spawnIndex, spawnTime = spawnTime, enqueueOwner = false }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.TransformCreate, data); Node.activeNode.BroadcastMessage(NetworkChannel.Object, networkMessage.GetBytes()); } } } protected override void FixedUpdate() { base.FixedUpdate(); if (!isValid) { return; } JointCheck(); if (IsOwner() && Object.op_Implicit((Object)(object)rb)) { bool flag = rb.IsSleeping(); if (flag && !wasSleeping) { SendRestState(); } wasSleeping = flag; } if (!IsOwner()) { SetHealth(float.PositiveInfinity); if (Object.op_Implicit((Object)(object)rb) && hasNetTarget && !rb.isKinematic && !IsPluggedIntoGun()) { InterpolateRemote(); } } } protected bool TryGetRigidbody(out Rigidbody rigidbody) { if (Object.op_Implicit((Object)(object)rb)) { rigidbody = rb; return true; } rigidbody = ((Component)this).gameObject.GetComponent(); rb = rigidbody; return (Object)(object)rigidbody != (Object)null; } public static Syncable CreateSync(long owner, Rigidbody rigidbody = null, ushort? objectId = null) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)rigidbody)) { return null; } GameObject gameObject = ((Component)rigidbody).gameObject; TransformSyncable orAdd = cache.GetOrAdd(gameObject); if (Object.op_Implicit((Object)(object)orAdd)) { orAdd.ForceOwner(owner); if (objectId.HasValue) { ObjectSync.MoveSyncable(orAdd, objectId.Value); } return orAdd; } rigidbody.velocity = Vector3.zero; TransformSyncable transformSyncable = gameObject.AddComponent(); cache.Add(gameObject, transformSyncable); transformSyncable._CachedPlug = gameObject.GetComponentInChildren(true); transformSyncable._CachedGun = gameObject.GetComponentInChildren(true); transformSyncable._CachedDestructable = gameObject.GetComponentInChildren(true); transformSyncable._CachedHealth = gameObject.GetComponentInChildren(true); if (Object.op_Implicit((Object)(object)transformSyncable._CachedDestructable)) { DestructCache.Add(((Component)transformSyncable._CachedDestructable).gameObject, transformSyncable); } if (Object.op_Implicit((Object)(object)transformSyncable._CachedHealth)) { DestructCache.Add(((Component)transformSyncable._CachedHealth).gameObject, transformSyncable); } transformSyncable.events = Il2CppArrayBase.op_Implicit(((Component)transformSyncable).GetComponentsInChildren(true)); transformSyncable.SetupEvents(); transformSyncable.rb = rigidbody; transformSyncable.startDrag = rigidbody.drag; transformSyncable.startAngularDrag = rigidbody.angularDrag; transformSyncable._CachedBodies = ((Component)transformSyncable).transform.GetJointedBodies(); transformSyncable.ForceOwner(owner); if (objectId.HasValue) { ushort num = (transformSyncable.objectId = objectId.Value); transformSyncable.isValid = true; ObjectSync.RegisterSyncable(transformSyncable, num); } return transformSyncable; } public override void SendEnqueue() { MelonCoroutines.Start(WaitUntilValid(OnValidEnqueue)); } public override void SendDequeue() { MelonCoroutines.Start(WaitUntilValid(OnValidDequeue)); } public void OnValidEnqueue() { long currentUserId = SteamIntegration.currentUserId; if (!ownerQueue.Contains(currentUserId)) { if (Server.instance != null) { EnqueueOwner(currentUserId); } TransformQueueMessageData data = new TransformQueueMessageData { userId = currentUserId, objectId = objectId, isAdd = true }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.TransformQueue, data); Node.activeNode.BroadcastMessage(NetworkChannel.Object, networkMessage.GetBytes()); } } public void OnValidDequeue() { long currentUserId = SteamIntegration.currentUserId; if (ownerQueue.Contains(currentUserId)) { if (Server.instance != null) { DequeueOwner(currentUserId); } TransformQueueMessageData data = new TransformQueueMessageData { userId = currentUserId, objectId = objectId, isAdd = false }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.TransformQueue, data); Node.activeNode.BroadcastMessage(NetworkChannel.Object, networkMessage.GetBytes()); } } public IEnumerator WaitUntilValid(Action onFinish) { while (!isValid) { yield return null; } onFinish?.Invoke(); } public void ApplyTransform(SimplifiedTransform simplifiedTransform) { //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) ApplyTransform(simplifiedTransform, Vector3.zero, Vector3.zero); } public void ApplyTransform(SimplifiedTransform simplifiedTransform, Vector3 velocity, Vector3 angularVelocity) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (SteamIntegration.currentUserId != staleOwner && (!Object.op_Implicit((Object)(object)_CachedPlug) || !_CachedPlug.EnteringOrInside()) && !(Time.time - lastRestTime < 0.35f)) { netPosition = simplifiedTransform.position; netRotation = simplifiedTransform.rotation.ExpandQuat(); netVelocity = velocity; netAngularVelocity = angularVelocity; netReceiveTime = Time.time; hasNetTarget = true; if (!Object.op_Implicit((Object)(object)rb)) { simplifiedTransform.Apply(((Component)this).transform); } if (!((Component)((Component)this).transform).gameObject.activeSelf && Mathf.Abs(Time.realtimeSinceStartup - timeOfDisable) >= 2f) { ((Component)((Component)this).transform).gameObject.SetActive(true); } } } protected void InterpolateRemote() { //IL_001f: 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_002b: 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_0035: 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) //IL_003c: 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_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_0076: 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_0080: 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_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_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0249: 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) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010f: 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_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0135: 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_00bf: 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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_026c: 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_0286: 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_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: 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_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: 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_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: 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_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: 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_01f6: 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) //IL_0208: Unknown result type (might be due to invalid IL or missing references) float fixedDeltaTime = Time.fixedDeltaTime; float num = Mathf.Min(Time.time - netReceiveTime, 0.25f); Vector3 val = netPosition + netVelocity * num; Quaternion val2 = netRotation; float magnitude = ((Vector3)(ref netAngularVelocity)).magnitude; if (magnitude > 0.001f) { val2 = Quaternion.AngleAxis(magnitude * num * 57.29578f, netAngularVelocity / magnitude) * netRotation; } if (isWorldConstrained) { Vector3 val3 = val - rb.position; if (((Vector3)(ref val3)).sqrMagnitude > 4f) { rb.position = val; rb.rotation = val2; rb.velocity = netVelocity; rb.angularVelocity = netAngularVelocity; return; } rb.velocity = netVelocity + val3 * 24f; Quaternion val4 = val2 * Quaternion.Inverse(rb.rotation); if (val4.w < 0f) { val4.x = 0f - val4.x; val4.y = 0f - val4.y; val4.z = 0f - val4.z; val4.w = 0f - val4.w; } float num2 = Mathf.Clamp(val4.w, -1f, 1f); float num3 = 2f * Mathf.Acos(num2); float num4 = Mathf.Sqrt(1f - num2 * num2); Vector3 val5 = Vector3.zero; if (num4 > 0.001f) { val5 = new Vector3(val4.x / num4, val4.y / num4, val4.z / num4) * (num3 * 18f); } rb.angularVelocity = netAngularVelocity + val5; } else if (Object.op_Implicit((Object)(object)targetBody)) { Vector3 val6 = targetBody.position - val; if (((Vector3)(ref val6)).sqrMagnitude > 4f) { targetBody.position = val; targetBody.rotation = val2; rb.position = val; rb.rotation = val2; rb.velocity = netVelocity; rb.angularVelocity = netAngularVelocity; } else { float num5 = 1f - Mathf.Exp(-24f * fixedDeltaTime); targetBody.MovePosition(Vector3.Lerp(targetBody.position, val, num5)); targetBody.MoveRotation(Quaternion.Slerp(targetBody.rotation, val2, num5)); } } } protected void ReCreateJoint() { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0084: 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_00d4: 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_010c: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: 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_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0265: 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_026f: 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_0278: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)targetGo)) { Object.Destroy((Object)(object)targetGo); } targetGo = new GameObject($"TransformSyncFollow {((Object)((Component)this).transform).name}, {((Object)((Component)this).transform).GetInstanceID()}"); Rigidbody rigidbody; bool flag = TryGetRigidbody(out rigidbody); targetBody = targetGo.AddComponent(); targetBody.isKinematic = true; ((Component)targetBody).transform.position = ((Component)this).transform.position; ((Component)targetBody).transform.rotation = ((Component)this).transform.rotation; DestroyJoint(); if (flag) { targetBody.mass = rigidbody.mass; targetBody.centerOfMass = rigidbody.centerOfMass; ((Component)targetBody).transform.position = ((Component)rigidbody).transform.position; ((Component)targetBody).transform.rotation = ((Component)rigidbody).transform.rotation; rigidbody.isKinematic = false; rigidbody.drag = 0f; rigidbody.angularDrag = 0f; syncJoint = ((Component)rigidbody).gameObject.AddComponent(); ((Joint)syncJoint).axis = Vector3.zero; syncJoint.secondaryAxis = Vector3.zero; ((Joint)syncJoint).connectedBody = targetBody; ((Joint)syncJoint).autoConfigureConnectedAnchor = false; ((Joint)syncJoint).anchor = Vector3.zero; ((Joint)syncJoint).connectedAnchor = Vector3.zero; syncJoint.SetMotion((ConfigurableJointMotion)1); syncJoint.SetDrive(5000000f * rigidbody.mass, 100000f * rigidbody.mass, 50000f * rigidbody.mass); syncJoint.projectionMode = (JointProjectionMode)1; ConfigurableJoint obj = syncJoint; SoftJointLimit lowAngularXLimit = default(SoftJointLimit); ((SoftJointLimit)(ref lowAngularXLimit)).limit = 0.005f; obj.linearLimit = lowAngularXLimit; ConfigurableJoint obj2 = syncJoint; lowAngularXLimit = default(SoftJointLimit); ((SoftJointLimit)(ref lowAngularXLimit)).limit = -5f; obj2.lowAngularXLimit = lowAngularXLimit; ConfigurableJoint obj3 = syncJoint; ConfigurableJoint obj4 = syncJoint; ConfigurableJoint obj5 = syncJoint; SoftJointLimit val = default(SoftJointLimit); ((SoftJointLimit)(ref val)).limit = 5f; SoftJointLimit val2 = (obj5.angularYLimit = val); lowAngularXLimit = (obj3.highAngularXLimit = (obj4.angularZLimit = val2)); syncJoint.projectionDistance = 0.005f; syncJoint.projectionAngle = 5f; ((Joint)syncJoint).enablePreprocessing = false; } } public bool IsPluggedIntoGun() { return Object.op_Implicit((Object)(object)_CachedPlug) && _CachedPlug.EnteringOrInside(); } protected void JointCheck() { if (!IsOwner()) { if (IsPluggedIntoGun()) { DestroyJoint(); SetRemoteInterpolation(remote: true); return; } if (Object.op_Implicit((Object)(object)rb) && Time.time >= nextConstraintCheck) { isWorldConstrained = ComputeWorldConstrained(); nextConstraintCheck = Time.time + 2f; } if (Object.op_Implicit((Object)(object)rb) && !isWorldConstrained && !Object.op_Implicit((Object)(object)syncJoint)) { ReCreateJoint(); } if (Object.op_Implicit((Object)(object)rb) && isWorldConstrained && Object.op_Implicit((Object)(object)syncJoint)) { DestroyJoint(); } SetRemoteInterpolation(remote: true); } else { DestroyJoint(); SetRemoteInterpolation(remote: false); nextConstraintCheck = 0f; } } protected bool ComputeWorldConstrained() { Joint[] array = Il2CppArrayBase.op_Implicit(((Component)this).GetComponents()); Joint[] array2 = array; foreach (Joint val in array2) { if (!Object.op_Implicit((Object)(object)syncJoint) || !((Object)(object)val == (Object)(object)syncJoint)) { return true; } } return false; } protected void SetRemoteInterpolation(bool remote) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)rb)) { if (remote && !interpolationOverridden) { startInterpolation = rb.interpolation; rb.interpolation = (RigidbodyInterpolation)1; interpolationOverridden = true; } else if (!remote && interpolationOverridden) { rb.interpolation = startInterpolation; interpolationOverridden = false; } } } protected void DestroyJoint() { if (Object.op_Implicit((Object)(object)syncJoint)) { Object.Destroy((Object)(object)syncJoint); syncJoint = null; if (TryGetRigidbody(out var rigidbody)) { rigidbody.drag = startDrag; rigidbody.angularDrag = startAngularDrag; } } } } public static class SceneEventSync { private static Dictionary lastEventTimes = new Dictionary(); public const float debounceWindow = 0.4f; private static List firedEvents = new List(); public static long EventKey(int instanceId, SceneEventType type) { return ((long)instanceId << 8) | (long)type; } public static bool MarkEvent(long key) { if (lastEventTimes.TryGetValue(key, out var value) && Time.time - value < 0.4f) { return false; } lastEventTimes[key] = Time.time; return true; } public static void RecordForLateJoin(SceneEventType type, ushort arg, string objectPath) { if (Node.isServer) { firedEvents.Add(new SceneEventMessageData { eventType = type, arg = arg, objectPath = objectPath }); } } public static int ReplayEventsTo(long userId) { int num = 0; foreach (SceneEventMessageData firedEvent in firedEvents) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.SceneEvent, firedEvent); if (networkMessage != null) { Node.activeNode.SendMessage(userId, NetworkChannel.Reliable, networkMessage.GetBytes()); num++; } } return num; } public static void OnSceneCleanup() { lastEventTimes.Clear(); firedEvents.Clear(); ButtonTogglePatch.pressedStates.Clear(); PullDevicePatch.pulledStates.Clear(); } public static PooleeSyncable FindPooleeSyncable(Transform transform) { Transform val = transform; while (Object.op_Implicit((Object)(object)val)) { PooleeSyncable pooleeSyncable = PooleeSyncable._Cache.Get(((Component)val).gameObject); if (Object.op_Implicit((Object)(object)pooleeSyncable)) { return pooleeSyncable; } val = val.parent; } return null; } public static void SendEvent(SceneEventType type, Transform transform, ushort arg = 0) { if (SteamIntegration.hasLobby && Node.activeNode != null) { SceneEventMessageData sceneEventMessageData = new SceneEventMessageData { eventType = type, arg = arg, objectPath = transform.GetFullPath() }; RecordForLateJoin(type, arg, sceneEventMessageData.objectPath); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.SceneEvent, sceneEventMessageData); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); } } public static void ApplyRemoteEvent(SceneEventType type, ushort arg, string objectPath) { if (type == SceneEventType.NpcDeath || type == SceneEventType.NpcDespawn) { ApplyNpcEvent(type, arg, objectPath); return; } Transform fromFullPath = objectPath.GetFromFullPath(); if (!Object.op_Implicit((Object)(object)fromFullPath)) { return; } try { switch (type) { case SceneEventType.ButtonPress: case SceneEventType.ButtonDepress: { ButtonToggle component4 = ((Component)fromFullPath).GetComponent(); if (!Object.op_Implicit((Object)(object)component4) || !MarkEvent(EventKey(((Object)component4).GetInstanceID(), type))) { break; } if (type == SceneEventType.ButtonPress) { UnityEvent onPress = component4.onPress; if (onPress != null) { onPress.Invoke(); } if (!component4._hasBeenPressed) { UnityEvent onPressOneShot = component4.onPressOneShot; if (onPressOneShot != null) { onPressOneShot.Invoke(); } component4._hasBeenPressed = true; } } else { UnityEvent onDepress = component4.onDepress; if (onDepress != null) { onDepress.Invoke(); } } break; } case SceneEventType.KeyLock: case SceneEventType.KeyUnlock: { KeyReciever component2 = ((Component)fromFullPath).GetComponent(); if (!Object.op_Implicit((Object)(object)component2) || !MarkEvent(EventKey(((Object)component2).GetInstanceID(), type))) { break; } if (type == SceneEventType.KeyLock) { UnityEvent onUnlock = component2.onUnlock; if (onUnlock != null) { onUnlock.Invoke(); } } else { UnityEvent onLock = component2.onLock; if (onLock != null) { onLock.Invoke(); } } break; } case SceneEventType.PullDevicePull: { PullDevice component3 = ((Component)fromFullPath).GetComponent(); if (Object.op_Implicit((Object)(object)component3) && MarkEvent(EventKey(((Object)component3).GetInstanceID(), type))) { UnityEvent onHandlePull = component3.OnHandlePull; if (onHandlePull != null) { onHandlePull.Invoke(); } } break; } case SceneEventType.MonoMatInsert: { Control_MonoMat component = ((Component)fromFullPath).GetComponent(); if (!Object.op_Implicit((Object)(object)component) || !ObjectSync.TryGetSyncable(arg, out var syncable) || !(syncable is TransformSyncable)) { break; } TransformSyncable transformSyncable = ((Il2CppObjectBase)syncable).Cast(); Magazine componentInChildren = ((Component)transformSyncable).GetComponentInChildren(true); if (!Object.op_Implicit((Object)(object)componentInChildren)) { break; } MonoMatInsertPatch.isRemoteInsert = true; try { component.InsertMagazine(componentInChildren); break; } finally { MonoMatInsertPatch.isRemoteInsert = false; } } } } catch (Exception ex) { EntangleLogger.Warn($"Failed to apply scene event {type} at {objectPath}: {ex.Message}"); } } private static void ApplyNpcEvent(SceneEventType type, ushort pooleeId, string objectPath) { Transform val = null; if (pooleeId != 0 && PooleeSyncable._PooleeLookup.TryGetValue(pooleeId, out var value) && Object.op_Implicit((Object)(object)value)) { val = ((Component)value).transform; } else if (!string.IsNullOrEmpty(objectPath)) { val = objectPath.GetFromFullPath(); } if (!Object.op_Implicit((Object)(object)val)) { return; } try { switch (type) { case SceneEventType.NpcDeath: { AIBrain componentInChildren = ((Component)val).GetComponentInChildren(true); if (!Object.op_Implicit((Object)(object)componentInChildren) || componentInChildren.isDead) { break; } AIBrainDeathPatch.isRemoteDeath = true; try { componentInChildren.OnDeath(); if (Object.op_Implicit((Object)(object)componentInChildren.puppetMaster)) { componentInChildren.puppetMaster.Kill(); } break; } finally { AIBrainDeathPatch.isRemoteDeath = false; } } case SceneEventType.NpcDespawn: { Poolee component = ((Component)val).GetComponent(); if (!Object.op_Implicit((Object)(object)component) || !((Component)val).gameObject.activeInHierarchy) { break; } AIBrainDespawnPatch.isRemoteDespawn = true; try { component.Despawn((Nullable)null, (Nullable)null); break; } finally { AIBrainDespawnPatch.isRemoteDespawn = false; } } } } catch (Exception ex) { EntangleLogger.Warn($"Failed to apply NPC event {type} for poolee {pooleeId}: {ex.Message}"); } } } } namespace Entanglement.Modularity { public static class DllTools { [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule); [DllImport("Kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("Kernel32.dll")] public static extern uint GetLastError(); public static T GetFunction(string signature, IntPtr hModule) where T : Delegate { if (hModule == IntPtr.Zero) { throw new ArgumentException("hModule was a nullptr!"); } IntPtr procAddress = GetProcAddress(hModule, signature); return Marshal.GetDelegateForFunctionPointer(procAddress); } } public abstract class EntanglementModule { public virtual void OnModuleLoaded() { } public virtual void Update() { } public virtual void LateUpdate() { } public virtual void FixedUpdate() { } public virtual void OnSceneWasInitialized(int buildIndex, string sceneName) { } public virtual void OnLoadingScreen() { } public virtual void OnApplicationQuit() { } } public static class ModuleHandler { public static readonly List loadedModules = new List(); public static string modulePath = Directory.GetCurrentDirectory().Replace('\\', '/') + "/UserData/Entanglement/Modules/"; public static string moduleDependencyPath = modulePath + "Dependencies/"; public static void LoadEmbeddedModule(Assembly holder, string resPath) { byte[] array = EmbeddedResource.LoadFromAssembly(holder, resPath); if (array == null) { throw new Exception("Failed to load resource at '" + resPath + "'"); } Assembly moduleAssembly = Assembly.Load(array); SetupModule(moduleAssembly); } public static void SetupModule(Assembly moduleAssembly) { if (SteamIntegration.isInvalid || !(moduleAssembly != null)) { return; } EntanglementModuleInfo customAttribute = moduleAssembly.GetCustomAttribute(); if (customAttribute != null && customAttribute.moduleType != null) { PrintSpacer(customAttribute); if (typeof(EntanglementModule).IsAssignableFrom(customAttribute.moduleType) && !customAttribute.moduleType.IsAbstract && Activator.CreateInstance(customAttribute.moduleType) is EntanglementModule entanglementModule) { loadedModules.Add(entanglementModule); entanglementModule.OnModuleLoaded(); } } } internal static void PrintSpacer(EntanglementModuleInfo moduleInfo) { EntangleLogger.Log("--==== Entanglement Module ====--", ConsoleColor.Magenta); EntangleLogger.Log(moduleInfo.name + " - v" + moduleInfo.version); if (!string.IsNullOrEmpty(moduleInfo.abbreviation)) { EntangleLogger.Log("aka [" + moduleInfo.abbreviation + "]"); } EntangleLogger.Log("by " + moduleInfo.author); EntangleLogger.Log("--=============================--", ConsoleColor.Magenta); } public static void Update() { foreach (EntanglementModule loadedModule in loadedModules) { loadedModule.Update(); } } public static void FixedUpdate() { foreach (EntanglementModule loadedModule in loadedModules) { loadedModule.FixedUpdate(); } } public static void LateUpdate() { foreach (EntanglementModule loadedModule in loadedModules) { loadedModule.LateUpdate(); } } public static void OnSceneWasInitialized(int buildIndex, string sceneName) { foreach (EntanglementModule loadedModule in loadedModules) { loadedModule.OnSceneWasInitialized(buildIndex, sceneName); } } public static void OnLoadingScreen() { foreach (EntanglementModule loadedModule in loadedModules) { loadedModule.OnLoadingScreen(); } } public static void OnApplicationQuit() { foreach (EntanglementModule loadedModule in loadedModules) { loadedModule.OnApplicationQuit(); } } } [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] public sealed class EntanglementModuleInfo : Attribute { public readonly Type moduleType; public readonly string name; public readonly string author; public readonly string version; public readonly string abbreviation; public EntanglementModuleInfo(Type moduleType, string name, string version = null, string author = null, string abbreviation = null) { this.moduleType = moduleType; this.name = name; this.author = author; this.version = version; if (version == null) { this.version = "0.0.0"; } if (author == null) { this.author = "Unknown"; } this.abbreviation = abbreviation; } } public static class ModuleLogger { public static string GetCallerName() { StackTrace stackTrace = new StackTrace(3, fNeedFileInfo: true); for (int i = 0; i < 3; i++) { StackFrame frame = stackTrace.GetFrame(i); EntanglementModuleInfo customAttribute = frame.GetMethod().DeclaringType.Assembly.GetCustomAttribute(); if (customAttribute != null) { if (string.IsNullOrEmpty(customAttribute.abbreviation)) { return customAttribute.name; } return customAttribute.abbreviation; } } return "Unknown"; } internal static string GetFullMsg(string message) { return "-> [" + GetCallerName() + "] " + message; } public static void Msg(string message) { Msg(ConsoleColor.White, message); } public static void Msg(ConsoleColor color, string message) { EntangleLogger.Log(GetFullMsg(message), color); } public static void Warn(string message) { EntangleLogger.Warn(GetFullMsg(message)); } public static void Error(string message) { EntangleLogger.Error(GetFullMsg(message)); } } } namespace Entanglement.Managers { public static class PlayerDeathManager { public static bool hasDied; public static event Action OnLocalPlayerDied; public static void Initialize() { Player_Health.OnPlayerDeath += PlayerDeath.op_Implicit((Action)DeathHook); } public static void Suicide() { if (!hasDied && !((Object)(object)PlayerScripts.playerHealth == (Object)null) && PlayerScripts.playerHealth.alive) { MelonCoroutines.Start(DoSuicide()); } } private static IEnumerator DoSuicide() { Player_Health health = PlayerScripts.playerHealth; health.ToggleInstantDeathMode(true); health.TAKEDAMAGE(1000f, false); float waited = 0f; while (health.alive && waited < 1f) { waited += Time.deltaTime; yield return null; } health.ToggleInstantDeathMode(false); } public static void CheckLethality() { if (SteamIntegration.hasLobby && !hasDied) { Player_Health playerHealth = PlayerScripts.playerHealth; if (!((Object)(object)playerHealth == (Object)null) && playerHealth.alive && playerHealth.deathIsImminent && playerHealth.curr_Health <= playerHealth.max_Health * 0.5f) { Suicide(); } } } public static void DeathHook() { if (!hasDied) { hasDied = true; MelonCoroutines.Start(OnDeathFinished()); } } public static IEnumerator OnDeathFinished() { PlayerDeathManager.OnLocalPlayerDied?.Invoke(); yield return (object)new WaitForSeconds(1f); EntangleLogger.Log("Died! Sending Death event to all players!"); NetworkMessage message = NetworkMessage.CreateMessage(data: new PlayerEventMessageData { type = PlayerEventType.Death }, type: BuiltInMessageType.PlayerEvent); Node.activeNode.BroadcastMessageP2P(NetworkChannel.Reliable, message.GetBytes()); if (PlayerRepresentation.debugRepresentation != null) { PlayerRepresentation.debugRepresentation.CreateRagdoll(); } float waited = 0f; while (!PlayerScripts.playerHealth.alive && waited < 15f) { waited += Time.deltaTime; yield return null; } hasDied = false; } } [RegisterTypeInIl2Cpp] public class RagdollBehaviour : MonoBehaviour { public Rigidbody[] rbs; public const float idleDespawnSeconds = 120f; public const float activitySpeed = 0.25f; private float lastActivityTime; private float nextActivityCheck; private bool isDespawning; public RagdollBehaviour(IntPtr intPtr) : base(intPtr) { } public void Start() { rbs = Il2CppArrayBase.op_Implicit(((Component)this).GetComponentsInChildren(true)); lastActivityTime = Time.time; } public void FixedUpdate() { //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_0076: 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 (isDespawning) { return; } if (Time.time >= nextActivityCheck) { nextActivityCheck = Time.time + 0.5f; Rigidbody[] array = rbs; foreach (Rigidbody val in array) { if (!Object.op_Implicit((Object)(object)val)) { continue; } Vector3 val2 = val.velocity; if (!(((Vector3)(ref val2)).sqrMagnitude > 0.0625f)) { val2 = val.angularVelocity; if (!(((Vector3)(ref val2)).sqrMagnitude > 1f)) { continue; } } lastActivityTime = Time.time; break; } } if (Time.time - lastActivityTime >= 120f) { isDespawning = true; MelonCoroutines.Start(Despawn()); } } public IEnumerator Despawn() { ((Component)this).transform.position = ((Component)this).transform.GetChild(0).position; ((Component)this).transform.GetChild(0).localPosition = Vector3.zero; Rigidbody[] array = rbs; foreach (Rigidbody rb in array) { rb.isKinematic = true; rb.detectCollisions = false; } float elapsed = 0f; while (elapsed < 1f) { elapsed += Time.deltaTime; ((Component)this).transform.localScale = Vector3.Lerp(Vector3.one, Vector3.zero, elapsed); yield return null; } Object.Destroy((Object)(object)((Component)this).gameObject); } } } namespace Entanglement.Extensions { public static class ArrayExtensions { [StructLayout(LayoutKind.Explicit)] private struct FloatIntUnion { [FieldOffset(0)] public float f; [FieldOffset(0)] public int i; } public static byte[] AddBytes(this byte[] self, byte[] array, ref int index) { for (int i = 0; i < array.Length; i++) { self[index++] = array[i]; } return self; } public static byte[] AddBytes(this byte[] self, byte[] array, int index) { for (int i = 0; i < array.Length; i++) { self[index++] = array[i]; } return self; } public static void WriteFloat(this byte[] self, ref int index, float value) { FloatIntUnion floatIntUnion = new FloatIntUnion { f = value }; self[index++] = (byte)floatIntUnion.i; self[index++] = (byte)(floatIntUnion.i >> 8); self[index++] = (byte)(floatIntUnion.i >> 16); self[index++] = (byte)(floatIntUnion.i >> 24); } public static void WriteShort(this byte[] self, ref int index, short value) { self[index++] = (byte)value; self[index++] = (byte)(value >> 8); } public static void WriteUShort(this byte[] self, ref int index, ushort value) { self[index++] = (byte)value; self[index++] = (byte)(value >> 8); } } public static class AssetBundleUtilities { public static void TryUnloadBundle(string path, bool unloadAllLoadedObjects) { string fileName = Path.GetFileName(path); AssetBundle val = TryGetBundle(fileName); if (val != null) { val.Unload(unloadAllLoadedObjects); } } public static AssetBundle TryLoadBundle(string path, string userDataFolder) { string fileName = Path.GetFileName(path); AssetBundle val = TryGetBundle(fileName); if (!Object.op_Implicit((Object)(object)val)) { val = AssetBundle.LoadFromFile(Path.Combine(userDataFolder, fileName)); if (Object.op_Implicit((Object)(object)val)) { ((Object)val).name = fileName.ToLower(); } } return val; } public static AssetBundle TryGetBundle(string fileName) { fileName = fileName.ToLower(); Il2CppArrayBase val = ((Il2CppObjectBase)AssetBundle.GetAllLoadedAssetBundles()).Cast>(); foreach (AssetBundle item in val) { if (((Object)item).name.ToLower() == fileName) { return item; } } return null; } } public class CustomComponentCache where T : Object { public Dictionary m_Cache = new Dictionary((IEqualityComparer?)new UnityComparer()); public Dictionary m_ChildrenCache = new Dictionary((IEqualityComparer?)new UnityComparer()); public T Get(GameObject go) { if (m_Cache.ContainsKey(go)) { return m_Cache[go]; } return default(T); } public T GetOrAdd(GameObject go) { T val = Get(go); if (Object.op_Implicit((Object)(object)val)) { return val; } T component = go.GetComponent(); Add(go, component); return component; } public T[] GetChildren(GameObject go) { if (m_ChildrenCache.ContainsKey(go)) { T[] array = m_ChildrenCache[go]; if (array.Any((T o) => (Object)(object)o == (Object)null)) { return null; } return array; } return null; } public T[] GetOrAddChildren(GameObject go) { T[] children = GetChildren(go); if (children != null) { return children; } T[] array = Il2CppArrayBase.op_Implicit(go.GetComponentsInChildren()); AddChildren(go, array); return array; } public void Add(GameObject go, T value) { if (m_Cache.ContainsKey(go)) { m_Cache.Remove(go); } m_Cache.Add(go, value); } public void AddChildren(GameObject go, T[] values) { if (m_ChildrenCache.ContainsKey(go)) { m_ChildrenCache.Remove(go); } m_ChildrenCache.Add(go, values); } public void Remove(GameObject go) { m_Cache.Remove(go); } } public static class ComponentCacheExtensions { public static CustomComponentCache m_RigidbodyCache = new CustomComponentCache(); } public static class ConfigurableJointExtensions { public static void SetMotion(this ConfigurableJoint joint, ConfigurableJointMotion motion) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0024: 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) //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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) ConfigurableJointMotion val = (joint.angularZMotion = motion); ConfigurableJointMotion val3 = (joint.angularYMotion = val); ConfigurableJointMotion val5 = (joint.angularXMotion = val3); ConfigurableJointMotion val7 = (joint.zMotion = val5); ConfigurableJointMotion xMotion = (joint.yMotion = val7); joint.xMotion = xMotion; } public static void SetDrive(this ConfigurableJoint joint, float spring, float damper, float maximumForce = float.MaxValue) { //IL_0003: 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_0031: 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_0039: 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_0042: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) JointDrive val = default(JointDrive); ((JointDrive)(ref val)).positionSpring = spring; ((JointDrive)(ref val)).positionDamper = damper; ((JointDrive)(ref val)).maximumForce = maximumForce; joint.rotationDriveMode = (RotationDriveMode)1; JointDrive val2 = (joint.slerpDrive = val); JointDrive val4 = (joint.zDrive = val2); JointDrive xDrive = (joint.yDrive = val4); joint.xDrive = xDrive; } } public static class DictionaryExtensions { public static V TryIdx(this Dictionary dict, K idx) { if (dict.ContainsKey(idx)) { return dict[idx]; } return default(V); } } public static class IEnumerableExtensions { public static void ForEach(this IEnumerable enumerable, Action action) { foreach (T item in enumerable) { action(item); } } } public static class ListExtensions { public static bool Has(this List list, T obj) where T : Object { return list.Any((T o) => (Object)(object)o == (Object)(object)obj); } public static bool Has(this T[] array, T obj) where T : Object { return array.Any((T o) => (Object)(object)o == (Object)(object)obj); } } public static class SearchUtilities { public static Rigidbody[] GetChildBodies(this Transform transform) { return ComponentCacheExtensions.m_RigidbodyCache.GetOrAddChildren(((Component)transform).gameObject); } public static Rigidbody[] GetJointedBodies(this Transform transform) { return transform.GetJointedRoot().GetChildBodies(); } public static Transform GetJointedRoot(this Transform transform) { Transform root = transform.root; if (Object.op_Implicit((Object)(object)Poolee.Cache.Get(((Component)root).gameObject))) { return root; } Transform val = transform; Rigidbody[] array = Il2CppArrayBase.op_Implicit(((Component)val).GetComponentsInParent()); if (array.Length == 0) { return val; } Rigidbody val2 = array[^1]; return ((Component)val2).transform; } } public static class SyncUtilities { public static void UpdateBodyAttached(Rigidbody rb, string rootName, short spawnIndex, float spawnTime) { TransformSyncable orAdd = TransformSyncable.cache.GetOrAdd(((Component)rb).gameObject); if (Object.op_Implicit((Object)(object)orAdd)) { orAdd.SendEnqueue(); } else if (!rb.isKinematic) { long currentUserId = SteamIntegration.currentUserId; ushort? objectId = null; ushort callbackIndex = 0; if (Node.isServer) { objectId = ObjectSync.GetNextObjectId(); } Syncable syncable = TransformSyncable.CreateSync(currentUserId, rb, objectId); syncable.EnqueueOwner(currentUserId); if (Server.instance == null) { callbackIndex = ObjectSync.QueueSyncable(syncable); } TransformCreateMessageData data = new TransformCreateMessageData { ownerId = currentUserId, objectId = (ushort)(objectId.HasValue ? objectId.Value : 0), callbackIndex = callbackIndex, objectPath = ((Component)rb).transform.GetFullPath(rootName), spawnIndex = spawnIndex, spawnTime = spawnTime }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.TransformCreate, data); Node.activeNode.BroadcastMessage(NetworkChannel.Object, networkMessage.GetBytes()); } } public static void UpdateBodyDetached(Rigidbody rb) { TransformSyncable orAdd = TransformSyncable.cache.GetOrAdd(((Component)rb).gameObject); if (Object.op_Implicit((Object)(object)orAdd)) { orAdd.SendDequeue(); } } } public static class TransformExtensions { public static bool InHierarchyOf(this Transform t, string parentName) { if (((Object)t).name == parentName) { return true; } if ((Object)(object)t.parent == (Object)null) { return false; } t = t.parent; return t.InHierarchyOf(parentName); } public static string GetPathToRoot(this Transform t, Transform root) { string text = "/" + ((Object)t).name; while ((Object)(object)t.parent != (Object)null && (Object)(object)t != (Object)(object)root) { t = t.parent; text = "/" + ((Object)t).name + text; } return text; } public static void ForceActivate(this Transform transform) { ((Component)transform).gameObject.SetActive(true); if ((Object)(object)transform.parent != (Object)null) { transform.parent.ForceActivate(); } } public static string GetFullPath(this Transform current, string otherRootName = null) { if ((Object)(object)current.parent == (Object)null) { return (otherRootName != null) ? otherRootName : ((Object)current).name; } return current.parent.GetFullPath(otherRootName) + "/" + Array.FindIndex(current.parent.GetChildrenWithName(((Object)current).name), (Transform o) => (Object)(object)o == (Object)(object)current) + "/" + ((Object)current).name; } public static Transform GetFromFullPath(this string path, int spawnIndex = -1, float spawnTime = -1f) { string[] array = path.Split(new char[1] { '/' }); int num = 0; string text = array[num++]; GameObject val = null; if (spawnIndex < 0) { val = GameObject.Find("/" + text); } else { Pool spawnablePool = SpawnableData.GetSpawnablePool(text); if (Object.op_Implicit((Object)(object)spawnablePool) && Object.op_Implicit((Object)(object)spawnablePool)) { if (ObjectSync.CheckForInstantiation(spawnablePool.Prefab, text)) { val = Object.Instantiate(spawnablePool.Prefab); } else { Poolee accuratePoolee = spawnablePool.GetAccuratePoolee(spawnIndex, spawnTime); if (Object.op_Implicit((Object)(object)accuratePoolee)) { val = ((Component)accuratePoolee).gameObject; } } } } if (!Object.op_Implicit((Object)(object)val)) { return null; } Transform val2 = val.transform; if (((Object)val).name == "CUSTOM_MAP_ROOT") { string text2 = "/CUSTOM_MAP_ROOT"; int num2; for (num2 = 1; num2 < array.Length; num2++) { num2++; text2 = text2 + "/" + array[num2]; } GameObject val3 = GameObject.Find(text2); val2 = ((!Object.op_Implicit((Object)(object)val3)) ? null : val3.transform); } else { for (int i = num; i < array.Length; i++) { val2 = (Object.op_Implicit((Object)(object)val2) ? val2.GetChildWithName(int.Parse(array[i++]), array[i]) : val.transform.GetChildWithName(int.Parse(array[i++]), array[i])); } } return val2; } public static Transform GetChildWithName(this Transform transform, int index, string name) { return transform.GetChildrenWithName(name)[index]; } public static Transform[] GetChildrenWithName(this Transform t, string name) { Transform[] array = (Transform[])(object)new Transform[t.childCount]; for (int i = 0; i < t.childCount; i++) { Transform child = t.GetChild(i); if (((Object)child).name == name) { array[i] = child; } } return array; } public static Transform[] GetGrandChildren(this Transform t) { List list = new List(t.childCount * t.childCount); for (int i = 0; i < t.childCount; i++) { Transform child = t.GetChild(i); list.Add(child); list.AddRange(child.GetGrandChildren()); } return list.ToArray(); } public static Vector3 TransformPosition(this Transform t, Vector3 position) { //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) return position + t.position; } public static Vector3 TransformPosition(this Vector3 v, Vector3 position) { //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_0002: Unknown result type (might be due to invalid IL or missing references) return position + v; } public static Vector3 InverseTransformPosition(this Transform t, Vector3 position) { //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) return position - t.position; } public static Vector3 InverseTransformPosition(this Vector3 v, Vector3 position) { //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_0002: Unknown result type (might be due to invalid IL or missing references) return position - v; } } public class UnityComparer : IEqualityComparer, IEqualityComparer { public bool Equals(Object lft, Object rht) { return lft == rht; } public bool Equals(ushort lft, ushort rht) { return lft == rht; } public int GetHashCode(Object obj) { return obj.GetInstanceID(); } public int GetHashCode(ushort sh) { return sh.GetHashCode(); } } public static class Vector3Extensions { public static byte[] GetBytes(this Vector3 vector3) { //IL_000c: 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_0034: Unknown result type (might be due to invalid IL or missing references) byte[] self = new byte[12]; int index = 0; self = self.AddBytes(BitConverter.GetBytes(vector3.x), ref index); self = self.AddBytes(BitConverter.GetBytes(vector3.y), ref index); return self.AddBytes(BitConverter.GetBytes(vector3.z), ref index); } public static byte[] GetShortBytes(this Vector3 vector3, float decimal_precision = 1000f) { //IL_000b: 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_0039: Unknown result type (might be due to invalid IL or missing references) byte[] self = new byte[6]; int index = 0; self = self.AddBytes(BitConverter.GetBytes((short)(vector3.x * decimal_precision)), ref index); self = self.AddBytes(BitConverter.GetBytes((short)(vector3.y * decimal_precision)), ref index); return self.AddBytes(BitConverter.GetBytes((short)(vector3.z * decimal_precision)), ref index); } public static void FromBytes(this Vector3 vector3, byte[] bytes, int offset = 0) { int num = offset; vector3.x = BitConverter.ToSingle(bytes, num); num += 4; vector3.y = BitConverter.ToSingle(bytes, num); num += 4; vector3.z = BitConverter.ToSingle(bytes, num); } public static Vector3 FromShortBytes(this byte[] bytes, int offset = 0, float decimal_precision = 1000f) { //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_0066: 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_006a: Unknown result type (might be due to invalid IL or missing references) int num = offset; Vector3 zero = Vector3.zero; zero.x = BitConverter.ToInt16(bytes, num); num += 2; zero.y = BitConverter.ToInt16(bytes, num); num += 2; zero.z = BitConverter.ToInt16(bytes, num); num += 2; zero.x /= decimal_precision; zero.y /= decimal_precision; zero.z /= decimal_precision; return zero; } public static ulong ToULong(this Vector3 vector3) { //IL_0001: 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_0033: Unknown result type (might be due to invalid IL or missing references) ulong num = (ulong)(Mathf.RoundToInt(vector3.x * 100f) + 32768); ulong num2 = (ulong)(Mathf.RoundToInt(vector3.y * 100f) + 32768); ulong num3 = (ulong)(Mathf.RoundToInt(vector3.z * 100f) + 32768); return num + num2 * 65536 + num3 * 4294967296L; } public static Vector3 ToVector3(this ulong u) { //IL_0066: 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_006e: Unknown result type (might be due to invalid IL or missing references) ulong num = u / 4294967296L; ulong num2 = (u - num * 4294967296L) / 65536; ulong num3 = u - num2 * 65536 - num * 4294967296L; return new Vector3(((float)num3 - 32768f) / 100f, ((float)num2 - 32768f) / 100f, ((float)num - 32768f) / 100f); } } public static class XElementExtensions { public static bool TryGetAttribute(this XElement element, string tag, out string value, string fallback = "") { XAttribute val = element.Attribute(XName.op_Implicit(tag)); value = fallback; if (val != null) { value = val.Value; } return val != null; } } } namespace Entanglement.Exceptions { public class ExpectedClientException : Exception { public override string Message => "ExpectedClientException: Server has received a Message which expects a Client."; } public class ExpectedServerException : Exception { public override string Message => "ExpectedServerException: Client has received a Message which expects a Server."; } } namespace Entanglement.Data { public static class BanList { public static List> bannedUsers = new List>(); public static string banlistPath; public static void PullFromFile() { XDocument val = null; banlistPath = PersistentData.GetPath("banlist.xml"); try { if (File.Exists(banlistPath)) { EntangleLogger.Log("Banlist was found, attempting to read it!", ConsoleColor.DarkCyan); string text = File.ReadAllText(banlistPath); val = XDocument.Parse(text); if (val.Root.Name != XName.op_Implicit("BanList")) { throw new ArgumentException("Xml root wasn't BanList, recreating the xml..."); } } } catch (Exception ex) { EntangleLogger.Error("Encountered error while parsing banlist: " + ex.Message + ", it must be recreated to ensure validity, sorry about that!"); val = InstantiateDefault("malformed"); } if (val == null) { val = InstantiateDefault(); } if (val == null) { return; } ((XContainer)val).Descendants(XName.op_Implicit("Ban")).ForEach(delegate(XElement element) { if (element.TryGetAttribute("id", out var value) && element.TryGetAttribute("name", out var value2) && long.TryParse(value, out var result)) { bannedUsers.Add(new Tuple(result, value2)); EntangleLogger.Log($"Found banned id {result}", ConsoleColor.DarkRed); } }); static XDocument InstantiateDefault(string verb = "missing") { EntangleLogger.Log("Banlist was " + verb + ", created it!", ConsoleColor.DarkCyan); XDocument val2 = CreateDefault(); File.WriteAllText(banlistPath, ((object)val2).ToString()); return val2; } } public static XDocument CreateDefault() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown XDocument val = new XDocument(); ((XContainer)val).Add((object)new XElement(XName.op_Implicit("BanList"))); ((XContainer)val.Root).Add((object)new XComment("Example ban: ")); return val; } public static void UpdateBanFile() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown XDocument val = CreateDefault(); foreach (Tuple bannedUser in bannedUsers) { XElement val2 = new XElement(XName.op_Implicit("Ban")); val2.SetAttributeValue(XName.op_Implicit("id"), (object)bannedUser.Item1); XComment val3 = new XComment(bannedUser.Item2); ((XContainer)val.Root).Add((object)val3); ((XContainer)val.Root).Add((object)val2); } EntangleLogger.Log("Banlist changed, updating the xml!", ConsoleColor.DarkCyan); File.WriteAllText(banlistPath, ((object)val).ToString()); } public static void BanUser(long userId, string userName) { Tuple item = new Tuple(userId, userName); if (!bannedUsers.Contains(item)) { bannedUsers.Add(item); } EntangleLogger.Log($"Banned {userName}, id is {userId}!", ConsoleColor.DarkRed); UpdateBanFile(); } public static void UnbanUser(long userId, string userName) { Tuple item = new Tuple(userId, userName); if (bannedUsers.Contains(item)) { bannedUsers.Remove(item); } EntangleLogger.Log($"Unbanned {userName}, id is {userId}!", ConsoleColor.DarkCyan); UpdateBanFile(); } } public static class EmbeddedResource { public static void ListResourcesFromAssembly(Assembly assembly) { string[] manifestResourceNames = assembly.GetManifestResourceNames(); foreach (string text in manifestResourceNames) { EntangleLogger.Log("Resource: " + text, ConsoleColor.DarkCyan); } } public static byte[] LoadFromAssembly(Assembly assembly, string name) { string[] manifestResourceNames = assembly.GetManifestResourceNames(); if (manifestResourceNames.Contains(name)) { EntangleLogger.Log("Loading embedded resource data " + name + "...", ConsoleColor.DarkCyan); using Stream stream = assembly.GetManifestResourceStream(name); using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); EntangleLogger.Log("Done!", ConsoleColor.DarkCyan); return memoryStream.ToArray(); } return null; } } public static class EmebeddedAssetBundle { public static AssetBundle LoadFromAssembly(Assembly assembly, string name) { string[] manifestResourceNames = assembly.GetManifestResourceNames(); if (manifestResourceNames.Contains(name)) { EntangleLogger.Log("Loading embedded bundle data " + name + "...", ConsoleColor.DarkCyan); byte[] array; using (Stream stream = assembly.GetManifestResourceStream(name)) { using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); array = memoryStream.ToArray(); } EntangleLogger.Log("Loading bundle from data " + name + ", please be patient...", ConsoleColor.DarkCyan); AssetBundle result = AssetBundle.LoadFromMemory(Il2CppStructArray.op_Implicit(array)); EntangleLogger.Log("Done!", ConsoleColor.DarkCyan); return result; } return null; } } public class GameSDK { public static void LoadGameSDK() { string path = PersistentData.GetPath("steam_api64.dll"); if (!File.Exists(path)) { EntangleLogger.Log("steam_api64.dll was missing, autoextracting it!"); File.WriteAllBytes(path, EmbeddedResource.LoadFromAssembly(EntanglementMod.entanglementAssembly, "Entanglement.resources.steam_api64.dll")); } DllTools.LoadLibrary(path); } } public static class PersistentData { public static string persistentPath { get; private set; } public static void Initialize() { string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); persistentPath = folderPath + "/EntanglementMod/"; EntangleLogger.Log("Data is at %AppData%/EntanglementMod/", ConsoleColor.DarkCyan); ValidateDirectory(persistentPath); } public static void ValidateDirectory(string path) { if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } } public static string GetPath(string appended) { return persistentPath + appended; } } public static class PhysicsData { public const float Deg2Rad = (float)Math.PI / 180f; public static Vector3 GetVelocity(Vector3 current, Vector3 last, float time) { //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_0002: 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) return (current - last) / time; } public static Vector3 GetVelocity(Vector3 current, Vector3 last) { //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_0007: Unknown result type (might be due to invalid IL or missing references) return GetVelocity(current, last, Time.deltaTime); } public static Vector3 GetAngularDisplacement(Quaternion from, Quaternion to) { //IL_0001: 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_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_000d: 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_006d: 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_0078: 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_007b: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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_003e: 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_0086: Unknown result type (might be due to invalid IL or missing references) Quaternion val = to * Quaternion.Inverse(from); if (val.w < 0f) { val.x = 0f - val.x; val.y = 0f - val.y; val.z = 0f - val.z; val.w = 0f - val.w; } float num = default(float); Vector3 val2 = default(Vector3); ((Quaternion)(ref val)).ToAngleAxis(ref num, ref val2); ((Vector3)(ref val2)).Normalize(); val2 *= (float)Math.PI / 180f; val2 *= num; return val2; } public static Vector3 GetAngularVelocity(Quaternion from, Quaternion to, float time) { //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_0002: 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) return GetAngularDisplacement(from, to) / time; } public static Vector3 GetAngularVelocity(Quaternion from, Quaternion to) { //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_0007: Unknown result type (might be due to invalid IL or missing references) return GetAngularVelocity(from, to, Time.deltaTime); } } public static class PlayerScripts { public static RigManager playerRig; public static PhysBody playerPhysBody; public static Player_Health playerHealth; public static PhysGrounder playerGrounder; public static Hand playerLeftHand; public static Hand playerRightHand; public static bool reloadLevelOnDeath; public static RuntimeAnimatorController playerAnimatorController; public static Il2CppStringArray playerHandPoses; public static void GetPlayerScripts() { playerRig = Player.GetRigManager().GetComponent(); playerHealth = playerRig.playerHealth; reloadLevelOnDeath = playerHealth.reloadLevelOnDeath; if (SteamIntegration.hasLobby) { playerHealth.reloadLevelOnDeath = false; } PhysicsRig physicsRig = playerRig.physicsRig; playerPhysBody = physicsRig.physBody; playerGrounder = playerPhysBody.physG; playerLeftHand = physicsRig.leftHand; playerRightHand = physicsRig.rightHand; playerAnimatorController = playerRig.gameWorldSkeletonRig.characterAnimationManager.animator.runtimeAnimatorController; GetHandPoses(); } public static void GetHandPoses() { if (playerHandPoses == null) { CharacterAnimationManager.FetchHandPoseList(ref playerHandPoses); } } public static Rigidbody GetHeldObject(this Hand hand) { if (!Object.op_Implicit((Object)(object)hand.m_CurrentAttachedObject)) { return null; } return Grip.Cache.Get(hand.m_CurrentAttachedObject).host.rb; } public static bool IsHolding(this Rigidbody[] rigidbodies) { Rigidbody heldObject = playerLeftHand.GetHeldObject(); Rigidbody heldObject2 = playerRightHand.GetHeldObject(); if ((Object.op_Implicit((Object)(object)heldObject) && rigidbodies.Has(heldObject)) || (Object.op_Implicit((Object)(object)heldObject2) && rigidbodies.Has(heldObject2))) { return true; } return false; } } public struct SimplifiedHand { public const ushort size = 5; public float indexCurl; public float middleCurl; public float ringCurl; public float pinkyCurl; public float thumbCurl; public SimplifiedHand(float indexCurl, float middleCurl, float ringCurl, float pinkyCurl, float thumbCurl) { this.indexCurl = indexCurl; this.middleCurl = middleCurl; this.ringCurl = ringCurl; this.pinkyCurl = pinkyCurl; this.thumbCurl = thumbCurl; } public SimplifiedHand(FingerCurl curler) { indexCurl = curler.index; middleCurl = curler.middle; ringCurl = curler.ring; pinkyCurl = curler.pinky; thumbCurl = curler.thumb; } public byte[] GetBytes() { return new byte[5] { (byte)(indexCurl * 255f), (byte)(middleCurl * 255f), (byte)(ringCurl * 255f), (byte)(pinkyCurl * 255f), (byte)(thumbCurl * 255f) }; } public static SimplifiedHand FromBytes(byte[] bytes) { return new SimplifiedHand((float)(int)bytes[0] / 255f, (float)(int)bytes[1] / 255f, (float)(int)bytes[2] / 255f, (float)(int)bytes[3] / 255f, (float)(int)bytes[4] / 255f); } } public struct SimplifiedQuaternion { public short c1; public short c2; public short c3; public byte loss; public const ushort size = 7; public const float PRECISION_OFFSET = 10000f; public static SimplifiedQuaternion SimplifyQuat(Quaternion quat) { //IL_0009: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) float[] array = new float[4] { quat.x, quat.y, quat.z, quat.w }; byte b = 0; float num = 0f; float num2 = 0f; for (byte b2 = 0; b2 < 4; b2++) { if (Math.Abs(array[b2]) > num) { num2 = ((!(array[b2] < 0f)) ? 1 : (-1)); b = b2; num = array[b2]; } } short[] array2 = new short[3]; int num3 = 0; for (int i = 0; i < 4; i++) { if (i != b) { array2[num3++] = (short)(array[i] * num2 * 10000f); } } SimplifiedQuaternion result = default(SimplifiedQuaternion); result.c1 = array2[0]; result.c2 = array2[1]; result.c3 = array2[2]; result.loss = b; return result; } public Quaternion ExpandQuat() { //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) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00db: 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_00d7: Unknown result type (might be due to invalid IL or missing references) if (loss >= 4) { throw new DataCorruptionException($"Expanding a quaternion led to a lost component of {loss}!"); } float num = (float)c1 / 10000f; float num2 = (float)c2 / 10000f; float num3 = (float)c3 / 10000f; float num4 = Mathf.Sqrt(1f - Pow(num) - Pow(num2) - Pow(num3)); return (Quaternion)(loss switch { 0 => new Quaternion(num4, num, num2, num3), 1 => new Quaternion(num, num4, num2, num3), 2 => new Quaternion(num, num2, num4, num3), 3 => new Quaternion(num, num2, num3, num4), _ => Quaternion.identity, }); static float Pow(float x) { return x * x; } } } public struct SimplifiedTransform { public const ushort size = 19; public const ushort size_small = 13; public Vector3 position; public SimplifiedQuaternion rotation; public SimplifiedTransform(Vector3 position, Quaternion rotation) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: 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) this.position = position; this.rotation = SimplifiedQuaternion.SimplifyQuat(rotation); } public SimplifiedTransform(Transform transform) { //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_000f: Unknown result type (might be due to invalid IL or missing references) position = transform.position; rotation = SimplifiedQuaternion.SimplifyQuat(transform.rotation); } public byte[] GetBytes() { List list = new List(); list.AddRange(BitConverter.GetBytes(position.x)); list.AddRange(BitConverter.GetBytes(position.y)); list.AddRange(BitConverter.GetBytes(position.z)); list.AddRange(BitConverter.GetBytes(rotation.c1)); list.AddRange(BitConverter.GetBytes(rotation.c2)); list.AddRange(BitConverter.GetBytes(rotation.c3)); list.Add(rotation.loss); return list.ToArray(); } public byte[] GetSmallBytes(Vector3 root) { //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_000f: Unknown result type (might be due to invalid IL or missing references) List list = new List(); list.AddRange(root.InverseTransformPosition(position).GetShortBytes()); list.AddRange(BitConverter.GetBytes(rotation.c1)); list.AddRange(BitConverter.GetBytes(rotation.c2)); list.AddRange(BitConverter.GetBytes(rotation.c3)); list.Add(rotation.loss); return list.ToArray(); } public static SimplifiedTransform SimplyTransform(Transform transform) { //IL_0001: 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) return SimplyTransform(transform.position, transform.rotation); } public static SimplifiedTransform SimplyTransform(Vector3 position, Quaternion rotation) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return new SimplifiedTransform { position = position, rotation = SimplifiedQuaternion.SimplifyQuat(rotation) }; } public void WriteTo(byte[] buffer, ref int index) { buffer.WriteFloat(ref index, position.x); buffer.WriteFloat(ref index, position.y); buffer.WriteFloat(ref index, position.z); buffer.WriteShort(ref index, rotation.c1); buffer.WriteShort(ref index, rotation.c2); buffer.WriteShort(ref index, rotation.c3); buffer[index++] = rotation.loss; } public static SimplifiedTransform FromBytes(byte[] bytes, int index) { SimplifiedTransform result = new SimplifiedTransform { position = { x = BitConverter.ToSingle(bytes, index) } }; index += 4; result.position.y = BitConverter.ToSingle(bytes, index); index += 4; result.position.z = BitConverter.ToSingle(bytes, index); index += 4; result.rotation.c1 = BitConverter.ToInt16(bytes, index); index += 2; result.rotation.c2 = BitConverter.ToInt16(bytes, index); index += 2; result.rotation.c3 = BitConverter.ToInt16(bytes, index); index += 2; result.rotation.loss = bytes[index]; return result; } public static SimplifiedTransform FromBytes(byte[] bytes) { SimplifiedTransform result = default(SimplifiedTransform); int num = 0; result.position.x = BitConverter.ToSingle(bytes, num); num += 4; result.position.y = BitConverter.ToSingle(bytes, num); num += 4; result.position.z = BitConverter.ToSingle(bytes, num); num += 4; result.rotation.c1 = BitConverter.ToInt16(bytes, num); num += 2; result.rotation.c2 = BitConverter.ToInt16(bytes, num); num += 2; result.rotation.c3 = BitConverter.ToInt16(bytes, num); num += 2; result.rotation.loss = bytes[num]; return result; } public static SimplifiedTransform FromSmallBytes(byte[] bytes, Vector3 root) { //IL_000d: 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_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) SimplifiedTransform result = default(SimplifiedTransform); int num = 0; result.position = root.TransformPosition(bytes.FromShortBytes(num)); num += 6; result.rotation.c1 = BitConverter.ToInt16(bytes, num); num += 2; result.rotation.c2 = BitConverter.ToInt16(bytes, num); num += 2; result.rotation.c3 = BitConverter.ToInt16(bytes, num); num += 2; result.rotation.loss = bytes[num]; return result; } public void Apply(Transform target) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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) Vector3 val = position; Quaternion val2 = rotation.ExpandQuat(); target.position = val; target.rotation = val2; } public void Apply(Rigidbody target) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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) Vector3 val = position; Quaternion val2 = rotation.ExpandQuat(); target.MovePosition(val); target.MoveRotation(val2); } } public static class SpawnableData { public static readonly Dictionary spawnableObjects = new Dictionary(); public static void GetData() { spawnableObjects.Clear(); Il2CppReferenceArray val = Object.FindObjectsOfTypeIncludingAssets(Il2CppType.Of()); foreach (Object item in (Il2CppArrayBase)(object)val) { SpawnableObject val2 = ((Il2CppObjectBase)item).Cast(); if (!spawnableObjects.ContainsKey(val2.title)) { spawnableObjects.Add(val2.title, val2); } } } public static bool TryRegister(string title, out SpawnableObject spawnable) { bool flag = false; if (flag = spawnableObjects.TryGetValue(title, out spawnable)) { PoolManager.RegisterPool(spawnable); } return flag; } public static SpawnableObject TryGetSpawnable(string title) { SpawnableObject spawnable = PoolManager.GetRegisteredSpawnable(title); if (!Object.op_Implicit((Object)(object)spawnable)) { TryRegister(title, out spawnable); } return spawnable; } public static Pool GetSpawnablePool(string title) { if (!Object.op_Implicit((Object)(object)PoolManager.GetRegisteredSpawnable(title))) { TryRegister(title, out var _); } if (!PoolManager.DynamicPools.ContainsKey(title)) { return null; } return PoolManager.DynamicPools[title]; } } public class Checksum { public byte[] hash; public string base64; public Checksum(byte[] hash) { this.hash = hash; base64 = Convert.ToBase64String(hash); } } public static class ChecksumTool { public static Checksum current; public static Checksum Calculate(byte[] data) { using MD5 mD = MD5.Create(); return new Checksum(mD.ComputeHash(data)); } public static void CalculateMod() { using (MD5.Create()) { string currentDirectory = Directory.GetCurrentDirectory(); currentDirectory += "/Mods/Entanglement.dll"; current = Calculate(File.ReadAllBytes(currentDirectory)); EntangleLogger.Log("Hash Base64: " + current.base64, ConsoleColor.DarkCyan); } } } [Serializable] public class DataCorruptionException : Exception { public DataCorruptionException(string message) : base(message) { } public DataCorruptionException(string message, Exception inner) : base(message, inner) { } protected DataCorruptionException(SerializationInfo info, StreamingContext context) : base(info, context) { } } } namespace Entanglement.Network { [Net.SkipHandleOnLoading] public class ItemSyncRequestMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ItemSyncRequest; public override NetworkMessage CreateMessage(ItemSyncRequestData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = Encoding.UTF8.GetBytes(data.title); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length != 0) { string title = Encoding.UTF8.GetString(message.messageData); CustomItemSync.OnItemRequested(sender, title); } } } public class ItemSyncRequestData : NetworkMessageData { public string title; } [Net.SkipHandleOnLoading] public class ItemSyncUnavailableMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ItemSyncUnavailable; public override NetworkMessage CreateMessage(ItemSyncUnavailableData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = Encoding.UTF8.GetBytes(data.title); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length != 0) { string title = Encoding.UTF8.GetString(message.messageData); CustomItemSync.OnItemUnavailable(sender, title); } } } public class ItemSyncUnavailableData : NetworkMessageData { public string title; } [Net.SkipHandleOnLoading] public class ItemSyncFileIncomingMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ItemSyncFileIncoming; public override NetworkMessage CreateMessage(ItemSyncFileIncomingData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.title); byte[] bytes2 = Encoding.UTF8.GetBytes(data.fileName); networkMessage.messageData = new byte[1 + bytes.Length + bytes2.Length]; int num = 0; networkMessage.messageData[num++] = (byte)bytes.Length; Array.Copy(bytes, 0, networkMessage.messageData, num, bytes.Length); num += bytes.Length; Array.Copy(bytes2, 0, networkMessage.messageData, num, bytes2.Length); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length != 0) { byte b = message.messageData[0]; string title = Encoding.UTF8.GetString(message.messageData, 1, b); string fileName = Encoding.UTF8.GetString(message.messageData, 1 + b, message.messageData.Length - 1 - b); CustomItemSync.OnFileIncoming(sender, title, fileName); } } } public class ItemSyncFileIncomingData : NetworkMessageData { public string title; public string fileName; } [Net.SkipHandleOnLoading] public class PlayermodelSyncRequestMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.PlayermodelSyncRequest; public override NetworkMessage CreateMessage(PlayermodelSyncRequestData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = Encoding.UTF8.GetBytes(data.modelPath); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length != 0) { string modelPath = Encoding.UTF8.GetString(message.messageData); PlayermodelSync.OnModelRequested(sender, modelPath); } } } public class PlayermodelSyncRequestData : NetworkMessageData { public string modelPath; } public class FantasyChallengeMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.FantasyChal; public override NetworkMessage CreateMessage(FantasyChallengeMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1] { Convert.ToByte(data.index) }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } Arena_GameManager instance = Arena_GameManager.instance; if (Object.op_Implicit((Object)(object)instance)) { Arena_Challenge val = instance.masterChallengeList.ToArray()[(int)message.messageData[0]]; bool isLocked = val.profile.isLocked; val.profile.isLocked = false; instance.arenaChallengeUI.HoverButton(val); val.profile.isLocked = isLocked; } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class FantasyChallengeMessageData : NetworkMessageData { public byte index; } public class FantasyDifficultyMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.FantasyDiff; public override NetworkMessage CreateMessage(FantasyDifficultyMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1] { Convert.ToByte(data.difficulty) }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } Arena_GameManager instance = Arena_GameManager.instance; if (Object.op_Implicit((Object)(object)instance)) { FantasyArena_Settings.m_invalidSettings = true; switch (message.messageData[0]) { default: instance.arenaChallengeUI.SetEasyDifficulty(); break; case 1: instance.arenaChallengeUI.SetMediumDifficulty(); break; case 2: instance.arenaChallengeUI.SetHardDifficulty(); break; } Control_UI_Arena arenaChallengeUI = instance.arenaChallengeUI; GameObject difficultyPageObj = arenaChallengeUI.difficultyPageObj; arenaChallengeUI.ActiveChallengePage(difficultyPageObj); arenaChallengeUI.homePageObj.SetActive(false); arenaChallengeUI.trialsPageObj.SetActive(false); ((Component)arenaChallengeUI.resumeSurvivalButtonObj.transform.parent).gameObject.SetActive(false); ((Component)arenaChallengeUI.challengeDescriptionText.transform.parent).gameObject.SetActive(false); Transform obj = ((Component)arenaChallengeUI).transform.Find("Page_Brawl"); if (obj != null) { GameObject gameObject = ((Component)obj).gameObject; if (gameObject != null) { gameObject.SetActive(false); } } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class FantasyDifficultyMessageData : NetworkMessageData { public byte difficulty; } [Net.HandleOnLoaded] public class FantasyEnemyCountMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.FantasyCount; public override NetworkMessage CreateMessage(FantasyEnemyCountMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1] { Convert.ToByte(data.isLow) }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } bool enemyCount = Convert.ToBoolean(message.messageData[0]); Arena_GameManager instance = Arena_GameManager.instance; if (Object.op_Implicit((Object)(object)instance)) { instance.arenaChallengeUI.SetEnemyCount(enemyCount); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class FantasyEnemyCountMessageData : NetworkMessageData { public bool isLow = true; } [Net.HandleOnLoaded] public class ZombieDifficultyMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ZombieDiff; public override NetworkMessage CreateMessage(ZombieDifficultyMessageData data) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1] { (byte)data.difficulty }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_002e: 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 (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } Zombie_GameControl instance = Zombie_GameControl.instance; if (Object.op_Implicit((Object)(object)instance)) { Difficulty dif = (Difficulty)message.messageData[0]; instance.SetDifficulty(dif); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class ZombieDifficultyMessageData : NetworkMessageData { public Difficulty difficulty; } [Net.HandleOnLoaded] public class ZombieLoadoutMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ZombieLoadout; public override NetworkMessage CreateMessage(ZombieLoadoutMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1] { data.loadIndex }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } Zombie_GameControl instance = Zombie_GameControl.instance; if (Object.op_Implicit((Object)(object)instance)) { byte b = message.messageData[0]; ZombieMode_Settings.m_invalidSettings = true; instance.ToggleLoadout((int)b); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class ZombieLoadoutMessageData : NetworkMessageData { public byte loadIndex; } [Net.HandleOnLoaded] public class ZombieModeMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ZombieMode; public override NetworkMessage CreateMessage(ZombieModeMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1] { data.mode }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } Zombie_GameControl instance = Zombie_GameControl.instance; if (Object.op_Implicit((Object)(object)instance)) { byte gameMode = message.messageData[0]; ZombieMode_Settings.m_invalidSettings = true; instance.SetGameMode((int)gameMode); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class ZombieModeMessageData : NetworkMessageData { public byte mode; } [Net.HandleOnLoaded] public class ZombieStartMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ZombieStart; public override NetworkMessage CreateMessage(EmptyMessageData data) { return new NetworkMessage(); } public override void HandleMessage(NetworkMessage message, long sender) { Zombie_GameControl instance = Zombie_GameControl.instance; if (Object.op_Implicit((Object)(object)instance)) { ZombieMode_Settings.m_invalidSettings = true; instance.StartSelectedMode(); ZombieMode_Settings.m_invalidSettings = false; instance.uiGameDisplayPageObj.SetActive(true); instance.uiSelectPageObj.SetActive(false); Transform parent = instance.uiGameDisplayPageObj.transform.parent; ((Component)parent.Find("LoadoutPage")).gameObject.SetActive(false); ((Component)parent.Find("CustomModePage")).gameObject.SetActive(false); ((Component)parent.Find("DifficultySelectPage")).gameObject.SetActive(false); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } [Net.SkipHandleOnLoading] public class BalloonShotMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.BalloonShot; public override NetworkMessage CreateMessage(BalloonShotMessageData data) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[15]; int num = 0; networkMessage.messageData[num++] = SteamIntegration.GetByteId(data.userId); networkMessage.messageData[num++] = (byte)data.balloonColor; byte[] smallBytes = data.balloonTransform.GetSmallBytes(PlayerRepresentation.syncedRoot.position); for (int i = 0; i < 13; i++) { networkMessage.messageData[num++] = smallBytes[i]; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_0035: 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_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) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00b9: 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_00c2: 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) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); BalloonColor val = (BalloonColor)message.messageData[num++]; if (PlayerRepresentation.representations.ContainsKey(longId)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[longId]; byte[] array = new byte[13]; for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromSmallBytes(array, playerRepresentation.repRoot.position); Vector3 position = simplifiedTransform.position; Quaternion val2 = simplifiedTransform.rotation.ExpandQuat(); PoolSpawner.SpawnBalloonProjectile(position, val2, val); PoolSpawner.SpawnMuzzleFlare(position, val2, (MuzzleFlareType)0); simplifiedTransform.Apply(((Component)playerRepresentation.repBalloonSFX).transform); playerRepresentation.repBalloonSFX.GunShot(); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Attack, bytes, longId); } } } public class BalloonShotMessageData : NetworkMessageData { public long userId; public BalloonColor balloonColor; public SimplifiedTransform balloonTransform; } [Net.SkipHandleOnLoading] public class FileTransferBeginMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.FileTransferBegin; public override NetworkMessage CreateMessage(FileTransferBeginData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.fileName); networkMessage.messageData = new byte[8 + bytes.Length]; int num = 0; networkMessage.messageData[num++] = (byte)(data.transferId & 0xFF); networkMessage.messageData[num++] = (byte)(data.transferId >> 8); networkMessage.messageData[num++] = (byte)data.category; byte[] bytes2 = BitConverter.GetBytes(data.totalBytes); Array.Copy(bytes2, 0, networkMessage.messageData, num, 4); num += 4; networkMessage.messageData[num++] = (byte)bytes.Length; Array.Copy(bytes, 0, networkMessage.messageData, num, bytes.Length); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length >= 8) { int num = 0; ushort transferId = (ushort)(message.messageData[num] | (message.messageData[num + 1] << 8)); num += 2; FileTransferCategory category = (FileTransferCategory)message.messageData[num++]; int totalBytes = BitConverter.ToInt32(message.messageData, num); num += 4; byte count = message.messageData[num++]; string fileName = Encoding.UTF8.GetString(message.messageData, num, count); FileTransferManager.OnBeginReceived(sender, new FileTransferBeginData { transferId = transferId, category = category, totalBytes = totalBytes, fileName = fileName }); } } } public class FileTransferBeginData : NetworkMessageData { public ushort transferId; public FileTransferCategory category; public int totalBytes; public string fileName; } [Net.SkipHandleOnLoading] public class FileTransferChunkMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.FileTransferChunk; public override NetworkMessage CreateMessage(FileTransferChunkData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[2 + data.chunk.Length]; networkMessage.messageData[0] = (byte)(data.transferId & 0xFF); networkMessage.messageData[1] = (byte)(data.transferId >> 8); Array.Copy(data.chunk, 0, networkMessage.messageData, 2, data.chunk.Length); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length >= 2) { ushort transferId = (ushort)(message.messageData[0] | (message.messageData[1] << 8)); byte[] array = new byte[message.messageData.Length - 2]; Array.Copy(message.messageData, 2, array, 0, array.Length); FileTransferManager.OnChunkReceived(sender, new FileTransferChunkData { transferId = transferId, chunk = array }); } } } public class FileTransferChunkData : NetworkMessageData { public ushort transferId; public byte[] chunk; } [Net.SkipHandleOnLoading] public class ObjectDestroyMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ObjectDestroy; public override NetworkMessage CreateMessage(ObjectDestroyMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[2]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectId), ref index); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } ushort id = BitConverter.ToUInt16(message.messageData, 0); if (ObjectSync.TryGetSyncable(id, out var syncable) && syncable is TransformSyncable) { TransformSyncable transformSyncable = ((Il2CppObjectBase)syncable).Cast(); transformSyncable.Destruct(); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class ObjectDestroyMessageData : NetworkMessageData { public ushort objectId; } public class EmptyMessageData : NetworkMessageData { } [Net.SkipHandleOnLoading] public class GripEventMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.GripEvent; public override NetworkMessage CreateMessage(GripEventMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[4]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectId), ref index); networkMessage.messageData[index++] = data.index; networkMessage.messageData[index++] = (byte)data.type; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; if (ObjectSync.TryGetSyncable(id, out var syncable) && syncable is TransformSyncable) { TransformSyncable transformSyncable = ((Il2CppObjectBase)syncable).Cast(); byte idx = message.messageData[num++]; TransformSyncable.GripEventType type = (TransformSyncable.GripEventType)message.messageData[num++]; transformSyncable.CallEvent(type, idx); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class GripEventMessageData : NetworkMessageData { public ushort objectId; public byte index; public TransformSyncable.GripEventType type; } [Net.SkipHandleOnLoading] public class IDCallbackMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.IDCallback; public override NetworkMessage CreateMessage(IDCallbackMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[2 * (data.destroySync ? 1 : 2) + 1]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectIndex), ref index); networkMessage.messageData[index++] = Convert.ToByte(data.destroySync); if (!data.destroySync) { networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.newId), ref index); } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; ushort index = BitConverter.ToUInt16(message.messageData, num); num += 2; if (!Convert.ToBoolean(message.messageData[num++])) { ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; try { Syncable syncable = ObjectSync.queuedSyncs[index]; syncable.RemoveFromQueue(id); return; } catch { return; } } Syncable syncable2 = ObjectSync.queuedSyncs[index]; syncable2.Cleanup(); } } public class IDCallbackMessageData : NetworkMessageData { public ushort objectIndex; public bool destroySync = false; public ushort newId; } [Net.SkipHandleOnLoading] public class MagazinePlugMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.MagazinePlug; public override NetworkMessage CreateMessage(MagazinePlugMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[5]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.magId), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.gunId), ref index); networkMessage.messageData[index++] = Convert.ToByte(data.isInsert); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { EntangleLogger.Log("Received mag sync message!"); if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } EntangleLogger.Log("Got past load and length!"); int num = 0; ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; ushort id2 = BitConverter.ToUInt16(message.messageData, num); num += 2; if (ObjectSync.TryGetSyncable(id, out var syncable) && ObjectSync.TryGetSyncable(id2, out var syncable2)) { EntangleLogger.Log("Got syncables!"); TransformSyncable transformSyncable = ((Il2CppObjectBase)syncable).TryCast(); TransformSyncable transformSyncable2 = ((Il2CppObjectBase)syncable2).TryCast(); if (Object.op_Implicit((Object)(object)transformSyncable) && Object.op_Implicit((Object)(object)transformSyncable2)) { if (Object.op_Implicit((Object)(object)transformSyncable._CachedPlug) && Object.op_Implicit((Object)(object)transformSyncable2._CachedGun)) { bool flag = Convert.ToBoolean(message.messageData[num++]); MagazineSocket magazineSocket = transformSyncable2._CachedGun.magazineSocket; if (flag) { ((AlignPlug)transformSyncable._CachedPlug).InsertPlug((Socket)(object)magazineSocket); EntangleLogger.Log("Trying to insert a magazine!"); } else { transformSyncable._CachedPlug.ForceEject(); EntangleLogger.Log("Trying to eject a magazine!"); } } else { EntangleLogger.Log("No cached plug or gun? Object names are mag " + ((Object)transformSyncable).name + " and gun " + ((Object)transformSyncable2).name + "."); } } else { EntangleLogger.Log("Failed to cast syncables to TransformSyncable!"); } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class MagazinePlugMessageData : NetworkMessageData { public ushort magId; public ushort gunId; public bool isInsert = true; } [Net.SkipHandleOnLoading] public class TransformCollisionMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.TransformCollision; public override NetworkMessage CreateMessage(TransformCollisionMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[3]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectId), ref index); networkMessage.messageData[index++] = Convert.ToByte(data.enabled); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; bool detectCollisions = Convert.ToBoolean(message.messageData[num++]); if (ObjectSync.TryGetSyncable(id, out var syncable) && syncable is TransformSyncable) { TransformSyncable transformSyncable = ((Il2CppObjectBase)syncable).Cast(); if (Object.op_Implicit((Object)(object)transformSyncable.rb)) { transformSyncable.rb.detectCollisions = detectCollisions; } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Object, bytes, sender); } } } public class TransformCollisionMessageData : NetworkMessageData { public ushort objectId; public bool enabled; } [Net.SkipHandleOnLoading] public class TransformCreateMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.TransformCreate; public override NetworkMessage CreateMessage(TransformCreateMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.objectPath); networkMessage.messageData = new byte[12 + bytes.Length]; int index = 0; networkMessage.messageData[index++] = SteamIntegration.GetByteId(data.ownerId); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectId), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.callbackIndex), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.spawnIndex), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.spawnTime), ref index); networkMessage.messageData[index++] = Convert.ToByte(data.enqueueOwner); networkMessage.messageData = networkMessage.messageData.AddBytes(bytes, ref index); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); ushort num2 = 0; ushort objectIndex = 0; if (Server.instance != null) { num2 = ObjectSync.GetNextObjectId(); message.messageData = message.messageData.AddBytes(BitConverter.GetBytes(num2), num); num += 2; objectIndex = BitConverter.ToUInt16(message.messageData, num); num += 2; } else { num2 = (ObjectSync.lastId = BitConverter.ToUInt16(message.messageData, num)); num += 4; } short spawnIndex = BitConverter.ToInt16(message.messageData, num); num += 2; float spawnTime = BitConverter.ToSingle(message.messageData, num); num += 4; bool flag = Convert.ToBoolean(message.messageData[num++]); byte[] array = new byte[message.messageData.Length - num]; for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } string text = Encoding.UTF8.GetString(array); Transform fromFullPath = text.GetFromFullPath(spawnIndex, spawnTime); bool destroySync = false; if (Object.op_Implicit((Object)(object)fromFullPath)) { EntangleLogger.Log("Retrieved object from path " + text + "!"); TransformSyncable orAdd = TransformSyncable.cache.GetOrAdd(((Component)fromFullPath).gameObject); if (Object.op_Implicit((Object)(object)orAdd)) { EntangleLogger.Log("Object is already synced! Don't freeze it!"); ObjectSync.MoveSyncable(orAdd, num2); orAdd.ClearOwner(); orAdd.TrySetStale(longId); if (flag) { orAdd.EnqueueOwner(longId); } } else { EntangleLogger.Log("Creating sync object!"); Syncable syncable = TransformSyncable.CreateSync(longId, ComponentCacheExtensions.m_RigidbodyCache.GetOrAdd(((Component)fromFullPath).gameObject), num2); if (flag) { syncable.EnqueueOwner(longId); } } } else { EntangleLogger.Warn("Failed to retrieve object from path " + text + "!"); } ObjectSync.lastId = num2; if (Server.instance != null) { IDCallbackMessageData data = new IDCallbackMessageData { objectIndex = objectIndex, newId = num2, destroySync = destroySync }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.IDCallback, data); Server.instance.SendMessage(longId, NetworkChannel.Object, networkMessage.GetBytes()); byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Object, bytes, longId); } } } public class TransformCreateMessageData : NetworkMessageData { public long ownerId; public ushort objectId; public ushort callbackIndex; public short spawnIndex = -1; public float spawnTime = -1f; public bool enqueueOwner = true; public string objectPath; } [Net.SkipHandleOnLoading] public class TransformQueueMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.TransformQueue; public override NetworkMessage CreateMessage(TransformQueueMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[4]; int index = 0; networkMessage.messageData[index++] = SteamIntegration.GetByteId(data.userId); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectId), ref index); networkMessage.messageData[index++] = Convert.ToByte(data.isAdd); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; if (ObjectSync.TryGetSyncable(id, out var syncable)) { if (Convert.ToBoolean(message.messageData[num++])) { syncable.EnqueueOwner(longId); } else { syncable.DequeueOwner(longId); } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessage(NetworkChannel.Object, bytes); } } } public class TransformQueueMessageData : NetworkMessageData { public long userId; public ushort objectId; public bool isAdd; } [Net.SkipHandleOnLoading] public class TransformSyncBatchMessageHandler : NetworkMessageHandler { public const int entrySize = 46; public const int maxEntriesPerMessage = 24; public override byte? MessageIndex => BuiltInMessageType.TransformSyncBatch; public override NetworkMessage CreateMessage(TransformSyncBatchData data) { NetworkMessage networkMessage = new NetworkMessage(); int count = data.entries.Count; networkMessage.messageData = new byte[1 + 46 * count]; int index = 0; networkMessage.messageData[index++] = (byte)count; for (int i = 0; i < count; i++) { TransformSyncMessageData transformSyncMessageData = data.entries[i]; networkMessage.messageData.WriteUShort(ref index, transformSyncMessageData.objectId); transformSyncMessageData.simplifiedTransform.WriteTo(networkMessage.messageData, ref index); networkMessage.messageData.WriteFloat(ref index, transformSyncMessageData.velocity.x); networkMessage.messageData.WriteFloat(ref index, transformSyncMessageData.velocity.y); networkMessage.messageData.WriteFloat(ref index, transformSyncMessageData.velocity.z); networkMessage.messageData.WriteFloat(ref index, transformSyncMessageData.angularVelocity.x); networkMessage.messageData.WriteFloat(ref index, transformSyncMessageData.angularVelocity.y); networkMessage.messageData.WriteFloat(ref index, transformSyncMessageData.angularVelocity.z); networkMessage.messageData[index++] = (byte)(transformSyncMessageData.resting ? 1 : 0); } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_0145: 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) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; byte b = message.messageData[num++]; Vector3 velocity = default(Vector3); Vector3 angularVelocity = default(Vector3); for (int i = 0; i < b; i++) { if (message.messageData.Length < num + 46) { break; } ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromBytes(message.messageData, num); num += 19; velocity.x = BitConverter.ToSingle(message.messageData, num); num += 4; velocity.y = BitConverter.ToSingle(message.messageData, num); num += 4; velocity.z = BitConverter.ToSingle(message.messageData, num); num += 4; angularVelocity.x = BitConverter.ToSingle(message.messageData, num); num += 4; angularVelocity.y = BitConverter.ToSingle(message.messageData, num); num += 4; angularVelocity.z = BitConverter.ToSingle(message.messageData, num); num += 4; bool flag = message.messageData[num++] != 0; if (ObjectSync.TryGetSyncable(id, out var syncable) && syncable is TransformSyncable) { TransformSyncable transformSyncable = ((Il2CppObjectBase)syncable).Cast(); if (flag) { transformSyncable.ApplyRestState(simplifiedTransform); } else { transformSyncable.ApplyTransform(simplifiedTransform, velocity, angularVelocity); } } } if (Server.instance != null) { NetworkChannel channel = ((b <= 0 || message.messageData[46] == 0) ? NetworkChannel.Unreliable : NetworkChannel.Reliable); Server.instance.BroadcastMessageExcept(channel, message.GetBytes(), sender); } } } public class TransformSyncBatchData : NetworkMessageData { public List entries = new List(); } public static class TransformSyncBatcher { private static readonly Dictionary pending = new Dictionary(); private static readonly Dictionary pendingRest = new Dictionary(); private static readonly TransformSyncBatchData reusedBatch = new TransformSyncBatchData(); public static void Enqueue(TransformSyncMessageData data) { if (!pendingRest.ContainsKey(data.objectId)) { pending[data.objectId] = data; } } public static void EnqueueRest(TransformSyncMessageData data) { pending.Remove(data.objectId); pendingRest[data.objectId] = data; } public static void Flush() { if (pending.Count != 0 || pendingRest.Count != 0) { if (Node.activeNode == null || !SteamIntegration.hasLobby) { pending.Clear(); pendingRest.Clear(); } else { FlushSet(pending, NetworkChannel.Unreliable); FlushSet(pendingRest, NetworkChannel.Reliable); } } } private static void FlushSet(Dictionary set, NetworkChannel channel) { if (set.Count == 0) { return; } reusedBatch.entries.Clear(); foreach (TransformSyncMessageData value in set.Values) { reusedBatch.entries.Add(value); if (reusedBatch.entries.Count >= 24) { Send(reusedBatch, channel); reusedBatch.entries.Clear(); } } if (reusedBatch.entries.Count > 0) { Send(reusedBatch, channel); } set.Clear(); } private static void Send(TransformSyncBatchData batch, NetworkChannel channel) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.TransformSyncBatch, batch); if (networkMessage != null) { Node.activeNode.BroadcastMessage(channel, networkMessage.GetBytes()); } } public static void Clear() { pending.Clear(); pendingRest.Clear(); } } [Net.SkipHandleOnLoading] public class TransformSyncMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.TransformSync; public override NetworkMessage CreateMessage(TransformSyncMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[45]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectId), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(data.simplifiedTransform.GetBytes(), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.velocity.x), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.velocity.y), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.velocity.z), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.angularVelocity.x), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.angularVelocity.y), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.angularVelocity.z), ref index); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_007a: 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_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_012e: 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) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; if (ObjectSync.TryGetSyncable(id, out var syncable) && syncable is TransformSyncable) { TransformSyncable transformSyncable = ((Il2CppObjectBase)syncable).Cast(); SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromBytes(message.messageData.ToList().GetRange(num, 19).ToArray()); int num2 = num + 19; Vector3 zero = Vector3.zero; Vector3 zero2 = Vector3.zero; if (message.messageData.Length >= num2 + 24) { zero.x = BitConverter.ToSingle(message.messageData, num2); zero.y = BitConverter.ToSingle(message.messageData, num2 + 4); zero.z = BitConverter.ToSingle(message.messageData, num2 + 8); zero2.x = BitConverter.ToSingle(message.messageData, num2 + 12); zero2.y = BitConverter.ToSingle(message.messageData, num2 + 16); zero2.z = BitConverter.ToSingle(message.messageData, num2 + 20); } transformSyncable.ApplyTransform(simplifiedTransform, zero, zero2); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Unreliable, bytes, sender); } } } public class TransformSyncMessageData : NetworkMessageData { public ushort objectId; public SimplifiedTransform simplifiedTransform; public Vector3 velocity; public Vector3 angularVelocity; public bool resting; } public enum SceneEventType : byte { ButtonPress, ButtonDepress, KeyLock, KeyUnlock, MonoMatInsert, PullDevicePull, NpcDeath, NpcDespawn } [Net.SkipHandleOnLoading] public class SceneEventMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.SceneEvent; public override NetworkMessage CreateMessage(SceneEventMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.objectPath); networkMessage.messageData = new byte[3 + bytes.Length]; int index = 0; networkMessage.messageData[index++] = (byte)data.eventType; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.arg), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(bytes, ref index); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; SceneEventType type = (SceneEventType)message.messageData[num++]; ushort arg = BitConverter.ToUInt16(message.messageData, num); num += 2; byte[] array = new byte[message.messageData.Length - num]; for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } string objectPath = Encoding.UTF8.GetString(array); SceneEventSync.ApplyRemoteEvent(type, arg, objectPath); if (Server.instance != null) { SceneEventSync.RecordForLateJoin(type, arg, objectPath); byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, sender); } } } public class SceneEventMessageData : NetworkMessageData { public SceneEventType eventType; public ushort arg; public string objectPath; } [Net.HandleOnLoaded] public class SpawnClientMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.SpawnClient; public override NetworkMessage CreateMessage(SpawnClientMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.title); networkMessage.messageData = new byte[3 + bytes.Length + 19]; int index = 0; networkMessage.messageData[index++] = data.rbCount; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.spawnId), ref index); byte[] bytes2 = data.transform.GetBytes(); for (int i = 0; i < 19; i++) { networkMessage.messageData[index++] = bytes2[i]; } for (int j = 0; j < bytes.Length; j++) { networkMessage.messageData[index++] = bytes[j]; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } if (!Node.isServer) { int num = 0; byte rbCount = message.messageData[num++]; ushort id = BitConverter.ToUInt16(message.messageData, num); num += 2; byte[] array = new byte[19]; for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } int num2 = message.messageData.Length - num; byte[] array2 = new byte[num2]; for (int j = 0; j < num2; j++) { array2[j] = message.messageData[num++]; } string text = Encoding.UTF8.GetString(array2); SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromBytes(array); EntangleLogger.Log("Received Object Spawn for Spawnable " + text + "!"); MelonCoroutines.Start(RegisterAndSpawn(text, simplifiedTransform.position, simplifiedTransform.rotation.ExpandQuat(), id, rbCount)); return; } throw new ExpectedClientException(); } internal static IEnumerator RegisterAndSpawn(string title, Vector3 position, Quaternion rotation, ushort id, byte rbCount) { //IL_000e: 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_0015: 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) SpawnableObject spawnable = SpawnableData.TryGetSpawnable(title); yield return null; yield return null; if (!Object.op_Implicit((Object)(object)spawnable)) { yield break; } Vector3 scale = spawnable.prefab.transform.localScale; SpawnManager.SpawnOverride = true; Poolee poolee = PoolManager.DynamicPools[title].InstantiatePoolee(position, rotation); ((Component)poolee).transform.position = position; ((Component)poolee).transform.rotation = rotation; ((Component)poolee).transform.localScale = scale; ((Component)poolee).transform.parent = null; ((Component)poolee).gameObject.SetActive(true); SpawnManager.SpawnOverride = false; Rigidbody[] rbs = Il2CppArrayBase.op_Implicit(((Component)poolee).GetComponentsInChildren()); if (rbs.Length == rbCount) { for (ushort i = 0; i < rbs.Length; i++) { Rigidbody rb = rbs[i]; GameObject go = ((Component)rb).gameObject; ushort thisId = (ushort)(i + id); TransformSyncable existingSync = TransformSyncable.cache.GetOrAdd(go); if (Object.op_Implicit((Object)(object)existingSync)) { ObjectSync.MoveSyncable(existingSync, thisId); existingSync.ClearOwner(); existingSync.TrySetStale(SteamIntegration.lobbyOwnerId); } else { TransformSyncable.CreateSync(SteamIntegration.lobbyOwnerId, ComponentCacheExtensions.m_RigidbodyCache.GetOrAdd(go), thisId); } ObjectSync.lastId = thisId; } } if (rbCount == 0) { ObjectSync.lastId = id; } GameObject spawnedObject = ((Component)poolee).gameObject; PooleeSyncable pooleeSyncable = spawnedObject.AddComponent(); pooleeSyncable.id = id; pooleeSyncable.transforms = Il2CppArrayBase.op_Implicit(spawnedObject.GetComponentsInChildren(true)); PuppetMaster puppetMaster = spawnedObject.GetComponentInChildren(true); if (Object.op_Implicit((Object)(object)puppetMaster)) { puppetMaster.muscleWeight = 0f; } } } public class SpawnClientMessageData : NetworkMessageData { public ushort spawnId; public byte rbCount; public string title; public SimplifiedTransform transform; } [Net.HandleOnLoaded] public class SpawnRequestMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.SpawnRequest; public override NetworkMessage CreateMessage(SpawnRequestMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.title); networkMessage.messageData = new byte[bytes.Length + 19]; int num = 0; byte[] bytes2 = data.transform.GetBytes(); for (int i = 0; i < 19; i++) { networkMessage.messageData[num++] = bytes2[i]; } for (int j = 0; j < bytes.Length; j++) { networkMessage.messageData[num++] = bytes[j]; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } if (Node.isServer) { byte[] array = new byte[19]; int num = 0; for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } int num2 = message.messageData.Length - num; byte[] array2 = new byte[num2]; for (int j = 0; j < num2; j++) { array2[j] = message.messageData[num++]; } string text = Encoding.UTF8.GetString(array2); SimplifiedTransform transform = SimplifiedTransform.FromBytes(array); EntangleLogger.Log("Received Object Request for Spawnable " + text + "!"); MelonCoroutines.Start(RegisterAndSpawn(text, transform)); return; } throw new ExpectedServerException(); } internal static IEnumerator RegisterAndSpawn(string title, SimplifiedTransform transform) { SpawnableObject spawnable = SpawnableData.TryGetSpawnable(title); yield return null; yield return null; byte rbCount = 0; ushort id = 0; if (Object.op_Implicit((Object)(object)spawnable)) { Vector3 position = transform.position; Quaternion rotation = transform.rotation.ExpandQuat(); Vector3 scale = spawnable.prefab.transform.localScale; SpawnManager.SpawnOverride = true; Poolee poolee = PoolManager.DynamicPools[title].InstantiatePoolee(position, rotation); ((Component)poolee).transform.position = position; ((Component)poolee).transform.rotation = rotation; ((Component)poolee).transform.localScale = scale; ((Component)poolee).transform.parent = null; ((Component)poolee).gameObject.SetActive(true); SpawnManager.SpawnOverride = false; Rigidbody[] rbs = Il2CppArrayBase.op_Implicit(((Component)poolee).GetComponentsInChildren()); rbCount = (byte)rbs.Length; id = ObjectSync.GetNextObjectIdBlock(rbs.Length); for (ushort i = 0; i < rbs.Length; i++) { Rigidbody rb = rbs[i]; GameObject go = ((Component)rb).gameObject; ushort thisId = (ushort)(i + id); TransformSyncable existingSync = TransformSyncable.cache.GetOrAdd(go); if (Object.op_Implicit((Object)(object)existingSync)) { ObjectSync.MoveSyncable(existingSync, thisId); existingSync.ClearOwner(); existingSync.TrySetStale(SteamIntegration.lobbyOwnerId); } else { TransformSyncable.CreateSync(SteamIntegration.lobbyOwnerId, ComponentCacheExtensions.m_RigidbodyCache.GetOrAdd(go), thisId); } ObjectSync.lastId = thisId; } GameObject spawnedObject = ((Component)poolee).gameObject; PooleeSyncable pooleeSyncable = spawnedObject.AddComponent(); pooleeSyncable.id = id; pooleeSyncable.transforms = Il2CppArrayBase.op_Implicit(spawnedObject.GetComponentsInChildren(true)); } NetworkMessage clientMessage = NetworkMessage.CreateMessage(data: new SpawnClientMessageData { rbCount = rbCount, spawnId = id, title = title, transform = transform }, type: BuiltInMessageType.SpawnClient); Node.activeNode.BroadcastMessage(NetworkChannel.Object, clientMessage.GetBytes()); } } public class SpawnRequestMessageData : NetworkMessageData { public string title; public SimplifiedTransform transform; } [Net.HandleOnLoaded] public class SpawnTransferMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.SpawnTransfer; public override NetworkMessage CreateMessage(SpawnTransferMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[21]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.spawnId), ref index); byte[] bytes = data.transform.GetBytes(); for (int i = 0; i < 19; i++) { networkMessage.messageData[index++] = bytes[i]; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } if (!Node.isServer) { int num = 0; ushort key = BitConverter.ToUInt16(message.messageData, num); num += 2; byte[] array = new byte[19]; for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromBytes(array); if (PooleeSyncable._PooleeLookup.TryGetValue(key, out var value)) { value.OnSpawn(SteamIntegration.lobbyOwnerId, simplifiedTransform); } return; } throw new ExpectedClientException(); } } public class SpawnTransferMessageData : NetworkMessageData { public ushort spawnId; public SimplifiedTransform transform; } public enum PlayerEventType { Death } [Net.SkipHandleOnLoading] public class PlayerEventMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.PlayerEvent; public override NetworkMessage CreateMessage(PlayerEventMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1] { (byte)data.type }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } if (message.messageData[0] == 0 && PlayerRepresentation.representations.ContainsKey(sender)) { PlayerRepresentation.representations[sender].CreateRagdoll(); } } } public class PlayerEventMessageData : NetworkMessageData { public PlayerEventType type; } public class DisconnectMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.Disconnect; public override NetworkMessage CreateMessage(DisconnectMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1 + data.additionalReason.Length]; networkMessage.messageData[0] = data.disconnectReason; int num = 1; byte[] bytes = Encoding.ASCII.GetBytes(data.additionalReason); foreach (byte b in bytes) { networkMessage.messageData[num] = b; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } Client.instance?.DisconnectFromServer(notif: false); byte b = message.messageData[0]; string text = ""; for (int i = 1; i < message.messageData.Length; i++) { text += Encoding.ASCII.GetChars(message.messageData, i, 1); } EntangleLogger.Log("You were disconnected for reason " + Enum.GetName(typeof(DisconnectReason), b)); EntangleNotif.PlayerDisconnect((DisconnectReason)b); if (text != string.Empty) { EntangleLogger.Log("Additional reason: " + text); } } } public enum DisconnectReason : byte { Unknown = 0, ServerFull = 19, ServerClosed = 20, Kicked = 50, Banned = 51, OutdatedClient = 100, OutdatedServer = 101 } public class DisconnectMessageData : NetworkMessageData { public byte disconnectReason = 0; public string additionalReason = ""; } [Net.SkipHandleOnLoading] public class GripRadiusMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.GripRadius; public override NetworkMessage CreateMessage(GripRadiusMessageData data) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[3]; int num = 0; networkMessage.messageData[num++] = SteamIntegration.GetByteId(data.userId); networkMessage.messageData[num++] = (byte)data.hand; networkMessage.messageData[num++] = (byte)(data.radius * 255f); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_0061: 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) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); if (PlayerRepresentation.representations.ContainsKey(longId)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[longId]; if (Object.op_Implicit((Object)(object)playerRepresentation.repFord)) { Handedness hand = (Handedness)message.messageData[num]; num++; float radius = (float)(int)message.messageData[num] / 255f; playerRepresentation.UpdatePoseRadius(hand, radius); } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, longId); } } } public class GripRadiusMessageData : NetworkMessageData { public long userId; public Handedness hand; public float radius; } [Net.SkipHandleOnLoading] public class GunShotMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.GunShot; public override NetworkMessage CreateMessage(GunShotMessageData data) { //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_0044: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0066: 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_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[29]; int index = 0; networkMessage.messageData[index++] = SteamIntegration.GetByteId(data.userId); AmmoVariables ammoVariables = data.bulletObject.ammoVariables; networkMessage.messageData[index++] = (byte)ammoVariables.cartridgeType; networkMessage.messageData[index++] = (byte)ammoVariables.AttackType; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes((ushort)Math.Min(ammoVariables.AttackDamage * 100f, 65535f)), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes((short)(ammoVariables.ProjectileMass * 10000f)), ref index); networkMessage.messageData[index++] = Convert.ToByte(ammoVariables.Tracer); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes((short)ammoVariables.ExitVelocity), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(data.bulletTransform.GetBytes(), ref index); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_0037: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00b6: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: 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_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); Cart cartridgeType = (Cart)message.messageData[num++]; AttackType attackType = (AttackType)message.messageData[num++]; float attackDamage = (float)(int)BitConverter.ToUInt16(message.messageData, num) / 100f; num += 2; float projectileMass = (float)BitConverter.ToInt16(message.messageData, num) / 10000f; num += 2; bool tracer = Convert.ToBoolean(message.messageData[num++]); float exitVelocity = BitConverter.ToInt16(message.messageData, num); num += 2; BulletObject val = new BulletObject(); val.ammoVariables = new AmmoVariables { cartridgeType = cartridgeType, AttackType = attackType, AttackDamage = attackDamage, ProjectileMass = projectileMass, Tracer = tracer, ExitVelocity = exitVelocity }; byte[] array = new byte[19]; for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromBytes(array); Vector3 position = simplifiedTransform.position; Quaternion val2 = simplifiedTransform.rotation.ExpandQuat(); PoolSpawner.SpawnProjectile(position, val2, val, "1911", (TriggerRefProxy)null); PoolSpawner.SpawnMuzzleFlare(position, val2, (MuzzleFlareType)0); if (PlayerRepresentation.representations.ContainsKey(longId)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[longId]; simplifiedTransform.Apply(((Component)playerRepresentation.repGunSFX).transform); playerRepresentation.repGunSFX.GunShot(); } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Attack, bytes, longId); } } } public class GunShotMessageData : NetworkMessageData { public long userId; public BulletObject bulletObject; public SimplifiedTransform bulletTransform; } [Net.SkipHandleOnLoading] public class HandPoseChangeMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.HandPose; public override NetworkMessage CreateMessage(HandPoseChangeMessageData data) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[4]; int num = 0; networkMessage.messageData[num++] = SteamIntegration.GetByteId(data.userId); networkMessage.messageData[num++] = (byte)data.hand; byte[] bytes = BitConverter.GetBytes(data.poseIndex); for (int i = 0; i < 2; i++) { networkMessage.messageData[num++] = bytes[i]; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_0061: 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 (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); if (PlayerRepresentation.representations.ContainsKey(longId)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[longId]; if (Object.op_Implicit((Object)(object)playerRepresentation.repFord)) { Handedness hand = (Handedness)message.messageData[num]; num++; int index = BitConverter.ToUInt16(message.messageData, num); num += 2; playerRepresentation.UpdatePose(hand, index); } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, longId); } } } public class HandPoseChangeMessageData : NetworkMessageData { public long userId; public Handedness hand; public ushort poseIndex; } public class HeartbeatMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.Heartbeat; public override NetworkMessage CreateMessage(EmptyMessageData data) { return new NetworkMessage(); } public override void HandleMessage(NetworkMessage message, long sender) { if (Node.activeNode is Server server) { if (server.userBeats.ContainsKey(sender)) { server.userBeats[sender] = 0f; } } else if (Node.activeNode is Client client && client.hostId == sender) { client.hostHeartbeat = 0f; } } } public class LevelChangeMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.LevelChange; public override NetworkMessage CreateMessage(LevelChangeMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[2] { data.sceneIndex, Convert.ToByte(data.sceneReload) }; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } byte b = message.messageData[0]; bool flag = Convert.ToBoolean(message.messageData[1]); if (b == Client.instance.currentScene && !flag) { if (SteamIntegration.hasLobby && !Node.isServer && Node.activeNode != null) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ClientReady, new EmptyMessageData()); if (networkMessage != null) { Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, networkMessage.GetBytes()); } } } else { BoneworksSceneManager.LoadScene((int)b); } } } public class LevelChangeMessageData : NetworkMessageData { public byte sceneIndex; public bool sceneReload; } [Net.SkipHandleOnLoading] public class PowerPunchMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.PowerPunch; public override NetworkMessage CreateMessage(PowerPunchMessageData data) { //IL_001e: 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) NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[16]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.force.ToULong()), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.localPosition.ToULong()), ref index); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; Vector3 val = BitConverter.ToUInt64(message.messageData, num).ToVector3(); num += 8; PlayerScripts.playerPhysBody.AddImpulseForce(val); if (PlayerRepresentation.representations.ContainsKey(sender)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[sender]; Vector3 position = BitConverter.ToUInt64(message.messageData, num).ToVector3(); Vector3 val2 = playerRepresentation.repRoot.TransformPosition(position); Quaternion val3 = Quaternion.LookRotation(((Vector3)(ref val)).normalized); ((Component)playerRepresentation.repPowerPunchSFX).transform.position = val2; playerRepresentation.repPowerPunchSFX.GravFire(); PoolSpawner.SpawnBlaster((BlasterType)4, val2, val3); PoolSpawner.SpawnSmoker(val2, val3); } } } public class PowerPunchMessageData : NetworkMessageData { public Vector3 force; public Vector3 localPosition; } [Net.SkipHandleOnLoading] public class PlayerAttackMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.PlayerAttack; public static event Action OnDamageReceived; public override NetworkMessage CreateMessage(PlayerAttackMessageData data) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[3]; int index = 0; networkMessage.messageData[index++] = (byte)data.attackType; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes((ushort)Math.Min(data.attackDamage * 10000f, 65535f)), ref index); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int startIndex = 0; AttackType val = (AttackType)message.messageData[startIndex++]; float num = (float)(int)BitConverter.ToUInt16(message.messageData, startIndex) / 10000f; if (!GamemodeHandler.ShouldBlockDamage(sender)) { PlayerScripts.playerHealth.TAKEDAMAGE(num, false); PlayerAttackMessageHandler.OnDamageReceived?.Invoke(sender, num); } if (PlayerRepresentation.representations.ContainsKey(sender)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[sender]; AttackType val2 = val; AttackType val3 = val2; if ((int)val3 == 32) { playerRepresentation.repStabSFX.GunShot(); } } } } public class PlayerAttackMessageData : NetworkMessageData { public AttackType attackType = (AttackType)64; public float attackDamage; } [Net.SkipHandleOnLoading] public class PlayerRepSyncHandler : NetworkMessageHandler { private static readonly byte[] limbScratch = new byte[13]; private static readonly byte[] handScratch = new byte[5]; private static readonly Vector3[] positionScratch = (Vector3[])(object)new Vector3[3]; private static readonly Quaternion[] rotationScratch = (Quaternion[])(object)new Quaternion[3]; public override byte? MessageIndex => BuiltInMessageType.PlayerRepSync; public override NetworkMessage CreateMessage(PlayerRepSyncData data) { //IL_0033: 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) NetworkMessage networkMessage = new NetworkMessage(); List list = new List(); list.Add(SteamIntegration.GetByteId(data.userId)); list.Add(Convert.ToByte(data.isGrounded)); list.AddRange(data.rootPosition.GetBytes()); for (int i = 0; i < data.simplifiedTransforms.Length; i++) { list.AddRange(data.simplifiedTransforms[i].GetSmallBytes(data.rootPosition)); } list.AddRange(data.simplifiedLeftHand.GetBytes()); list.AddRange(data.simplifiedRightHand.GetBytes()); networkMessage.messageData = list.ToArray(); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_007d: 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_00ff: 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_0117: 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_013d: Unknown result type (might be due to invalid IL or missing references) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); if (PlayerRepresentation.representations.ContainsKey(longId)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[longId]; if (Object.op_Implicit((Object)(object)playerRepresentation.repFord)) { bool isGrounded = Convert.ToBoolean(message.messageData[num]); num++; playerRepresentation.isGrounded = isGrounded; Vector3 val = new Vector3 { x = BitConverter.ToSingle(message.messageData, num) }; num += 4; val.y = BitConverter.ToSingle(message.messageData, num); num += 4; val.z = BitConverter.ToSingle(message.messageData, num); num += 4; for (int i = 0; i < playerRepresentation.repTransforms.Length; i++) { Buffer.BlockCopy(message.messageData, num, limbScratch, 0, 13); SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromSmallBytes(limbScratch, val); num += 13; positionScratch[i] = simplifiedTransform.position; rotationScratch[i] = simplifiedTransform.rotation.ExpandQuat(); } playerRepresentation.SetNetTargets(val, positionScratch, rotationScratch); Buffer.BlockCopy(message.messageData, num, handScratch, 0, 5); SimplifiedHand handData = SimplifiedHand.FromBytes(handScratch); num += 5; Buffer.BlockCopy(message.messageData, num, handScratch, 0, 5); SimplifiedHand handData2 = SimplifiedHand.FromBytes(handScratch); playerRepresentation.UpdateFingers((Handedness)1, handData); playerRepresentation.UpdateFingers((Handedness)2, handData2); } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Unreliable, bytes, longId); } } } public class PlayerRepSyncData : NetworkMessageData { public long userId; public bool isGrounded; public SimplifiedTransform[] simplifiedTransforms = new SimplifiedTransform[3]; public Vector3 rootPosition; public SimplifiedHand simplifiedLeftHand; public SimplifiedHand simplifiedRightHand; } public class ShortIdMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ShortId; public override NetworkMessage CreateMessage(ShortIdMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[9]; int index = 0; networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.userId), ref index); networkMessage.messageData[index++] = data.byteId; return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long num2 = BitConverter.ToInt64(message.messageData, num); num += 8; byte b = message.messageData[num++]; if (num2 == SteamIntegration.currentUserId) { SteamIntegration.localByteId = b; } SteamIntegration.RegisterUser(num2, b); } } public class ShortIdMessageData : NetworkMessageData { public long userId; public byte byteId; } [Net.SkipHandleOnLoading] public class SpawnObjectMessage : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.SpawnObject; public override NetworkMessage CreateMessage(SpawnMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.spawnableTitle); networkMessage.messageData = new byte[24 + bytes.Length]; int index = 0; networkMessage.messageData[index++] = SteamIntegration.GetByteId(data.userId); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.objectId), ref index); networkMessage.messageData = networkMessage.messageData.AddBytes(BitConverter.GetBytes(data.callbackIndex), ref index); byte[] bytes2 = data.spawnTransform.GetBytes(); for (int i = 0; i < 19; i++) { networkMessage.messageData[index++] = bytes2[i]; } for (int j = 0; j < bytes.Length; j++) { networkMessage.messageData[index++] = bytes[j]; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } byte[] array = new byte[19]; int num = 0; long longId = SteamIntegration.GetLongId(message.messageData[num++]); ushort num2 = 0; if (Server.instance != null) { num2 = ObjectSync.GetNextObjectId(); message.messageData = message.messageData.AddBytes(BitConverter.GetBytes(num2), num); num += 2; ushort objectIndex = BitConverter.ToUInt16(message.messageData, num); num += 2; IDCallbackMessageData data = new IDCallbackMessageData { objectIndex = objectIndex, newId = num2, destroySync = false }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.IDCallback, data); Server.instance.SendMessage(longId, NetworkChannel.Object, networkMessage.GetBytes()); byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Object, bytes, longId); } else { num2 = (ObjectSync.lastId = BitConverter.ToUInt16(message.messageData, num)); num += 4; } for (int i = 0; i < array.Length; i++) { array[i] = message.messageData[num++]; } int num3 = message.messageData.Length - num; byte[] array2 = new byte[num3]; for (int j = 0; j < num3; j++) { array2[j] = message.messageData[num++]; } string text = Encoding.UTF8.GetString(array2); SimplifiedTransform simplifiedTransform = SimplifiedTransform.FromBytes(array); EntangleLogger.Log("Received object spawn for title " + text + "!"); ObjectSync.lastId = num2; MelonCoroutines.Start(RegisterAndSpawn(text, simplifiedTransform.position, simplifiedTransform.rotation.ExpandQuat(), num2, longId)); } public static IEnumerator RegisterAndSpawn(string title, Vector3 position, Quaternion rotation, ushort objectId, long userId) { //IL_000e: 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_0015: 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) SpawnableObject spawnable = SpawnableData.TryGetSpawnable(title); yield return null; yield return null; if (!Object.op_Implicit((Object)(object)spawnable)) { CustomItemSync.RequestItem(userId, title, position, rotation, objectId, userId); yield break; } Vector3 scale = spawnable.prefab.transform.localScale; GameObject obj = null; try { obj = GlobalPool.Spawn(title, position, rotation, scale); } catch { } if (Object.op_Implicit((Object)(object)obj)) { EntangleLogger.Log("Successfully spawned obj " + ((Object)obj).name + "!"); TransformSyncable.CreateSync(userId, ComponentCacheExtensions.m_RigidbodyCache.GetOrAdd(obj), objectId); } else { EntangleLogger.Warn("No object spawned for " + title + "!"); } } } public class SpawnMessageData : NetworkMessageData { public long userId; public ushort objectId; public ushort callbackIndex; public string spawnableTitle; public SimplifiedTransform spawnTransform; } public static class Net { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public class NoAutoRegister : Attribute { } [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public class HandleOnLoaded : Attribute { } [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public class SkipHandleOnLoading : Attribute { } } public class BuiltInMessageType { public static byte Unknown = 0; public static byte PlayerRepSync = 1; public static byte GunShot = 2; public static byte SpawnObject = 3; public static byte LevelChange = 4; public static byte PlayerAttack = 5; public static byte Connection = 6; public static byte Disconnect = 7; public static byte ModAsset = 8; public static byte HandPose = 9; public static byte GripRadius = 10; public static byte BalloonShot = 11; public static byte PowerPunch = 12; public static byte TransformSync = 13; public static byte PuppetSync = 14; public static byte TransformQueue = 15; public static byte PuppetQueue = 16; public static byte TransformCreate = 17; public static byte PuppetCreate = 18; public static byte IDCallback = 19; public static byte ZombieMode = 20; public static byte ZombieLoadout = 21; public static byte ZombieDiff = 22; public static byte ZombieStart = 23; public static byte ZombieWave = 24; public static byte FantasyCount = 25; public static byte FantasyDiff = 26; public static byte FantasyChal = 27; public static byte ShortId = 28; public static byte MagazinePlug = 29; public static byte FileTransferBegin = 30; public static byte FileTransferChunk = 31; public static byte ObjectDestroy = 32; public static byte Heartbeat = 33; public static byte TransformCollision = 34; public static byte SpawnRequest = 35; public static byte SpawnClient = 36; public static byte SpawnTransfer = 37; public static byte GripEvent = 38; public static byte PlayerEvent = 39; public static byte SceneEvent = 40; public static byte ClientReady = 41; public static byte TransformSyncBatch = 42; public static byte VoiceData = 43; public static byte ItemSyncRequest = 44; public static byte ItemSyncUnavailable = 45; public static byte ItemSyncFileIncoming = 46; public static byte PlayermodelSyncRequest = 47; public static byte GamemodeState = 48; public static byte GamemodeEvent = 49; } public class Client : Node { public static bool nameTagsVisible = true; public static Client instance = null; public long hostId; public string hostName = "Host"; public byte currentScene = 0; public float hostHeartbeat; private Callback joinRequestedCallback; private CallResult lobbyEnterResult; public static void StartClient() { if (instance != null) { throw new Exception("Can't create another client instance!"); } EntangleLogger.Log("Started client!"); Node.activeNode = (instance = new Client()); } private Client() { joinRequestedCallback = Callback.Create((DispatchDelegate)delegate(GameLobbyJoinRequested_t request) { //IL_0001: 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) JoinLobby(request.m_steamIDLobby); }); lobbyEnterResult = CallResult.Create((APIDispatchDelegate)OnLobbyEntered); } public void JoinLobby(CSteamID lobbyId) { //IL_001e: 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 (SteamIntegration.hasLobby) { EntangleLogger.Error("You are already in a lobby!"); } else { lobbyEnterResult.Set(SteamMatchmaking.JoinLobby(lobbyId), (APIDispatchDelegate)null); } } public void OnLobbyEntered(LobbyEnter_t result, bool bIOFailure) { //IL_001e: 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_0058: 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_008c: 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) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (SteamIntegration.hasLobby) { EntangleLogger.Error("You are already in a lobby!"); return; } if (bIOFailure || result.m_EChatRoomEnterResponse != 1) { EntangleLogger.Error($"Failed to join the lobby! Response: {(object)(EChatRoomEnterResponse)result.m_EChatRoomEnterResponse}"); return; } SteamIntegration.lobby = new CSteamID(result.m_ulSteamIDLobby); RegisterLobbyCallbacks(); hostId = SteamIntegration.lobbyOwnerId; SteamIntegration.FetchUser(hostId, OnHostUserFetched); int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(SteamIntegration.lobby); for (int i = 0; i < numLobbyMembers; i++) { long steamID = (long)SteamMatchmaking.GetLobbyMemberByIndex(SteamIntegration.lobby, i).m_SteamID; if (steamID != SteamIntegration.currentUserId && steamID != hostId) { CreatePlayerRep(steamID); } } SteamIntegration.UpdateActivity(); ObjectSync.OnCleanup(); if (Object.op_Implicit((Object)(object)PlayerScripts.playerHealth)) { PlayerScripts.playerHealth.reloadLevelOnDeath = false; } } public void OnHostUserFetched(long userId, string userName) { PlayerRepresentation.representations.Add(userId, new PlayerRepresentation(userName, userId)); userNames.Add(userId, userName); hostName = userName; EntangleLogger.Log("Joined " + hostName + "'s server!"); EntangleNotif.JoinServer(hostName); ConnectionMessageData connectionMessageData = new ConnectionMessageData(); connectionMessageData.packedVersion = BitConverter.ToUInt16(new byte[2] { 0, 4 }, 0); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.Connection, connectionMessageData); SendMessage(hostId, NetworkChannel.Reliable, networkMessage.GetBytes()); SteamIntegration.RegisterUser(hostId, 0); } public override void UserConnectedEvent(long userId) { SteamIntegration.UpdateActivity(); } public override void UserDisconnectEvent(long userId) { SteamIntegration.UpdateActivity(); } public void DisconnectFromServer(bool notif = true) { //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_0015: Unknown result type (might be due to invalid IL or missing references) if (notif) { EntangleNotif.LeftServer(); } if (SteamIntegration.hasLobby) { SteamMatchmaking.LeaveLobby(SteamIntegration.lobby); } SteamIntegration.lobby = CSteamID.Nil; SteamIntegration.DefaultRichPresence(); CleanData(); } public override void BroadcastMessage(NetworkChannel channel, byte[] data) { SendMessage(hostId, channel, data); } public override void Shutdown() { DisconnectFromServer(); } } public abstract class Node { public List connectedUsers = new List(); public Dictionary userNames = new Dictionary(); public uint sentByteCount; public uint recievedByteCount; public static Node activeNode; public static readonly NetworkChannel[] allChannels = new NetworkChannel[5] { NetworkChannel.Reliable, NetworkChannel.Unreliable, NetworkChannel.Attack, NetworkChannel.Object, NetworkChannel.Transaction }; protected Callback lobbyChatUpdateCallback; public static bool isServer => activeNode is Server; public void ClearMessageBuffer() { try { int num = 0; NetworkChannel[] array = allChannels; uint num2 = default(uint); uint num3 = default(uint); CSteamID val = default(CSteamID); foreach (NetworkChannel networkChannel in array) { while (SteamNetworking.IsP2PPacketAvailable(ref num2, (int)networkChannel)) { byte[] array2 = new byte[num2]; if (!SteamNetworking.ReadP2PPacket(array2, num2, ref num3, ref val, (int)networkChannel)) { break; } num++; } } if (num > 0) { EntangleLogger.Log($"Drained {num} stale network packets after suspend."); } } catch (Exception ex) { EntangleLogger.Warn("Failed to drain the network backlog: " + ex.Message); } } public void RegisterLobbyCallbacks() { if (lobbyChatUpdateCallback == null) { lobbyChatUpdateCallback = Callback.Create((DispatchDelegate)OnLobbyChatUpdate); } } public void OnLobbyChatUpdate(LobbyChatUpdate_t update) { //IL_0001: 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_0031: Unknown result type (might be due to invalid IL or missing references) if (update.m_ulSteamIDLobby != SteamIntegration.lobby.m_SteamID) { return; } long ulSteamIDUserChanged = (long)update.m_ulSteamIDUserChanged; if (ulSteamIDUserChanged != SteamIntegration.currentUserId) { if ((update.m_rgfChatMemberStateChange & 1) != 0) { OnUserJoined(ulSteamIDUserChanged); } else { OnUserLeft(ulSteamIDUserChanged); } } } public void OnUserJoined(long userId) { CreatePlayerRep(userId); if (PlayermodelsPatch.lastLoadedPath != null) { string lastLoadedPath = PlayermodelsPatch.lastLoadedPath; LoadCustomPlayerMessageData loadCustomPlayerMessageData = new LoadCustomPlayerMessageData(); loadCustomPlayerMessageData.userId = SteamIntegration.currentUserId; loadCustomPlayerMessageData.modelPath = Path.GetFileName(lastLoadedPath); loadCustomPlayerMessageData.requestCallback = true; SendMessage(userId, NetworkChannel.Reliable, NetworkMessage.CreateMessage(CompatMessageType.PlayerModel, loadCustomPlayerMessageData).GetBytes()); } UserConnectedEvent(userId); if (isServer) { GamemodeHandler.ActiveMode?.OnPlayerJoined(userId); } } public void OnUserLeft(long userId) { if (isServer) { GamemodeHandler.ActiveMode?.OnPlayerLeft(userId); } if (PlayerRepresentation.representations.TryGetValue(userId, out var value)) { EntangleNotif.PlayerLeave(value.playerName ?? ""); value.DeleteRepresentations(); PlayerRepresentation.representations.Remove(userId); } userNames.Remove(userId); connectedUsers.Remove(userId); SteamIntegration.RemoveUser(userId); SteamIntegration.CloseSession(userId); UserDisconnectEvent(userId); } public void CreatePlayerRep(long userId) { if (!connectedUsers.Contains(userId)) { connectedUsers.Add(userId); SteamIntegration.FetchUser(userId, OnUserFetched); } } public void CleanData() { foreach (long connectedUser in connectedUsers) { SteamIntegration.CloseSession(connectedUser); } connectedUsers.Clear(); userNames.Clear(); ObjectSync.OnCleanup(); foreach (PlayerRepresentation value in PlayerRepresentation.representations.Values) { value.DeleteRepresentations(); } PlayerRepresentation.representations.Clear(); SteamIntegration.byteIds.Clear(); SteamIntegration.localByteId = 0; SteamIntegration.lastByteId = 1; GamemodeHandler.Clear(); if (Object.op_Implicit((Object)(object)PlayerScripts.playerHealth)) { PlayerScripts.playerHealth.reloadLevelOnDeath = PlayerScripts.reloadLevelOnDeath; } if (lobbyChatUpdateCallback != null) { lobbyChatUpdateCallback.Dispose(); lobbyChatUpdateCallback = null; } CleanupEvent(); } public void OnUserFetched(long userId, string userName) { if (!PlayerRepresentation.representations.ContainsKey(userId)) { PlayerRepresentation.representations.Add(userId, new PlayerRepresentation(userName, userId)); userNames.Add(userId, userName); EntangleNotif.PlayerJoin(userName ?? ""); } } public void ReceiveMessages() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) NetworkChannel[] array = allChannels; uint num = default(uint); uint num2 = default(uint); CSteamID val = default(CSteamID); foreach (NetworkChannel networkChannel in array) { while (SteamNetworking.IsP2PPacketAvailable(ref num, (int)networkChannel)) { byte[] array2 = new byte[num]; if (SteamNetworking.ReadP2PPacket(array2, num, ref num2, ref val, (int)networkChannel)) { OnMessageReceived((long)val.m_SteamID, array2); } } } } public void OnMessageReceived(long userId, byte[] data) { if (data.Length == 0) { throw new Exception("Data was invalid!"); } NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageType = data[0]; networkMessage.messageData = new byte[data.Length - 1]; for (int i = 1; i < data.Length; i++) { networkMessage.messageData[i - 1] = data[i]; } recievedByteCount += (uint)data.Length; NetworkMessage.ReadMessage(networkMessage, userId); } public void SendMessage(long userId, NetworkChannel channel, byte[] data) { if (SteamIntegration.hasLobby) { SteamIntegration.SendPacket(userId, channel, data); sentByteCount += (uint)data.Length; } } public virtual void BroadcastMessage(NetworkChannel channel, byte[] data) { } public void BroadcastMessageP2P(NetworkChannel channel, byte[] data) { connectedUsers.ForEach(delegate(long user) { SendMessage(user, channel, data); }); if (!isServer) { SendMessage(SteamIntegration.lobbyOwnerId, channel, data); } } public virtual void Tick() { } public virtual void UserConnectedEvent(long userId) { } public virtual void UserDisconnectEvent(long userId) { } public virtual void CleanupEvent() { } public virtual void Shutdown() { } } public static class SteamIntegration { public const string notHosting = "This user isn't hosting a game!"; public static string targetScene = "undefined"; public static bool isInvalid; public static long currentUserId; public static string currentUserName = "Unknown"; public static CSteamID lobby = CSteamID.Nil; public static Dictionary byteIds = new Dictionary(); public static byte localByteId = 0; public static byte lastByteId = 1; private static Callback sessionRequestCallback; private static Callback sessionConnectFailCallback; private static Callback personaStateCallback; private static Dictionary>> pendingUserFetches = new Dictionary>>(); public static bool hasLobby => lobby.m_SteamID != 0; public static long lobbyOwnerId => (long)(hasLobby ? SteamMatchmaking.GetLobbyOwner(lobby).m_SteamID : 0); public static bool isHost => hasLobby && lobbyOwnerId == currentUserId; public static bool isConnected => hasLobby && lobbyOwnerId != currentUserId; public static long GetLongId(byte shortId) { if (shortId == 0) { return lobbyOwnerId; } return byteIds.TryIdx(shortId); } public static byte GetByteId(long longId) { if (longId == currentUserId) { return localByteId; } return byteIds.FirstOrDefault((KeyValuePair o) => o.Value == longId).Key; } public static byte CreateByteId() { return lastByteId++; } public static void RegisterUser(long userId, byte byteId) { byteIds.Add(byteId, userId); } public static byte RegisterUser(long userId) { byte b = CreateByteId(); RegisterUser(userId, b); return b; } public static void RemoveUser(long userId) { byteIds.Remove(GetByteId(userId)); } public static void Initialize() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) try { if (!SteamAPI.Init()) { EntangleLogger.Error("Failed to initialize the Steam API! Continuing without Entanglement!\nMake sure Steam is running and you are logged in, then launch the game through Steam."); isInvalid = true; return; } currentUserId = (long)SteamUser.GetSteamID().m_SteamID; currentUserName = SteamFriends.GetPersonaName(); EntangleLogger.Log("Current Steam User: " + currentUserName); sessionRequestCallback = Callback.Create((DispatchDelegate)OnSessionRequest); sessionConnectFailCallback = Callback.Create((DispatchDelegate)OnSessionConnectFail); personaStateCallback = Callback.Create((DispatchDelegate)OnPersonaStateChange); DefaultRichPresence(); } catch (Exception ex) { EntangleLogger.Error("Failed to initialize the Steam API! Continuing without Entanglement!\nIs Steamworks.NET.dll and steam_api64.dll present, and is Steam running?\nFailed with reason: " + ex.Message + "\nTrace: " + ex.StackTrace); isInvalid = true; } } private static void OnSessionRequest(P2PSessionRequest_t request) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (hasLobby && IsLobbyMember(request.m_steamIDRemote)) { SteamNetworking.AcceptP2PSessionWithUser(request.m_steamIDRemote); } } private static void OnSessionConnectFail(P2PSessionConnectFail_t fail) { //IL_000f: 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_001f: Unknown result type (might be due to invalid IL or missing references) if (hasLobby) { EntangleLogger.Warn($"P2P session with {fail.m_steamIDRemote.m_SteamID} failed with error {(object)(EP2PSessionError)fail.m_eP2PSessionError}!"); } } public static bool IsLobbyMember(CSteamID user) { //IL_0011: 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_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) if (!hasLobby) { return false; } int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(lobby); for (int i = 0; i < numLobbyMembers; i++) { if (SteamMatchmaking.GetLobbyMemberByIndex(lobby, i) == user) { return true; } } return false; } public static string GetUserName(long userId) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (userId == currentUserId) { return currentUserName; } return SteamFriends.GetFriendPersonaName(new CSteamID((ulong)userId)); } public static void FetchUser(long userId, Action callback) { //IL_0009: 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_001a: 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) CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor((ulong)userId); if (!SteamFriends.RequestUserInformation(val, true)) { callback(userId, SteamFriends.GetFriendPersonaName(val)); return; } if (!pendingUserFetches.TryGetValue(val.m_SteamID, out var value)) { value = new List>(); pendingUserFetches.Add(val.m_SteamID, value); } value.Add(callback); } private static void OnPersonaStateChange(PersonaStateChange_t change) { //IL_0006: 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_002d: 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_003e: Unknown result type (might be due to invalid IL or missing references) if (!pendingUserFetches.TryGetValue(change.m_ulSteamID, out var value)) { return; } pendingUserFetches.Remove(change.m_ulSteamID); string friendPersonaName = SteamFriends.GetFriendPersonaName(new CSteamID(change.m_ulSteamID)); long ulSteamID = (long)change.m_ulSteamID; foreach (Action item in value) { item(ulSteamID, friendPersonaName); } } public static bool SendPacket(long userId, NetworkChannel channel, byte[] data) { //IL_0009: 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_0014: Unknown result type (might be due to invalid IL or missing references) EP2PSend val = (EP2PSend)((channel != NetworkChannel.Unreliable) ? 2 : 0); return SteamNetworking.SendP2PPacket(new CSteamID((ulong)userId), data, (uint)data.Length, val, (int)channel); } public static void CloseSession(long userId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) SteamNetworking.CloseP2PSessionWithUser(new CSteamID((ulong)userId)); } public static void DefaultRichPresence() { SteamFriends.ClearRichPresence(); SteamFriends.SetRichPresence("status", "Playing solo (Entanglement v" + EntanglementMod.VersionString + ")"); } public static void UpdateActivity() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (!hasLobby) { DefaultRichPresence(); return; } int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(lobby); int lobbyMemberLimit = SteamMatchmaking.GetLobbyMemberLimit(lobby); SteamFriends.SetRichPresence("status", string.Format("{0} {1} ({2}/{3}) - Entanglement v{4}", isHost ? "Hosting" : "Playing", ParseScene(targetScene), numLobbyMembers, lobbyMemberLimit, EntanglementMod.VersionString)); SteamFriends.SetRichPresence("steam_player_group", lobby.m_SteamID.ToString()); SteamFriends.SetRichPresence("steam_player_group_size", numLobbyMembers.ToString()); if (isHost) { SteamMatchmaking.SetLobbyData(lobby, "scene", ParseScene(targetScene)); } } public static string ParseScene(string scene) { return scene.ToLower() switch { "sandbox_blankbox" => "Blankbox", "scene_redactedchamber" => "[REDACTED] Chamber", "scene_mainmenu" => "Main Menu", "scene_breakroom" => "Breakroom", "scene_streets" => "Streets", "scene_tuscany" => "Tuscany", "zombie_warehouse" => "Zombie Warehouse", "scene_throneroom" => "Throne Room", "scene_runoff" => "Runoff", "scene_arena" => "Arena Campaign", "arena_fantasy" => "Arena Gamemode", "sandbox_museumbasement" => "Museum Basement", "sandbox_handgunbox" => "Handgun Range", "scene_hoverjunkers" => "Hover Junkers", "scene_tower" => "Tower", "scene_warehouse" => "Warehouse", "scene_towerboss" => "Time Tower", "scene_sewerstation" => "Sewers", "scene_museum" => "Museum", "scene_dungeon" => "Dungeon", "scene_subwaystation" => "Central Station", _ => scene, }; } public static void Tick() { SteamAPI.RunCallbacks(); if (hasLobby) { Node.activeNode?.ReceiveMessages(); } } public static void Shutdown() { SteamFriends.ClearRichPresence(); SteamAPI.Shutdown(); } } public class ClientReadyMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.ClientReady; public override NetworkMessage CreateMessage(EmptyMessageData data) { return new NetworkMessage(); } public override void HandleMessage(NetworkMessage message, long sender) { Server.instance?.ReplayWorldStateTo(sender); } } public class ConnectionMessageHandler : NetworkMessageHandler { private static readonly Dictionary lastConnectionTimes = new Dictionary(); private const float CONNECTION_DEBOUNCE_SECONDS = 2f; public override byte? MessageIndex => BuiltInMessageType.Connection; public override NetworkMessage CreateMessage(ConnectionMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[18]; int num = 0; byte[] bytes = BitConverter.GetBytes(data.packedVersion); foreach (byte b in bytes) { networkMessage.messageData[num++] = b; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } byte b = message.messageData[0]; byte b2 = message.messageData[1]; if (lastConnectionTimes.TryGetValue(sender, out var value) && Time.realtimeSinceStartup - value < 2f) { return; } lastConnectionTimes[sender] = Time.realtimeSinceStartup; bool flag = b == 0 && b2 == 4; EntangleLogger.Log($"A client connected with version '{b}.{b2}.*'..."); DisconnectMessageData disconnectMessageData = new DisconnectMessageData(); if (!flag) { if (b < 0 || b2 < 4) { EntangleLogger.Log("A client was removed for having an outdated client!"); disconnectMessageData.disconnectReason = 100; } if (b > 0 || b2 > 4) { EntangleLogger.Log("A client was removed for having too new of a client! Please update your mod!"); disconnectMessageData.disconnectReason = 101; } } else if (Server.instance.connectedUsers.Count >= Server.maxPlayers) { EntangleLogger.Log("A client was removed since the server is full!"); disconnectMessageData.disconnectReason = 19; } if (BanList.bannedUsers.Any((Tuple tuple) => tuple.Item1 == sender)) { disconnectMessageData.disconnectReason = 51; } if (disconnectMessageData.disconnectReason != 0) { EntangleLogger.Log("Disconnecting sender for reason " + Enum.GetName(typeof(DisconnectReason), disconnectMessageData.disconnectReason) + "..."); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.Disconnect, disconnectMessageData); Server.instance?.SendMessage(sender, NetworkChannel.Reliable, networkMessage.GetBytes()); } } } public class ConnectionMessageData : NetworkMessageData { public ushort packedVersion; } public enum NetworkChannel : byte { Reliable, Unreliable, Attack, Object, Transaction } public class NetworkMessage { public byte messageType; public byte[] messageData = new byte[0]; public static readonly NetworkMessageHandler[] handlers = new NetworkMessageHandler[255]; public byte[] GetBytes() { byte[] array = new byte[1 + messageData.Length]; array[0] = messageType; for (int i = 1; i < array.Length; i++) { array[i] = messageData[i - 1]; } return array; } public static void RegisterHandlersFromAssembly(Assembly targetAssembly) { if (targetAssembly == null) { throw new NullReferenceException("Can't register from a null assembly!"); } EntangleLogger.Log("Populating MessageHandler list from " + targetAssembly.GetName().Name + "!"); (from type in targetAssembly.GetTypes() where typeof(NetworkMessageHandler).IsAssignableFrom(type) && !type.IsAbstract where type.GetCustomAttribute() == null select type).ForEach(delegate(Type type) { try { RegisterHandler(type); } catch (Exception ex) { EntangleLogger.Error(ex.Message); } }); } public static void RegisterHandler() where T : NetworkMessageHandler { RegisterHandler(typeof(T)); } protected static void RegisterHandler(Type type) { NetworkMessageHandler networkMessageHandler = Activator.CreateInstance(type) as NetworkMessageHandler; if (!networkMessageHandler.MessageIndex.HasValue) { EntangleLogger.Warn("Didn't register " + type.Name + " because its message index was null!"); return; } byte value = networkMessageHandler.MessageIndex.Value; if (handlers[value] != null) { throw new Exception(type.Name + " has the same index as " + handlers[value].GetType().Name + ", we can't replace handlers!"); } EntangleLogger.Log("Registered " + type.Name); IEnumerable customAttributes = type.GetCustomAttributes(); List list = new List(); foreach (Attribute item in customAttributes) { if (!(item is Net.NoAutoRegister)) { list.Add(item.GetType()); } } networkMessageHandler.Attributes = list.ToArray(); handlers[value] = networkMessageHandler; } public static NetworkMessage CreateMessage(byte type, NetworkMessageData data) { try { return handlers[type].CreateMessage(data); } catch (Exception ex) { EntangleLogger.Error("Failed creating network message with reason: " + ex.Message + "\nTrace:" + ex.StackTrace); } return null; } public static void ReadMessage(NetworkMessage message, long sender) { try { handlers[message.messageType].ReadMessage(message, sender); } catch (Exception ex) { EntangleLogger.Error("Failed handling network message with reason: " + ex.Message + "\nTrace:" + ex.StackTrace); } } } [Obsolete("Please use the new method of registering methods, without a decorator! Check the example message for the new method!", true)] [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public class NetworkMessageHandlerIndex : Attribute { public byte messageIndex; public NetworkMessageHandlerIndex(byte messageType) { messageIndex = messageType; } } public abstract class NetworkMessageData { } public abstract class NetworkMessageHandler { public virtual byte? MessageIndex { get; } = null; public Type[] Attributes { get; set; } public void ReadMessage(NetworkMessage message, long sender) { if (SceneLoader.loading) { if (!Attributes.Contains(typeof(Net.SkipHandleOnLoading)) && Attributes.Contains(typeof(Net.HandleOnLoaded))) { MelonCoroutines.Start(HandleOnLoaded(message, sender)); } } else { HandleMessage(message, sender); } } public IEnumerator HandleOnLoaded(NetworkMessage message, long sender) { while (SceneLoader.loading) { yield return null; } HandleMessage(message, sender); } public abstract void HandleMessage(NetworkMessage message, long sender); public abstract NetworkMessage CreateMessage(NetworkMessageData data); } public abstract class NetworkMessageHandler : NetworkMessageHandler where TData : NetworkMessageData { public sealed override NetworkMessage CreateMessage(NetworkMessageData data) { if (data is TData data2) { if (!MessageIndex.HasValue) { throw new ArgumentNullException("MessageIndex is null, we can't write messages without an index!"); } NetworkMessage networkMessage = CreateMessage(data2); networkMessage.messageType = MessageIndex.Value; return networkMessage; } throw new Exception("Provided message data was not of type " + typeof(TData).Name + " or was null!"); } public abstract NetworkMessage CreateMessage(TData data); } public enum ServerVisibility : byte { Private, FriendsOnly, Public } public class Server : Node { public static byte maxPlayers = 8; public static bool isLocked = false; public static ServerVisibility visibility = ServerVisibility.Private; public const byte serverMinimum = 1; public const byte serverCapacity = 250; public Dictionary userBeats = new Dictionary(); public static Server instance = null; private CallResult lobbyCreatedResult; public readonly HashSet replayedUsers = new HashSet(); public static void StartServer() { if (instance != null) { instance.Shutdown(); } if (SteamIntegration.isConnected) { EntangleLogger.Error("Already in a server!"); return; } EntangleLogger.Log("Started a new server instance!"); Node.activeNode = (instance = new Server()); if (Object.op_Implicit((Object)(object)PlayerScripts.playerHealth)) { PlayerScripts.playerHealth.reloadLevelOnDeath = false; } } private Server() { //IL_0057: 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) EntangleLogger.Log($"Creating a Steam lobby with a capacity of {maxPlayers} players!"); lobbyCreatedResult = CallResult.Create((APIDispatchDelegate)OnLobbyCreated); lobbyCreatedResult.Set(SteamMatchmaking.CreateLobby(GetLobbyType(), (int)maxPlayers), (APIDispatchDelegate)null); } public static ELobbyType GetLobbyType() { //IL_001e: 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_0026: 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) return (ELobbyType)(visibility switch { ServerVisibility.FriendsOnly => 1, ServerVisibility.Public => 2, _ => 0, }); } public void OnLobbyCreated(LobbyCreated_t result, bool bIOFailure) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //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_0055: 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_007f: 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_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) if (bIOFailure || (int)result.m_eResult != 1) { EntangleLogger.Error($"Failed to create a Steam lobby with result {result.m_eResult}!"); instance = null; Node.activeNode = Client.instance; return; } SteamIntegration.lobby = new CSteamID(result.m_ulSteamIDLobby); SteamMatchmaking.SetLobbyData(SteamIntegration.lobby, "entanglement", "true"); SteamMatchmaking.SetLobbyData(SteamIntegration.lobby, "version", EntanglementMod.VersionString); SteamMatchmaking.SetLobbyData(SteamIntegration.lobby, "host_name", SteamIntegration.currentUserName); SteamMatchmaking.SetLobbyJoinable(SteamIntegration.lobby, !isLocked); RegisterLobbyCallbacks(); SteamIntegration.UpdateActivity(); EntangleNotif.LobbyStarted(); } public override void Tick() { if (EntanglementMod.sceneChange.HasValue) { EntangleLogger.Log($"Notifying clients of scene change to {EntanglementMod.sceneChange}..."); LevelChangeMessageData data = new LevelChangeMessageData { sceneIndex = EntanglementMod.sceneChange.Value, sceneReload = true }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.LevelChange, data); byte[] bytes = networkMessage.GetBytes(); foreach (long connectedUser in connectedUsers) { SendMessage(connectedUser, NetworkChannel.Reliable, bytes); } EntanglementMod.sceneChange = null; } base.Tick(); } public void UpdateLobbyConfig() { //IL_001c: 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_002c: 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) if (!SteamIntegration.hasLobby || !SteamIntegration.isHost) { return; } SteamMatchmaking.SetLobbyType(SteamIntegration.lobby, GetLobbyType()); SteamMatchmaking.SetLobbyMemberLimit(SteamIntegration.lobby, (int)maxPlayers); SteamMatchmaking.SetLobbyJoinable(SteamIntegration.lobby, !isLocked); SteamIntegration.UpdateActivity(); if (maxPlayers < connectedUsers.Count) { uint num = (uint)(connectedUsers.Count - maxPlayers); DisconnectMessageData disconnectMessageData = new DisconnectMessageData(); disconnectMessageData.disconnectReason = 19; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.Disconnect, disconnectMessageData); byte[] bytes = networkMessage.GetBytes(); for (int i = 0; i < num; i++) { SendMessage(connectedUsers[i], NetworkChannel.Reliable, bytes); } } } public void CloseLobby() { //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_006c: Unknown result type (might be due to invalid IL or missing references) DisconnectMessageData disconnectMessageData = new DisconnectMessageData(); disconnectMessageData.disconnectReason = 20; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.Disconnect, disconnectMessageData); byte[] bytes = networkMessage.GetBytes(); foreach (long connectedUser in connectedUsers) { SendMessage(connectedUser, NetworkChannel.Reliable, bytes); } if (SteamIntegration.hasLobby) { SteamMatchmaking.LeaveLobby(SteamIntegration.lobby); } SteamIntegration.lobby = CSteamID.Nil; CleanData(); } public override void Shutdown() { if (SteamIntegration.hasLobby && !SteamIntegration.isHost) { EntangleLogger.Error("Unable to close the server as a client!"); return; } CloseLobby(); SteamIntegration.DefaultRichPresence(); instance = null; Node.activeNode = Client.instance; } public override void UserConnectedEvent(long userId) { LevelChangeMessageData data = new LevelChangeMessageData { sceneIndex = (byte)BoneworksSceneManager.currentSceneIndex }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.LevelChange, data); SendMessage(userId, NetworkChannel.Reliable, networkMessage.GetBytes()); SteamIntegration.UpdateActivity(); foreach (KeyValuePair byteId in SteamIntegration.byteIds) { if (byteId.Value != userId) { ShortIdMessageData data2 = new ShortIdMessageData { userId = byteId.Value, byteId = byteId.Key }; NetworkMessage networkMessage2 = NetworkMessage.CreateMessage(BuiltInMessageType.ShortId, data2); SendMessage(userId, NetworkChannel.Reliable, networkMessage2.GetBytes()); } } ShortIdMessageData data3 = new ShortIdMessageData { userId = userId, byteId = SteamIntegration.RegisterUser(userId) }; NetworkMessage networkMessage3 = NetworkMessage.CreateMessage(BuiltInMessageType.ShortId, data3); BroadcastMessage(NetworkChannel.Reliable, networkMessage3.GetBytes()); userBeats.Add(userId, 0f); } public override void UserDisconnectEvent(long userId) { SteamIntegration.UpdateActivity(); userBeats.Remove(userId); replayedUsers.Remove(userId); } public void ReplayWorldStateTo(long userId) { //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0284: 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_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: 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) if (!replayedUsers.Add(userId)) { return; } int num = 0; int num2 = 0; int num3 = 0; PooleeSyncable[] array = PooleeSyncable._PooleeLookup.Values.ToArray(); foreach (PooleeSyncable pooleeSyncable in array) { if (Object.op_Implicit((Object)(object)pooleeSyncable) && Object.op_Implicit((Object)(object)((Component)pooleeSyncable).gameObject) && ((Component)pooleeSyncable).gameObject.activeInHierarchy && Object.op_Implicit((Object)(object)pooleeSyncable.Poolee) && Object.op_Implicit((Object)(object)pooleeSyncable.Poolee.pool)) { Rigidbody[] array2 = Il2CppArrayBase.op_Implicit(((Component)pooleeSyncable).GetComponentsInChildren()); SpawnClientMessageData data = new SpawnClientMessageData { rbCount = (byte)array2.Length, spawnId = pooleeSyncable.id, title = SpawnManager.GetPoolTitle(pooleeSyncable.Poolee.pool), transform = new SimplifiedTransform(((Component)pooleeSyncable).transform) }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.SpawnClient, data); if (networkMessage != null) { SendMessage(userId, NetworkChannel.Reliable, networkMessage.GetBytes()); num++; } } } Syncable[] array3 = ObjectSync.syncedObjects.Values.ToArray(); foreach (Syncable syncable in array3) { TransformSyncable transformSyncable = syncable as TransformSyncable; if (!Object.op_Implicit((Object)(object)transformSyncable) || !Object.op_Implicit((Object)(object)((Component)transformSyncable).transform) || Object.op_Implicit((Object)(object)SceneEventSync.FindPooleeSyncable(((Component)transformSyncable).transform))) { continue; } ObjectSync.GetPooleeData(((Component)transformSyncable).transform, out var _, out var overrideRootName, out var spawnIndex, out var spawnTime); TransformCreateMessageData data2 = new TransformCreateMessageData { ownerId = ((transformSyncable.staleOwner != 0L) ? transformSyncable.staleOwner : SteamIntegration.lobbyOwnerId), objectId = transformSyncable.objectId, callbackIndex = 0, spawnIndex = spawnIndex, spawnTime = spawnTime, enqueueOwner = false, objectPath = ((Component)transformSyncable).transform.GetFullPath(overrideRootName) }; NetworkMessage networkMessage2 = NetworkMessage.CreateMessage(BuiltInMessageType.TransformCreate, data2); if (networkMessage2 != null) { SendMessage(userId, NetworkChannel.Reliable, networkMessage2.GetBytes()); TransformSyncMessageData data3 = new TransformSyncMessageData { objectId = transformSyncable.objectId, simplifiedTransform = new SimplifiedTransform(((Component)transformSyncable).transform), velocity = (Object.op_Implicit((Object)(object)transformSyncable.rb) ? transformSyncable.rb.velocity : Vector3.zero), angularVelocity = (Object.op_Implicit((Object)(object)transformSyncable.rb) ? transformSyncable.rb.angularVelocity : Vector3.zero) }; NetworkMessage networkMessage3 = NetworkMessage.CreateMessage(BuiltInMessageType.TransformSync, data3); if (networkMessage3 != null) { SendMessage(userId, NetworkChannel.Reliable, networkMessage3.GetBytes()); } num2++; } } Syncable[] array4 = ObjectSync.syncedObjects.Values.ToArray(); foreach (Syncable syncable2 in array4) { if ((Object)(object)syncable2 == (Object)null) { continue; } foreach (long item in syncable2.ownerQueue) { TransformQueueMessageData data4 = new TransformQueueMessageData { userId = item, objectId = syncable2.objectId, isAdd = true }; NetworkMessage networkMessage4 = NetworkMessage.CreateMessage(BuiltInMessageType.TransformQueue, data4); if (networkMessage4 != null) { SendMessage(userId, NetworkChannel.Reliable, networkMessage4.GetBytes()); num3++; } } } int num4 = SceneEventSync.ReplayEventsTo(userId); EntangleLogger.Log($"Replayed world state to {userId}: {num} spawns, {num2} props, {num3} grips, {num4} scene events."); } public override void BroadcastMessage(NetworkChannel channel, byte[] data) { BroadcastMessageP2P(channel, data); } public void BroadcastMessageExcept(NetworkChannel channel, byte[] data, long toIgnore) { connectedUsers.ForEach(delegate(long user) { if (user != toIgnore) { SendMessage(user, channel, data); } }); } public void KickUser(long userId, string userName = null, DisconnectReason reason = DisconnectReason.Kicked) { DisconnectMessageData disconnectMessageData = new DisconnectMessageData(); disconnectMessageData.disconnectReason = (byte)reason; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.Disconnect, disconnectMessageData); byte[] bytes = networkMessage.GetBytes(); SendMessage(userId, NetworkChannel.Reliable, bytes); if (userName != null) { EntangleLogger.Log("Kicked " + userName + " from the server."); } } public void TeleportTo(long userId) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (PlayerRepresentation.representations.ContainsKey(userId)) { PlayerRepresentation playerRepresentation = PlayerRepresentation.representations[userId]; PlayerScripts.playerRig.Teleport(playerRepresentation.repRoot.position, true); PlayerScripts.playerRig.physicsRig.ResetHands((Handedness)3); } } } [Net.SkipHandleOnLoading] public class GamemodeStateMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.GamemodeState; public override NetworkMessage CreateMessage(GamemodeStateData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.activeModeId ?? ""); List list = new List(); list.Add((byte)bytes.Length); list.AddRange(bytes); list.Add((byte)(data.roundActive ? 1 : 0)); list.AddRange(BitConverter.GetBytes(data.roundTimeRemaining)); list.Add((byte)Math.Min(data.scores.Count, 255)); int num = 0; foreach (KeyValuePair score in data.scores) { if (num++ >= 255) { break; } list.AddRange(BitConverter.GetBytes(score.Key)); list.AddRange(BitConverter.GetBytes(score.Value)); } list.Add((byte)Math.Min(data.teams.Count, 255)); num = 0; foreach (KeyValuePair team in data.teams) { if (num++ >= 255) { break; } list.AddRange(BitConverter.GetBytes(team.Key)); list.Add(team.Value); } list.Add((byte)Math.Min(data.eliminated.Count, 255)); num = 0; foreach (long item in data.eliminated) { if (num++ >= 255) { break; } list.AddRange(BitConverter.GetBytes(item)); } networkMessage.messageData = list.ToArray(); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (Node.isServer || message.messageData.Length == 0) { return; } int num = 0; byte b = message.messageData[num++]; string activeModeId = Encoding.UTF8.GetString(message.messageData, num, b); num += b; bool roundActive = message.messageData[num++] != 0; float roundTimeRemaining = BitConverter.ToSingle(message.messageData, num); num += 4; GamemodeStateData gamemodeStateData = new GamemodeStateData { activeModeId = activeModeId, roundActive = roundActive, roundTimeRemaining = roundTimeRemaining, scores = new Dictionary(), teams = new Dictionary(), eliminated = new List() }; byte b2 = message.messageData[num++]; for (int i = 0; i < b2; i++) { long key = BitConverter.ToInt64(message.messageData, num); num += 8; int value = BitConverter.ToInt32(message.messageData, num); num += 4; gamemodeStateData.scores[key] = value; } byte b3 = message.messageData[num++]; for (int j = 0; j < b3; j++) { long key2 = BitConverter.ToInt64(message.messageData, num); num += 8; byte value2 = message.messageData[num++]; gamemodeStateData.teams[key2] = value2; } if (num < message.messageData.Length) { byte b4 = message.messageData[num++]; for (int k = 0; k < b4; k++) { long item = BitConverter.ToInt64(message.messageData, num); num += 8; gamemodeStateData.eliminated.Add(item); } } GamemodeHandler.ApplyState(gamemodeStateData); } } public class GamemodeStateData : NetworkMessageData { public string activeModeId; public bool roundActive; public float roundTimeRemaining; public Dictionary scores = new Dictionary(); public Dictionary teams = new Dictionary(); public List eliminated = new List(); } [Net.SkipHandleOnLoading] public class GamemodeEventMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => BuiltInMessageType.GamemodeEvent; public override NetworkMessage CreateMessage(GamemodeEventData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.message ?? ""); networkMessage.messageData = new byte[23 + bytes.Length]; int num = 0; networkMessage.messageData[num++] = (byte)data.type; Array.Copy(BitConverter.GetBytes(data.a), 0, networkMessage.messageData, num, 8); num += 8; Array.Copy(BitConverter.GetBytes(data.b), 0, networkMessage.messageData, num, 8); num += 8; Array.Copy(BitConverter.GetBytes(data.value), 0, networkMessage.messageData, num, 4); num += 4; Array.Copy(BitConverter.GetBytes((ushort)bytes.Length), 0, networkMessage.messageData, num, 2); num += 2; Array.Copy(bytes, 0, networkMessage.messageData, num, bytes.Length); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length != 0) { int num = 0; GamemodeEventType type = (GamemodeEventType)message.messageData[num++]; long a = BitConverter.ToInt64(message.messageData, num); num += 8; long b = BitConverter.ToInt64(message.messageData, num); num += 8; int value = BitConverter.ToInt32(message.messageData, num); num += 4; ushort num2 = BitConverter.ToUInt16(message.messageData, num); num += 2; string message2 = ((num2 > 0) ? Encoding.UTF8.GetString(message.messageData, num, num2) : ""); GamemodeHandler.ApplyEvent(sender, new GamemodeEventData { type = type, a = a, b = b, value = value, message = message2 }); } } } public class GamemodeEventData : NetworkMessageData { public GamemodeEventType type; public long a; public long b; public int value; public string message; } [Net.SkipHandleOnLoading] public class VoiceDataMessageHandler : NetworkMessageHandler { private static readonly VoiceDataMessageData cachedData = new VoiceDataMessageData(); public override byte? MessageIndex => BuiltInMessageType.VoiceData; public static void SendVoice(byte[] compressed, int count) { if (Node.activeNode != null) { cachedData.compressed = compressed; cachedData.count = count; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.VoiceData, cachedData); if (networkMessage != null) { Node.activeNode.BroadcastMessage(NetworkChannel.Unreliable, networkMessage.GetBytes()); } } } public override NetworkMessage CreateMessage(VoiceDataMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = new byte[1 + data.count]; networkMessage.messageData[0] = SteamIntegration.GetByteId(SteamIntegration.currentUserId); Buffer.BlockCopy(data.compressed, 0, networkMessage.messageData, 1, data.count); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length > 1) { long num = SteamIntegration.GetLongId(message.messageData[0]); if (num == 0) { num = sender; } if (num != SteamIntegration.currentUserId) { VoiceManager.ReceiveVoice(num, message.messageData, 1, message.messageData.Length - 1); } if (Server.instance != null) { Server.instance.BroadcastMessageExcept(NetworkChannel.Unreliable, message.GetBytes(), sender); } } } } public class VoiceDataMessageData : NetworkMessageData { public byte[] compressed; public int count; } } namespace Entanglement.Sync { public static class SyncPrefs { private static readonly MelonPreferences_Category category; public static readonly MelonPreferences_Entry itemSyncEnabled; public static readonly MelonPreferences_Entry playermodelSyncEnabled; public static readonly MelonPreferences_Entry maxSyncSizeKB; public static readonly MelonPreferences_Entry blacklistedPaths; public static readonly MelonPreferences_Entry blockedUsers; static SyncPrefs() { category = MelonPreferences.CreateCategory("EntanglementFileSync"); itemSyncEnabled = category.CreateEntry("itemSyncEnabled", true, (string)null, "Automatically send/receive custom spawned items you don't have installed", false, false, (ValueValidator)null, (string)null); playermodelSyncEnabled = category.CreateEntry("playermodelSyncEnabled", true, (string)null, "Automatically send/receive playermodels other players are wearing", false, false, (ValueValidator)null, (string)null); maxSyncSizeKB = category.CreateEntry("maxSyncSizeKB", 102400, (string)null, "Refuse to send or receive a single item/model file larger than this many KB (default 100MB)", false, false, (ValueValidator)null, (string)null); blacklistedPaths = category.CreateEntry("blacklistedPaths", new string[0], (string)null, "Melon/model file paths that will never be sent to other players, even if requested", false, false, (ValueValidator)null, (string)null); blockedUsers = category.CreateEntry("blockedUsers", new long[0], (string)null, "Steam ids to never send files to or accept files from", false, false, (ValueValidator)null, (string)null); category.SaveToFile(false); } public static bool IsUserBlocked(long userId) { long[] value = blockedUsers.Value; for (int i = 0; i < value.Length; i++) { if (value[i] == userId) { return true; } } return false; } public static bool IsPathBlacklisted(string path) { string[] value = blacklistedPaths.Value; for (int i = 0; i < value.Length; i++) { if (value[i] == path) { return true; } } return false; } } public static class CustomItemSync { private struct PendingSpawn { public Vector3 position; public Quaternion rotation; public ushort objectId; public long userId; } private static readonly Dictionary> waitingSpawns = new Dictionary>(); private static readonly Dictionary incomingFileTitles = new Dictionary(); private static readonly HashSet requestedTitles = new HashSet(); private static bool initialized; public static string syncFolder => Path.Combine(MelonUtils.UserDataDirectory, "Entanglement", "SyncedItems"); public static void Initialize() { if (!initialized) { initialized = true; if (!Directory.Exists(syncFolder)) { Directory.CreateDirectory(syncFolder); } FileTransferManager.RegisterCategoryHandler(FileTransferCategory.CustomItem, OnItemFileReceived, OnItemFileFailed); } } public static void RequestItem(long ownerPeer, string title, Vector3 position, Quaternion rotation, ushort objectId, long userId) { //IL_006f: 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_0078: Unknown result type (might be due to invalid IL or missing references) if (!SyncPrefs.itemSyncEnabled.Value || !SteamIntegration.hasLobby || SyncPrefs.IsUserBlocked(ownerPeer)) { return; } if (!waitingSpawns.TryGetValue(title, out var value)) { value = new List(); waitingSpawns[title] = value; } value.Add(new PendingSpawn { position = position, rotation = rotation, objectId = objectId, userId = userId }); if (requestedTitles.Add(title)) { EntangleLogger.Log($"[ItemSync] Missing custom item '{title}', asking {ownerPeer} for it"); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ItemSyncRequest, new ItemSyncRequestData { title = title }); if (networkMessage != null) { Node.activeNode?.SendMessage(ownerPeer, NetworkChannel.Reliable, networkMessage.GetBytes()); } } } public static void OnItemRequested(long requester, string title) { if (!SyncPrefs.itemSyncEnabled.Value) { return; } if (SyncPrefs.IsUserBlocked(requester)) { SendUnavailable(requester, title); return; } bool hasExecutableCode; string text = TryFindMelonPathForItem(title, out hasExecutableCode); if (text == null) { EntangleLogger.Log($"[ItemSync] {requester} asked for '{title}' but we can't find which melon it came from"); SendUnavailable(requester, title); return; } if (hasExecutableCode) { EntangleLogger.Warn($"[ItemSync] Refusing to send '{title}' to {requester} - its melon contains compiled code, which this sync will never transfer"); SendUnavailable(requester, title); return; } if (SyncPrefs.IsPathBlacklisted(text)) { EntangleLogger.Log("[ItemSync] Not sending '" + title + "' - " + text + " is blacklisted"); SendUnavailable(requester, title); return; } FileInfo fileInfo = new FileInfo(text); if (!fileInfo.Exists) { SendUnavailable(requester, title); return; } if (fileInfo.Length / 1024 > SyncPrefs.maxSyncSizeKB.Value) { EntangleLogger.Log($"[ItemSync] Not sending '{title}' - {fileInfo.Length / 1024}KB is over the {SyncPrefs.maxSyncSizeKB.Value}KB sync limit"); SendUnavailable(requester, title); return; } string fileName = Path.GetFileName(text); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ItemSyncFileIncoming, new ItemSyncFileIncomingData { title = title, fileName = fileName }); if (networkMessage != null) { Node.activeNode?.SendMessage(requester, NetworkChannel.Reliable, networkMessage.GetBytes()); } EntangleLogger.Log($"[ItemSync] Sending '{title}' ({text}) to {requester}"); FileTransferManager.SendFile(requester, text, FileTransferCategory.CustomItem); } private static void SendUnavailable(long requester, string title) { NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.ItemSyncUnavailable, new ItemSyncUnavailableData { title = title }); if (networkMessage != null) { Node.activeNode?.SendMessage(requester, NetworkChannel.Reliable, networkMessage.GetBytes()); } } public static void OnItemUnavailable(long sender, string title) { EntangleLogger.Warn($"[ItemSync] {sender} doesn't have '{title}' either, giving up on it"); requestedTitles.Remove(title); waitingSpawns.Remove(title); } public static void OnFileIncoming(long sender, string title, string fileName) { incomingFileTitles[$"{sender}|{fileName}"] = title; } private static void OnItemFileReceived(FileTransfer transfer) { string key = $"{transfer.peer}|{transfer.fileName}"; if (!incomingFileTitles.TryGetValue(key, out var value)) { EntangleLogger.Warn($"[ItemSync] Received {transfer.fileName} from {transfer.peer} but never announced/requested it, ignoring"); return; } incomingFileTitles.Remove(key); string text = Path.Combine(syncFolder, transfer.fileName); try { FileTransferManager.WriteReceivedFile(transfer, text); } catch (Exception ex) { EntangleLogger.Error("[ItemSync] Failed writing " + text + ": " + ex.Message); waitingSpawns.Remove(value); requestedTitles.Remove(value); return; } MelonCoroutines.Start(LoadAndReplay(value, text)); } private static void OnItemFileFailed(FileTransfer transfer) { EntangleLogger.Warn(string.Format("[ItemSync] Transfer failed for {0} from {1}", transfer?.fileName ?? "unknown file", transfer?.peer)); } private static IEnumerator LoadAndReplay(string title, string bundlePath) { AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(bundlePath); while (!((AsyncOperation)request).isDone) { yield return null; } AssetBundle bundle = request.assetBundle; if ((Object)(object)bundle == (Object)null) { EntangleLogger.Warn("[ItemSync] " + bundlePath + " did not load as a valid asset bundle"); waitingSpawns.Remove(title); requestedTitles.Remove(title); yield break; } if (((IEnumerable)bundle.GetAllAssetNames()).Any((string a) => a.EndsWith(".bytes"))) { EntangleLogger.Warn("[ItemSync] " + bundlePath + " contains compiled code (.bytes) - refusing to load it. This should have been caught before the transfer started."); bundle.Unload(true); waitingSpawns.Remove(title); requestedTitles.Remove(title); yield break; } if (!TryLoadMelonBundle(bundle, out var loadError)) { EntangleLogger.Error("[ItemSync] Failed to hand off " + bundlePath + " to the item loader: " + loadError); bundle.Unload(true); waitingSpawns.Remove(title); requestedTitles.Remove(title); yield break; } EntangleLogger.Log($"[ItemSync] Loaded '{title}', spawning {(waitingSpawns.TryGetValue(title, out var p) ? p.Count : 0)} pending item(s)"); if (waitingSpawns.TryGetValue(title, out var pending)) { foreach (PendingSpawn spawn in pending) { MelonCoroutines.Start(SpawnObjectMessage.RegisterAndSpawn(title, spawn.position, spawn.rotation, spawn.objectId, spawn.userId)); } } waitingSpawns.Remove(title); requestedTitles.Remove(title); } private static string TryFindMelonPathForItem(string title, out bool hasExecutableCode) { hasExecutableCode = false; try { Type type = Type.GetType("ModThatIsNotMod.Internals.ItemLoading, ModThatIsNotMod"); if (type == null) { return null; } object obj = type.GetField("loadedMelons", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null); if (!(obj is IEnumerable enumerable)) { return null; } foreach (object item in enumerable) { Type type2 = item.GetType(); object obj2 = type2.GetField("loadedItems")?.GetValue(item); if (!(obj2 is IEnumerable enumerable2)) { continue; } bool flag = false; foreach (object item2 in enumerable2) { string text = item2.GetType().GetField("itemName")?.GetValue(item2) as string; if (text == title) { flag = true; break; } } if (!flag) { continue; } string text2 = type2.GetField("filePath")?.GetValue(item) as string; if (string.IsNullOrEmpty(text2)) { return null; } string text3 = Path.Combine(MelonUtils.UserDataDirectory, text2); try { AssetBundle val = AssetBundle.LoadFromFile(text3); if ((Object)(object)val != (Object)null) { hasExecutableCode = ((IEnumerable)val.GetAllAssetNames()).Any((string a) => a.EndsWith(".bytes")); val.Unload(false); } } catch { } return text3; } } catch (Exception ex) { EntangleLogger.Warn("[ItemSync] Couldn't look up melon for item '" + title + "': " + ex.Message); } return null; } private static bool TryLoadMelonBundle(AssetBundle bundle, out string error) { error = null; try { MethodInfo methodInfo = Type.GetType("ModThatIsNotMod.Internals.ItemLoading, ModThatIsNotMod")?.GetMethod("LoadFromBundle", BindingFlags.Static | BindingFlags.Public); if (methodInfo == null) { error = "ItemLoading.LoadFromBundle not found (ModThatIsNotMod version mismatch?)"; return false; } object obj = methodInfo.Invoke(null, new object[1] { bundle }); if (obj == null) { error = "LoadFromBundle returned null"; return false; } object obj2 = obj.GetType().GetField("loadedItems")?.GetValue(obj); if (obj2 is IEnumerable enumerable) { Pool[] source = Il2CppArrayBase.op_Implicit(Object.FindObjectsOfType()); foreach (object item in enumerable) { string itemName = item.GetType().GetField("itemName")?.GetValue(item) as string; if (!string.IsNullOrEmpty(itemName)) { Pool val = ((IEnumerable)source).FirstOrDefault((Func)((Pool p) => ((Object)p).name == "pool - " + itemName)); if ((Object)(object)val != (Object)null) { PoolManager.DynamicPools[itemName] = val; } } } } return true; } catch (Exception ex) { error = ex.Message; return false; } } } public static class PlayermodelSync { private static readonly Dictionary> waitingRepsByFileName = new Dictionary>(); private static readonly HashSet requestedFiles = new HashSet(); private static bool initialized; public static void Initialize() { if (!initialized) { initialized = true; FileTransferManager.RegisterCategoryHandler(FileTransferCategory.Playermodel, OnModelFileReceived, OnModelFileFailed); } } public static void RequestModelIfMissing(long ownerUserId, string modelPath, long repOwnerId) { if (!SyncPrefs.playermodelSyncEnabled.Value || !SteamIntegration.hasLobby || SyncPrefs.IsUserBlocked(ownerUserId)) { return; } string path = Path.Combine(PlayermodelsPatch.playerModelsPath, modelPath); if (File.Exists(path)) { return; } string fileName = Path.GetFileName(modelPath); if (!waitingRepsByFileName.TryGetValue(fileName, out var value)) { value = new List(); waitingRepsByFileName[fileName] = value; } if (!value.Contains(repOwnerId)) { value.Add(repOwnerId); } if (requestedFiles.Add(fileName)) { EntangleLogger.Log($"[PlayermodelSync] Missing playermodel '{modelPath}', asking {ownerUserId} for it"); NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.PlayermodelSyncRequest, new PlayermodelSyncRequestData { modelPath = modelPath }); if (networkMessage != null) { Node.activeNode?.SendMessage(ownerUserId, NetworkChannel.Reliable, networkMessage.GetBytes()); } } } public static void OnModelRequested(long requester, string modelPath) { if (!SyncPrefs.playermodelSyncEnabled.Value || SyncPrefs.IsUserBlocked(requester)) { return; } string text = Path.Combine(PlayermodelsPatch.playerModelsPath, modelPath); if (!File.Exists(text)) { EntangleLogger.Warn($"[PlayermodelSync] {requester} asked for '{modelPath}' but we no longer have that file"); return; } if (SyncPrefs.IsPathBlacklisted(modelPath)) { EntangleLogger.Log("[PlayermodelSync] Not sending '" + modelPath + "' - blacklisted"); return; } FileInfo fileInfo = new FileInfo(text); if (fileInfo.Length / 1024 > SyncPrefs.maxSyncSizeKB.Value) { EntangleLogger.Log($"[PlayermodelSync] Not sending '{modelPath}' - {fileInfo.Length / 1024}KB is over the {SyncPrefs.maxSyncSizeKB.Value}KB sync limit"); return; } EntangleLogger.Log($"[PlayermodelSync] Sending '{modelPath}' to {requester}"); FileTransferManager.SendFile(requester, text, FileTransferCategory.Playermodel); } private static void OnModelFileReceived(FileTransfer transfer) { string text = Path.Combine(PlayermodelsPatch.playerModelsPath, transfer.fileName); try { FileTransferManager.WriteReceivedFile(transfer, text); } catch (Exception ex) { EntangleLogger.Error("[PlayermodelSync] Failed writing " + text + ": " + ex.Message); return; } requestedFiles.Remove(transfer.fileName); if (waitingRepsByFileName.TryGetValue(transfer.fileName, out var value)) { foreach (long item in value) { if (PlayerRepresentation.representations.TryGetValue(item, out var value2)) { PlayerSkinLoader.ApplyPlayermodel(value2, text); } } waitingRepsByFileName.Remove(transfer.fileName); } EntangleLogger.Log("[PlayermodelSync] Applied '" + transfer.fileName + "' after sync"); } private static void OnModelFileFailed(FileTransfer transfer) { if (transfer != null) { EntangleLogger.Warn($"[PlayermodelSync] Failed to receive '{transfer.fileName}' from {transfer.peer}"); requestedFiles.Remove(transfer.fileName); waitingRepsByFileName.Remove(transfer.fileName); } } } public enum FileTransferCategory : byte { CustomItem, Playermodel, Custom1, Custom2, Custom3, Custom4 } public class FileTransfer { public ushort id; public long peer; public FileTransferCategory category; public string fileName; public int totalBytes; public bool outgoing; public byte[] sourceBytes; public int sentBytes; public byte[] receiveBuffer; public int receivedBytes; public Action onComplete; public Action onFailed; public float lastActivity; public int lastLoggedProgress; } public static class FileTransferManager { public const int chunkSize = 16000; public const int chunksPerFrame = 4; public const int maxFileBytes = 209715200; public const float timeoutSeconds = 60f; private static ushort nextId = 1; private static readonly Dictionary outgoing = new Dictionary(); private static readonly Dictionary incoming = new Dictionary(); private static readonly Dictionary> categoryHandlers = new Dictionary>(); private static readonly Dictionary> categoryFailHandlers = new Dictionary>(); public static ushort SendFile(long peer, string filePath, FileTransferCategory category, Action onComplete = null, Action onFailed = null) { byte[] array; try { array = File.ReadAllBytes(filePath); } catch (Exception ex) { EntangleLogger.Warn("[FileTransfer] Failed to read " + filePath + ": " + ex.Message); onFailed?.Invoke(null); return 0; } if (array.Length > 209715200) { EntangleLogger.Warn($"[FileTransfer] {filePath} is {array.Length / 1024 / 1024}MB, over the {200}MB sync limit. Not sending."); onFailed?.Invoke(null); return 0; } ushort num = nextId++; if (nextId == 0) { nextId = 1; } FileTransfer fileTransfer = new FileTransfer { id = num, peer = peer, category = category, fileName = Path.GetFileName(filePath), totalBytes = array.Length, outgoing = true, sourceBytes = array, onComplete = onComplete, onFailed = onFailed, lastActivity = Time.time }; outgoing[num] = fileTransfer; FileTransferBeginData data = new FileTransferBeginData { transferId = num, category = category, totalBytes = array.Length, fileName = fileTransfer.fileName }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.FileTransferBegin, data); if (networkMessage != null) { Node.activeNode?.SendMessage(peer, NetworkChannel.Transaction, networkMessage.GetBytes()); } EntangleLogger.Log($"[FileTransfer] Sending {fileTransfer.fileName} ({array.Length / 1024}KB) to {peer}"); return num; } public static void RegisterCategoryHandler(FileTransferCategory category, Action onComplete, Action onFailed = null) { categoryHandlers[category] = onComplete; if (onFailed != null) { categoryFailHandlers[category] = onFailed; } } internal static void OnBeginReceived(long sender, FileTransferBeginData data) { if (data.totalBytes <= 0 || data.totalBytes > 209715200) { EntangleLogger.Warn($"[FileTransfer] Rejecting transfer {data.fileName} from {sender}, size {data.totalBytes} is invalid or over the limit"); return; } categoryHandlers.TryGetValue(data.category, out var value); categoryFailHandlers.TryGetValue(data.category, out var value2); FileTransfer fileTransfer = new FileTransfer(); fileTransfer.id = data.transferId; fileTransfer.peer = sender; fileTransfer.category = data.category; fileTransfer.fileName = data.fileName; fileTransfer.totalBytes = data.totalBytes; fileTransfer.outgoing = false; fileTransfer.receiveBuffer = new byte[data.totalBytes]; fileTransfer.onComplete = value; fileTransfer.onFailed = value2; fileTransfer.lastActivity = Time.time; FileTransfer value3 = fileTransfer; incoming[data.transferId] = value3; EntangleLogger.Log($"[FileTransfer] Downloading {data.fileName} ({data.totalBytes / 1024}KB) from {sender}..."); if (value == null) { EntangleLogger.Warn($"[FileTransfer] No registered handler for category {data.category}, {data.fileName} will download but won't be used"); } } private static void LogProgress(FileTransfer transfer, int currentBytes) { if (transfer.totalBytes > 0) { int num = (int)(100L * (long)currentBytes / transfer.totalBytes); int num2 = num / 25 * 25; if (num2 > transfer.lastLoggedProgress && num2 > 0 && num2 < 100) { transfer.lastLoggedProgress = num2; string text = (transfer.outgoing ? $"to {transfer.peer}" : $"from {transfer.peer}"); EntangleLogger.Log(string.Format("[FileTransfer] {0} {1} {2}: {3}% ({4}/{5}KB)", transfer.outgoing ? "Sending" : "Downloading", transfer.fileName, text, num, currentBytes / 1024, transfer.totalBytes / 1024)); } } } internal static void OnChunkReceived(long sender, FileTransferChunkData data) { if (!incoming.TryGetValue(data.transferId, out var value) || value.peer != sender) { return; } value.lastActivity = Time.time; if (value.receivedBytes + data.chunk.Length > value.receiveBuffer.Length) { EntangleLogger.Warn("[FileTransfer] Chunk overrun for " + value.fileName + ", aborting transfer"); incoming.Remove(data.transferId); value.onFailed?.Invoke(value); return; } Buffer.BlockCopy(data.chunk, 0, value.receiveBuffer, value.receivedBytes, data.chunk.Length); value.receivedBytes += data.chunk.Length; LogProgress(value, value.receivedBytes); if (value.receivedBytes >= value.totalBytes) { incoming.Remove(data.transferId); EntangleLogger.Log($"[FileTransfer] Finished downloading {value.fileName} ({value.totalBytes / 1024}KB) from {sender}"); value.onComplete?.Invoke(value); } } public static void Tick() { if (outgoing.Count > 0) { List list = null; foreach (KeyValuePair item in outgoing) { FileTransfer value = item.Value; for (int i = 0; i < 4; i++) { if (value.sentBytes >= value.totalBytes) { break; } int val = value.totalBytes - value.sentBytes; int num = Math.Min(16000, val); byte[] array = new byte[num]; Buffer.BlockCopy(value.sourceBytes, value.sentBytes, array, 0, num); FileTransferChunkData data = new FileTransferChunkData { transferId = value.id, chunk = array }; NetworkMessage networkMessage = NetworkMessage.CreateMessage(BuiltInMessageType.FileTransferChunk, data); if (networkMessage != null) { Node.activeNode?.SendMessage(value.peer, NetworkChannel.Transaction, networkMessage.GetBytes()); } value.sentBytes += num; } LogProgress(value, value.sentBytes); if (value.sentBytes >= value.totalBytes) { EntangleLogger.Log($"[FileTransfer] Finished sending {value.fileName} ({value.totalBytes / 1024}KB) to {value.peer}"); (list ?? (list = new List())).Add(item.Key); value.onComplete?.Invoke(value); } } if (list != null) { foreach (ushort item2 in list) { outgoing.Remove(item2); } } } if (incoming.Count <= 0) { return; } List list2 = null; foreach (KeyValuePair item3 in incoming) { if (Time.time - item3.Value.lastActivity > 60f) { (list2 ?? (list2 = new List())).Add(item3.Key); } } if (list2 == null) { return; } foreach (ushort item4 in list2) { FileTransfer fileTransfer = incoming[item4]; incoming.Remove(item4); int num2 = (int)((fileTransfer.totalBytes > 0) ? (100L * (long)fileTransfer.receivedBytes / fileTransfer.totalBytes) : 0); EntangleLogger.Warn($"[FileTransfer] Timed out downloading {fileTransfer.fileName} from {fileTransfer.peer} - stalled at {num2}% ({fileTransfer.receivedBytes / 1024}/{fileTransfer.totalBytes / 1024}KB) after {60f:F0}s of silence"); fileTransfer.onFailed?.Invoke(fileTransfer); } } public static void Clear() { outgoing.Clear(); incoming.Clear(); } public static void WriteReceivedFile(FileTransfer transfer, string destinationPath) { string directoryName = Path.GetDirectoryName(destinationPath); if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } File.WriteAllBytes(destinationPath, transfer.receiveBuffer); } } } namespace Entanglement.Compat { public class CompatMessageType { public static byte CustomMap = 80; public static byte PlayerModel = 81; } [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public sealed class OptionalAssemblyTarget : Attribute { public readonly string targetAssembly; public OptionalAssemblyTarget(string targetAssembly) { this.targetAssembly = targetAssembly; } } public abstract class OptionalAssemblyPatch { public static void AttemptPatches() { EntangleLogger.Log("Logging loaded assemblies...", ConsoleColor.DarkMagenta); AppDomain.CurrentDomain.GetAssemblies().ForEach(delegate(Assembly asm) { EntangleLogger.Log(asm.GetName().Name, ConsoleColor.DarkMagenta); }); EntangleLogger.Log("Done!", ConsoleColor.DarkMagenta); IEnumerable enumerable = from type in EntanglementMod.entanglementAssembly.GetTypes() where typeof(OptionalAssemblyPatch).IsAssignableFrom(type) && !type.IsAbstract select type; foreach (Type item in enumerable) { foreach (Attribute customAttribute in item.GetCustomAttributes()) { if (customAttribute is OptionalAssemblyTarget optionalAssemblyTarget) { OptionalAssemblyPatch optionalAssemblyPatch = Activator.CreateInstance(item) as OptionalAssemblyPatch; optionalAssemblyPatch.TryPatch(optionalAssemblyTarget.targetAssembly); } } } } public bool TryPatch(string assemblyName) { try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().First((Assembly asm) => asm.GetName().Name == assemblyName); if (assembly != null) { EntangleLogger.Log("Optional assembly " + assemblyName + " was found! Patching methods for compatibility!"); DoPatches(assembly); } return true; } catch (Exception ex) { if (ex is InvalidOperationException) { EntangleLogger.Warn(assemblyName + " is not installed! If you aren't using " + assemblyName + " compatibility ignore this."); } else { EntangleLogger.Error("Failed patching " + assemblyName + " because " + ex.Message + "\n Trace:\n" + ex.StackTrace); } return false; } } public abstract void DoPatches(Assembly target); } } namespace Entanglement.Compat.Playermodels { [Net.NoAutoRegister] public class LoadCustomPlayerMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => CompatMessageType.PlayerModel; public override NetworkMessage CreateMessage(LoadCustomPlayerMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); byte[] bytes = Encoding.UTF8.GetBytes(data.modelPath); networkMessage.messageData = new byte[9 + bytes.Length]; int num = 0; byte[] bytes2 = BitConverter.GetBytes(data.userId); for (int i = 0; i < 8; i++) { networkMessage.messageData[num++] = bytes2[i]; } networkMessage.messageData[num++] = Convert.ToByte(data.requestCallback); for (int j = 0; j < bytes.Length; j++) { networkMessage.messageData[num++] = bytes[j]; } return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } int num = 0; long num2 = BitConverter.ToInt64(message.messageData, num); num += 8; bool flag = Convert.ToBoolean(message.messageData[num]); num++; if (flag && PlayermodelsPatch.lastLoadedPath != null) { string lastLoadedPath = PlayermodelsPatch.lastLoadedPath; LoadCustomPlayerMessageData loadCustomPlayerMessageData = new LoadCustomPlayerMessageData(); loadCustomPlayerMessageData.userId = SteamIntegration.currentUserId; loadCustomPlayerMessageData.modelPath = Path.GetFileName(lastLoadedPath); Node.activeNode.SendMessage(num2, NetworkChannel.Reliable, NetworkMessage.CreateMessage(CompatMessageType.PlayerModel, loadCustomPlayerMessageData).GetBytes()); } if (PlayerRepresentation.representations.ContainsKey(num2)) { PlayerRepresentation rep = PlayerRepresentation.representations[num2]; int num3 = message.messageData.Length - num; byte[] array = new byte[num3]; for (int i = 0; i < num3; i++) { array[i] = message.messageData[num++]; } string text = Encoding.UTF8.GetString(array); if (string.IsNullOrWhiteSpace(text)) { PlayerSkinLoader.ClearPlayermodel(rep); } else { string path = Path.Combine(PlayermodelsPatch.playerModelsPath, text); if (File.Exists(path)) { PlayerSkinLoader.ApplyPlayermodel(rep, path); } else { PlayermodelSync.RequestModelIfMissing(num2, text, num2); } } } if (Server.instance != null) { byte[] bytes = message.GetBytes(); Server.instance.BroadcastMessageExcept(NetworkChannel.Reliable, bytes, num2); } } } public class LoadCustomPlayerMessageData : NetworkMessageData { public long userId; public bool requestCallback = false; public string modelPath; } public static class PlayerSkinLoader { public static void ClearPlayermodel(PlayerRepresentation rep) { if (Object.op_Implicit((Object)(object)rep.repFord)) { UnloadPlayermodel(rep); rep.repAnimationManager.animator = rep.repAnimator; rep.activeAnimator = rep.repAnimator; rep.repBody.ArtToBlender.bones = rep.repBody.ArtToBlender.bones.FillBones(((Component)rep.repAnimator).transform, rep.repAnimator); FetchFingerTransforms(rep); if (Object.op_Implicit((Object)(object)rep.currentSkinBundle)) { rep.currentSkinBundle.Unload(false); } rep.currentSkinObject = null; rep.currentSkinPath = null; rep.currentSkinBundle = null; ((Component)rep.repGeo).gameObject.SetActive(true); ((Component)rep.repSHJnt).gameObject.SetActive(true); rep.repBody.OnStart(); rep.isCustomSkinned = false; } } public static void ApplyPlayermodel(PlayerRepresentation rep, string path) { //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Invalid comparison between Unknown and I4 //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Invalid comparison between Unknown and I4 ClearPlayermodel(rep); rep.currentSkinPath = path; AssetBundle val = AssetBundleUtilities.TryLoadBundle(path, PlayermodelsPatch.playerModelsPath); if ((Object)(object)val == (Object)null) { EntangleLogger.Warn("PlayerModel failed to load for player " + rep.playerName + ".\nIf the PlayerModel was sent over discord, spaces in the name may have been replaced by underscores."); return; } rep.currentSkinBundle = val; rep.isCustomSkinned = true; if (!Object.op_Implicit((Object)(object)rep.repFord)) { return; } GameObject val2 = rep.currentSkinBundle.LoadAsset("Assets/PlayerModels/PlayerModel.prefab"); if ((Object)(object)val2 == (Object)null) { return; } rep.currentSkinObject = Object.Instantiate(val2); CustomItems.FixObjectShaders(rep.currentSkinObject); ((Object)rep.currentSkinObject).name = ((Object)rep.repFord).name; rep.skinAnimator = rep.currentSkinObject.GetComponent(); rep.skinAnimator.runtimeAnimatorController = rep.repAnimator.runtimeAnimatorController; ((Behaviour)rep.skinAnimator).enabled = false; rep.repAnimationManager.animator = rep.skinAnimator; rep.activeAnimator = rep.skinAnimator; rep.repBody.ArtToBlender.bones = rep.repBody.ArtToBlender.bones.FillBones(((Component)rep.skinAnimator).transform, rep.skinAnimator); FetchFingerTransforms(rep); rep.repBody.OnStart(); ((Component)rep.repGeo).gameObject.SetActive(false); ((Component)rep.repSHJnt).gameObject.SetActive(false); Renderer[] array = Il2CppArrayBase.op_Implicit(rep.currentSkinObject.GetComponentsInChildren()); bool flag = false; Renderer[] array2 = array; foreach (Renderer val3 in array2) { if (((Object)val3).name.ToLower().Contains("head") && Object.op_Implicit((Object)(object)val3.material) && !((Object)val3.material.shader).name.Contains("ShadowOnly") && (int)val3.shadowCastingMode != 3) { flag = true; break; } } if (flag) { return; } Renderer[] array3 = array; foreach (Renderer val4 in array3) { if (Object.op_Implicit((Object)(object)val4.material) && ((Object)val4.material.shader).name.Contains("ShadowOnly")) { val4.material = rep.repHologram; } else if ((int)val4.shadowCastingMode == 3) { val4.shadowCastingMode = (ShadowCastingMode)1; } } } public static void UnloadPlayermodel(PlayerRepresentation rep) { if (Object.op_Implicit((Object)(object)rep.currentSkinObject)) { Object.Destroy((Object)(object)rep.currentSkinObject); } if (Object.op_Implicit((Object)(object)rep.currentSkinBundle)) { rep.currentSkinBundle.Unload(false); rep.currentSkinBundle = null; } } public static void FetchFingerTransforms(PlayerRepresentation rep) { Transform boneTransform = rep.activeAnimator.GetBoneTransform((HumanBodyBones)17); Transform val = TransformDeepChildExtension.FindDeepChild(boneTransform, "l_Hand_2SHJnt"); Transform leftHandleTransform = TransformDeepChildExtension.FindDeepChild(val, "l_GripPoint_AuxSHJnt"); Transform leftOpenHandTransform = TransformDeepChildExtension.FindDeepChild(val.parent, "l_Hand_2SHJnt_open"); rep.repAnimationManager.leftHandTransform = val; rep.repAnimationManager.leftHandleTransform = leftHandleTransform; rep.repAnimationManager.leftOpenHandTransform = leftOpenHandTransform; rep.repAnimationManager.leftClosedHandTransform = val; Transform boneTransform2 = rep.activeAnimator.GetBoneTransform((HumanBodyBones)18); Transform val2 = TransformDeepChildExtension.FindDeepChild(boneTransform2, "r_Hand_2SHJnt"); Transform rightHandleTransform = TransformDeepChildExtension.FindDeepChild(val2, "r_GripPoint_AuxSHJnt"); Transform rightOpenHandTransform = TransformDeepChildExtension.FindDeepChild(val2.parent, "r_Hand_2SHJnt_open"); rep.repAnimationManager.rightHandTransform = val2; rep.repAnimationManager.rightHandleTransform = rightHandleTransform; rep.repAnimationManager.rightOpenHandTransform = rightOpenHandTransform; rep.repAnimationManager.rightClosedHandTransform = val2; rep.repAnimationManager.CalculateHandPoseRefs(); } } [OptionalAssemblyTarget("PlayerModels")] public class PlayermodelsPatch : OptionalAssemblyPatch { public static Type skinLoadingType; public static FieldInfo currentBundleInfo; public static string lastLoadedPath; public static string playerModelsPath => string.Join("", MelonUtils.UserDataDirectory, "\\PlayerModels\\"); public override void DoPatches(Assembly target) { //IL_0093: 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_00a3: Expected O, but got Unknown //IL_00a3: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown skinLoadingType = target.GetType("PlayerModels.SkinLoading"); currentBundleInfo = skinLoadingType.GetField("currentLoadedBundle", BindingFlags.Static | BindingFlags.Public); MethodBase method = skinLoadingType.GetMethod("ApplyPlayerModel", BindingFlags.Static | BindingFlags.Public); MethodInfo method2 = typeof(PlayermodelsPatch).GetMethod("ApplyPlayerModel_Postfix", BindingFlags.Static | BindingFlags.Public); MethodInfo method3 = typeof(PlayermodelsPatch).GetMethod("ApplyPlayerModel_Prefix", BindingFlags.Static | BindingFlags.Public); MethodBase method4 = skinLoadingType.GetMethod("ClearPlayerModel", BindingFlags.Static | BindingFlags.Public); MethodInfo method5 = typeof(PlayermodelsPatch).GetMethod("ClearPlayerModel_Prefix", BindingFlags.Static | BindingFlags.Public); Patcher.Patch(method, new HarmonyMethod(method3), new HarmonyMethod(method2)); Patcher.Patch(method4, new HarmonyMethod(method5)); NetworkMessage.RegisterHandler(); } public static void ClearPlayerModel_Prefix() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) object value = currentBundleInfo.GetValue(null); if (value != null) { AssetBundle val = (AssetBundle)value; if ((int)val != 0) { val.Unload(false); } } BroadcastPlayermodel(" "); lastLoadedPath = null; if (PlayerRepresentation.debugRepresentation != null) { PlayerSkinLoader.ClearPlayermodel(PlayerRepresentation.debugRepresentation); } } public static void ApplyPlayerModel_Prefix(string path) { AssetBundleUtilities.TryUnloadBundle(path, unloadAllLoadedObjects: false); } public static void ApplyPlayerModel_Postfix(string path) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown lastLoadedPath = path; string name = Path.GetFileName(path).ToLower(); object value = currentBundleInfo.GetValue(null); if (value != null) { AssetBundle val = (AssetBundle)value; ((Object)val).name = name; } BroadcastPlayermodel(path); if (PlayerRepresentation.debugRepresentation != null) { PlayerSkinLoader.ApplyPlayermodel(PlayerRepresentation.debugRepresentation, Path.Combine(playerModelsPath, path)); } } public static void BroadcastPlayermodel(string path) { LoadCustomPlayerMessageData loadCustomPlayerMessageData = new LoadCustomPlayerMessageData(); loadCustomPlayerMessageData.userId = SteamIntegration.currentUserId; loadCustomPlayerMessageData.modelPath = Path.GetFileName(path); Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, NetworkMessage.CreateMessage(CompatMessageType.PlayerModel, loadCustomPlayerMessageData).GetBytes()); } } } namespace Entanglement.Compat.CustomMaps { [OptionalAssemblyTarget("CustomMaps")] public class CustomMapsPatch : OptionalAssemblyPatch { public static MethodInfo queueMapInfo; public static MethodInfo queueMapAssetBundle; public static MethodInfo queueMapArchive; public static bool isNewCustomMaps; public static string customMapsPath = Path.Combine(MelonUtils.UserDataDirectory, "CustomMaps"); public override void DoPatches(Assembly target) { //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown Type type = target.GetType("CustomMaps.CustomMaps"); Type type2 = target.GetType("CustomMaps.MapLoader"); queueMapInfo = type.GetMethod("QueueMap", BindingFlags.Static | BindingFlags.Public); if (type2 != null) { isNewCustomMaps = true; queueMapAssetBundle = type2.GetMethod("LoadMapBundle", BindingFlags.Static | BindingFlags.Public); queueMapArchive = type2.GetMethod("LoadArchiveMap", BindingFlags.Static | BindingFlags.Public); } if (!isNewCustomMaps) { MethodInfo method = typeof(CustomMapsPatch).GetMethod("QueueMap_Prefix", BindingFlags.Static | BindingFlags.Public); Type type3 = target.GetType("CustomMaps.MapLoading"); MethodInfo method2 = type3.GetMethod("LoadMap", BindingFlags.Static | BindingFlags.Public); MethodInfo method3 = typeof(CustomMapsPatch).GetMethod("LoadMap_Postfix", BindingFlags.Static | BindingFlags.Public); Patcher.Patch(queueMapInfo, new HarmonyMethod(method)); Patcher.Patch(method2, null, new HarmonyMethod(method3)); } else { MethodInfo method4 = typeof(CustomMapsPatch).GetMethod("LoadMapBundle_Prefix", BindingFlags.Static | BindingFlags.Public); MethodInfo method5 = typeof(CustomMapsPatch).GetMethod("LoadMapArchive_Prefix", BindingFlags.Static | BindingFlags.Public); MethodInfo method6 = type2.GetMethod("PostScenePass", BindingFlags.Static | BindingFlags.Public); MethodInfo method7 = typeof(CustomMapsPatch).GetMethod("PostScenePass_Postfix", BindingFlags.Static | BindingFlags.Public); Patcher.Patch(queueMapAssetBundle, new HarmonyMethod(method4)); Patcher.Patch(queueMapArchive, new HarmonyMethod(method5)); Patcher.Patch(method6, null, new HarmonyMethod(method7)); } NetworkMessage.RegisterHandler(); } public static void LoadMap_Postfix(string path) { SpawnableData.GetData(); } public static void QueueMap_Prefix(string mapToLoad) { if (Node.activeNode is Server) { LoadCustomMapMessageData data = new LoadCustomMapMessageData { mapPath = Path.GetFileName(mapToLoad) }; Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, NetworkMessage.CreateMessage(CompatMessageType.CustomMap, data).GetBytes()); } } public static void PostScenePass_Postfix() { SpawnableData.GetData(); } public static void LoadMapBundle_Prefix(string path) { if (Node.activeNode is Server) { LoadCustomMapMessageData data = new LoadCustomMapMessageData { mapPath = Path.GetFileName(path) }; Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, NetworkMessage.CreateMessage(CompatMessageType.CustomMap, data).GetBytes()); } } public static void LoadMapArchive_Prefix(string archivePath) { if (Node.activeNode is Server) { LoadCustomMapMessageData data = new LoadCustomMapMessageData { mapPath = Path.GetFileName(archivePath) }; Node.activeNode.BroadcastMessage(NetworkChannel.Reliable, NetworkMessage.CreateMessage(CompatMessageType.CustomMap, data).GetBytes()); } } public static void TryLoadMap(string path) { if (!isNewCustomMaps) { queueMapInfo.Invoke(null, new object[1] { Path.Combine(customMapsPath, path) }); } else if (path.EndsWith("cma")) { queueMapArchive.Invoke(null, new object[1] { Path.Combine(customMapsPath, path) }); } else { queueMapAssetBundle.Invoke(null, new object[1] { Path.Combine(customMapsPath, path) }); } } } [Net.NoAutoRegister] public class LoadCustomMapMessageHandler : NetworkMessageHandler { public override byte? MessageIndex => CompatMessageType.CustomMap; public override NetworkMessage CreateMessage(LoadCustomMapMessageData data) { NetworkMessage networkMessage = new NetworkMessage(); networkMessage.messageData = Encoding.UTF8.GetBytes(data.mapPath); return networkMessage; } public override void HandleMessage(NetworkMessage message, long sender) { if (message.messageData.Length == 0) { throw new IndexOutOfRangeException(); } CustomMapsPatch.TryLoadMap(Encoding.UTF8.GetString(message.messageData)); } } public class LoadCustomMapMessageData : NetworkMessageData { public string mapPath; } }