- 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.
117 lines
5.1 KiB
C#
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);
|
|
}
|
|
}
|
|
|
|
}
|