using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Numerics; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading.Tasks; using BabyStepsMenuLib; using BabyStepsMultiplayerClient; using BabyStepsMultiplayerClient.Audio; using BabyStepsMultiplayerClient.Components; using BabyStepsMultiplayerClient.Config; using BabyStepsMultiplayerClient.Extensions; using BabyStepsMultiplayerClient.Localization; using BabyStepsMultiplayerClient.Networking; using BabyStepsMultiplayerClient.Networking.Steam; using BabyStepsMultiplayerClient.Player; using BabyStepsMultiplayerClient.UI; using BabyStepsMultiplayerClient.UI.Elements; using BabyStepsNetworking.Client; using BabyStepsNetworking.Host; using BabyStepsNetworking.Packets; using BabyStepsNetworking.ServerBrowser; using BabyStepsNetworking.Shared; using BabyStepsNetworking.Transport; using BabyStepsNetworking.Transport.LiteNetLib; using BabyStepsNetworking.Transport.LocalLoopback; using BabyStepsNetworking.Transport.Steam; using Concentus.Enums; using Concentus.Structs; using HarmonyLib; using Il2Cpp; using Il2CppBabySteps.Core.Audio; using Il2CppCinemachine; using Il2CppFMOD; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppNWH.DWP2.WaterObjects; using Il2CppRewired; using Il2CppSteamworks; using Il2CppSystem; using Il2CppSystem.Collections; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using Tomlet; using Tomlet.Models; using Unity.LiveCapture.ARKitFaceCapture; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.EventSystems; using UnityEngine.Events; 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: MelonInfo(typeof(Core), "BabyStepsMultiplayerClient", "1.3.2", "Caleb Orchard", "https://github.com/caleborchard/Baby-Steps-Multiplayer-Mod-Client")] [assembly: MelonGame("DefaultCompany", "BabySteps")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Caleb Orchard")] [assembly: AssemblyConfiguration("Retail - Debug")] [assembly: AssemblyDescription("A MelonLoader mod for Baby Steps that adds multiplayer to the game.")] [assembly: AssemblyFileVersion("1.3.2")] [assembly: AssemblyInformationalVersion("1.3.2+c32baaf3066f84489719f6b43cbaf0715cdaa69b")] [assembly: AssemblyProduct("BabyStepsMultiplayerClient")] [assembly: AssemblyTitle("BabyStepsMultiplayerClient")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/caleborchard/Baby-Steps-Multiplayer-Mod-Client")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsUnmanagedAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BabyStepsMultiplayerClient { public class Core : MelonMod { public const string SERVER_VERSION = "108"; public static string CLIENT_VERSION; public const string cloneText = "(Clone)"; public static Instance logger; public static UIManager uiManager; public static NetworkManager networkManager; public static InGameHost inGameHost; public static Action OnConnectionStateChanged; private static ulong _pendingInviteConnect = 0uL; private static string _pendingInviteKey = string.Empty; private static float _continueBtnFirstSeenAt = -1f; private static float _lastContinueClickAt = -10f; private static ulong _pendingJoinLobbyCall = 0uL; private static ulong _pendingJoinLobbyId = 0uL; public override void OnLateInitializeMelon() { CLIENT_VERSION = ((MelonBase)this).Info.Version; logger = ((MelonBase)this).LoggerInstance; try { ((MelonBase)this).HarmonyInstance.PatchAll(Assembly.GetExecutingAssembly()); ((MelonBase)this).HarmonyInstance.PatchAll(typeof(MenuInjectionLibrary).Assembly); } catch (Exception value) { logger.Error($"Harmony patching failed: {value}"); } ManagedEnumerator.Register(); try { SteamClient.Init(0u, false); logger.Msg("[Steam] Facepunch initialized for invite callbacks"); } catch (Exception ex) when (ex.Message.Contains("already initialized") || ex.Message.Contains("SteamAPI_SteamInput") || ex.Message.Contains("entry point") || ex.Message.Contains("SteamInput")) { logger.Msg("[Steam] Facepunch init note: " + ex.Message); } catch (Exception ex2) { logger.Warning("[Steam] Facepunch init failed: " + ex2.Message); } uiManager = new UIManager(); networkManager = new NetworkManager(); MultiplayerMenu.Initialize(); NativeSteamAPI.RegisterLobbyJoinRequestedCallback(delegate(ulong lobbyId) { logger.Msg($"[Steam] Lobby invite accepted — lobbyId={lobbyId}, calling JoinLobby to fetch host ID…"); if (_pendingJoinLobbyCall != 0) { logger.Warning($"[Steam] Overwriting a pending JoinLobby call ({_pendingJoinLobbyCall}) for lobby {_pendingJoinLobbyId}"); } _pendingJoinLobbyCall = NativeSteamAPI.JoinLobby(lobbyId); _pendingJoinLobbyId = lobbyId; logger.Msg($"[Steam] JoinLobby handle={_pendingJoinLobbyCall} (will poll for result in OnUpdate)"); }); NativeSteamAPI.RegisterJoinRequestedCallback(delegate(string connectStr) { logger.Msg("[Steam] GameRichPresenceJoinRequested fired — connectStr='" + connectStr + "'"); if (ulong.TryParse(connectStr.Trim(), out var result3) && result3 != 0) { logger.Msg($"[Steam] Game invite accepted — queuing connect to {result3}"); _pendingInviteConnect = result3; } else { logger.Warning("[Steam] GameRichPresenceJoinRequested: could not parse '" + connectStr + "' as SteamID64"); } }); try { SteamFriends.OnGameLobbyJoinRequested += delegate(Lobby lobby, SteamId friend) { //IL_0003: 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) ulong num = SteamId.op_Implicit(((Lobby)(ref lobby)).Id); logger.Msg($"[Steam] Facepunch GameLobbyJoinRequested lid={num} friend={friend.Value}"); if (_pendingJoinLobbyCall != 0) { logger.Warning($"[Steam] Overwriting pending JoinLobby {_pendingJoinLobbyCall}"); } _pendingJoinLobbyCall = NativeSteamAPI.JoinLobby(num); _pendingJoinLobbyId = num; logger.Msg($"[Steam] JoinLobby handle={_pendingJoinLobbyCall}"); }; logger.Msg("[Steam] Facepunch OnGameLobbyJoinRequested hooked"); } catch (Exception ex3) { logger.Msg("[Steam] Facepunch GameLobbyJoinRequested hook: " + ex3.Message); } try { SteamFriends.OnGameRichPresenceJoinRequested += delegate(Friend friend, string connectStr) { //IL_0035: 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) logger.Msg($"[Steam] Facepunch GameRichPresenceJoinRequested connectStr='{connectStr}' friend={friend.Id.Value}"); if (ulong.TryParse(connectStr.Trim(), out var result2) && result2 != 0) { _pendingInviteConnect = result2; } else { logger.Warning("[Steam] Facepunch GameRichPresenceJoinRequested: could not parse '" + connectStr + "'"); } }; logger.Msg("[Steam] Facepunch OnGameRichPresenceJoinRequested hooked"); } catch (Exception ex4) { logger.Msg("[Steam] Facepunch GameRichPresenceJoinRequested hook: " + ex4.Message); } try { string text = NativeSteamAPI.GetLaunchCommandLine().Trim(); if (!string.IsNullOrEmpty(text) && ulong.TryParse(text, out var result) && result != 0) { logger.Msg($"[Steam] Launched from game invite, will connect to {result}"); _pendingInviteConnect = result; } } catch { } logger.Msg("Initialized!"); VersionCheck.CheckForUpdate(); } public override void OnGUI() { if (uiManager != null) { uiManager.Draw(); } } public override void OnUpdate() { if (uiManager == null || networkManager == null) { return; } uiManager.Update(); networkManager.Update(); inGameHost?.Tick(Time.deltaTime * 1000f); MultiplayerMenu.Update(); if (LocalPlayer.Instance != null) { LocalPlayer.Instance.Update(); } InGameHost obj = inGameHost; if (obj == null || !obj.IsRunning) { try { NativeSteamAPI.RunCallbacks(); } catch { } try { SteamClient.RunCallbacks(); } catch { } } if (_pendingJoinLobbyCall != 0) { bool flag; bool failed; try { flag = NativeSteamAPI.IsAPICallCompleted(_pendingJoinLobbyCall, out failed); } catch (Exception ex) { logger.Warning("[Steam] IsAPICallCompleted threw: " + ex.Message); flag = false; failed = false; } if (flag) { ulong pendingJoinLobbyCall = _pendingJoinLobbyCall; ulong pendingJoinLobbyId = _pendingJoinLobbyId; _pendingJoinLobbyCall = 0uL; _pendingJoinLobbyId = 0uL; logger.Msg($"[Steam] JoinLobby call={pendingJoinLobbyCall} lobby={pendingJoinLobbyId} completed, failed={failed}"); if (!failed) { string lobbyData = NativeSteamAPI.GetLobbyData(pendingJoinLobbyId, "bbs_host"); string lobbyData2 = NativeSteamAPI.GetLobbyData(pendingJoinLobbyId, "bbs_invite_key"); ulong lobbyOwner = NativeSteamAPI.GetLobbyOwner(pendingJoinLobbyId); logger.Msg($"[Steam] Invite lobby {pendingJoinLobbyId}: bbs_host='{lobbyData}' GetLobbyOwner={lobbyOwner} hasInviteKey={!string.IsNullOrEmpty(lobbyData2)}"); ulong result = 0uL; if (!string.IsNullOrEmpty(lobbyData) && lobbyData != "0") { ulong.TryParse(lobbyData, out result); } if (result == 0) { result = lobbyOwner; } if (result != 0) { logger.Msg($"[Steam] Resolved invite host SteamID: {result}, queuing connect…"); _pendingInviteConnect = result; _pendingInviteKey = lobbyData2; } else { logger.Warning($"[Steam] Could not resolve host SteamID from invite lobby {pendingJoinLobbyId} — bbs_host='{lobbyData}' owner={lobbyOwner}"); } } else { logger.Warning($"[Steam] JoinLobby failed for lobby {pendingJoinLobbyId}"); } try { NativeSteamAPI.LeaveLobby(pendingJoinLobbyId); } catch { } } } if (_pendingInviteConnect != 0L && !networkManager.IsConnected) { if (!HasLoadedGame()) { Menu me = Menu.me; Button val = ((me != null) ? me.ContinueGameButton : null); if ((Object)(object)val != (Object)null) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (_continueBtnFirstSeenAt < 0f) { _continueBtnFirstSeenAt = realtimeSinceStartup; logger.Msg("[Invite] ContinueGameButton found — waiting for menu to settle before clicking"); } float num = realtimeSinceStartup - _continueBtnFirstSeenAt; float num2 = realtimeSinceStartup - _lastContinueClickAt; if (num >= 3f && num2 >= 5f) { _lastContinueClickAt = realtimeSinceStartup; logger.Msg($"[Invite] Clicking ContinueGameButton (interactable={((Selectable)val).interactable}, sinceAppear={num:F1}s, sinceClick={num2:F1}s)"); ((UnityEvent)val.onClick).Invoke(); } } else { _continueBtnFirstSeenAt = -1f; float realtimeSinceStartup2 = Time.realtimeSinceStartup; if (realtimeSinceStartup2 - _lastContinueClickAt >= 5f) { _lastContinueClickAt = realtimeSinceStartup2; logger.Msg($"[Invite] Waiting for main menu (Menu.me={(((Object)(object)Menu.me != (Object)null) ? "exists" : "null")}, HasLoadedGame={HasLoadedGame()})"); } } } else { _continueBtnFirstSeenAt = -1f; _lastContinueClickAt = -10f; ulong pendingInviteConnect = _pendingInviteConnect; string pendingInviteKey = _pendingInviteKey; _pendingInviteConnect = 0uL; _pendingInviteKey = string.Empty; logger.Msg($"[Steam] Auto-connecting to invite host {pendingInviteConnect} (inviteKey={!string.IsNullOrEmpty(pendingInviteKey)})"); networkManager.ConnectSteam(pendingInviteConnect.ToString(), pendingInviteKey); } } else if (_pendingInviteConnect == 0) { _continueBtnFirstSeenAt = -1f; _lastContinueClickAt = -10f; } SteamP2PTest.Poll(); if (MelonDebug.IsEnabled()) { if (Input.GetKeyDown((KeyCode)284)) { networkManager.Connect(ModSettings.connection.Address.Value, ModSettings.connection.Port.Value, ModSettings.connection.Password.Value); } if (Input.GetKeyDown((KeyCode)285)) { networkManager.Disconnect(); } } } public override void OnLateUpdate() { if (networkManager != null) { if (LocalPlayer.Instance != null) { LocalPlayer.Instance.LateUpdate(); } networkManager.LateUpdate(); } } public override void OnApplicationQuit() { InGameHost obj = inGameHost; if (obj != null && obj.IsRunning) { inGameHost.Stop(); } networkManager?.Disconnect(); } public static bool HasLoadedGame() { if ((Object)(object)Menu.me == (Object)null) { return false; } return Menu.me.gameInProgress; } public static void DebugMsg(string msg) { logger.Msg(msg); } public static bool RegisterComponent(params Type[] interfaces) where T : class { //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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown try { RegisterTypeOptions val = new RegisterTypeOptions(); val.set_LogSuccess(true); val.set_Interfaces(Il2CppInterfaceCollection.op_Implicit(interfaces)); ClassInjector.RegisterTypeInIl2Cpp(val); } catch (Exception value) { logger.Error($"Exception while attempting to Register {typeof(T).Name}: {value}"); return false; } return true; } } public static class ModSettings { public static readonly ConnectionConfig connection = new ConnectionConfig(); public static readonly PlayerConfig player = new PlayerConfig(); public static readonly AudioConfig audio = new AudioConfig(); public static void Load() { connection.Load(); player.Load(); audio.Load(); } public static void Save() { connection.Save(); player.Save(); audio.Save(); } } } namespace BabyStepsMultiplayerClient.UI { internal static class InputBindingHelper { private static readonly bool CaptureLoggingEnabled = true; private const string RewiredActionPrefix = "RWACTION:"; private const string RewiredButtonPrefix = "RWBTN:"; private const string RewiredAxisPrefix = "RWAXIS:"; private const float AxisThreshold = 0.5f; private const int MaxRawRewiredButtonId = 1023; private static readonly Dictionary _previousState = new Dictionary(); private static readonly int[] _inputActionIds = BuildInputActionIdList(); private static readonly KeyCode[] _joystickButtonKeyCodes = BuildJoystickButtonKeyCodes(); public static bool IsInputHeldForCapture() { if (Input.anyKey) { return true; } for (int i = 0; i < _joystickButtonKeyCodes.Length; i++) { if (Input.GetKey(_joystickButtonKeyCodes[i])) { return true; } } return false; } public static bool TryCapturePressedBinding(out string binding, out string displayName) { //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_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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < _joystickButtonKeyCodes.Length; i++) { KeyCode val = _joystickButtonKeyCodes[i]; if (Input.GetKeyDown(val)) { binding = ((object)(KeyCode)(ref val)).ToString(); displayName = ((object)(KeyCode)(ref val)).ToString(); LogCapture("joystick-keycode", binding, displayName); return true; } } foreach (KeyCode value in Enum.GetValues(typeof(KeyCode))) { KeyCode val2 = value; if ((int)val2 == 0 || IsJoystickKeyCode(val2) || !Input.GetKeyDown(val2)) { continue; } binding = ((object)(KeyCode)(ref val2)).ToString(); displayName = ((object)(KeyCode)(ref val2)).ToString(); LogCapture("keyboard", binding, displayName); return true; } Menu me = Menu.me; Player val3 = ((me != null) ? me.rwPlayer : null); if (val3 != null) { for (int j = 0; j < _inputActionIds.Length; j++) { int num = _inputActionIds[j]; if (val3.GetButtonDown(num)) { if (!TryGetInputActionName(num, out var actionName)) { actionName = "Action_" + num; } if (!IsAxisLikeAction(num, actionName)) { binding = "RWACTION:" + actionName; displayName = FormatActionDisplayName(actionName); LogCapture("rewired-action", binding, displayName); return true; } } } if (TryCaptureAxisDown(out binding, out displayName)) { return true; } for (int k = 0; k <= 1023; k++) { if (val3.GetButtonDown(k)) { binding = "RWBTN:" + k; displayName = GetRewiredButtonDisplayName(k); LogCapture("rewired-raw-button", binding, displayName); return true; } } } binding = null; displayName = null; return false; } public static bool IsPressed(string binding) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (TryParseKeyCode(binding, out var keyCode)) { return Input.GetKey(keyCode); } if (TryParseRewiredButton(binding, out var actionId)) { Menu me = Menu.me; int result; if (me == null) { result = 0; } else { Player rwPlayer = me.rwPlayer; result = (((rwPlayer != null) ? new bool?(rwPlayer.GetButton(actionId)) : null).GetValueOrDefault() ? 1 : 0); } return (byte)result != 0; } if (TryParseRewiredAction(binding, out var actionName) && TryGetInputActionId(actionName, out var actionId2)) { Menu me2 = Menu.me; int result2; if (me2 == null) { result2 = 0; } else { Player rwPlayer2 = me2.rwPlayer; result2 = (((rwPlayer2 != null) ? new bool?(rwPlayer2.GetButton(actionId2)) : null).GetValueOrDefault() ? 1 : 0); } return (byte)result2 != 0; } if (TryParseRewiredAxis(binding, out var axisId, out var direction)) { Menu me3 = Menu.me; Player val = ((me3 != null) ? me3.rwPlayer : null); if (val == null) { return false; } float axis = val.GetAxis(axisId); return (direction > 0) ? (axis > 0.5f) : (axis < -0.5f); } return false; } public static bool IsDown(string binding) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (TryParseKeyCode(binding, out var keyCode)) { return Input.GetKeyDown(keyCode); } if (TryParseRewiredButton(binding, out var actionId)) { Menu me = Menu.me; int result; if (me == null) { result = 0; } else { Player rwPlayer = me.rwPlayer; result = (((rwPlayer != null) ? new bool?(rwPlayer.GetButtonDown(actionId)) : null).GetValueOrDefault() ? 1 : 0); } return (byte)result != 0; } if (TryParseRewiredAction(binding, out var actionName) && TryGetInputActionId(actionName, out var actionId2)) { Menu me2 = Menu.me; int result2; if (me2 == null) { result2 = 0; } else { Player rwPlayer2 = me2.rwPlayer; result2 = (((rwPlayer2 != null) ? new bool?(rwPlayer2.GetButtonDown(actionId2)) : null).GetValueOrDefault() ? 1 : 0); } return (byte)result2 != 0; } if (TryParseRewiredAxis(binding, out var _, out var _)) { bool flag = IsPressed(binding); _previousState.TryGetValue(binding, out var value); _previousState[binding] = flag; return flag && !value; } return false; } public static bool IsControllerBinding(string binding) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(binding)) { return false; } if (TryParseKeyCode(binding, out var keyCode)) { return IsJoystickKeyCode(keyCode); } if (binding.StartsWith("RWACTION:", StringComparison.Ordinal) || binding.StartsWith("RWBTN:", StringComparison.Ordinal) || binding.StartsWith("RWAXIS:", StringComparison.Ordinal)) { return true; } return false; } public static string GetDisplayName(string binding) { if (TryParseRewiredAction(binding, out var actionName)) { return FormatActionDisplayName(actionName); } if (TryParseRewiredButton(binding, out var actionId)) { return GetRewiredButtonDisplayName(actionId); } if (TryParseRewiredAxis(binding, out var axisId, out var direction)) { switch (axisId) { case 13: return (direction > 0) ? "AXIS Up" : "AXIS Down"; case 12: return (direction > 0) ? "AXIS Right" : "AXIS Left"; } } return binding; } private static bool TryParseKeyCode(string binding, out KeyCode keyCode) { if (string.IsNullOrWhiteSpace(binding)) { keyCode = (KeyCode)0; return false; } if (!Enum.TryParse(binding, ignoreCase: true, out keyCode)) { return false; } return (int)keyCode != 0; } private static bool TryParseRewiredButton(string binding, out int actionId) { actionId = -1; if (string.IsNullOrWhiteSpace(binding) || !binding.StartsWith("RWBTN:", StringComparison.Ordinal)) { return false; } return int.TryParse(binding.Substring("RWBTN:".Length), out actionId); } private static bool TryParseRewiredAction(string binding, out string actionName) { actionName = null; if (string.IsNullOrWhiteSpace(binding) || !binding.StartsWith("RWACTION:", StringComparison.Ordinal)) { return false; } actionName = binding.Substring("RWACTION:".Length); return !string.IsNullOrWhiteSpace(actionName); } private static bool TryParseRewiredAxis(string binding, out int axisId, out int direction) { axisId = -1; direction = 0; if (string.IsNullOrWhiteSpace(binding) || !binding.StartsWith("RWAXIS:", StringComparison.Ordinal)) { return false; } string text = binding.Substring("RWAXIS:".Length); string[] array = text.Split(':'); if (array.Length != 2) { return false; } if (!int.TryParse(array[0], out axisId)) { return false; } if (array[1] == "+") { direction = 1; return true; } if (array[1] == "-") { direction = -1; return true; } return false; } private static bool TryCaptureAxisDown(out string binding, out string displayName) { Menu me = Menu.me; Player val = ((me != null) ? me.rwPlayer : null); if (val == null) { binding = null; displayName = null; return false; } float axis = val.GetAxis(12); float axisPrev = val.GetAxisPrev(12); float axis2 = val.GetAxis(13); float axisPrev2 = val.GetAxisPrev(13); if (axis2 > 0.5f && axisPrev2 <= 0.5f) { binding = "RWAXIS:" + 13 + ":+"; displayName = "AXIS Up"; LogCapture("rewired-axis", binding, displayName); return true; } if (axis2 < -0.5f && axisPrev2 >= -0.5f) { binding = "RWAXIS:" + 13 + ":-"; displayName = "AXIS Down"; LogCapture("rewired-axis", binding, displayName); return true; } if (axis > 0.5f && axisPrev <= 0.5f) { binding = "RWAXIS:" + 12 + ":+"; displayName = "AXIS Right"; LogCapture("rewired-axis", binding, displayName); return true; } if (axis < -0.5f && axisPrev >= -0.5f) { binding = "RWAXIS:" + 12 + ":-"; displayName = "AXIS Left"; LogCapture("rewired-axis", binding, displayName); return true; } binding = null; displayName = null; return false; } private static string GetRewiredButtonDisplayName(int actionId) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (Enum.IsDefined(typeof(InputActions), actionId)) { InputActions val = (InputActions)actionId; return ((object)(InputActions)(ref val)).ToString(); } return "Controller Button " + actionId; } private static int[] BuildInputActionIdList() { Array values = Enum.GetValues(typeof(InputActions)); List list = new List(values.Length); HashSet hashSet = new HashSet(); for (int i = 0; i < values.Length; i++) { int item = (int)values.GetValue(i); if (hashSet.Add(item)) { list.Add(item); } } list.Sort(); return list.ToArray(); } private static bool TryGetInputActionName(int actionId, out string actionName) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) actionName = null; if (!Enum.IsDefined(typeof(InputActions), actionId)) { return false; } InputActions val = (InputActions)actionId; actionName = ((object)(InputActions)(ref val)).ToString(); return !string.IsNullOrWhiteSpace(actionName); } private static bool TryGetInputActionId(string actionName, out int actionId) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected I4, but got Unknown actionId = -1; if (string.IsNullOrWhiteSpace(actionName)) { return false; } if (!Enum.TryParse(actionName, ignoreCase: true, out InputActions result)) { return false; } actionId = (int)result; return true; } private static bool IsAxisLikeAction(int actionId, string actionName) { if (actionId == 12 || actionId == 13) { return true; } if (string.IsNullOrWhiteSpace(actionName)) { return false; } return actionName.IndexOf("Horizontal", StringComparison.OrdinalIgnoreCase) >= 0 || actionName.IndexOf("Vertical", StringComparison.OrdinalIgnoreCase) >= 0; } private static KeyCode[] BuildJoystickButtonKeyCodes() { //IL_0029: 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) List list = new List(); for (int i = 0; i <= 19; i++) { if (Enum.TryParse("JoystickButton" + i, out KeyCode result)) { list.Add(result); } } for (int j = 1; j <= 8; j++) { for (int k = 0; k <= 19; k++) { if (Enum.TryParse("Joystick" + j + "Button" + k, out KeyCode result2)) { list.Add(result2); } } } return list.ToArray(); } private static bool IsJoystickKeyCode(KeyCode keyCode) { string text = ((object)(KeyCode)(ref keyCode)).ToString(); return text.IndexOf("Joystick", StringComparison.OrdinalIgnoreCase) >= 0; } private static string FormatActionDisplayName(string actionName) { if (string.IsNullOrWhiteSpace(actionName)) { return actionName; } if (actionName.StartsWith("UI", StringComparison.OrdinalIgnoreCase)) { return "DPAD " + actionName.Substring(2); } return actionName; } private static void LogCapture(string source, string binding, string displayName) { if (CaptureLoggingEnabled) { Core.DebugMsg($"[BindCapture] source={source} binding={binding} display={displayName}"); } } } public static class MultiplayerMenu { private enum KeybindCaptureTarget { None, PushToTalk, TabMenu, ChatMenu } private static InjectedMenu _menu; private static Toggle _collisionToggle; private static Toggle _cutsceneToggle; private static Toggle _nametagToggle; private static Button _pttKeyBtn; private static Button _tabMenuKeyBtn; private static Button _chatMenuKeyBtn; private static Button _audioDeviceBtn; private static float _lastAudioDeviceLabelRefresh = -10f; private static Image _colorPreviewImage; private static KeybindCaptureTarget _keybindCaptureTarget; private static bool _waitingForRelease; private static BaseInputModule _cachedInputModule; private static bool _playerMovementSuppressedForCapture; private static bool _playerMovementWasEnabledBeforeCapture; private static Button _connectTabBtn; private static Button _hostLanTabBtn; private static Button _hostSteamTabBtn; private static string _lastKnownLanguage; private static TMP_Text _lobbyBrowserLabel; private static TMP_Text _audioDeviceStaticLabel; private static TMP_Text _gainLabel; private static Button _tabBtn0; private static Button _tabBtn1; private static Button _tabBtn2; private static Button _tabBtn3; private static Button _tabBtn4; private static Toggle _microphoneToggle; private static Toggle _deafenToggle; private static Toggle _pttToggle; private static Button _updateAppearanceBtn; private static TMP_Text _serverIpLabel; private static TMP_Text _serverPortLabel; private static TMP_Text _passwordOptLabel; private static InputFieldInfo _serverIpInputInfo; private static InputFieldInfo _serverPortInputInfo; private static InputFieldInfo _lanPasswordInputInfo; private static InputFieldInfo _hostingPasswordInputInfo; private static TMP_Text _pwEnterTmp; private static TMP_Text _pwBackTmp; private const int PublicMainPort = 7777; private const int PublicMainStatusPort = 7778; private const string PublicMainName = "Public Main"; private static SteamLobbyBrowser _lobbyBrowser; private static readonly List _foundLobbies = new List(); private static readonly List _displayLobbies = new List(); private static readonly List