using System; using System.Reflection; using BepInEx; using HarmonyLib; using UnityEngine.Networking; using BD2.GameNames; using static BD2.GameNames.Game; using static Bd2LoginUI.LoginPanel; using static Bd2LoginUI.LoginRuntime; using static Bd2LoginUI.SessionRecovery; using static Bd2LoginUI.LoginController; namespace Bd2LoginUI; [BepInPlugin(Guid, Name, Version)] [BepInDependency("bd2.localidentity", BepInDependency.DependencyFlags.HardDependency)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "bd2.login.ui"; public const string Name = "BD2 Login UI"; public const string Version = Bd2Build.Versions.Plugin; private void OnApplicationQuit() { DisposeGameRelay(); } private void Awake() { try { Log = Logger; AppDomain.CurrentDomain.ProcessExit += (_, _) => DisposeGameRelay(); Game.Validate(typeof(Plugin).Assembly, Bd2Build.Versions.Game, message => Logger.LogInfo(message)); EnsureRecoveryHost(); InitializeCredentials(); InitializeBranding(); Type introUI = typeof(IntroUI); MethodInfo awake = introUI?.GetGameMethod( "Awake", BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (awake == null || awake.ReturnType != typeof(void)) { throw new MissingMethodException("IntroUI.Awake() was not found (client version mismatch)"); } SendMaintenance = Game.Method(ui => ui.SendMaintenanceInfo(false)); SetIntroState = introUI.GetGameMethod("SetIntroState", BindingFlags.Instance | BindingFlags.NonPublic); EnterGame = introUI.GetGameMethod( "Enter", BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); OpenPCLoginPopup = FindOpenPCLoginPopup(); MethodInfo accessTokenGetter = FindAccessTokenGetter(); MethodInfo clearPCLocalData = FindClearPCLocalData(); MethodInfo disposeWebRequest = typeof(UnityWebRequest).GetGameMethod( nameof(UnityWebRequest.Dispose), BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null); Type networkManager = typeof(BDNetwork.NetworkManager); MethodInfo clientNetworkError = networkManager?.GetGameMethod( nameof(BDNetwork.NetworkManager.ClientNetworkError), BindingFlags.Instance | BindingFlags.Public); MethodInfo exponentialBackOff = networkManager?.GetGameMethod( "ExponetialBackOff", BindingFlags.Instance | BindingFlags.NonPublic); if (SendMaintenance == null || SetIntroState == null || OpenPCLoginPopup == null || accessTokenGetter == null || clearPCLocalData == null || disposeWebRequest == null || clientNetworkError == null || exponentialBackOff == null || EnterGame == null) { throw new MissingMethodException("IntroUI authentication transition methods were not found (client version mismatch)"); } Harmony harmony = new Harmony(Guid); harmony.Patch(awake, postfix: new HarmonyMethod(typeof(SessionRecovery), nameof(IntroAwakePostfix))); HarmonyMethod maintenancePrefix = new HarmonyMethod(typeof(LoginController), nameof(SendMaintenancePrefix)); maintenancePrefix.after = new[] { "bd2.localidentity" }; harmony.Patch(SendMaintenance, prefix: maintenancePrefix); harmony.Patch( accessTokenGetter, prefix: new HarmonyMethod(typeof(LoginController), nameof(AccessTokenPrefix))); harmony.Patch( clearPCLocalData, postfix: new HarmonyMethod(typeof(LoginController), nameof(ClearPCLocalDataPostfix))); harmony.Patch( typeof(UnityWebRequest).GetMethod(nameof(UnityWebRequest.SendWebRequest), Type.EmptyTypes), prefix: new HarmonyMethod(typeof(SessionRecovery), nameof(SendWebRequestPrefix))); harmony.Patch( disposeWebRequest, prefix: new HarmonyMethod(typeof(SessionRecovery), nameof(DisposeWebRequestPrefix))); harmony.Patch( clientNetworkError, prefix: new HarmonyMethod(typeof(SessionRecovery), nameof(SuppressNetworkErrorDuringRecovery))); harmony.Patch( exponentialBackOff, prefix: new HarmonyMethod(typeof(SessionRecovery), nameof(ExponentialBackoffPrefix))); harmony.Patch( SetIntroState, postfix: new HarmonyMethod(typeof(SessionRecovery), nameof(SetIntroStatePostfix))); Logger.LogInfo("Server-authoritative Discord and Google login UI patch installed"); } catch (Exception ex) { Logger.LogError("Login UI patch failed: " + ex); } } }