feat: 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.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using BD2.GameNames;
|
||||
using BepInEx.Logging;
|
||||
|
||||
using static BD2.GameNames.Game;
|
||||
|
||||
namespace Bd2LoginUI;
|
||||
|
||||
// Shared server origin and resolved client transition bindings; feature state stays in its owner.
|
||||
internal static class LoginRuntime
|
||||
{
|
||||
internal static ManualLogSource Log;
|
||||
internal static MethodInfo SetIntroState;
|
||||
internal static MethodInfo SendMaintenance;
|
||||
internal static Uri ServerRoot;
|
||||
internal static MethodInfo OpenPCLoginPopup;
|
||||
internal static MethodInfo EnterGame;
|
||||
internal static string NormalizedServerOrigin()
|
||||
{
|
||||
return NormalizeOrigin(ServerRoot);
|
||||
}
|
||||
|
||||
internal static bool SameOrigin(Uri left, Uri right)
|
||||
{
|
||||
return string.Equals(NormalizeOrigin(left), NormalizeOrigin(right), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static string NormalizeOrigin(Uri uri)
|
||||
{
|
||||
if (uri == null || !uri.IsAbsoluteUri || string.IsNullOrEmpty(uri.Host))
|
||||
{
|
||||
throw new InvalidOperationException("authentication server origin is unavailable");
|
||||
}
|
||||
string host = uri.IdnHost.ToLowerInvariant();
|
||||
int port = uri.IsDefaultPort ? -1 : uri.Port;
|
||||
UriBuilder builder = new UriBuilder(uri.Scheme.ToLowerInvariant(), host, port);
|
||||
return builder.Uri.GetLeftPart(UriPartial.Authority).TrimEnd('/');
|
||||
}
|
||||
|
||||
internal static void ValidateOAuthTransport(Uri uri)
|
||||
{
|
||||
if (uri == null || !uri.IsAbsoluteUri ||
|
||||
(uri.Scheme != Uri.UriSchemeHttps && !(uri.Scheme == Uri.UriSchemeHttp && uri.IsLoopback)))
|
||||
{
|
||||
throw new InvalidOperationException("OAuth requires HTTPS except when connecting to a loopback server");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ValidateBrowserURL(string raw)
|
||||
{
|
||||
if (!Uri.TryCreate(raw, UriKind.Absolute, out Uri uri) ||
|
||||
(uri.Scheme != Uri.UriSchemeHttps && !(uri.Scheme == Uri.UriSchemeHttp && uri.IsLoopback)) ||
|
||||
uri.UserInfo.Length != 0)
|
||||
{
|
||||
throw new InvalidDataException("server returned an unsafe browser login URL");
|
||||
}
|
||||
}
|
||||
|
||||
internal static T FindUnitySingleton<T>() where T : UnityEngine.Object
|
||||
{
|
||||
return UnityEngine.Object.FindObjectOfType<T>();
|
||||
}
|
||||
|
||||
internal static Uri CurrentMaintenanceUri()
|
||||
{
|
||||
Type serverURLInfo = typeof(BDNetwork.ServerURLInfo);
|
||||
FieldInfo maintenanceUri = serverURLInfo?.GetGameField(
|
||||
nameof(BDNetwork.ServerURLInfo.MaintenanceUri),
|
||||
BindingFlags.Static | BindingFlags.Public);
|
||||
Uri result = maintenanceUri?.GetValue(null) as Uri;
|
||||
if (result == null || (result.Scheme != Uri.UriSchemeHttp && result.Scheme != Uri.UriSchemeHttps))
|
||||
{
|
||||
throw new InvalidOperationException("current server maintenance URL is unavailable");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static MethodInfo FindOpenPCLoginPopup()
|
||||
{
|
||||
Type uiManager = typeof(UIManager);
|
||||
MethodInfo method = uiManager.GetGameMethod(
|
||||
nameof(UIManager.OpenPCLoginPopupUI),
|
||||
BindingFlags.Static | BindingFlags.Public,
|
||||
null,
|
||||
new[] { typeof(Action) },
|
||||
null);
|
||||
return method != null && method.ReturnType == typeof(void) ? method : null;
|
||||
}
|
||||
|
||||
internal static MethodInfo FindAccessTokenGetter()
|
||||
{
|
||||
MethodInfo getter = Game.Getter(() => BDNetwork.CommonPacket.AccessToken);
|
||||
return getter != null && getter.IsStatic && getter.ReturnType == typeof(string) &&
|
||||
getter.GetParameters().Length == 0 ? getter : null;
|
||||
}
|
||||
|
||||
internal static MethodInfo FindClearPCLocalData()
|
||||
{
|
||||
Type platformManager = typeof(gamfs.Platform.PlatformManager);
|
||||
MethodInfo method = platformManager?.GetGameMethod(
|
||||
nameof(gamfs.Platform.PlatformManager.ClearPCLocalData),
|
||||
BindingFlags.Instance | BindingFlags.Public,
|
||||
null,
|
||||
Type.EmptyTypes,
|
||||
null);
|
||||
return method != null && method.ReturnType == typeof(void) ? method : null;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user