diff --git a/src/Snap.Hutao/Snap.Hutao.Test/IncomingFeature/UnlockerIslandFunctionOffsetTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/IncomingFeature/UnlockerIslandFunctionOffsetTest.cs new file mode 100644 index 00000000..bfea39e5 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao.Test/IncomingFeature/UnlockerIslandFunctionOffsetTest.cs @@ -0,0 +1,51 @@ +using System; +using System.Text.Json; + +namespace Snap.Hutao.Test.IncomingFeature; + +[TestClass] +public class UnlockerIslandFunctionOffsetTest +{ + private static readonly JsonSerializerOptions Options = new() + { + WriteIndented = true, + }; + + [TestMethod] + public void GenerateJson() + { + UnlockerIslandConfigurationWrapper wrapper = new() + { + Oversea = new() + { + FunctionOffsetFieldOfView = 0x00000000_01688E60, + FunctionOffsetTargetFrameRate = 0x00000000_018834D0, + FunctionOffsetFog = 0x00000000_00FB2AD0, + }, + Chinese = new() + { + FunctionOffsetFieldOfView = 0x00000000_01684560, + FunctionOffsetTargetFrameRate = 0x00000000_0187EBD0, + FunctionOffsetFog = 0x00000000_00FAE1D0, + }, + }; + + Console.WriteLine(JsonSerializer.Serialize(wrapper, Options)); + } + + private sealed class UnlockerIslandConfigurationWrapper + { + public required UnlockerIslandConfiguration Oversea { get; set; } + + public required UnlockerIslandConfiguration Chinese { get; set; } + } + + private sealed class UnlockerIslandConfiguration + { + public required uint FunctionOffsetFieldOfView { get; set; } + + public required uint FunctionOffsetTargetFrameRate { get; set; } + + public required uint FunctionOffsetFog { get; set; } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs index 0e07b71d..e16fb79b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/SettingEntry.Constant.cs @@ -41,8 +41,9 @@ internal sealed partial class SettingEntry public const string LaunchScreenHeight = "Launch.ScreenHeight"; public const string LaunchIsScreenHeightEnabled = "Launch.IsScreenHeightEnabled"; public const string LaunchUnlockFps = "Launch.UnlockFps"; - public const string LaunchUnlockerKind = "Launch.UnlockerKind"; public const string LaunchTargetFps = "Launch.TargetFps"; + public const string LaunchTargetFov = "Launch.TargetFov"; + public const string LaunchDisableFog = "Launch.DisableFog"; public const string LaunchMonitor = "Launch.Monitor"; public const string LaunchIsMonitorEnabled = "Launch.IsMonitorEnabled"; public const string LaunchIsUseCloudThirdPartyMobile = "Launch.IsUseCloudThirdPartyMobile"; @@ -51,6 +52,9 @@ internal sealed partial class SettingEntry public const string LaunchUseBetterGenshinImpactAutomation = "Launch.UseBetterGenshinImpactAutomation"; public const string LaunchSetDiscordActivityWhenPlaying = "Launch.SetDiscordActivityWhenPlaying"; + [Obsolete("不再区分解锁器类型,统一使用注入")] + public const string LaunchUnlockerKind = "Launch.UnlockerKind"; + [Obsolete("不再支持多开")] public const string MultipleInstances = "Launch.MultipleInstances"; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Feature/FeatureService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Feature/FeatureService.cs new file mode 100644 index 00000000..a1035732 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Feature/FeatureService.cs @@ -0,0 +1,37 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Service.Game.Unlocker.Island; +using Snap.Hutao.Web; +using System.Net.Http; +using System.Net.Http.Json; + +namespace Snap.Hutao.Service.Feature; + +[ConstructorGenerated] +[Injection(InjectAs.Singleton, typeof(IFeatureService))] +[HttpClient(HttpClientConfiguration.Default)] +internal sealed partial class FeatureService : IFeatureService +{ + private readonly IServiceScopeFactory serviceScopeFactory; + + public async ValueTask GetIslandFeatureAsync() + { + using (IServiceScope scope = serviceScopeFactory.CreateScope()) + { + IHttpClientFactory httpClientFactory = scope.ServiceProvider.GetRequiredService(); + using (HttpClient httpClient = httpClientFactory.CreateClient(nameof(FeatureService))) + { + try + { + return await httpClient.GetFromJsonAsync(HutaoEndpoints.Feature("UnlockerIsland")).ConfigureAwait(false); + } + catch + { + return default; + } + } + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Feature/IFeatureService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Feature/IFeatureService.cs new file mode 100644 index 00000000..6414f481 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Feature/IFeatureService.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Service.Game.Unlocker.Island; + +namespace Snap.Hutao.Service.Feature; + +internal interface IFeatureService +{ + ValueTask GetIslandFeatureAsync(); +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs index f9f04ccc..1bd34ef5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/LaunchOptions.cs @@ -41,6 +41,8 @@ internal sealed class LaunchOptions : DbStoreOptions private bool? unlockFps; private NameDescriptionValue? unlockerKind; private int? targetFps; + private float? targetFov; + private bool? disableFog; private NameValue? monitor; private bool? isMonitorEnabled; private bool? isUseCloudThirdPartyMobile; @@ -169,40 +171,17 @@ internal sealed class LaunchOptions : DbStoreOptions set => SetOption(ref unlockFps, SettingEntry.LaunchUnlockFps, value); } - public List> UnlockerKinds { get; } = - [ - new(SH.ServiceGameLaunchUnlockerKindLegacyName, SH.ServiceGameLaunchUnlockerKindLegacyDescription, GameFpsUnlockerKind.Legacy), - new(SH.ServiceGameLaunchUnlockerKindIslandName, SH.ServiceGameLaunchUnlockerKindIslandDescription, GameFpsUnlockerKind.Island), - ]; - - public NameDescriptionValue UnlockerKind - { - get - { - return GetOption(ref unlockerKind, SettingEntry.LaunchUnlockerKind, name => GetKind(name, UnlockerKinds), UnlockerKinds[0]); - - static NameDescriptionValue GetKind(string name, List> unlockerKinds) - { - GameFpsUnlockerKind kind = Enum.Parse(name); - return unlockerKinds.Single(entry => entry.Value == kind); - } - } - - set - { - if (value is not null) - { - SetOption(ref unlockerKind, SettingEntry.LaunchUnlockerKind, value, selected => selected.Value.ToString()); - } - } - } - public int TargetFps { get => GetOption(ref targetFps, SettingEntry.LaunchTargetFps, primaryScreenFps); set => SetOption(ref targetFps, SettingEntry.LaunchTargetFps, value); } + public float TargetFov + { + get=> GetOption(ref targetFov, SettingEntry.LaunchTargetFov, 45f); + } + public List> Monitors { get; } = []; [AllowNull] diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs index 19a84476..90e9b64e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs @@ -1,27 +1,46 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Core; using Snap.Hutao.Core.ExceptionService; +using Snap.Hutao.Service.AvatarInfo.Factory.Builder; +using Snap.Hutao.Service.Feature; +using Snap.Hutao.Service.Game.Unlocker.Island; using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.System.LibraryLoader; using Snap.Hutao.Win32.System.Threading; +using Snap.Hutao.Win32.UI.WindowsAndMessaging; using System.Diagnostics; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Runtime.InteropServices; +using static Snap.Hutao.Win32.ConstValues; using static Snap.Hutao.Win32.Kernel32; +using static Snap.Hutao.Win32.Macros; +using static Snap.Hutao.Win32.User32; namespace Snap.Hutao.Service.Game.Unlocker; -internal abstract class GameFpsUnlocker : IGameFpsUnlocker +internal sealed class GameFpsUnlocker : IGameFpsUnlocker { + private const string IslandEnvironmentName = "4F3E8543-40F7-4808-82DC-21E48A6037A7"; private readonly LaunchOptions launchOptions; - private readonly GameFpsUnlockerContext context = new(); + private readonly IFeatureService featureService; - public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess, in UnlockOptions options, IProgress progress) + private readonly GameFpsUnlockerContext context = new(); + private readonly string dataFolderIslandPath; + + private IslandFunctionOffsets? offsets; + + public GameFpsUnlocker(IServiceProvider serviceProvider, Process gameProcess, IProgress progress) { launchOptions = serviceProvider.GetRequiredService(); + featureService = serviceProvider.GetRequiredService(); + + RuntimeOptions runtimeOptions = serviceProvider.GetRequiredService(); + dataFolderIslandPath = Path.Combine(runtimeOptions.DataFolder, "Snap.Hutao.UnlockerIsland.dll"); context.GameProcess = gameProcess; - context.AllAccess = OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_ALL_ACCESS, false, (uint)gameProcess.Id); - context.Options = options; context.Progress = progress; context.Logger = serviceProvider.GetRequiredService>(); } @@ -29,31 +48,91 @@ internal abstract class GameFpsUnlocker : IGameFpsUnlocker public async ValueTask UnlockAsync(CancellationToken token = default) { HutaoException.ThrowIfNot(context.IsUnlockerValid, "This Unlocker is invalid"); - (FindModuleResult result, RequiredRemoteModule remoteModule) = await GameProcessModule.FindModuleAsync(context).ConfigureAwait(false); - HutaoException.ThrowIfNot(result != FindModuleResult.TimeLimitExeeded, SH.ServiceGameUnlockerFindModuleTimeLimitExeeded); - HutaoException.ThrowIfNot(result != FindModuleResult.NoModuleFound, SH.ServiceGameUnlockerFindModuleNoModuleFound); - - using (RequiredLocalModule localModule = LoadRequiredLocalModule(context.Options.GameFileSystem)) + if (await featureService.GetIslandFeatureAsync().ConfigureAwait(false) is not { } feature) { - GameFpsAddress.UnsafeFindFpsAddress(context, remoteModule, localModule); + return false; } + offsets = string.Equals(GameConstants.GenshinImpactProcessName, context.GameProcess.ProcessName, StringComparison.OrdinalIgnoreCase) + ? feature.Oversea + : feature.Chinese; + context.Report(); - return context.FpsAddress != 0U; + return true; } - public ValueTask PostUnlockAsync(CancellationToken token = default) + public async ValueTask PostUnlockAsync(CancellationToken token = default) { - return PostUnlockOverrideAsync(context, launchOptions, context.Logger, token); + if (offsets is null) + { + return; + } + + try + { + File.Copy(InstalledLocation.GetAbsolutePath("Snap.Hutao.UnlockerIsland.dll"), dataFolderIslandPath, true); + } + catch + { + context.Logger.LogError("Failed to copy island file."); + return; + } + + try + { + using (MemoryMappedFile file = MemoryMappedFile.CreateOrOpen(IslandEnvironmentName, 1024)) + { + using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor()) + { + nint handle = accessor.SafeMemoryMappedViewHandle.DangerousGetHandle(); + InitializeIslandEnvironment(handle, offsets); + InitializeIsland(context.GameProcess); + await context.GameProcess.WaitForExitAsync().ConfigureAwait(false); + } + } + } + finally + { + context.Logger.LogInformation("Exit PostUnlockAsync"); + } } - protected abstract ValueTask PostUnlockOverrideAsync(GameFpsUnlockerContext context, LaunchOptions launchOptions, ILogger logger, CancellationToken token = default); - - private static RequiredLocalModule LoadRequiredLocalModule(GameFileSystem gameFileSystem) + private unsafe void InitializeIslandEnvironment(nint handle, IslandFunctionOffsets offsets, LaunchOptions options) { - LOAD_LIBRARY_FLAGS flags = LOAD_LIBRARY_FLAGS.LOAD_LIBRARY_AS_IMAGE_RESOURCE; - HMODULE executaleAddress = LoadLibraryExW(gameFileSystem.GameFilePath, default, flags); + IslandEnvironment* pIslandEnvironment = (IslandEnvironment*)handle; + pIslandEnvironment->FunctionOffsetFieldOfView = offsets.FunctionOffsetFieldOfView; + pIslandEnvironment->FunctionOffsetTargetFrameRate = offsets.FunctionOffsetTargetFrameRate; + pIslandEnvironment->FunctionOffsetFog = offsets.FunctionOffsetFog; + pIslandEnvironment->TargetFrameRate = options.TargetFps; + } - return new(executaleAddress); + private unsafe void InitializeIsland(Process gameProcess) + { + HANDLE hModule = default; + try + { + hModule = NativeLibrary.Load(dataFolderIslandPath); + nint pIslandGetWindowHook = NativeLibrary.GetExport((nint)(hModule & ~0x3L), "IslandGetWindowHook"); + + HOOKPROC hookProc = default; + ((delegate* unmanaged[Stdcall])pIslandGetWindowHook)(&hookProc); + + SpinWait.SpinUntil(() => gameProcess.MainWindowHandle is not 0); + uint threadId = GetWindowThreadProcessId(gameProcess.MainWindowHandle, default); + HHOOK hHook = SetWindowsHookExW(WINDOWS_HOOK_ID.WH_GETMESSAGE, hookProc, (HINSTANCE)hModule, threadId); + if (hHook.Value is 0) + { + Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(GetLastError())); + } + + if (!PostThreadMessageW(threadId, WM_NULL, default, default)) + { + Marshal.ThrowExceptionForHR(HRESULT_FROM_WIN32(GetLastError())); + } + } + finally + { + NativeLibrary.Free(hModule); + } } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerContext.cs index c73bbf4f..f5b4dc93 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlockerContext.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Win32.Foundation; using System.Diagnostics; namespace Snap.Hutao.Service.Game.Unlocker; @@ -10,18 +9,10 @@ internal sealed class GameFpsUnlockerContext { public string Description { get; set; } = default!; - public FindModuleResult FindModuleResult { get; set; } - public bool IsUnlockerValid { get; set; } = true; - public nuint FpsAddress { get; set; } - - public UnlockOptions Options { get; set; } - public Process GameProcess { get; set; } = default!; - public HANDLE AllAccess { get; set; } - public IProgress Progress { get; set; } = default!; public ILogger Logger { get; set; } = default!; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironment.cs index e8262efb..f0bf9f0e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironment.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironment.cs @@ -7,9 +7,15 @@ namespace Snap.Hutao.Service.Game.Unlocker.Island; internal struct IslandEnvironment { - public nuint Address; - public int Value; + public nuint Reserved1; + public int Reserved2; public IslandState State; public WIN32_ERROR LastError; - public int Reserved; + public int Reserved3; + public float FieldOfView; + public float TargetFrameRate; + public bool DisableFog; + public nuint FunctionOffsetFieldOfView; + public nuint FunctionOffsetTargetFrameRate; + public nuint FunctionOffsetFog; } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironmentView.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironmentView.cs index cb988734..5001e31f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironmentView.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandEnvironmentView.cs @@ -7,9 +7,15 @@ namespace Snap.Hutao.Service.Game.Unlocker.Island; internal struct IslandEnvironmentView { - public nuint Address; - public int Value; + public nuint Reserved1; + public int Reserved2; public IslandState State; public WIN32_ERROR LastError; - public int Reserved; + public int Reserved3; + public float FieldOfView; + public float TargetFrameRate; + public bool DisableFog; + public nuint FunctionOffsetFieldOfView; + public nuint FunctionOffsetTargetFrameRate; + public nuint FunctionOffsetFog; } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandFeature.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandFeature.cs new file mode 100644 index 00000000..1f4cf24a --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandFeature.cs @@ -0,0 +1,11 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Unlocker.Island; + +internal sealed class IslandFeature +{ + public required IslandFunctionOffsets Oversea { get; set; } + + public required IslandFunctionOffsets Chinese { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandFunctionOffsets.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandFunctionOffsets.cs new file mode 100644 index 00000000..6b6bfe4e --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/Island/IslandFunctionOffsets.cs @@ -0,0 +1,13 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Service.Game.Unlocker.Island; + +internal sealed class IslandFunctionOffsets +{ + public required uint FunctionOffsetFieldOfView { get; set; } + + public required uint FunctionOffsetTargetFrameRate { get; set; } + + public required uint FunctionOffsetFog { get; set; } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 47a36cab..e9f90f1e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -320,7 +320,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoInfrastructureClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoInfrastructureClient.cs index 09ff3439..da2825ec 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoInfrastructureClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HutaoInfrastructureClient.cs @@ -1,8 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -#define IS_ALPHA_BUILD - using Snap.Hutao.Core; using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Web.Hutao.Response; @@ -24,7 +22,7 @@ internal sealed partial class HutaoInfrastructureClient public async ValueTask> GetStaticSizeAsync(CancellationToken token = default) { HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() - .SetRequestUri(HutaoEndpoints.StaticSize) + .SetRequestUri(HutaoEndpoints.StaticSize()) .Get(); HutaoResponse? resp = await builder.SendAsync>(httpClient, logger, token).ConfigureAwait(false); @@ -34,7 +32,7 @@ internal sealed partial class HutaoInfrastructureClient public async ValueTask> GetIPInformationAsync(CancellationToken token = default) { HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() - .SetRequestUri(HutaoEndpoints.Ip) + .SetRequestUri(HutaoEndpoints.Ip()) .Get(); HutaoResponse? resp = await builder.SendAsync>(httpClient, logger, token).ConfigureAwait(false); @@ -43,12 +41,7 @@ internal sealed partial class HutaoInfrastructureClient public async ValueTask> GetHutaoVersionInfomationAsync(CancellationToken token = default) { - string url -#if IS_ALPHA_BUILD - = HutaoEndpoints.PatchAlphaSnapHutao(Core.Setting.LocalSetting.Get(Core.Setting.SettingKeys.AlphaBuildUseCNPatchEndpoint, false)); -#else - = HutaoEndpoints.PatchSnapHutao; -#endif + string url = HutaoEndpoints.PatchSnapHutao(); HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() .SetRequestUri(url) @@ -62,7 +55,7 @@ internal sealed partial class HutaoInfrastructureClient public async ValueTask> GetYaeVersionInformationAsync(CancellationToken token = default) { HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create() - .SetRequestUri(HutaoEndpoints.PatchYaeAchievement) + .SetRequestUri(HutaoEndpoints.PatchYaeAchievement()) .Get(); HutaoResponse? resp = await builder.SendAsync>(httpClient, logger, token).ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/HutaoWallpaperClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/HutaoWallpaperClient.cs index c1be3f4d..ed09eac2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/HutaoWallpaperClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Wallpaper/HutaoWallpaperClient.cs @@ -19,17 +19,17 @@ internal sealed partial class HutaoWallpaperClient public ValueTask> GetBingWallpaperAsync(CancellationToken token = default) { - return GetWallpaperAsync(HutaoEndpoints.WallpaperBing, token); + return GetWallpaperAsync(HutaoEndpoints.WallpaperBing(), token); } public ValueTask> GetLauncherWallpaperAsync(CancellationToken token = default) { - return GetWallpaperAsync(HutaoEndpoints.WallpaperGenshinLauncher, token); + return GetWallpaperAsync(HutaoEndpoints.WallpaperGenshinLauncher(), token); } public ValueTask> GetTodayWallpaperAsync(CancellationToken token = default) { - return GetWallpaperAsync(HutaoEndpoints.WallpaperToday, token); + return GetWallpaperAsync(HutaoEndpoints.WallpaperToday(), token); } private async ValueTask> GetWallpaperAsync(string url, CancellationToken token = default) diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs index ed438d69..c82cc72c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs @@ -3,12 +3,13 @@ using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hutao.GachaLog; +using System.Runtime.CompilerServices; namespace Snap.Hutao.Web; [SuppressMessage("", "SA1201")] [SuppressMessage("", "SA1203")] -internal static class HutaoEndpoints +internal static partial class HutaoEndpoints { #region HomaAPI @@ -107,32 +108,77 @@ internal static class HutaoEndpoints #region Infrasturcture public static string Enka(in PlayerUid uid) { - return $"{ApiSnapGenshinEnka}/{uid}"; + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/enka/{uid}", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/enka/{uid}", + _ => $"{ApiSnapGenshin}/enka/{uid}", + }; } public static string EnkaPlayerInfo(in PlayerUid uid) { - return $"{ApiSnapGenshinEnka}/{uid}/info"; + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/enka/{uid}/info", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/enka/{uid}/info", + _ => $"{ApiSnapGenshin}/enka/{uid}/info", + }; } - public const string Ip = $"{ApiSnapGenshin}/ip"; + public static string Ip() + { + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/ip", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/ip", + _ => $"{ApiSnapGenshin}/ip", + }; + } + + #region Feature + public static string Feature(string name) + { + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/client/{name}.json", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/client/{name}.json", + _ => $"{ApiSnapGenshin}/client/{name}.json", + }; + } + #endregion #region Metadata public static string Metadata(string locale, string fileName) { - return $"{ApiSnapGenshinMetadata}/Genshin/{locale}/{fileName}"; + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/metadata/Genshin/{locale}/{fileName}", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/metadata/Genshin/{locale}/{fileName}", + _ => $"{ApiSnapGenshin}/metadata/Genshin/{locale}/{fileName}", + }; } #endregion #region Patch - public const string PatchYaeAchievement = $"{ApiSnapGenshinPatch}/yae"; - public const string PatchSnapHutao = $"{ApiSnapGenshinPatch}/hutao"; - - public static string PatchAlphaSnapHutao(bool isCN) + public static string PatchYaeAchievement() { - return isCN - ? $"{ApiAlphaSnapGenshin}/cn/patch/hutao" - : $"{ApiAlphaSnapGenshin}/global/patch/hutao"; + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/patch/yae", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/patch/yae", + _ => $"{ApiAlphaSnapGenshin}/patch/yae", + }; + } + + public static string PatchSnapHutao() + { + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/patch/hutao", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/patch/hutao", + _ => $"{ApiAlphaSnapGenshin}/patch/hutao", + }; } #endregion @@ -143,34 +189,95 @@ internal static class HutaoEndpoints public static string StaticRaw(string category, string fileName) { - return $"{ApiSnapGenshinStaticRaw}/{category}/{fileName}"; + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/static/raw/{category}/{fileName}", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/static/raw/{category}/{fileName}", + _ => $"{ApiSnapGenshin}/static/raw/{category}/{fileName}", + }; } public static string StaticZip(string fileName) { - return $"{ApiSnapGenshinStaticZip}/{fileName}.zip"; + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/static/zip/{fileName}.zip", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/static/zip/{fileName}.zip", + _ => $"{ApiSnapGenshin}/static/zip/{fileName}.zip", + }; } - public const string StaticSize = $"{ApiSnapGenshin}/static/size"; + public static string StaticSize() + { + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/static/size", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/static/size", + _ => $"{ApiSnapGenshin}/static/size", + }; + } #endregion #region Wallpaper - public const string WallpaperBing = $"{ApiSnapGenshin}/wallpaper/bing"; + public static string WallpaperBing() + { + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/wallpaper/bing", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/wallpaper/bing", + _ => $"{ApiSnapGenshin}/wallpaper/bing", + }; + } - public const string WallpaperGenshinLauncher = $"{ApiSnapGenshin}/wallpaper/hoyoplay"; + public static string WallpaperGenshinLauncher() + { + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/wallpaper/genshinlauncher", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/wallpaper/genshinlauncher", + _ => $"{ApiSnapGenshin}/wallpaper/genshinlauncher", + }; + } - public const string WallpaperToday = $"{ApiSnapGenshin}/wallpaper/today"; + public static string WallpaperToday() + { + return Kind switch + { + ApiKind.AlphaCN => $"{ApiAlphaSnapGenshin}/cn/wallpaper/today", + ApiKind.AlphaOS => $"{ApiAlphaSnapGenshin}/global/wallpaper/today", + _ => $"{ApiSnapGenshin}/wallpaper/today", + }; + } #endregion #endregion + private const string ApiSnapGenshin = "https://api.snapgenshin.com"; + private const string HomaSnapGenshin = "https://homa.snapgenshin.com"; +} + +internal static partial class HutaoEndpoints +{ + private enum ApiKind + { + AlphaCN, + AlphaOS, + Formal, + } + + private static ApiKind Kind + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if IS_ALPHA_BUILD + return Core.Setting.LocalSetting.Get(Core.Setting.SettingKeys.AlphaBuildUseCNPatchEndpoint, false) ? ApiKind.AlphaCN : ApiKind.AlphaOS; +#else + return ApiKind.Formal; +#endif + } + } + private const string ApiAlphaSnapGenshin = "https://api-alpha.snapgenshin.cn"; - private const string ApiSnapGenshin = "https://api.snapgenshin.com"; - private const string ApiSnapGenshinMetadata = $"{ApiSnapGenshin}/metadata"; - private const string ApiSnapGenshinPatch = $"{ApiSnapGenshin}/patch"; - private const string ApiSnapGenshinStaticRaw = $"{ApiSnapGenshin}/static/raw"; - private const string ApiSnapGenshinStaticZip = $"{ApiSnapGenshin}/static/zip"; - private const string ApiSnapGenshinEnka = $"{ApiSnapGenshin}/enka"; - private const string HomaSnapGenshin = "https://homa.snapgenshin.com"; } \ No newline at end of file