The BepInEx console will not appear when launching like it does for other games on Thunderstore. This is normal (and helps prevent crashes during startup). You can turn it back on in your BepInEx.cfg file.
Decompiled source of LocalMultiplayer v1.0.0
plugins/com.quackandcheese.LocalMultiplayer.dll
Decompiled a week agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json.Linq; using Photon.Pun; using Photon.Realtime; using Steamworks; using UnityEngine; using Zorro.Core; using Zorro.Settings; using com.quackandcheese.LocalMultiplayer.Helpers; using com.quackandcheese.LocalMultiplayer.Objects; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Ashley.MeshSplitter")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("com.rlabrecque.steamworks.net")] [assembly: IgnoresAccessChecksTo("DemiLib")] [assembly: IgnoresAccessChecksTo("DOTween")] [assembly: IgnoresAccessChecksTo("DOTweenPro")] [assembly: IgnoresAccessChecksTo("HBAO.Runtime")] [assembly: IgnoresAccessChecksTo("HBAO.Universal.Runtime")] [assembly: IgnoresAccessChecksTo("Newtonsoft.Json")] [assembly: IgnoresAccessChecksTo("Photon3Unity3D")] [assembly: IgnoresAccessChecksTo("PhotonChat")] [assembly: IgnoresAccessChecksTo("PhotonRealtime")] [assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Demos")] [assembly: IgnoresAccessChecksTo("PhotonUnityNetworking")] [assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Utilities")] [assembly: IgnoresAccessChecksTo("PhotonVoice.API")] [assembly: IgnoresAccessChecksTo("PhotonVoice")] [assembly: IgnoresAccessChecksTo("PhotonVoice.PUN")] [assembly: IgnoresAccessChecksTo("pworld")] [assembly: IgnoresAccessChecksTo("sc.posteffects.runtime")] [assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Attributes")] [assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Modules.Unity.Addressables")] [assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Modules.UnityLocalization")] [assembly: IgnoresAccessChecksTo("Sirenix.Serialization.Config")] [assembly: IgnoresAccessChecksTo("Sirenix.Serialization")] [assembly: IgnoresAccessChecksTo("Sirenix.Utilities")] [assembly: IgnoresAccessChecksTo("Tayx.Graphy")] [assembly: IgnoresAccessChecksTo("unity-websocket-sharp")] [assembly: IgnoresAccessChecksTo("Unity.Addressables")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.InternalAPIEngineBridge.013")] [assembly: IgnoresAccessChecksTo("Unity.Localization")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.MemoryProfiler")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Center.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Playmode.Common.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Playmode")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.Rendering.LightTransport.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipeline.Universal.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime.Shared")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.GPUDriven.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Universal.2D.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Universal.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Universal.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Universal.Shaders")] [assembly: IgnoresAccessChecksTo("Unity.ResourceManager")] [assembly: IgnoresAccessChecksTo("Unity.ScriptableBuildPipeline")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication.PlayerAccounts")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.Multiplayer")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Wire.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Splines")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("UnityEngine.AccessibilityModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.AIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.AMDModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.AndroidJNIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.AnimationModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.AssetBundleModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.AudioModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ClothModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ClusterInputModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ClusterRendererModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ContentLoadModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.CoreModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.CrashReportingModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.DirectorModule")] [assembly: IgnoresAccessChecksTo("UnityEngine")] [assembly: IgnoresAccessChecksTo("UnityEngine.DSPGraphModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.GameCenterModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.GIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.GraphicsStateCollectionSerializerModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.GridModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.HierarchyCoreModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.HotReloadModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ImageConversionModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.IMGUIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.InputForUIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.InputLegacyModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.InputModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.JSONSerializeModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.LocalizationModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.MarshallingModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.MultiplayerModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ParticleSystemModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.PerformanceReportingModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.Physics2DModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.PhysicsModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.PropertiesModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ScreenCaptureModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.ShaderVariantAnalyticsModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SharedInternalsModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpriteMaskModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpriteShapeModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.StreamingModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SubstanceModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SubsystemsModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.TerrainModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.TerrainPhysicsModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.TextCoreFontEngineModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.TextCoreTextEngineModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.TextRenderingModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.TilemapModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.TLSModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.UIElementsModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UIModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UmbraModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityAnalyticsCommonModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityAnalyticsModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityConnectModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityCurlModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityTestProtocolModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityWebRequestAssetBundleModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityWebRequestAudioModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityWebRequestModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityWebRequestTextureModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UnityWebRequestWWWModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.VehiclesModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.VFXModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.VideoModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.VirtualTexturingModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.VRModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.WindModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.XRModule")] [assembly: IgnoresAccessChecksTo("UnityUIExtensions")] [assembly: IgnoresAccessChecksTo("websocket-sharp")] [assembly: IgnoresAccessChecksTo("Whinarn.UnityMeshSimplifier.Runtime")] [assembly: IgnoresAccessChecksTo("Zorro.AutoLOD")] [assembly: IgnoresAccessChecksTo("Zorro.ControllerSupport")] [assembly: IgnoresAccessChecksTo("Zorro.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Zorro.JiggleBones")] [assembly: IgnoresAccessChecksTo("Zorro.PhotonUtility")] [assembly: IgnoresAccessChecksTo("Zorro.Settings.Runtime")] [assembly: IgnoresAccessChecksTo("Zorro.UI.Runtime")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("com.quackandcheese.LocalMultiplayer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+261a6f2fb674ae4291be3a56501e76c654468f2e")] [assembly: AssemblyProduct("com.quackandcheese.LocalMultiplayer")] [assembly: AssemblyTitle("LocalMultiplayer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.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] [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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace com.quackandcheese.LocalMultiplayer { internal static class ConfigManager { public static ConfigFile ConfigFile { get; private set; } public static ConfigEntry<bool> ExtendedLogging { get; private set; } public static ConfigEntry<string> Photon_AppIdRealtime { get; private set; } public static ConfigEntry<string> Photon_AppIdVoice { get; private set; } public static void Initialize(ConfigFile configFile) { ConfigFile = configFile; BindConfigs(); } private static void BindConfigs() { ExtendedLogging = ConfigFile.Bind<bool>("General", "ExtendedLogging", false, "Enable extended logging."); Photon_AppIdRealtime = ConfigFile.Bind<string>("Photon", "AppIdRealtime", "", "The App ID of your Photon Pun application."); Photon_AppIdVoice = ConfigFile.Bind<string>("Photon", "AppIdVoice", "", "The App ID of your Photon Voice application."); } } [BepInPlugin("com.quackandcheese.LocalMultiplayer", "LocalMultiplayer", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Id = "com.quackandcheese.LocalMultiplayer"; internal static ManualLogSource Log { get; private set; } internal static Plugin Instance { get; private set; } internal static ConfigFile Config { get; private set; } internal static Harmony? Harmony { get; set; } public static string Name => "LocalMultiplayer"; public static string Version => "1.0.0"; private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; Config = Utils.CreateGlobalConfigFile((BaseUnityPlugin)(object)this); Patch(); ConfigManager.Initialize(Config); Log.LogInfo((object)("Plugin " + Name + " is loaded!")); } internal void Patch() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown if (Harmony == null) { Harmony = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); } Log.LogDebug((object)"Patching..."); Harmony.PatchAll(); Log.LogDebug((object)"Finished patching!"); } } internal static class SteamAccountManager { public static SteamAccount SpoofAccount; private static bool _initialized; public static SteamAccount RealAccount { get; private set; } public static bool IsUsingSpoofAccount => SpoofAccount != default(SteamAccount); public static void Initialize() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!_initialized) { RealAccount = new SteamAccount(SteamFriends.GetPersonaName(), SteamUser.GetSteamID().m_SteamID); CreateSpoofAccounts(); Application.quitting += UnassignSpoofAccount; _initialized = true; } } private static void CreateSpoofAccounts() { List<SteamAccount> value = GlobalSaveHelper.SpoofSteamAccounts.Value; int num = 5; if (value.Count < num) { for (int i = value.Count; i < num; i++) { value.Add(new SteamAccount($"Player {i + 2}", SteamHelper.GenerateRandomSteamId())); } GlobalSaveHelper.SpoofSteamAccounts.Value = value; } } public static void AssignSpoofAccount() { if (!IsUsingSpoofAccount) { SteamAccount availableSpoofAccount = GetAvailableSpoofAccount(); AddSpoofAccountInUse(availableSpoofAccount); SpoofAccount = availableSpoofAccount; PhotonNetwork.NickName = availableSpoofAccount.Username; } } public static void UnassignSpoofAccount() { if (IsUsingSpoofAccount) { RemoveSpoofAccountInUse(SpoofAccount); SpoofAccount = default(SteamAccount); PhotonNetwork.NickName = RealAccount.Username; } } public static void ResetSpoofAccountsInUse() { GlobalSaveHelper.SpoofSteamAccountsInUse.Value = new List<SteamAccount>(); } public static bool TryGetSpoofAccount(ulong steamId, out SteamAccount steamAccount) { List<SteamAccount> value = GlobalSaveHelper.SpoofSteamAccounts.Value; foreach (SteamAccount item in value) { if (item.SteamId == steamId) { steamAccount = item; return true; } } steamAccount = default(SteamAccount); return false; } private static void UpdateCurrentSpoofAccountData() { if (!IsUsingSpoofAccount) { return; } List<SteamAccount> value = GlobalSaveHelper.SpoofSteamAccounts.Value; for (int i = 0; i < value.Count; i++) { if (value[i] == SpoofAccount) { value[i] = SpoofAccount; break; } } GlobalSaveHelper.SpoofSteamAccounts.Value = value; } private static List<SteamAccount> GetAvailableSpoofAccounts() { List<SteamAccount> list = new List<SteamAccount>(); List<SteamAccount> value = GlobalSaveHelper.SpoofSteamAccountsInUse.Value; foreach (SteamAccount item in GlobalSaveHelper.SpoofSteamAccounts.Value) { if (!value.Contains(item)) { list.Add(item); } } return list; } private static SteamAccount GetAvailableSpoofAccount() { List<SteamAccount> availableSpoofAccounts = GetAvailableSpoofAccounts(); if (availableSpoofAccounts.Count == 0) { Plugin.Log.LogWarning((object)"SteamHelper: No cached spoof steam accounts available. Generating new spoof steam account."); return new SteamAccount($"Player {Random.Range(100, 999)}", SteamHelper.GenerateRandomSteamId()); } return availableSpoofAccounts[0]; } private static void AddSpoofAccountInUse(SteamAccount account) { List<SteamAccount> value = GlobalSaveHelper.SpoofSteamAccountsInUse.Value; if (!value.Contains(account)) { value.Add(account); GlobalSaveHelper.SpoofSteamAccountsInUse.Value = value; } } private static void RemoveSpoofAccountInUse(SteamAccount account) { List<SteamAccount> value = GlobalSaveHelper.SpoofSteamAccountsInUse.Value; if (value.Contains(account)) { value.Remove(account); GlobalSaveHelper.SpoofSteamAccountsInUse.Value = value; } } } internal static class Utils { public static string GetPluginPersistentDataPath() { return Path.Combine(Application.persistentDataPath, Plugin.Name); } public static ConfigFile CreateConfigFile(BaseUnityPlugin plugin, string path, string name = null, bool saveOnInit = false) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin); if (name == null) { name = metadata.GUID; } name += ".cfg"; return new ConfigFile(Path.Combine(path, name), saveOnInit, metadata); } public static ConfigFile CreateLocalConfigFile(BaseUnityPlugin plugin, string name = null, bool saveOnInit = false) { return CreateConfigFile(plugin, Paths.ConfigPath, name, saveOnInit); } public static ConfigFile CreateGlobalConfigFile(BaseUnityPlugin plugin, string name = null, bool saveOnInit = false) { string pluginPersistentDataPath = GetPluginPersistentDataPath(); if (name == null) { name = "global"; } return CreateConfigFile(plugin, pluginPersistentDataPath, name, saveOnInit); } } } namespace com.quackandcheese.LocalMultiplayer.Patches { [HarmonyPatch(typeof(MainMenu))] internal static class MenuPageMainPatch { [HarmonyPatch("Initialize")] [HarmonyPostfix] private static void InitializePatch() { SteamAccountManager.UnassignSpoofAccount(); } [HarmonyPatch("PlaySoloClicked")] [HarmonyPrefix] private static bool PlaySoloClickedPatch(ref MainMenu __instance) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) SteamAccountManager.AssignSpoofAccount(); PhotonNetworkHelper.SetPhotonServerSettings(); GameHandler.GetService<SteamLobbyHandler>().TryJoinLobby(new CSteamID(GlobalSaveHelper.SteamLobbyId.Value)); return false; } } [HarmonyPatch(typeof(NetworkConnector))] internal static class NetworkConnectorPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPatch() { PhotonNetworkHelper.SetPhotonServerSettings(); } [HarmonyPatch("LoadUserID")] [HarmonyPrefix] private static bool LoadUserIDPatch(ref AuthenticationValues __result) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown __result = new AuthenticationValues(Guid.NewGuid().ToString()); return false; } } [HarmonyPatch(typeof(SettingsHandler))] internal static class SettingsHandlerPatch { [HarmonyPatch("SaveSetting")] [HarmonyPrefix] private static bool SaveSettingPatch(ref Setting setting) { if (!SteamAccountManager.IsUsingSpoofAccount) { return true; } return false; } } [HarmonyPatch(typeof(SteamAuthTicketService))] internal static class SteamAuthTicketServicePatch { [HarmonyPatch("GenerateNewTicket")] [HarmonyPostfix] private static void GenerateNewTicketPatch() { if (!IsValidClient()) { Application.Quit(); } } private static bool IsValidClient() { //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_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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 Optionable<GeneratedTicket> currentTicket = GameHandler.GetService<SteamAuthTicketService>().CurrentTicket; if (!currentTicket.IsSome) { return false; } GeneratedTicket value = currentTicket.Value; byte[] array = HexStringToByteArray(((GeneratedTicket)(ref value)).TicketData); value = currentTicket.Value; EBeginAuthSessionResult val = SteamUser.BeginAuthSession(array, ((GeneratedTicket)(ref value)).TicketData.Length, SteamUser.GetSteamID()); return (int)val == 0; } public static byte[] HexStringToByteArray(string hex) { if (hex.Length % 2 != 0) { throw new ArgumentException("Invalid length for a hex string."); } byte[] array = new byte[hex.Length / 2]; for (int i = 0; i < array.Length; i++) { string value = hex.Substring(i * 2, 2); array[i] = Convert.ToByte(value, 16); } return array; } } [HarmonyPatch] internal static class SteamClientPatch { [HarmonyPatch(typeof(SteamFriends), "GetPersonaName")] [HarmonyPrefix] private static bool GetPersonaNamePatch(ref string __result) { if (!SteamAccountManager.IsUsingSpoofAccount) { return true; } __result = SteamAccountManager.SpoofAccount.Username; return false; } [HarmonyPatch(typeof(SteamUser), "GetSteamID")] [HarmonyPrefix] private static bool GetSteamIDPatch(ref CSteamID __result) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!SteamAccountManager.IsUsingSpoofAccount) { return true; } __result = new CSteamID(SteamAccountManager.SpoofAccount.SteamId); return false; } } [HarmonyPatch(typeof(SteamLobbyHandler))] internal static class SteamLobbyHandlerPatch { [HarmonyPatch("OnLobbyCreated")] [HarmonyPostfix] private static void OnLobbyCreatedPatch(ref LobbyCreated_t param) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)param.m_eResult == 1) { GlobalSaveHelper.SteamLobbyId.Value = param.m_ulSteamIDLobby; SteamAccountManager.ResetSpoofAccountsInUse(); } } [HarmonyPatch("OnLobbyChat")] [HarmonyPrefix] private static bool OnLobbyChatPatch(ref LobbyChatMsg_t param) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (!SteamAccountManager.IsUsingSpoofAccount && param.m_ulSteamIDUser == SteamUser.GetSteamID().m_SteamID) { param.m_ulSteamIDUser = SteamAccountManager.SpoofAccount.SteamId; } return true; } } [HarmonyPatch(typeof(SteamManager))] internal static class SteamManagerPatch { [HarmonyPatch("Awake")] [HarmonyPostfix] [HarmonyPriority(800)] private static void AwakePatch() { SteamAccountManager.Initialize(); } } } namespace com.quackandcheese.LocalMultiplayer.Objects { internal class JsonSave : IDisposable { private JObject _data; private readonly Mutex _mutex; private const int _mutexTimeoutMs = 5000; private bool _disposed; public string DirectoryPath { get; private set; } public string FileName { get; private set; } public string FilePath => Path.Combine(DirectoryPath, FileName); public JsonSave(string directoryPath, string fileName) { DirectoryPath = directoryPath; FileName = fileName; string name = "Global\\JsonSave_" + fileName.Replace(Path.DirectorySeparatorChar, '_'); _mutex = new Mutex(initiallyOwned: false, name); RefreshData(); Application.quitting += Dispose; } public bool KeyExists(string key) { RefreshData(); JObject data = _data; if (data == null) { return false; } return data.ContainsKey(key); } public T Load<T>(string key, T defaultValue = default(T)) { if (!TryLoad<T>(key, out var value)) { return defaultValue; } return value; } public bool TryLoad<T>(string key, out T value) { value = default(T); RefreshData(); if (_data == null) { Plugin.Log.LogError((object)("TryLoad: Data is null. Key: " + key)); return false; } JToken val = default(JToken); if (_data.TryGetValue(key, ref val)) { try { value = val.ToObject<T>(); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("TryLoad: Failed to deserialize key '" + key + "'. " + ex.Message)); } } return false; } public bool Save<T>(string key, T value) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown bool flag = false; try { flag = _mutex.WaitOne(5000); if (!flag) { Plugin.Log.LogWarning((object)"Save: Could not acquire mutex."); return false; } RefreshData(); if (_data == null) { _data = new JObject(); } _data[key] = JToken.FromObject((object)value); return WriteFile(_data); } catch (Exception ex) { Plugin.Log.LogError((object)("Save: Error saving key '" + key + "'. " + ex.Message)); return false; } finally { if (flag) { _mutex.ReleaseMutex(); } } } private JObject ReadFile() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown try { if (!File.Exists(FilePath)) { Plugin.Log.LogWarning((object)("ReadFile: Save file not found at \"" + FilePath + "\". Creating new.")); return new JObject(); } using FileStream stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); return JObject.Parse(streamReader.ReadToEnd()); } catch (Exception ex) { Plugin.Log.LogError((object)("ReadFile: Failed to read file \"" + FilePath + "\". " + ex.Message)); return new JObject(); } } private bool WriteFile(JObject data) { try { if (!Directory.Exists(DirectoryPath)) { Directory.CreateDirectory(DirectoryPath); } File.WriteAllText(FilePath, ((object)data).ToString(), Encoding.UTF8); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("WriteFile: Failed to write file \"" + FilePath + "\". " + ex.Message)); return false; } } private void RefreshData() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown _data = ReadFile(); if (_data == null) { Plugin.Log.LogError((object)"RefreshData: Data is null. Creating new."); _data = new JObject(); } } public void Dispose() { if (!_disposed) { _mutex?.Dispose(); _disposed = true; } } } internal class JsonSaveValue<T> { public JsonSave JsonSave { get; private set; } public string Key { get; private set; } public T DefaultValue { get; private set; } public T Value { get { return Load(); } set { Save(value); } } public bool HasValue { get { T value; return TryLoad(out value); } } public JsonSaveValue(JsonSave jsonSave, string key, T defaultValue = default(T)) { JsonSave = jsonSave; Key = key; DefaultValue = defaultValue; base..ctor(); } public T Load() { return JsonSave.Load(Key, DefaultValue); } public bool TryLoad(out T value) { return JsonSave.TryLoad<T>(Key, out value); } public void Save(T value) { JsonSave.Save(Key, value); } } public struct SteamAccount : IEquatable<SteamAccount> { public string Username; public ulong SteamId; public SteamAccount(string username, ulong steamId) { Username = username; SteamId = steamId; } public bool Equals(SteamAccount other) { return SteamId == other.SteamId; } public override bool Equals(object obj) { if (obj is SteamAccount other) { return Equals(other); } return false; } public static bool operator ==(SteamAccount a, SteamAccount b) { return a.Equals(b); } public static bool operator !=(SteamAccount a, SteamAccount b) { return !a.Equals(b); } public override int GetHashCode() { return SteamId.GetHashCode(); } } } namespace com.quackandcheese.LocalMultiplayer.Helpers { internal static class GlobalSaveHelper { public static JsonSave JsonSave { get; private set; } public static JsonSaveValue<ulong> SteamLobbyId { get; private set; } public static JsonSaveValue<List<SteamAccount>> SpoofSteamAccounts { get; private set; } public static JsonSaveValue<List<SteamAccount>> SpoofSteamAccountsInUse { get; private set; } static GlobalSaveHelper() { JsonSave = new JsonSave(Utils.GetPluginPersistentDataPath(), "GlobalSave"); SteamLobbyId = new JsonSaveValue<ulong>(JsonSave, "SteamLobbyId", 0uL); SpoofSteamAccounts = new JsonSaveValue<List<SteamAccount>>(JsonSave, "SpoofSteamAccounts", new List<SteamAccount>()); SpoofSteamAccountsInUse = new JsonSaveValue<List<SteamAccount>>(JsonSave, "SpoofSteamAccountsInUse", new List<SteamAccount>()); } public static bool KeyExists(string key) { return JsonSave.KeyExists(key); } public static T Load<T>(string key, T defaultValue = default(T)) { return JsonSave.Load(key, defaultValue); } public static bool TryLoad<T>(string key, out T value) { return JsonSave.TryLoad<T>(key, out value); } public static bool Save<T>(string key, T value) { return JsonSave.Save(key, value); } } internal static class PhotonNetworkHelper { public static void SetPhotonServerSettings() { AppSettings appSettings = PhotonNetwork.PhotonServerSettings.AppSettings; appSettings.AppIdRealtime = ConfigManager.Photon_AppIdRealtime.Value; appSettings.AppIdVoice = ConfigManager.Photon_AppIdVoice.Value; } } internal static class SteamHelper { public static ulong GenerateRandomSteamId() { ulong num = 76561197960265728uL; Random random = new Random(); uint num2 = (uint)random.Next(0, int.MaxValue); num2 += (uint)random.Next(0, int.MaxValue); return num + num2; } } } namespace com.quackandcheese.LocalMultiplayer.Extensions { internal static class StringExtensions { public static ulong ToUlong(this string value) { if (ulong.TryParse(value, out var result)) { return result; } return 0uL; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }