Latest versions of MelonLoader are known to have issues with some games. Use version 0.5.4 until the issue has been fixed!
Decompiled source of Entanglement Redux v2.1.0
EntanglementRedux.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.IO.Pipes; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Xml.Linq; using Boneworks; using Entanglement; using Entanglement.Compat; using Entanglement.Compat.CustomMaps; using Entanglement.Compat.Playermodels; using Entanglement.Data; using Entanglement.Exceptions; using Entanglement.Extensions; 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 Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using MelonLoader; using MelonLoader.Preferences; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; 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.Environment; using StressLevelZero.Interaction; using StressLevelZero.Player; using StressLevelZero.Pool; using StressLevelZero.Props; using StressLevelZero.Props.Weapons; using StressLevelZero.Rig; using StressLevelZero.SFX; using StressLevelZero.UI; using StressLevelZero.UI.Radial; using StressLevelZero.Utilities; using StressLevelZero.VRMK; using StressLevelZero.Zones; using TMPro; using UnhollowerBaseLib; using UnhollowerBaseLib.Runtime; using UnhollowerRuntimeLib; using UnityEngine; using UnityEngine.AI; using UnityEngine.Audio; 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.IgnoreSymbolStoreSequencePoints)] [assembly: Guid("490e160d-251d-4ab4-a3bb-f473961ff8a1")] [assembly: AssemblyTitle("Entanglement Redux")] [assembly: AssemblyFileVersion("2.1.0")] [assembly: MelonInfo(typeof(EntanglementMod), "Entanglement Redux", "2.1.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: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.1.0.0")] [module: UnverifiableCode] namespace Entanglement { public static class EntangleLogger { public static bool verbose; private static Instance Logger { get { try { EntanglementMod instance = EntanglementMod.Instance; if (instance != null) { return ((MelonBase)instance).LoggerInstance; } } catch { } return null; } } public static void Log(string txt, ConsoleColor txt_color = ConsoleColor.White) { Instance logger = Logger; if (logger != null) { logger.Msg(txt_color, txt); } else { MelonLogger.Msg(txt_color, "[Entanglement] " + txt); } } public static void Log(object obj, ConsoleColor txt_color = ConsoleColor.White) { Instance logger = Logger; if (logger != null) { logger.Msg(txt_color, obj); } else { MelonLogger.Msg(txt_color, "[Entanglement] " + obj); } } public static void Debug(string txt, ConsoleColor txt_color = ConsoleColor.White) { if (verbose) { Log(txt, txt_color); } } public static void Warn(string txt) { Instance logger = Logger; if (logger != null) { logger.Warning(txt); } else { MelonLogger.Warning("[Entanglement] " + txt); } } public static void Warn(object obj) { Instance logger = Logger; if (logger != null) { logger.Warning(obj); } else { MelonLogger.Warning("[Entanglement] " + obj); } } public static void Error(string txt) { Instance logger = Logger; if (logger != null) { logger.Error(txt); } else { MelonLogger.Error("[Entanglement] " + txt); } } public static void Error(object obj) { Instance logger = Logger; if (logger != null) { logger.Error(obj); } else { MelonLogger.Error("[Entanglement] " + obj); } } } public static class DebugLog { private static MelonPreferences_Category category; private static MelonPreferences_Entry<bool> saved; private static bool Saved { get { try { if (saved == null) { category = MelonPreferences.CreateCategory("EntanglementDebug"); saved = category.CreateEntry<bool>("debugLog", false, (string)null, "Detailed Entanglement logging, the same as the Representation Test's, without the dummy", false, false, (ValueValidator)null, (string)null); } return saved.Value; } catch { return false; } } } public static bool On => Saved; public static void Initialize() { Apply(); if (On) { EntangleLogger.Log("[Debug] Debug log is on (Settings > Debug to turn it off).", ConsoleColor.DarkYellow); } } public static void Toggle() { bool flag = !Saved; try { saved.Value = flag; MelonPreferences.Save(); } catch (Exception ex) { EntangleLogger.Warn("[Debug] Could not save the debug log setting: " + ex.Message); } Apply(); if (flag) { EntangleLogger.Log("[Debug] Debug log on.", ConsoleColor.DarkYellow); EntangleLogger.Log("[Debug] " + Snapshot(), ConsoleColor.DarkYellow); } else { EntangleLogger.Log("[Debug] Debug log off" + (PlayerRepresentation.RepresentationTestActive ? " (the Representation Test keeps it on until it is stopped)." : "."), ConsoleColor.DarkYellow); } } public static void Apply() { bool flag = (EntangleLogger.verbose = Saved || PlayerRepresentation.RepresentationTestActive); try { NpcTrace.enabled = flag; } catch { } try { BossClawSync.traceEnabled = flag; } catch { } try { SceneEventSync.traceEnabled = flag; } catch { } } public static string Snapshot() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) try { string text = ((!SteamIntegration.hasLobby) ? "no lobby" : (Node.isServer ? "host" : "client")); string text2; try { Scene activeScene = SceneManager.GetActiveScene(); text2 = ((Scene)(ref activeScene)).name; } catch { text2 = "?"; } return "Entanglement " + EntanglementMod.VersionString + " (build " + EntanglementVersion.BuildTag + "), " + text + ", " + PlayerRepresentation.representations.Count + " other player(s), scene " + text2 + ", slow-mo " + SlowMoSync.Mode.ToString() + ", holstered items pinned " + HolsterSync.PinnedCount + ", buttons held for others " + ButtonTogglePatch.RemoteHeldCount + ", gun slides shown for others " + GunSlideSync.MirrorCount + ", Discord " + (DiscordInvite.Connected ? "connected" : "not connected") + ((DiscordInvite.RivalApp != null) ? (" (" + DiscordInvite.RivalApp + " running)") : "") + ". " + NetStats.StateLine(); } catch (Exception ex) { return "snapshot unavailable: " + ex.Message; } } } public static class EntangleNotif { public static void PlayerJoin(string username) { Notifications.SendNotification(username + " has joined!", 4f); } public static void PlayerLeave(string username) { Notifications.SendNotification(username + " left.", 4f); } public static void PlayerDisconnect(DisconnectReason reason, string additionalReason = "") { if (reason == DisconnectReason.MissingMods && !string.IsNullOrEmpty(additionalReason)) { Notifications.SendNotification("You are missing mods:\n" + additionalReason, 8f); } else if (!string.IsNullOrEmpty(additionalReason)) { Notifications.SendNotification($"Disconnected: {reason}\n{additionalReason}", 6f); } else { Notifications.SendNotification($"Disconnected: {reason}", 4f); } } public static void MissingMods(string missingMods) { Notifications.SendNotification("Cannot join, you are missing mods:\n" + missingMods, 8f); } public static void LobbyStarted() { Notifications.SendNotification("Lobby has been created!", 4f); } public static void JoinServer(string username) { Notifications.SendNotification("Joined " + username + "'s game.", 4f); } public static void LeftServer() { Notifications.SendNotification("You have left", 4f); } public static void InvalidSteam() { Notifications.SendNotification("Steam did not start up, so Entanglement is off.\nCheck Steam is running and launch the game through it.", 4f); } } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct EntanglementVersion { public const byte versionMajor = 2; public const byte versionMinor = 1; public const short versionPatch = 0; public const byte minVersionMajorSupported = 2; public const byte minVersionMinorSupported = 1; public const byte protocolRevision = 19; private static ulong buildId; public static ulong BuildId { get { if (buildId == 0L) { try { buildId = BitConverter.ToUInt64(typeof(EntanglementMod).Module.ModuleVersionId.ToByteArray(), 0); } catch { buildId = 1uL; } } return buildId; } } public static string BuildTag => (byte)19 + ":" + BuildId.ToString("x16"); public static bool TryParseBuildTag(string tag, out byte revision, out ulong build) { revision = 0; build = 0uL; if (string.IsNullOrEmpty(tag)) { return false; } int num = tag.IndexOf(':'); if (num <= 0) { return false; } if (byte.TryParse(tag.Substring(0, num), out revision)) { return ulong.TryParse(tag.Substring(num + 1), NumberStyles.HexNumber, null, out build); } return false; } } public class EntanglementMod : MelonMod { public static byte? sceneChange; public static bool hostLoading; public static Assembly entanglementAssembly; public static bool hasUnpatched; private static int menuWait; private static bool uiUnlocked; private static float lastUpdateRealtime; private const float SUSPEND_GAP_SECONDS = 10f; private static readonly Dictionary<string, int> tickFailures; private static int loadsWhileWaiting; public static EntanglementMod Instance { get; protected set; } public static string VersionString { get; protected set; } static EntanglementMod() { sceneChange = null; hasUnpatched = false; menuWait = -1; lastUpdateRealtime = 0f; tickFailures = new Dictionary<string, int>(); SteamworksEmbed.HookResolve(); } public override void OnApplicationStart() { entanglementAssembly = Assembly.GetExecutingAssembly(); Instance = this; VersionString = $"{(byte)2}.{(byte)1}.{(short)0}"; EntangleLogger.Log("Current Entanglement version is " + VersionString + " (build " + EntanglementVersion.BuildTag + ")"); EntangleLogger.Log($"Minimum supported Entanglement version is {(byte)2}.{(byte)1}.*"); DebugLog.Initialize(); PersistentData.Initialize(); SteamworksEmbed.EnsureSteamworksOnDisk(); GameSDK.LoadGameSDK(); SteamIntegration.Initialize(); if (SteamIntegration.isInvalid) { EntangleNotif.InvalidSteam(); return; } Patcher.Initialize(); try { int num = 0; foreach (MethodBase patchedMethod in ((MelonBase)this).HarmonyInstance.GetPatchedMethods()) { _ = patchedMethod; num++; } EntangleLogger.Log($"Patched {num} game methods"); } catch { } NetworkMessage.RegisterHandlersFromAssembly(entanglementAssembly); Client.StartClient(); CustomItemSync.Initialize(); PlayermodelSync.Initialize(); MelonCoroutines.Start(CoDeferredBoot()); BanList.PullFromFile(); EntangleLogger.Log("Welcome to the Entanglement Redux Beta!", ConsoleColor.DarkYellow); UpdateCheck.Start(); } private static IEnumerator CoDeferredBoot() { EntangleLogger.Log("Waiting for Unity before loading embedded bundles..."); for (int i = 0; i < 60; i++) { yield return null; } try { EntangleLogger.Log("Loading playerrep bundle..."); PlayerRepresentation.LoadBundle(); EntangleLogger.Log("playerrep ready."); } catch (Exception ex) { EntangleLogger.Error("playerrep bundle failed: " + ex); } try { LoadingScreen.LoadBundle(); EntangleLogger.Log("logo bundle ready."); } catch (Exception ex2) { EntangleLogger.Error("logo bundle failed: " + ex2); } } public override void OnApplicationLateStart() { if (SteamIntegration.isInvalid) { ((MelonBase)this).HarmonyInstance.UnpatchSelf(); hasUnpatched = true; } } public override void OnUpdate() { if (SteamIntegration.isInvalid) { if (!hasUnpatched) { ((MelonBase)this).HarmonyInstance.UnpatchSelf(); hasUnpatched = true; } return; } NetStats.BeginFrame(); HitSource.BeginFrame(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (lastUpdateRealtime > 0f && realtimeSinceStartup - lastUpdateRealtime > 10f) { EntangleLogger.Debug($"App was suspended for {realtimeSinceStartup - lastUpdateRealtime:F1}s, draining the stale network backlog..."); Node.activeNode?.ClearMessageBuffer(); } lastUpdateRealtime = realtimeSinceStartup; SafeTick("TransformSyncBatcher", TransformSyncBatcher.Flush); SafeTick("VoiceManager", VoiceManager.Tick); SafeTick("LargePropSync", LargePropSync.Tick); SafeTick("SpawnCatchUp", SpawnCatchUp.Tick); SafeTick("NpcDespawnSync", NpcDespawnSync.Tick); SafeTick("NpcOwnerWatch", NpcOwnerWatch.Tick); SafeTick("RemoteZonePresence", RemoteZonePresence.Tick); SafeTick("RepTargetEmulator", RepTargetEmulator.Tick); SafeTick("SceneNpcSync", SceneNpcSync.Tick); SafeTick("NpcConfigSync", NpcConfigSync.Tick); SafeTick("TurretSync", TurretSync.Tick); SafeTick("ModuleHandler", ModuleHandler.Update); SafeTick("NetStats", NetStats.Tick); SafeTick("PhysicsCatchUp", PhysicsCatchUp.Tick); SafeTick("DownloadUI", DownloadUI.Update); if (!uiUnlocked) { if (menuWait > 0) { menuWait--; } else if (menuWait == 0) { uiUnlocked = true; menuWait = -1; } } if (uiUnlocked) { SafeTick("EntanglementRadial", EntanglementRadial.Tick); SafeTick("GameRadial", GameRadial.Tick); SafeTick("GameNotify", GameNotify.Tick); SafeTick("UpdateCheck", UpdateCheck.Tick); } SafeTick("SyncPlayerReps", PlayerRepresentation.SyncPlayerReps); SafeTick("GripRadius", GripRadiusPatch.Flush); SafeTick("UpdatePlayerReps.Frame", PlayerRepresentation.UpdatePlayerRepsPerFrame); SafeTick("FileTransfer", FileTransferManager.Tick); SafeTick("Thunderstore", Thunderstore.Tick); SafeTick("DownloadGate", DownloadGate.Tick); SafeTick("SlowMo", SlowMoSync.Tick); SafeTick("Lethality", PlayerDeathManager.CheckLethality); SafeTick("HeldGripGuard", HeldGripGuard.Tick); SafeTick("CorpseCleanup", CorpseCleanup.Tick); SafeTick("NpcPoolGuard", NpcPoolGuard.Tick); SafeTick("LocalZoneWatch", LocalZoneWatch.Tick); SafeTick("BossClawSync", BossClawSync.Tick); SafeTick("ManipulatorSync", ManipulatorSync.Tick); SafeTick("RepCollisionGuard", RepCollisionGuard.Tick); } private static void SafeTick(string name, Action tick) { long timestamp = Stopwatch.GetTimestamp(); try { tick(); } catch (Exception ex) { tickFailures.TryGetValue(name, out var value); value++; tickFailures[name] = value; if (value <= 3 || value % 600 == 0) { EntangleLogger.Warn("[Tick] " + name + " failed (" + value + "x): " + ex.Message); } } NetStats.AddCost(name, Stopwatch.GetTimestamp() - timestamp); } public override void OnFixedUpdate() { if (!SteamIntegration.isInvalid) { NetStats.MarkFixed(); SafeTick("ModuleHandler.Fixed", ModuleHandler.FixedUpdate); SafeTick("UpdatePlayerReps", PlayerRepresentation.UpdatePlayerReps); SafeTick("DriveRepPhysics", PlayerRepresentation.DriveRepPhysics); SafeTick("SoloDebug", SoloDebug.FixedTick); } } public override void OnLateUpdate() { if (!SteamIntegration.isInvalid) { SafeTick("ModuleHandler.Late", ModuleHandler.LateUpdate); SafeTick("Client.Tick", delegate { Client.instance?.Tick(); }); SafeTick("Server.Tick", delegate { Server.instance?.Tick(); }); SafeTick("SteamIntegration", SteamIntegration.Tick); SafeTick("PresentPlayerReps", PlayerRepresentation.PresentPlayerReps); SafeTick("HolsterSync", HolsterSync.Tick); SafeTick("SlowMo.Apply", SlowMoSync.Apply); SafeTick("GunSlide", GunSlideSync.Tick); NetStats.MarkLateEnd(); } } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { if (SteamIntegration.isInvalid) { return; } Application.backgroundLoadingPriority = (ThreadPriority)1; QualitySettings.asyncUploadTimeSlice = 2; QualitySettings.asyncUploadBufferSize = 4; if (EntangleLogger.verbose) { EntangleLogger.Debug("[Scene] ---- " + sceneName + " (" + buildIndex + ") loaded ---- " + DebugLog.Snapshot()); } ModuleHandler.OnSceneWasInitialized(buildIndex, sceneName); if (SteamIntegration.hasLobby) { SpawnableData.EnsureLobbyUnlocks(); } else { SpawnableData.GetData(); } PlayerScripts.GetPlayerScripts(); try { PlayerDeathManager.Initialize(); } catch (Exception ex) { EntangleLogger.Warn("PlayerDeathManager init: " + ex.Message); } CorpseCleanup.Clear(); TurretSync.OnSceneCleanup(); ManipulatorSync.OnSceneCleanup(); TurretSight.OnSceneCleanup(); LargePropSync.OnSceneCleanup(); BossClawSync.OnSceneCleanup(); RepCollisionGuard.OnSceneCleanup(); TransformMirrorPatch.OnSceneCleanup(); HeldGripGuard.OnSceneCleanup(); ZoneTrackingUtilities.OnSceneCleanup(); InstantiatePatch.OnSceneCleanup(); CustomItemSync.OnSceneCleanup(); SpawnCatchUp.OnSceneCleanup(); BreakSync.OnSceneCleanup(); NpcTrace.OnSceneCleanup(); RemoteZonePresence.OnSceneCleanup(); LocalZoneWatch.OnSceneCleanup(); NpcDespawnSync.OnSceneCleanup(); NpcConfigSync.OnSceneCleanup(); NpcPoolGuard.OnSceneCleanup(); RepTargetEmulator.OnSceneCleanup(); SoloDebug.OnSceneCleanup(); StoryInteractSync.OnSceneCleanup(); SlowMoSync.OnSceneLoaded(); SceneEventSync.OnSceneReady(); try { BreakPieces.CaptureHomes(); } catch (Exception ex2) { EntangleLogger.Warn("BreakPieces.CaptureHomes: " + ex2.Message); } PlayerRepresentation.GetPlayerTransforms(); menuWait = 180; uiUnlocked = false; EntanglementRadial.OnSceneWasInitialized(); GameRadial.OnSceneWasInitialized(); DownloadUI.OnSceneWasInitialized(); GameNotify.OnSceneWasInitialized(); foreach (PlayerRepresentation value in PlayerRepresentation.representations.Values) { value.RecreateRepresentations(); } Client.instance.currentScene = (byte)buildIndex; bool num = Client.instance.pendingScene >= 0 && Client.instance.pendingScene != buildIndex; bool flag = Node.isServer && LevelChangeAnnouncer.levelAnnounced && LevelChangeAnnouncer.lastAnnouncedIndex != buildIndex; bool flag2 = num || flag; if (flag2) { loadsWhileWaiting++; if (loadsWhileWaiting >= 2) { EntangleLogger.Warn("[Scene] Still waiting for scene " + Client.instance.pendingScene + " after " + loadsWhileWaiting + " loads; syncing to " + buildIndex + " instead."); Client.instance.pendingScene = -1; LevelChangeAnnouncer.levelAnnounced = false; flag2 = false; } } else { loadsWhileWaiting = 0; } if (!flag2) { Client.instance.pendingScene = -1; ClientSceneGate.Clear(); hostLoading = false; if (!LevelChangeAnnouncer.ConsumeAnnounce(buildIndex) && !CustomMapSync.HasCurrentMap) { 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()); } } } Client.TryCommandLineJoin(); SteamIntegration.targetScene = sceneName.ToLower(); SteamIntegration.UpdateActivity(); if (SteamIntegration.hasLobby) { TurretSync.OnSceneReady(); NpcPoolGuard.OnSceneReady(); LargePropSync.OnSceneReady(); } } public override void BONEWORKS_OnLoadingScreen() { if (!SteamIntegration.isInvalid) { Application.backgroundLoadingPriority = (ThreadPriority)4; QualitySettings.asyncUploadTimeSlice = 8; QualitySettings.asyncUploadBufferSize = 16; ModuleHandler.OnLoadingScreen(); hostLoading = SteamIntegration.hasLobby && Node.isServer; LoadingScreen.OverrideScreen(); ObjectSync.OnCleanup(); ObjectSync.poolPairs.Clear(); SceneEventSync.OnSceneCleanup(); TurretSync.OnSceneCleanup(); ManipulatorSync.OnSceneCleanup(); TurretSight.OnSceneCleanup(); LargePropSync.OnSceneCleanup(); BossClawSync.OnSceneCleanup(); RepCollisionGuard.OnSceneCleanup(); TransformMirrorPatch.OnSceneCleanup(); HeldGripGuard.OnSceneCleanup(); ZoneTrackingUtilities.OnSceneCleanup(); InstantiatePatch.OnSceneCleanup(); CustomItemSync.OnSceneCleanup(); SpawnCatchUp.OnSceneCleanup(); BreakSync.OnSceneCleanup(); NpcTrace.OnSceneCleanup(); RemoteZonePresence.OnSceneCleanup(); LocalZoneWatch.OnSceneCleanup(); NpcDespawnSync.OnSceneCleanup(); NpcConfigSync.OnSceneCleanup(); NpcPoolGuard.OnSceneCleanup(); RepTargetEmulator.OnSceneCleanup(); SoloDebug.OnSceneCleanup(); StoryInteractSync.OnSceneCleanup(); TriggerRelay.OnSceneCleanup(); SceneNpcSync.OnSceneCleanup(); PooleeSyncable.OnSceneCleanup(); NpcPending.OnSceneCleanup(); NpcOwnerWatch.OnSceneCleanup(); TransformSyncBatcher.Clear(); Server.instance?.replayedUsers.Clear(); } } public override void OnApplicationQuit() { if (!SteamIntegration.isInvalid) { ModuleHandler.OnApplicationQuit(); Node.activeNode.Shutdown(); SteamIntegration.Shutdown(); } } } public static class UpdateCheck { private const string packageUrl = "https://thunderstore.io/api/experimental/package/willpsdk/Entanglement_Redux/"; private static string pendingMessage; private static int started; public static void Start() { if (Interlocked.Exchange(ref started, 1) == 0) { Thread thread = new Thread(Run); thread.IsBackground = true; thread.Name = "EntanglementUpdateCheck"; thread.Start(); } } public static void Tick() { string text = Interlocked.Exchange(ref pendingMessage, null); if (!string.IsNullOrEmpty(text)) { GameNotify.Show(text, 8f); } } private static void Run() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) try { HttpClientHandler val = new HttpClientHandler { AutomaticDecompression = (DecompressionMethods.GZip | DecompressionMethods.Deflate) }; HttpClient val2 = new HttpClient((HttpMessageHandler)val) { Timeout = TimeSpan.FromSeconds(8.0) }; string result = val2.GetStringAsync("https://thunderstore.io/api/experimental/package/willpsdk/Entanglement_Redux/").GetAwaiter().GetResult(); ((HttpMessageInvoker)val2).Dispose(); ((HttpMessageHandler)val).Dispose(); string text = ReadLatestVersion(result); if (string.IsNullOrEmpty(text)) { EntangleLogger.Warn("Thunderstore didn't return a version for Entanglement Redux."); return; } if (!Version.TryParse(EntanglementMod.VersionString, out Version result2) || !Version.TryParse(text, out Version result3)) { EntangleLogger.Warn("Couldn't compare versions (yours " + EntanglementMod.VersionString + ", Thunderstore " + text + ")."); return; } int num = result2.CompareTo(result3); if (num < 0) { EntangleLogger.Warn($"Entanglement Redux is out of date. Yours: {result2}. Latest: {result3}."); pendingMessage = $"Entanglement Redux is out of date. Yours: {result2}. Latest: {result3}."; } else if (num == 0) { EntangleLogger.Log($"You're on the latest Entanglement Redux ({result2})."); } else { EntangleLogger.Log($"This build ({result2}) is newer than Thunderstore ({result3})."); } } catch (Exception ex) { EntangleLogger.Warn("Couldn't reach Thunderstore to check for an update (" + ex.GetType().Name + ")."); } } private static string ReadLatestVersion(string json) { if (string.IsNullOrEmpty(json)) { return null; } int num = json.IndexOf("\"latest\"", StringComparison.Ordinal); if (num < 0) { return null; } int num2 = json.IndexOf("\"version_number\":\"", num, StringComparison.Ordinal); if (num2 < 0) { return null; } num2 += "\"version_number\":\"".Length; int num3 = json.IndexOf('"', num2); if (num3 < 0) { return null; } return json.Substring(num2, num3 - num2); } } } namespace Entanglement.Voice { public enum VoiceMode : byte { Proximity, Global } public static class VoiceManager { private class VoicePlayer { public AudioSource source; public AudioLowPassFilter lowPass; public Transform head; public AudioClip clip; public int clipSamples; public long written; public long played; public int lastTimeSamples; public float lastReceiveTime; public float targetVolume = 1f; public float targetCutoff = 22000f; public float volume = 1f; public float cutoff = 22000f; public float nextOcclusionCheck; public float occlusion; } public static bool micEnabled = true; public static VoiceMode mode = VoiceMode.Proximity; public static int proximityRange = 30; 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<int, float[]> chunkPool = new Dictionary<int, float[]>(); public static float localVoiceTime = -10f; private const float speakingWindow = 0.3f; private static readonly Dictionary<long, VoicePlayer> players = new Dictionary<long, VoicePlayer>(); private static readonly HashSet<long> mutedPlayers = new HashSet<long>(); private const float ReferenceDistance = 1.2f; private const float OcclusionInterval = 0.1f; private const float AcousticSmoothing = 10f; private static AudioListener listener; private static readonly Il2CppStructArray<RaycastHit> occlusionHits = new Il2CppStructArray<RaycastHit>(16L); public static bool IsLocalSpeaking { get { if (micEnabled) { return Time.time - localVoiceTime < 0.3f; } return false; } } public static bool IsSpeaking(long userId) { if (players.TryGetValue(userId, out var value)) { return Time.time - value.lastReceiveTime < 0.3f; } return false; } 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_007f: 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) bool flag = false; 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; } uint num = default(uint); uint num2 = default(uint); if (recording && (int)SteamUser.GetAvailableVoice(ref num) == 0 && num != 0 && (int)SteamUser.GetVoice(true, compressedBuffer, (uint)compressedBuffer.Length, ref num2) == 0 && num2 != 0) { if (SteamIntegration.hasLobby) { VoiceDataMessageHandler.SendVoice(compressedBuffer, (int)num2); } localVoiceTime = Time.time; } try { UpdateAcoustics(); } catch (Exception ex) { EntangleLogger.Warn("[Voice] Acoustics: " + ex.Message); } 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_0055: 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); if ((int)SteamUser.DecompressVoice(receiveScratch, (uint)count, decompressBuffer, (uint)decompressBuffer.Length, ref num, sampleRate) != 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<float>.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<float>.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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_009b: Unknown result type (might be due to invalid IL or missing references) 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); val.transform.localPosition = new Vector3(0f, -0.07f, 0.09f); AudioSource val3 = val.AddComponent<AudioSource>(); val3.loop = true; val3.playOnAwake = false; val3.dopplerLevel = 0f; val3.spread = 0f; val3.reverbZoneMix = 1f; AudioLowPassFilter val4 = val.AddComponent<AudioLowPassFilter>(); val4.cutoffFrequency = 22000f; val4.lowpassResonanceQ = 1f; int num = (int)sampleRate; AudioClip clip = (val3.clip = AudioClip.Create($"VoiceClip {speakerId}", num, 1, (int)sampleRate, false)); value = new VoicePlayer { source = val3, lowPass = val4, head = val2, clip = clip, clipSamples = num }; players[speakerId] = value; ApplySettingsTo(value); return value; } private static PlayerRepresentation ResolveRep(long speakerId) { PlayerRepresentation.representations.TryGetValue(speakerId, out var value); return value; } 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)) { return; } player.source.volume = 1f; if (mode == VoiceMode.Global) { player.source.spatialBlend = 0f; player.source.spatialize = false; if (Object.op_Implicit((Object)(object)player.lowPass)) { ((Behaviour)player.lowPass).enabled = false; } return; } player.source.spatialBlend = 1f; player.source.spatialize = false; player.source.minDistance = 1.2f; player.source.maxDistance = Mathf.Max(5f, (float)proximityRange); player.source.rolloffMode = (AudioRolloffMode)2; player.source.SetCustomCurve((AudioSourceCurveType)0, BuildRolloff(player.source.maxDistance)); if (Object.op_Implicit((Object)(object)player.lowPass)) { ((Behaviour)player.lowPass).enabled = true; } } private static AnimationCurve BuildRolloff(float maxDistance) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown AnimationCurve val = new AnimationCurve(); for (int i = 0; i <= 24; i++) { float num = (float)i / 24f; float num2 = Mathf.Max(0.01f, num * maxDistance); float num3 = Mathf.Min(1f, 1.2f / num2); float num4 = Mathf.InverseLerp(1f, 0.75f, num); val.AddKey(num, num3 * Mathf.Clamp01(num4)); } return val; } private static Transform ListenerTransform() { try { if (!Object.op_Implicit((Object)(object)listener) || !((Behaviour)listener).isActiveAndEnabled) { listener = Object.FindObjectOfType<AudioListener>(); } } catch { listener = null; } if (Object.op_Implicit((Object)(object)listener)) { return ((Component)listener).transform; } if (!Object.op_Implicit((Object)(object)Camera.main)) { return null; } return ((Component)Camera.main).transform; } private static void UpdateAcoustics() { //IL_0082: 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_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) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) if (mode != VoiceMode.Proximity || players.Count == 0) { return; } Transform val = ListenerTransform(); if (!Object.op_Implicit((Object)(object)val)) { return; } float num = 1f - Mathf.Exp(-10f * Time.unscaledDeltaTime); foreach (VoicePlayer value in players.Values) { if (Object.op_Implicit((Object)(object)value.source) && Object.op_Implicit((Object)(object)value.lowPass)) { Vector3 position = ((Component)value.source).transform.position; Vector3 val2 = val.position - position; float magnitude = ((Vector3)(ref val2)).magnitude; if (Time.unscaledTime >= value.nextOcclusionCheck) { value.nextOcclusionCheck = Time.unscaledTime + 0.1f; value.occlusion = Occlusion(position, val.position, magnitude); } float num2 = 1f; float num3 = 22000f; if (value.occlusion > 0f) { num2 *= Mathf.Pow(0.35f, Mathf.Min(value.occlusion, 3f)); num3 = Mathf.Min(num3, Mathf.Lerp(2000f, 700f, Mathf.Clamp01(value.occlusion - 1f))); } if (Object.op_Implicit((Object)(object)value.head) && magnitude > 0.01f) { float num4 = Vector3.Dot(value.head.forward, val2 / magnitude); float num5 = Mathf.InverseLerp(0.3f, -1f, num4); num2 *= Mathf.Lerp(1f, 0.6f, num5); num3 = Mathf.Min(num3, Mathf.Lerp(22000f, 5000f, num5)); } float num6 = Mathf.Clamp01(magnitude / Mathf.Max(5f, (float)proximityRange)); num3 = Mathf.Min(num3, Mathf.Lerp(22000f, 7000f, num6)); value.targetVolume = num2; value.targetCutoff = num3; value.volume = Mathf.Lerp(value.volume, value.targetVolume, num); value.cutoff = Mathf.Lerp(value.cutoff, value.targetCutoff, num); value.source.volume = value.volume; value.lowPass.cutoffFrequency = value.cutoff; } } } private static float Occlusion(Vector3 mouth, Vector3 ear, float distance) { //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_0010: 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_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_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) if (distance < 0.3f) { return 0f; } Vector3 val = (ear - mouth) / distance; int num; try { num = Physics.RaycastNonAlloc(mouth, val, occlusionHits, distance, -1, (QueryTriggerInteraction)1); } catch { return 0f; } float num2 = 0f; for (int i = 0; i < num; i++) { RaycastHit val2 = ((Il2CppArrayBase<RaycastHit>)(object)occlusionHits)[i]; Collider collider = ((RaycastHit)(ref val2)).collider; if (!Object.op_Implicit((Object)(object)collider)) { continue; } try { Transform root = ((Component)collider).transform.root; if ((!Object.op_Implicit((Object)(object)root) || (!((Object)root).name.StartsWith("PlayerRep.") && !((Object)root).name.StartsWith("Ragdoll."))) && (!Object.op_Implicit((Object)(object)PlayerScripts.playerRig) || !((Component)collider).transform.IsChildOf(((Component)PlayerScripts.playerRig).transform))) { Rigidbody attachedRigidbody = collider.attachedRigidbody; if ((Object)(object)attachedRigidbody == (Object)null) { num2 += 1f; } else if (Object.op_Implicit((Object)(object)((Component)attachedRigidbody).GetComponent<Joint>())) { num2 += 0.5f; } } } catch { } } return num2; } 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(); listener = null; } } } namespace Entanglement.UI { 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() { if ((Object)(object)assetBundle == (Object)null) { return; } try { Texture2D val = assetBundle.LoadAsset<Texture2D>("entanglement.png"); if ((Object)(object)val == (Object)null) { return; } GameObject val2 = GameObject.Find("Canvas/RawImage (1)"); if (!((Object)(object)val2 == (Object)null)) { RawImage component = val2.GetComponent<RawImage>(); if (!((Object)(object)component == (Object)null)) { component.texture = (Texture)(object)val; } } } catch { } } } public static class DownloadUI { private const float Smooth = 0.35f; private const float Tau = (float)Math.PI * 2f; private const float HalfPi = (float)Math.PI / 2f; private const int Wedges = 16; private const float Inner = 0.016f; private const float Outer = 0.078f; private const float Gap = 0.045f; private static readonly Vector3 Offset = new Vector3(0.02f, 0.07f, 0.04f); private static GameObject root; private static MeshRenderer ringRenderer; private static Mesh ringMesh; private static Color32[] colors; private static TextMeshProUGUI label; private static bool buildFailed; private static bool snapped; private static Texture2D whiteTex; public static void OnSceneWasInitialized() { label = null; ringRenderer = null; ringMesh = null; colors = null; root = null; snapped = false; buildFailed = false; } public static void Update() { //IL_021d: Unknown result type (might be due to invalid IL or missing references) bool flag = FileTransferManager.HasPendingConsent || Thunderstore.HasPending; bool flag2 = FileTransferManager.HasActiveDownloads || Thunderstore.IsBusy; if (!flag && !flag2) { if ((Object)(object)root != (Object)null && root.activeSelf) { root.SetActive(false); } snapped = false; } else { if (!EnsureBuilt()) { return; } if (!root.activeSelf) { root.SetActive(true); } FollowWrist(); float num; Color color = default(Color); string text; string name; float doneMb; float totalMb; float progress; if (flag) { FileTransfer fileTransfer = FileTransferManager.LargestPendingConsent(); num = ((fileTransfer != null && fileTransfer.totalBytes > 0) ? Mathf.Clamp01((float)fileTransfer.receivedBytes / (float)fileTransfer.totalBytes) : 0.15f); ((Color)(ref color))..ctor(1f, 0.78f, 0.15f, 1f); if (fileTransfer != null) { text = SizeLine(Path.GetFileNameWithoutExtension(fileTransfer.fileName), (float)fileTransfer.receivedBytes / 1024f / 1024f, (float)fileTransfer.totalBytes / 1024f / 1024f); } else { List<Thunderstore.PendingOffer> pending = Thunderstore.GetPending(); text = ((pending.Count > 0) ? ("Download " + pending[0].label + "\nCircle, Downloads") : "Permission needed\nCircle, Downloads"); } } else if (Thunderstore.TryGetProgress(out name, out doneMb, out totalMb, out progress)) { num = progress; ((Color)(ref color))..ctor(0.35f, 0.9f, 1f, 1f); text = ((totalMb > 0f) ? SizeLine(name, doneMb, totalMb) : SizeLine(name, 0f, 0f)); } else { num = FileTransferManager.TotalDownloadProgress(); ((Color)(ref color))..ctor(0.35f, 0.9f, 1f, 1f); FileTransfer fileTransfer2 = FileTransferManager.LargestActiveDownload(); text = ((fileTransfer2 != null) ? SizeLine(Path.GetFileNameWithoutExtension(fileTransfer2.fileName), (float)fileTransfer2.receivedBytes / 1024f / 1024f, (float)fileTransfer2.totalBytes / 1024f / 1024f) : SizeLine("Downloading", num, 1f)); } Paint(num, color); if ((Object)(object)label != (Object)null) { ((TMP_Text)label).text = text; } } } private static bool EnsureBuilt() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown if ((Object)(object)root != (Object)null) { return true; } if (buildFailed) { return false; } if ((Object)(object)Wrist() == (Object)null || !Object.op_Implicit((Object)(object)PlayerScripts.playerRig)) { return false; } try { root = new GameObject("EntanglementDownloadPie"); BuildRing(); BuildLabel(); snapped = false; return true; } catch (Exception ex) { EntangleLogger.Warn("[DownloadUI] Failed to build the pie meter: " + ex.Message); buildFailed = true; if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } root = null; return false; } } private static void BuildRing() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) Vector3[] array = (Vector3[])(object)new Vector3[64]; int[] array2 = new int[96]; colors = (Color32[])(object)new Color32[64]; for (int i = 0; i < 16; i++) { float angle = (float)i / 16f * ((float)Math.PI * 2f) - (float)Math.PI / 2f + 0.0225f; float angle2 = (float)(i + 1) / 16f * ((float)Math.PI * 2f) - (float)Math.PI / 2f - 0.0225f; int num = i * 4; array[num] = Polar(angle, 0.016f); array[num + 1] = Polar(angle, 0.078f); array[num + 2] = Polar(angle2, 0.078f); array[num + 3] = Polar(angle2, 0.016f); int num2 = i * 6; array2[num2] = num; array2[num2 + 1] = num + 1; array2[num2 + 2] = num + 2; array2[num2 + 3] = num; array2[num2 + 4] = num + 2; array2[num2 + 5] = num + 3; } ringMesh = new Mesh(); ringMesh.vertices = Il2CppStructArray<Vector3>.op_Implicit(array); ringMesh.triangles = Il2CppStructArray<int>.op_Implicit(array2); ringMesh.colors32 = Il2CppStructArray<Color32>.op_Implicit(colors); ringMesh.RecalculateBounds(); GameObject val = new GameObject("Ring"); val.transform.SetParent(root.transform, false); val.AddComponent<MeshFilter>().sharedMesh = ringMesh; ringRenderer = val.AddComponent<MeshRenderer>(); ((Renderer)ringRenderer).sharedMaterial = RingMaterial(); ((Renderer)ringRenderer).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)ringRenderer).receiveShadows = false; } private static string SizeLine(string name, float doneMb, float totalMb) { if (string.IsNullOrEmpty(name)) { name = "Download"; } if (name.Length > 18) { name = name.Substring(0, 17) + "..."; } return $"{name}\n{doneMb:0.0}/{totalMb:0.0} MB"; } private static Vector3 Polar(float angle, float radius) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) return new Vector3(Mathf.Cos(angle) * radius, Mathf.Sin(angle) * radius, 0f); } private static Material RingMaterial() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("Sprites/Default"); if ((Object)(object)val == (Object)null) { val = Shader.Find("UI/Default"); } if ((Object)(object)val == (Object)null) { val = Shader.Find("Unlit/Transparent"); } Material val2 = new Material(val); val2.mainTexture = (Texture)(object)WhiteTex(); val2.color = Color.white; if (val2.HasProperty("_Cull")) { val2.SetInt("_Cull", 0); } val2.renderQueue = 4000; return val2; } private static Texture2D WhiteTex() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)whiteTex != (Object)null) { return whiteTex; } whiteTex = new Texture2D(1, 1, (TextureFormat)4, false); whiteTex.SetPixel(0, 0, Color.white); whiteTex.Apply(); return whiteTex; } private static void BuildLabel() { //IL_0005: 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_0020: 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_003f: 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_005e: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Label"); val.transform.SetParent(root.transform, false); val.transform.localPosition = new Vector3(0f, 0.012f, -0.02f); val.transform.localRotation = Quaternion.Euler(0f, 180f, 0f); val.transform.localScale = new Vector3(-0.0008f, 0.0008f, 0.0008f); Canvas obj = val.AddComponent<Canvas>(); obj.renderMode = (RenderMode)2; obj.sortingOrder = 500; label = val.AddComponent<TextMeshProUGUI>(); if ((Object)(object)((TMP_Text)label).font == (Object)null && Object.op_Implicit((Object)(object)PlayerScripts.playerRig)) { TextMeshProUGUI componentInChildren = ((Component)PlayerScripts.playerRig).GetComponentInChildren<TextMeshProUGUI>(true); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)((TMP_Text)componentInChildren).font != (Object)null) { ((TMP_Text)label).font = ((TMP_Text)componentInChildren).font; } } ((TMP_Text)label).alignment = (TextAlignmentOptions)514; ((TMP_Text)label).enableWordWrapping = false; ((TMP_Text)label).overflowMode = (TextOverflowModes)0; ((TMP_Text)label).color = Color.white; ((TMP_Text)label).fontSize = 48f; ((TMP_Text)label).rectTransform.sizeDelta = new Vector2(520f, 180f); ((TMP_Text)label).text = "test download\n0.0/10.0 MB"; } private static void FollowWrist() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //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_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_0093: 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_00ac: 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_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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: 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_0061: 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_006b: 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_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) Hand val = Wrist(); Transform val2 = Head(); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val.palmPositionTransform == (Object)null)) { Vector3 position = val.palmPositionTransform.position; Vector3 val3 = val2.position - position; val3.y = 0f; if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f) { val3 = -val2.forward; } ((Vector3)(ref val3)).Normalize(); Vector3 val4 = position + Vector3.up * Offset.y + ((Component)val).transform.right * Offset.x + val3 * Offset.z; Quaternion val5 = Quaternion.LookRotation(val4 - val2.position, Vector3.up); if (!snapped) { root.transform.SetPositionAndRotation(val4, val5); snapped = true; } else { root.transform.position = Vector3.Lerp(root.transform.position, val4, 0.35f); root.transform.rotation = Quaternion.Slerp(root.transform.rotation, val5, 0.35f); } } } private static void Paint(float progress, Color color) { //IL_0034: 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_003a: 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_004b: 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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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) if (!((Object)(object)ringMesh == (Object)null) && colors != null) { float num = Mathf.Clamp01(progress) * 16f; Color32 val = default(Color32); ((Color32)(ref val))..ctor((byte)28, (byte)30, (byte)36, (byte)230); Color32 val2 = Color32.op_Implicit(color); for (int i = 0; i < 16; i++) { float num2 = Mathf.Clamp01(num - (float)i); Color32 val3 = Color32.Lerp(val, val2, num2); int num3 = i * 4; colors[num3] = val3; colors[num3 + 1] = val3; colors[num3 + 2] = val3; colors[num3 + 3] = val3; } ringMesh.colors32 = Il2CppStructArray<Color32>.op_Implicit(colors); } } private static Hand Wrist() { Hand playerLeftHand = PlayerScripts.playerLeftHand; if ((Object)(object)playerLeftHand != (Object)null && (Object)(object)playerLeftHand.palmPositionTransform != (Object)null) { return playerLeftHand; } Hand playerRightHand = PlayerScripts.playerRightHand; if ((Object)(object)playerRightHand != (Object)null && (Object)(object)playerRightHand.palmPositionTransform != (Object)null) { return playerRightHand; } return null; } private static Transform Head() { if (!Object.op_Implicit((Object)(object)PlayerScripts.playerRig)) { return null; } Transform val = ((Component)PlayerScripts.playerRig).transform.Find("[PhysicsRig]/Head"); if ((Object)(object)val != (Object)null) { return val; } if (!((Object)(object)Camera.main != (Object)null)) { return null; } return ((Component)Camera.main).transform; } } public static class EntanglementRadial { private const string ButtonName = "Entanglement Redux"; private static bool added; private static int framesUntilTry = int.MaxValue; private static readonly string[] spareRegions = new string[6] { "button_Region_S", "button_Region_SW", "button_Region_W", "button_Region_NE", "button_Region_E", "button_Region_SE" }; public static void OnSceneWasInitialized() { added = false; framesUntilTry = 0; } public static void EnsureVisible() { framesUntilTry = 0; TryAdd(refreshPage: true); } public static void Tick() { if (!added) { if (framesUntilTry > 0) { framesUntilTry--; } else { TryAdd(); } } } private static void TryAdd(bool refreshPage = false) { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: 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) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown try { GameObject val = null; if (Object.op_Implicit((Object)(object)PlayerScripts.playerRig)) { val = ((Component)PlayerScripts.playerRig).gameObject; } if (!Object.op_Implicit((Object)(object)val)) { val = Player.GetRigManager(); } if (!Object.op_Implicit((Object)(object)val)) { return; } Transform val2 = val.transform.Find("[UIRig]/PLAYERUI/panel_Default"); if (!Object.op_Implicit((Object)(object)val2)) { return; } PageView component = ((Component)val2).GetComponent<PageView>(); if ((Object)(object)component == (Object)null || component.m_HomePage == null || component.m_HomePage.items == null) { return; } Object.FindObjectOfType<PopUpMenuView>(); RemoveExtraEntanglement(component); if (HasEntanglement(component)) { added = true; if (refreshPage) { component.Render(component.m_HomePage); } return; } string[] array = spareRegions; foreach (string text in array) { Directions val3 = DirectionFromRegion(text); if (DirectionTaken(component, val3)) { continue; } Transform val4 = val2.Find(text); if (!Object.op_Implicit((Object)(object)val4)) { continue; } PageItemView component2 = ((Component)val4).GetComponent<PageItemView>(); if (!Object.op_Implicit((Object)(object)component2)) { continue; } Action action = delegate { if (!GameRadial.Open()) { EntangleLogger.Warn("[EntanglementRadial] Could not open the Entanglement Redux menu."); } }; component2.m_Data = new PageItem("Entanglement Redux", val3, Action.op_Implicit(action)); component.m_HomePage.items.Add(component2.m_Data); added = true; if (refreshPage || !component.m_IsActivated) { component.Render(component.m_HomePage); } EntangleLogger.Log("[EntanglementRadial] Added Entanglement on " + text); return; } EntangleLogger.Warn("[EntanglementRadial] No free circle-menu slot."); added = true; } catch (Exception ex) { EntangleLogger.Warn("[EntanglementRadial] Failed to add circle button: " + ex.Message); added = true; } } private static bool HasEntanglement(PageView pageView) { for (int i = 0; i < pageView.m_HomePage.items.Count; i++) { PageItem val = pageView.m_HomePage.items[i]; if (val != null && val.name == "Entanglement Redux") { return true; } } return false; } private static void RemoveExtraEntanglement(PageView pageView) { bool flag = false; List<PageItem> items = pageView.m_HomePage.items; for (int num = items.Count - 1; num >= 0; num--) { PageItem val = items[num]; if (val != null && !(val.name != "Entanglement Redux")) { if (!flag) { flag = true; } else { items.RemoveAt(num); } } } } private static bool DirectionTaken(PageView pageView, Directions direction) { //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) for (int i = 0; i < pageView.m_HomePage.items.Count; i++) { PageItem val = pageView.m_HomePage.items[i]; if (val != null && val.direction == direction) { return true; } } return false; } private static Directions DirectionFromRegion(string region) { if (!region.EndsWith("_NE")) { if (!region.EndsWith("_NW")) { if (!region.EndsWith("_SE")) { if (!region.EndsWith("_SW")) { if (!region.EndsWith("_N")) { if (!region.EndsWith("_S")) { if (!region.EndsWith("_E")) { return (Directions)6; } return (Directions)2; } return (Directions)4; } return (Directions)0; } return (Directions)5; } return (Directions)3; } return (Directions)7; } return (Directions)1; } } [HarmonyPatch(typeof(PopUpMenuView), "Activate")] public static class EntanglementRadialOpenPatch { public static void Prefix() { EntanglementRadial.EnsureVisible(); } public static void Postfix() { try { GameRadial.OnMenuOpened(); } catch (Exception e) { PatchGuard.Report("EntanglementRadial.OpenPostfix", e); } } } public static class GameNotify { private struct Pending { public string text; public float seconds; } private static UI_Subtitle_Control subtitles; private static int framesUntilReady; private static bool ready; private static readonly List<Pending> queued = new List<Pending>(); public static void OnSceneWasInitialized() { subtitles = null; ready = false; framesUntilReady = 90; } public static void Tick() { if (ready || framesUntilReady <= 0) { return; } framesUntilReady--; if (framesUntilReady <= 0) { ready = true; for (int i = 0; i < queued.Count; i++) { Deliver(queued[i].text, queued[i].seconds); } queued.Clear(); } } public static void Show(string text, float seconds = 4f) { if (!string.IsNullOrEmpty(text)) { if (!ready) { queued.Add(new Pending { text = text, seconds = seconds }); } else { Deliver(text, seconds); } } } private static void Deliver(string text, float seconds) { string text2 = text.Replace("\r", " ").Replace("\n", " ").Trim(); if (text2.Length > 90) { text2 = text2.Substring(0, 87) + "..."; } try { if ((Object)(object)subtitles == (Object)null) { subtitles = Object.FindObjectOfType<UI_Subtitle_Control>(); } if ((Object)(object)subtitles != (Object)null) { subtitles.AddSubtitles(text2, Mathf.Max(2f, seconds), (SubtitlePlacement)1); return; } } catch (Exception ex) { subtitles = null; EntangleLogger.Warn("[GameNotify] Subtitle banner unavailable: " + ex.Message); } try { Notifications.SendNotification(text, seconds); } catch { } } } public static class GameRadial { private struct Entry { public string label; public Action action; public bool wide; public Entry(string label, Action action, bool wide = false) { this.label = label; this.action = action; this.wide = wide; } } private static Directions[] slots; private static PageView pageView; private static Page pending; private static bool animateNext = true; private static float nextClick; private static CallResult<LobbyMatchList_t> lobbySearch; private static int searchGen; private static int searchWaiting; private static bool viewingServers; private static readonly List<CSteamID> servers = new List<CSteamID>(); private static int playerPage; private static int downloadPage; private static int serverPage; private static bool downloadsWanted; private static int bannedPage; private static Directions[] Slots() { if (slots != null) { return slots; } Directions[] array = new Directions[7]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); slots = (Directions[])(object)array; return slots; } public static bool AllowClick() { if (Time.unscaledTime < nextClick) { return false; } nextClick = Time.unscaledTime + 0.35f; return true; } private static Action Once(Action action) { return delegate { if (AllowClick()) { action?.Invoke(); } }; } public static void OnSceneWasInitialized() { pageView = null; pending = null; } public static void Tick() { if (pending != null && Bind()) { if (animateNext) { pageView.ChangePage(pending); } else { pageView.Render(pending); } pending = null; animateNext = true; } } public static bool Open() { if (!AllowClick()) { return true; } if (!Bind()) { return false; } ShowHub(); return true; } private static bool Bind() { if (Object.op_Implicit((Object)(object)pageView)) { return true; } GameObject val = null; if (Object.op_Implicit((Object)(object)PlayerScripts.playerRig)) { val = ((Component)PlayerScripts.playerRig).gameObject; } if (!Object.op_Implicit((Object)(object)val)) { val = Player.GetRigManager(); } if (!Object.op_Implicit((Object)(object)val)) { return false; } Transform val2 = val.transform.Find("[UIRig]/PLAYERUI/panel_Default"); if (!Object.op_Implicit((Object)(object)val2)) { return false; } pageView = ((Component)val2).GetComponent<PageView>(); if ((Object)(object)pageView != (Object)null) { return pageView.m_HomePage != null; } return false; } private static void Show(Page page, bool animate = true) { pending = page; animateNext = animate; } private static void ShowHome() { viewingServers = false; if ((Object)(object)pageView != (Object)null && pageView.m_HomePage != null) { Show(pageView.m_HomePage); } } private static Page PageOf(params Entry[] entries) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown Page val = new Page(); val.items = new List<PageItem>(); Directions[] array = Slots(); int num = Math.Min(entries.Length, array.Length); for (int i = 0; i < num; i++) { val.items.Add(new PageItem(Fit(entries[i].label, entries[i].wide), array[i], Action.op_Implicit(Once(entries[i].action)))); } val.items.Add(new PageItem("Back", (Directions)4, Action.op_Implicit(Once(ShowHome)))); return val; } private static Page PageBack(Action back, params Entry[] entries) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown Page val = new Page(); val.items = new List<PageItem>(); Directions[] array = Slots(); int num = Math.Min(entries.Length, array.Length); for (int i = 0; i < num; i++) { val.items.Add(new PageItem(Fit(entries[i].label, entries[i].wide), array[i], Action.op_Implicit(Once(entries[i].action)))); } val.items.Add(new PageItem("Back", (Directions)4, Action.op_Implicit(Once(back)))); return val; } private static void ShowHub() { viewingServers = false; Show(PageOf(new Entry("Lobby", ShowLobby), new Entry("Server Browser", ShowServers), new Entry("Players", ShowPlayers), new Entry("Downloads", ShowDownloads), new Entry("Voice", ShowVoice), new Entry(VoiceManager.micEnabled ? "Mic On" : "Mic Off", delegate { VoiceManager.micEnabled = !VoiceManager.micEnabled; ShowHub(); }), new Entry("Settings", ShowSettings))); } private static void ShowLobby() { Show(PageBack(ShowHub, new Entry("Start Server", delegate { Server.StartServer(); ShowLobby(); }), new Entry("Suicide", delegate { if (!SteamIntegration.hasLobby) { GameNotify.Show("You need to be in a server to do that.", 3f); } else { PlayerDeathManager.Suicide(); } }), new Entry("Stop Server", delegate { if (Server.instance == null && (!SteamIntegration.hasLobby || !SteamIntegration.isHost)) { GameNotify.Show(SteamIntegration.hasLobby ? "You're not the host. Use Disconnect." : "No server running.", 3f); ShowLobby(); } else { if (Server.StopHosting()) { GameNotify.Show("Server stopped.", 3f); } else { GameNotify.Show("Couldn't stop the server.", 3f); } ShowLobby(); } }), new Entry("Disconnect", delegate { if (Node.isServer) { GameNotify.Show("You're the host. Use Stop Server.", 3f); ShowLobby(); } else { if (Node.activeNode is Client client) { client.DisconnectFromServer(); } else if (!SteamIntegration.hasLobby) { GameNotify.Show("You're not in a server.", 3f); } ShowLobby(); } }), new Entry("Invite", InviteSteam), new Entry("Lobby Options", ShowLobbyOptions)), animate: false); } private static void InviteSteam() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!SteamIntegration.hasLobby) { GameNotify.Show("Start or join a server first.", 3f); return; } SteamIntegration.UpdateActivity(); DiscordInvite.Refresh(); ShowHome(); SteamFriends.ActivateGameOverlayInviteDialog(SteamIntegration.lobby); } private static void ShowLobbyOptions() { Show(PageBack(ShowLobby, new Entry(PlayerSlotLabel(), delegate { int num = Server.maxPlayers + 1; if (num > 16) { num = 1; } Server.maxPlayers = (byte)num; Server.instance?.UpdateLobbyConfig(); ShowLobbyOptions(); }), new Entry(Server.isLocked ? "Locked" : "Open", delegate { Server.isLocked = !Server.isLocked; Server.instance?.UpdateLobbyConfig(); ShowLobbyOptions(); }), new Entry(Server.visibility.ToString(), delegate { Server.visibility = (ServerVisibility)((int)(Server.visibility + 1) % 3); Server.instance?.UpdateLobbyConfig(); ShowLobbyOptions(); }), new Entry(SlowMoSync.Label(), delegate { if (SteamIntegration.hasLobby && !SteamIntegration.isHost) { GameNotify.Show("Only the host can change slow-mo sync.", 3f); ShowLobbyOptions(); } else { SlowMoSync.Cycle(); ShowLobbyOptions(); } }), new Entry(Server.ClientTeleportAllowed ? "Teleport: Everyone" : "Teleport: Host Only", delegate { if (SteamIntegration.hasLobby && !SteamIntegration.isHost) { GameNotify.Show("Only the host can change who can teleport.", 3f); ShowLobbyOptions(); } else { Server.clientTeleport = !Server.clientTeleport; Server.instance?.UpdateLobbyConfig(); ShowLobbyOptions(); } })), animate: false); } private static void ShowServers() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) viewingServers = true; serverPage = 0; servers.Clear(); searchGen++; searchWaiting = searchGen; if (lobbySearch == null) { lobbySearch = CallResult<LobbyMatchList_t>.Create((APIDispatchDelegate<LobbyMatchList_t>)OnLobbySearch); } SteamMatchmaking.AddRequestLobbyListStringFilter("entanglement", "true", (ELobbyComparison)0); SteamMatchmaking.AddRequestLobbyListDistanceFilter((ELobbyDistanceFilter)3); SteamMatchmaking.AddRequestLobbyListResultCountFilter(50); lobbySearch.Set(SteamMatchmaking.RequestLobbyList(), (APIDispatchDelegate<LobbyMatchList_t>)null); ShowServerPage(searching: true); } private static void OnLobbySearch(LobbyMatchList_t result, bool failed) { //IL_001a: 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 (searchWaiting != searchGen) { return; } servers.Clear(); if (!failed) { int nLobbiesMatching = (int)result.m_nLobbiesMatching; for (int i = 0; i < nLobbiesMatching; i++) { servers.Add(SteamMatchmaking.GetLobbyByIndex(i)); } } IncludeLocalLobby(); if (viewingServers) { ShowServerPage(searching: false); } } private static void IncludeLocalLobby() { //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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!SteamIntegration.hasLobby) { return; } CSteamID lobby = SteamIntegration.lobby; for (int i = 0; i < servers.Count; i++) { if (servers[i] == lobby) { return; } } servers.Insert(0, lobby); } private static void ShowServerPage(bool searching) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Expected O, but got Unknown if (!viewingServers) { return; } int num = ((servers.Count == 0) ? 1 : ((servers.Count + 5 - 1) / 5)); if (serverPage >= num) { serverPage = 0; } if (serverPage < 0) { serverPage = 0; } Page val = new Page(); val.items = new List<PageItem>(); val.items.Add(new PageItem(PageArrow(-1, num), (Directions)5, Action.op_Implicit(Once(delegate { StepServerPage(-1); })))); val.items.Add(new PageItem(PageArrow(1, num), (Directions)3, Action.op_Implicit(Once(delegate { StepServerPage(1); })))); Directions[] array = new Directions[5]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); Directions[] array2 = (Directions[])(object)array; if (searching && servers.Count == 0) { val.items.Add(new PageItem("Searching...", array2[0], Action.op_Implicit(Once(delegate { })))); } else if (servers.Count == 0) { val.items.Add(new PageItem("No servers", array2[0], Action.op_Implicit(Once(delegate { })))); } else { int num2 = serverPage * 5; int num3 = 0; for (int num4 = num2; num4 < servers.Count; num4++) { if (num3 >= array2.Length) { break; } CSteamID id = servers[num4]; val.items.Add(new PageItem(Fit(ServerLabel(id)), array2[num3], Action.op_Implicit(Once(delegate { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Join(id); })))); num3++; } } val.items.Add(new PageItem("Back", (Directions)4, Action.op_Implicit(Once(ShowHub)))); Show(val, animate: false); } private static string PageArrow(int dir, int pages) { if (pages <= 1) { if (dir >= 0) { return ">"; } return "<"; } if (dir < 0) { return "< " + (serverPage + 1); } return serverPage + 1 + "/" + pages + " >"; } private static int ServerPageCount() { if (servers.Count == 0) { return 1; } return (servers.Count + 5 - 1) / 5; } private static void StepServerPage(int dir) { int num = ServerPageCount(); if (num <= 1) { GameNotify.Show("No more pages.", 2f); return; } serverPage = (serverPage + dir + num) % num; ShowServerPage(searching: false); } private static void Join(CSteamID lobbyId) { //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_005c: 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_009b: Unknown result type (might be due to invalid IL or missing references) viewingServers = false; ShowHome(); if (lobbyId == CSteamID.Nil) { GameNotify.Show("That Server no longer exists.", 3f); } else if (SteamIntegration.hasLobby) { if (SteamIntegration.lobby == lobbyId) { GameNotify.Show("You're already in this server.", 3f); } else { GameNotify.Show("Leave your server before joining another.", 3f); } } else { string lobbyData = SteamMatchmaking.GetLobbyData(lobbyId, "host_name"); GameNotify.Show(string.IsNullOrEmpty(lobbyData) ? "Joining..." : ("Joining " + lobbyData + "..."), 3f); Client.instance?.JoinLobby(lobbyId); } } private static string ServerLabel(CSteamID lobbyId) { //IL_0000: 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_0026: Unknown result type (might be due to invalid IL or missing references) string text = SteamMatchmaking.GetLobbyData(lobbyId, "host_name"); if (string.IsNullOrEmpty(text)) { text = "Unknown"; } SteamIntegration.LobbyOccupancy(lobbyId, out var current, out var limit, out var _); if (SteamMatchmaking.GetLobbyData(lobbyId, "locked") == "1") { return text + " locked"; } if (limit <= 0) { return text; } return text + " " + current + "/" + limit; } private static string PlayerSlotLabel() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (!SteamIntegration.hasLobby) { return $"Players {Server.maxPlayers}"; } SteamIntegration.LobbyOccupancy(SteamIntegration.lobby, out var _, out var limit, out var open); if (SteamIntegration.isHost && Server.isLocked) { return $"Players {limit} (Locked)"; } if (open <= 0) { return $"Players {limit} (Full)"; } return $"Players {limit} ({open} open)"; } private static void ShowPlayers() { playerPage = 0; ShowPlayerPage(); } private static void ShowPlayerPage() { ShowWheelPage(Members().ConvertAll(delegate(long id) { string name = SteamIntegration.GetUserName(id); return new Entry(name, delegate { ShowPlayer(id, name); }); }), playerPage, delegate(int page) { playerPage = page; ShowPlayerPage(); }, ShowHub, "Just you"); } private static void ShowPlayer(long userId, string name) { List<Entry> list = new List<Entry> { new Entry("Steam Profile", delegate { //IL_000b: Unknown result type (might be due to invalid IL or missing references) SteamFriends.ActivateGameOverlayToUser("steamid", new CSteamID((ulong)userId)); }) }; if (SteamIntegration.isHost) { list.Add(new Entry("Kick", delegate { Server.instance?.KickUser(userId, name); ShowPlayers(); })); list.Add(new Entry("Ban", delegate { BanList.BanUser(userId, name); Server.instance?.KickUser(userId, name, DisconnectReason.Banned); ShowPlayers(); })); } if (SteamIntegration.isHost || Server.ClientTeleportAllowed) { list.Add(new Entry("Teleport To", delegate { string text = Server.TeleportTo(userId); if (text != null) { GameNotify.Show(text, 3f); } })); } bool muted = VoiceManager.IsMuted(userId); list.Add(new Entry(muted ? "Unmute" : "Mute", delegate { VoiceManager.SetMuted(userId, !muted); ShowPlayer(userId, name); })); Show(PageBack(ShowPlayers, list.ToArray()), animate: false); } private static List<long> Members() { //IL_000f: 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) List<long> list = new List<long>(); if (!SteamIntegration.hasLobby) { return list; } 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) { list.Add(steamID); } } return list; } public static void ForceOpenDownloads() { downloadPage = 0; downloadsWanted = true; if (Bind()) { ShowDownloadPage(); } } public static void OnMenuOpened() { if (downloadsWanted) { if (!FileTransferManager.HasPendingConsent && !Thunderstore.HasPending) { downloadsWanted = false; } else if (Bind()) { downloadsWanted = false; downloadPage = 0; ShowDownloadPage(); } } } private static void ShowDownloads() { downloadPage = 0; ShowDownloadPage(); } private static void ShowDownloadPage() { List<FileTransfer> pendingConsentTransfers = FileTransferManager.GetPendingConsentTransfers(); List<Thunderstore.PendingOffer> list = Thunderstore.GetPending(); List<Entry> list2 = new List<Entry>(); if (pendingConsentTransfers.Count > 0 || list.Count > 0) { list2.Add(new Entry("Accept All", delegate { FileTransferManager.AcceptAllPending(); Thunderstore.AcceptAllPending(); GameNotify.Show("Accepted pending downloads.", 3f); ShowDownloads(); })); } for (int num = 0; num < list.Count; num++) { Thunderstore.PendingOffer pendingOffer = list[num]; string key = pendingOffer.key; string label = pendingOffer.label; list2.Add(new Entry(label, delegate { ShowStoreDownload(key, label); })); } for (int num2 = 0; num2 < pendingConsentTransfers.Count; num2++) { FileTransfer transfer = pendingConsentTransfers[num2]; string fileName = Path.GetFileName(transfer.fileName); list2.Add(new Entry(fileName, delegate { ShowDownload(transfer); })); } ShowWheelPage(list2, downloadPage, delegate(int page) { downloadPage = page; ShowDownloadPage(); }, ShowHub, "No downloads"); } private static void ShowStoreDownload(string key, string label) { Show(PageBack(ShowDownloads, new Entry("Accept Thunderstore", delegate { Thunderstore.AcceptPending(key); GameNotify.Show("Accepted " + label, 3f); ShowDownloads(); }), new Entry("Decline", delegate { Thunderstore.DeclinePending(key); GameNotify.Show("Declined " + label, 3f); ShowDownloads(); })), animate: false); } private static void ShowDownload(FileTransfer transfer) { string name = Path.GetFileName(transfer.fileName); float num = (float)transfer.totalBytes / 1024f / 1024f; ushort id = transfer.id; long peer = transfer.peer; Show(PageBack(ShowDownloads, new Entry($"Accept {num:F1} MB", delegate { FileTransferManager.AcceptPending(peer, id); GameNotify.Show("Accepted " + name, 3f); ShowDownloads(); }), new Entry("Decline", delegate { FileTransferManager.DenyPending(peer, id); GameNotify.Show("Declined " + name, 3f); ShowDownloads(); })), animate: false); } private static void ShowWheelPage(List<Entry> items, int page, Action<int> setPage, Action back, string emptyLabel) { int pages = Math.Max(1, (Math.Max(items.Count, 1) + 5 - 1) / 5); if (page >= pages) { page = 0; } List<Entry> list = new List<Entry>(); if (items.Count == 0) { list.Add(new Entry(emptyLabel, delegate { })); } else { for (int num = page * 5; num < items.Count; num++) { if (list.Count >= 5) { break; } list.Add(items[num]); } } if (pages > 1) { int prev = page; int next = page; list.Add(new Entry("Prev", delegate { if (prev > 0) { setPage(prev - 1); } })); list.Add(new Entry("Next", delegate { if (next < pages - 1) { setPage(next + 1); } })); } Show(PageBack(back, list.ToArray()), animate: false); } private static void ShowSettings() { Show(PageBack(ShowHub, new Entry(Client.nameTagsVisible ? "Names On" : "Names Off", delegate { Client.nameTagsVisible = !Client.nameTagsVisible; ShowSettings(); }), new Entry("File Sync", ShowSync), new Entry("Banned", delegate { bannedPage = 0; ShowBanned(); }), new Entry("Net " + NetStats.downKBps.ToString("F0") + "KB/s down, " + NetStats.upKBps.ToString("F0") + "KB/s up", ShowSettings, wide: true), new Entry("Debug", ShowDebug)), animate: false); } private static void ShowBanned() { List<Entry> list = new List<Entry>(); foreach (Tuple<long, string> bannedUser in BanList.bannedUsers) { long id = bannedUser.Item1; string name = bannedUser.Item2; list.Add(new Entry("Unban " + name, delegate { BanList.UnbanUser(id, name); GameNotify.Show("Unbanned " + name + ".", 3f); ShowBanned(); })); } ShowWheelPage(list, bannedPage, delegate(int page) { bannedPage = page; ShowBanned(); }, ShowSettings, "Nobody banned"); } private static void ShowDebug() { Show(PageBack(ShowSettings, new Entry(DebugLog.On ? "Debug Log On" : "Debug Log", delegate { DebugLog.Toggle(); GameNotify.Show(DebugLog.On ? "Debug log on. Details go to MelonLoader/Latest.log." : "Debug log off."); ShowDebug(); }), new Entry(PlayerRepresentation.RepresentationTestActive ? "Rep Test On" : "Representation Test", delegate { if (PlayerRepresentation.RepresentationTestActive) { PlayerRepresentation.StopRepresentationTest(); } else { PlayerRepresentation.StartRepresentationTest(); } ShowDebug(); }), new Entry(PlayerRepresentation.debugFrozen ? "Dummy Frozen" : "Freeze Dummy", delegate { if (!PlayerRepresentation.RepresentationTestActive) { GameNotify.Show("Turn on the Representation Test first.", 3f); } else { PlayerRepresentation.debugFrozen = !PlayerRepresentation.debugFrozen; ShowDebug(); } }), new Entry("NPCs At Dummy", delegate { GameNotify.Show(SoloDebug.SendNpcs(), 3f); }), new Entry("Sync Tests", ShowSyncTests), new Entry("Install Custom Content Dependencies", delegate { Thunderstore.InstallCustomContentDependencies(); PopUpMenuView instance = PopUpMenuView.instance; if ((Object)(object)instance != (Object)null) { instance.Deactivate(); } }, wide: true)), animate: false); } private static void ShowSyncTests() { Show(PageBack(ShowDebug, new Entry("Follow Test", delegate { GameNotify.Show(SoloDebug.StartFollowTest()); }), new Entry("NPC Follow Test", delegate { GameNotify.Show(SoloDebug.StartNpcFollowTest()); }), new Entry("Freeze Test", delegate { GameNotify.Show(SoloDebug.StartFreezeTest()); }), new Entry("Hit Check", delegate { GameNotify.Show(SoloDebug.HitCheck(), 5f); }), new Entry("Turrets And Claw", delegate { GameNotify.Show(SoloDebug.TurretAndClaw(), 6f); }), new Entry("Thunderstore Check", delegate { GameNotify.Show(SoloDebug.ThunderstoreCheck(), 6f); }), new Entry("More Tests", ShowMoreTests)), animate: false); } private static void ShowMoreTests() { Show(PageBack(ShowSyncTests, new Entry("Fizzle Nearest Corpse", delegate { GameNotify.Show(SoloDebug.FizzleNearest(), 3f); }), new Entry("Restart Nearest NPC", delegate { GameNotify.Show(SoloDebug.RestartNearestNpc(), 3f); })), animate: false); } private static void ShowVoice() { Show(PageBack(ShowHub, new Entry(VoiceManager.micEnabled ? "Mic On" : "Mic Off", delegate { VoiceManager.micEnabled = !VoiceManager.micEnabled; ShowVoice(); }), new Entry((VoiceManager.mode == VoiceMode.Global) ? "Global" : "Proximity", delegate { VoiceManager.mode = ((VoiceManager.mode != VoiceMode.Global) ? VoiceMode.Global : VoiceMode.Proximity); VoiceManager.ApplySettings(); ShowVoice(); }), new Entry($"Range {VoiceManager.proximityRange}m", delegate { VoiceManager.proximityRange += 5; if (VoiceManager.proximityRange > 60) { VoiceManager.proximityRange = 10; } VoiceManager.ApplySettings(); ShowVoice(); }), new Entry($"Vol {VoiceManager.outputVolume}%", delegate { VoiceManager.outputVolume += 10; if (VoiceManager.outputVolume > 200) { VoiceManager.outputVolume = 0; } VoiceManager.ApplySettings(); ShowVoice(); }), new Entry("Mic Help", delegate { GameNotify.Show("Voice Chat uses Steam Mic. Change in Steam Settings under Voice > Voice Input Device", 8f); })), animate: false); } private static void ShowSync() { Show(PageBack(ShowSettings, new Entry(SyncPrefs.itemSyncEnabled.Value ? "Items On" : "Items Off", delegate { SyncPrefs.itemSyncEnabled.Value = !SyncPrefs.itemSyncEnabled.Value; ShowSync(); }), new Entry(SyncPrefs.playermodelSyncEnabled.Value ? "Models On" : "Models Off", delegate { SyncPrefs.playermodelSyncEnabled.Value = !SyncPrefs.playermodelSyncEnabled.Value; ShowSync(); }), new Entry(SyncPrefs.mapSyncEnabled.Value ? "Maps On" : "Maps Off", delegate { SyncPrefs.mapSyncEnabled.Value = !SyncPrefs.mapSyncEnabled.Value; ShowSync(); }), new Entry(SyncPrefs.requireDownloadConsent.Value ? "Ask First" : "Auto Download", delegate { SyncPrefs.requireDownloadConsent.Value = !SyncPrefs.requireDownloadConsent.Value; ShowSync(); }), new Entry($"Max {SyncPrefs.maxSyncSizeKB.Value / 1024}MB", delegate { int num = SyncPrefs.maxSyncSizeKB.Value / 1024 + 10; if (num > 200) { num = 10; } SyncPrefs.maxSyncSizeKB.Value = num * 1024; ShowSync(); })), animate: false); } private static string Fit(string text, bool wide = false) { if (string.IsNullOrEmpty(text)) { return " "; } text = text.Replace("\n", " ").Trim(); int num = (wide ? 40 : 18); if (text.Length > num) { return text.Substring(0, num - 1) + "..."; } return text; } } } namespace Entanglement.Representation { public class PlayerRepresentation { private class GhostPart { public Transform source; public Transform part; public MeshFilter filter; public SkinnedMeshRenderer skinned; public Mesh baked; public Vector3 localPos; public Quaternion localRot = Quaternion.identity; } private class HeldGhost { public GameObject proxy; public GameObject source; public readonly List<GhostPart> parts = new List<GhostPart>(); } public static float legJitter = 10f; public static Dictionary<long, PlayerRepresentation> representations = new Dictionary<long, PlayerRepresentation>(); 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; private CanvasGroup repCanvasGroup; public TextMeshProUGUI repNameText; public RawImage repTalkingIcon; public Transform repSkull; public Transform repGeo; public Transform repSHJnt; public Collider[] colliders = (Collider[])(object)new Collider[0]; private Collider[] modelColliders = (Collider[])(object)new Collider[0]; private bool enemyTargetReady; private GameObject enemyTarget; private TriggerRefProxy enemyTargetProxy; 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 string loadingSkinPath; public int skinLoadGeneration; 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; 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]; private Transform[] posedBones; private Vector3[] posePrev; private Vector3[] poseCurr; private Quaternion[] rotPrev; private Quaternion[] rotCurr; private bool hasSolvedPose; private bool heldByLocalPlayer; private bool grabCollisionIgnored; private float heldReleaseUntil; public const float repFollowSharpness = 28f; public const float repLimbSharpness = 48f; public const float repExtrapolationLimit = 0.28f; public const float repSnapDistance = 2.5f; public const float repMaxPredictedSpeed = 18f; public const float crouchVerticalDelta = 0.06f; public const float crouchLateralMax = 0.45f; public const float heldMaxPlanarStep = 0.1f; public const float heldMaxVerticalStep = 0.018f; public const float heldReleaseCooldown = 0.18f; public const float localHoldVerticalDamp = 2.5f; public static PlayerRepresentation debugRepresentation; public static float representationTestOffset = 1.5f; public const long TesterRepId = -2L; private const int MaxGhostParts = 64; private static readonly HeldGhost[] debugGhosts = new HeldGhost[3] { null, new HeldGhost(), new HeldGhost() }; private static readonly Vector3[] debugPosScratch = (Vector3[])(object)new Vector3[3]; private static readonly Quaternion[] debugRotScratch = (Quaternion[])(object)new Quaternion[3]; public static bool debugFrozen; public static AssetBundle playerRepBundle; public bool previewOnly; public string lastCreateError; private static readonly Vector3 ParkSpot = new Vector3(0f, -10000f, 0f); private const float StaleAfter = 3f; private bool parked; private float lastPacketReal; private const float LoadFade = 0.8f; private const float LoadQuiet = 1f; private const float LoadHoldMax = 20f; private List<Renderer> awayHidden; private bool awayForLoad; private float awaySince; private static float lastLocalLoad = -10f; private const float DeadHoldMax = 6f; private const float DeathFade = 1f; private const float BackFade = 0.8f; private const float ArriveFade = 1f; private const float LeaveFade = 1f; private float deadUntil; private List<Renderer> deadHidden; private RepFade fade; private const float OverlapEnter = 0.35f; private const float OverlapLeave = 1.1f; private const float OverlapHeight = 1.8f; private const float AppearWindow = 1f; private const fl