Implement hunting AP and progress, event tasks, exchanges, games, stories and actions; connect battle and reward handling. Add CashShop plugin and installation, server purchases and entitlements, recharge rules and plugin version metadata. Distribute binary calendars independently; cache GameData in memory and fix single-connection query deadlocks. Improve automatic login and recovery diagnostics; document configuration and release flows.
104 lines
3.7 KiB
C#
104 lines
3.7 KiB
C#
using System;
|
|
using BD2.GameNames;
|
|
using static BD2.GameNames.Game;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using BepInEx;
|
|
using BepInEx.Logging;
|
|
using HarmonyLib;
|
|
using UnityEngine;
|
|
|
|
namespace Bd2LocalIdentity;
|
|
|
|
[BepInPlugin(Guid, Name, Version)]
|
|
public sealed class Plugin : BaseUnityPlugin
|
|
{
|
|
public const string Guid = "bd2.localidentity";
|
|
public const string Name = "BD2 Local Identity";
|
|
public const string Version = Bd2Build.Versions.Plugin;
|
|
private static ManualLogSource Log;
|
|
private static ClientRouting Routing;
|
|
private static int ShutdownHooksInstalled;
|
|
|
|
internal static void LogWarning(string message)
|
|
{
|
|
Log?.LogWarning(message);
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
try
|
|
{
|
|
Log = Logger;
|
|
Game.Validate(typeof(Plugin).Assembly, Bd2Build.Versions.Game, message => Logger.LogInfo(message));
|
|
Routing = ClientRouting.Load(Logger);
|
|
if (Interlocked.Exchange(ref ShutdownHooksInstalled, 1) == 0)
|
|
{
|
|
AppDomain.CurrentDomain.ProcessExit += delegate { DisposeRouting(); };
|
|
AppDomain.CurrentDomain.DomainUnload += delegate { DisposeRouting(); };
|
|
}
|
|
Type appManager = typeof(AppManager);
|
|
MethodInfo getter = Game.Getter<AppManager, bool>(app => app.IsPlatformLogin);
|
|
if (getter == null || getter.ReturnType != typeof(bool) || getter.GetParameters().Length != 0)
|
|
{
|
|
throw new MissingMethodException("AppManager.IsPlatformLogin getter was not found (client version mismatch)");
|
|
}
|
|
|
|
MethodInfo prefix = typeof(Plugin).GetGameMethod(
|
|
nameof(UseSdkPrefix),
|
|
BindingFlags.Static | BindingFlags.NonPublic);
|
|
Harmony harmony = new Harmony(Guid);
|
|
LocalLoginState.Install(harmony, Routing.ServerOrigin, Logger);
|
|
harmony.Patch(getter, prefix: new HarmonyMethod(prefix));
|
|
TryInstall("OS time zone device country", () => SystemTimeZoneRegion.Install(harmony, Logger));
|
|
|
|
ClientPresentation.Initialize(Logger);
|
|
LocalAccountPolicy.Initialize(Logger);
|
|
ClientDiagnostics.Initialize(Logger);
|
|
Type introUI = typeof(IntroUI);
|
|
Routing.Install(harmony);
|
|
TryInstall("startup fullscreen", () => ClientPresentation.InstallStartupFullscreen(harmony, appManager));
|
|
TryInstall("performance overlay suppression", () => ClientPresentation.InstallPerformanceOverlaySuppression(harmony));
|
|
TryInstall("maintenance timeout guard", () => ClientDiagnostics.InstallMaintenanceTimeoutGuard(harmony, introUI));
|
|
TryInstall("age-gate persistence", () => LocalAccountPolicy.InstallAgeGatePersistence(harmony));
|
|
TryInstall("database diagnostics", () => ClientDiagnostics.InstallDatabaseDiagnostics(harmony));
|
|
Logger.LogInfo("Local identity active: AppManager.IsPlatformLogin => false");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError("Local identity patch failed: " + ex);
|
|
}
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
DisposeRouting();
|
|
}
|
|
|
|
private static void DisposeRouting()
|
|
{
|
|
ClientRouting routing = Interlocked.Exchange(ref Routing, null);
|
|
routing?.Dispose();
|
|
}
|
|
|
|
private void TryInstall(string name, Action install)
|
|
{
|
|
try
|
|
{
|
|
install();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError("Local identity " + name + " patch failed: " + ex);
|
|
}
|
|
}
|
|
|
|
private static bool UseSdkPrefix(ref bool __result)
|
|
{
|
|
__result = false;
|
|
return false;
|
|
}
|
|
|
|
|
|
}
|