Files
bd2/plugins/LoginUI/Plugin.cs
T
Flechazo 992fa47fd5 feat(all): add game SDK and harden client login and game rules
- Add costume burst upgrades with durable replay and fix permanent SR equipment gacha.
- Add server game.json rules for optional collaboration UR weapons, with packaging and documentation.
- Add GameNames/GameSdk NuGet tooling, readable types, reobfuscation, complete source/PDB navigation and shared-runtime installation.
- Split plugin responsibilities, prefer typeof/nameof, centralize MSBuild settings and ignore local paths; remove the unused LoginUI test project.
- Fix login recovery and TLS through native platform HTTP and a private game relay; read device region directly from the OS time zone.

Validation: go test ./... -count=1, go vet ./..., plugin Release builds, SDK/runtime self-tests, Roslyn embedded-source navigation, gofmt check and git diff --check.
2026-10-03 23:04:32 +08:00

117 lines
5.1 KiB
C#

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<IntroUI>(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);
}
}
}