From 0a454eebb792e53232f401282a1a94a3f3b85df0 Mon Sep 17 00:00:00 2001 From: Scighost Date: Mon, 25 Nov 2024 22:58:27 +0800 Subject: [PATCH] hoyoplay service and better gameid --- src/Starward.Core/HoYoPlay/GameId.cs | 6 +- src/Starward.Core/HoYoPlay/LauncherId.cs | 5 + .../Features/Database/DatabaseService.cs | 2 +- .../Features/HoYoPlay/HoYoPlayService.cs | 350 ++++++++++++++++++ 4 files changed, 359 insertions(+), 4 deletions(-) create mode 100644 src/Starward/Features/HoYoPlay/HoYoPlayService.cs diff --git a/src/Starward.Core/HoYoPlay/GameId.cs b/src/Starward.Core/HoYoPlay/GameId.cs index 509c4444c..aa1b10f6d 100644 --- a/src/Starward.Core/HoYoPlay/GameId.cs +++ b/src/Starward.Core/HoYoPlay/GameId.cs @@ -30,13 +30,13 @@ public class GameId : IEquatable GameBiz.bh3_asia => new GameId { Id = "wkE5P5WsIf", GameBiz = "bh3_global" }, GameBiz.hk4e_cn => new GameId { Id = "1Z8W5NHUQb", GameBiz = "hk4e_cn" }, GameBiz.hk4e_global => new GameId { Id = "gopR6Cufr3", GameBiz = "hk4e_global" }, - GameBiz.hk4e_bilibili => new GameId { Id = "T2S0Gz4Dr2", GameBiz = "hk4e_cn" }, + GameBiz.hk4e_bilibili => new GameId { Id = "T2S0Gz4Dr2", GameBiz = "hk4e_bilibili" }, GameBiz.hkrpg_cn => new GameId { Id = "64kMb5iAWu", GameBiz = "hkrpg_cn" }, GameBiz.hkrpg_global => new GameId { Id = "4ziysqXOQ8", GameBiz = "hkrpg_global" }, - GameBiz.hkrpg_bilibili => new GameId { Id = "EdtUqXfCHh", GameBiz = "hkrpg_cn" }, + GameBiz.hkrpg_bilibili => new GameId { Id = "EdtUqXfCHh", GameBiz = "hkrpg_bilibili" }, GameBiz.nap_cn => new GameId { Id = "x6znKlJ0xK", GameBiz = "nap_cn" }, GameBiz.nap_global => new GameId { Id = "U5hbdsT9W7", GameBiz = "nap_global" }, - GameBiz.nap_bilibili => new GameId { Id = "HXAFlmYa17", GameBiz = "nap_cn" }, + GameBiz.nap_bilibili => new GameId { Id = "HXAFlmYa17", GameBiz = "nap_bilibili" }, _ => null, }; } diff --git a/src/Starward.Core/HoYoPlay/LauncherId.cs b/src/Starward.Core/HoYoPlay/LauncherId.cs index c8c4b377f..c35573430 100644 --- a/src/Starward.Core/HoYoPlay/LauncherId.cs +++ b/src/Starward.Core/HoYoPlay/LauncherId.cs @@ -45,11 +45,16 @@ public static bool IsBilibili(string launcherId) GameBiz.hk4e_bilibili => BilibiliGenshin, GameBiz.hkrpg_bilibili => BilibiliStarRail, GameBiz.nap_bilibili => BilibiliZZZ, + string value when value.EndsWith("_cn") => ChinaOfficial, + string value when value.EndsWith("_global") => GlobalOfficial, _ => null, }; } + public static string? FromGameId(GameId gameId) => FromGameBiz(gameId.GameBiz); + + public static List<(GameBiz GameBiz, string LauncherId)> GetBilibiliLaunchers() { return new List<(GameBiz, string)> diff --git a/src/Starward/Features/Database/DatabaseService.cs b/src/Starward/Features/Database/DatabaseService.cs index 7b32679e9..9ebe84c49 100644 --- a/src/Starward/Features/Database/DatabaseService.cs +++ b/src/Starward/Features/Database/DatabaseService.cs @@ -20,7 +20,7 @@ internal static class DatabaseService private static string _connectionString; - private static Lock _lock; + private static Lock _lock = new(); static DatabaseService() diff --git a/src/Starward/Features/HoYoPlay/HoYoPlayService.cs b/src/Starward/Features/HoYoPlay/HoYoPlayService.cs new file mode 100644 index 000000000..d489447a2 --- /dev/null +++ b/src/Starward/Features/HoYoPlay/HoYoPlayService.cs @@ -0,0 +1,350 @@ +using Microsoft.Extensions.Logging; +using Starward.Core; +using Starward.Core.HoYoPlay; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Starward.Features.HoYoPlay; + +public class HoYoPlayService +{ + + + private readonly ILogger _logger; + + private readonly HoYoPlayClient _client; + + private readonly HttpClient _httpClient; + + private readonly System.Timers.Timer _timer; + + + public HoYoPlayService(ILogger logger, HoYoPlayClient client, HttpClient httpClient) + { + _logger = logger; + _client = client; + _httpClient = httpClient; + _timer = new System.Timers.Timer + { + AutoReset = true, + Enabled = true, + Interval = TimeSpan.FromMinutes(10).TotalMilliseconds, + }; + _timer.Elapsed += async (_, _) => await PrepareDataAsync(); + } + + + + + private ConcurrentDictionary _gameInfo = new(); + + + private ConcurrentDictionary _gameBackground = new(); + + + private ConcurrentDictionary _gameContent = new(); + + + private ConcurrentDictionary _gamePackage = new(); + + + private ConcurrentDictionary _gameConfig = new(); + + + + public void ClearCache() + { + _gameInfo.Clear(); + _gameBackground.Clear(); + _gameContent.Clear(); + _gamePackage.Clear(); + } + + + + + public async Task PrepareDataAsync() + { + try + { + ClearCache(); + string lang = CultureInfo.CurrentUICulture.Name; + List tasks = []; + tasks.Add(PrepareDataForServerAsync(LauncherId.ChinaOfficial, lang)); + tasks.Add(PrepareDataForServerAsync(LauncherId.GlobalOfficial, lang)); + tasks.Add(PrepareDataForBilibiliServerAsync(lang)); + await Task.WhenAll(tasks); + await PrepareImagesAsync(); + } + catch (Exception ex) + { + _logger.LogError(ex, nameof(PrepareDataAsync)); + } + } + + + + private async Task PrepareDataForServerAsync(string launcherId, string? language = null) + { + try + { + language ??= CultureInfo.CurrentUICulture.Name; + List infos = await _client.GetGameInfoAsync(launcherId, language); + foreach (GameInfo item in infos) + { + _gameInfo[item] = item; + } + List backgrounds = await _client.GetGameBackgroundAsync(launcherId, language); + foreach (GameBackgroundInfo item in backgrounds) + { + _gameBackground[item.GameId] = item; + } + foreach (var item in infos) + { + GameContent content = await _client.GetGameContentAsync(launcherId, language, item); + _gameContent[content.GameId] = content; + } + List packages = await _client.GetGamePackageAsync(launcherId, language); + foreach (GamePackage item in packages) + { + _gamePackage[item.GameId] = item; + } + List configs = await _client.GetGameConfigAsync(launcherId, language); + foreach (GameConfig item in configs) + { + _gameConfig[item.GameId] = item; + } + } + catch (Exception ex) + { + _logger.LogError(ex, nameof(PrepareDataForServerAsync)); + } + } + + + + private async Task PrepareDataForBilibiliServerAsync(string? language = null) + { + try + { + language ??= CultureInfo.CurrentUICulture.Name; + foreach ((GameBiz biz, string launcherId) in LauncherId.GetBilibiliLaunchers()) + { + List infos = await _client.GetGameInfoAsync(launcherId, language); + foreach (GameInfo item in infos) + { + _gameInfo[item] = item; + } + List backgrounds = await _client.GetGameBackgroundAsync(launcherId, language); + foreach (GameBackgroundInfo item in backgrounds) + { + _gameBackground[item.GameId] = item; + } + foreach (GameInfo item in infos) + { + GameContent content = await _client.GetGameContentAsync(launcherId, language, item); + _gameContent[item] = content; + } + List packages = await _client.GetGamePackageAsync(launcherId, language); + foreach (GamePackage item in packages) + { + _gamePackage[item.GameId] = item; + } + List configs = await _client.GetGameConfigAsync(launcherId, language); + foreach (GameConfig item in configs) + { + _gameConfig[item.GameId] = item; + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, nameof(PrepareDataForBilibiliServerAsync)); + } + } + + + + private async Task PrepareImagesAsync() + { + try + { + List bizs = GetSelectedGameBizs(); + List urls = []; + foreach (GameBiz biz in bizs) + { + if (GameId.FromGameBiz(biz) is GameId gameId) + { + if (_gameInfo.TryGetValue(gameId, out GameInfo? info)) + { + urls.Add(info.Display.Background.Url); + } + if (_gameBackground.TryGetValue(gameId, out GameBackgroundInfo? background)) + { + urls.AddRange(background.Backgrounds.Select(x => x.Background.Url)); + } + } + } + string bg = Path.Combine(AppConfig.UserDataFolder, "bg"); + Directory.CreateDirectory(bg); + await Parallel.ForEachAsync(urls, async (url, _) => + { + try + { + if (string.IsNullOrWhiteSpace(url)) + { + return; + } + string name = Path.GetFileName(url); + string path = Path.Combine(bg, name); + if (!File.Exists(path)) + { + byte[] bytes = await _httpClient.GetByteArrayAsync(url); + await File.WriteAllBytesAsync(path, bytes); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Download image: {url}", url); + } + }); + } + catch (Exception ex) + { + _logger.LogError(ex, nameof(PrepareImagesAsync)); + } + } + + + + private static List GetSelectedGameBizs() + { + List bizs = new(); + foreach (string str in AppConfig.SelectedGameBizs?.Split(',') ?? []) + { + if (GameBiz.TryParse(str, out GameBiz biz)) + { + bizs.Add(biz); + } + } + return bizs; + } + + + + + public async Task GetGameInfoAsync(GameId gameId) + { + if (!_gameInfo.TryGetValue(gameId, out GameInfo? info)) + { + string lang = CultureInfo.CurrentUICulture.Name; + var list = await _client.GetGameInfoAsync(LauncherId.FromGameId(gameId)!, lang); + foreach (var item in list) + { + _gameInfo[item] = item; + } + info = list.First(x => x == gameId); + } + return info; + } + + + + public async Task GetGameBackgroundAsync(GameId gameId) + { + if (!_gameBackground.TryGetValue(gameId, out GameBackgroundInfo? background)) + { + string lang = CultureInfo.CurrentUICulture.Name; + var list = await _client.GetGameBackgroundAsync(LauncherId.FromGameId(gameId)!, lang); + foreach (var item in list) + { + _gameBackground[item.GameId] = item; + } + background = list.First(x => x.GameId == gameId); + } + return background; + } + + + + public async Task GetGameContentAsync(GameId gameId) + { + if (!_gameContent.TryGetValue(gameId, out GameContent? content)) + { + string lang = CultureInfo.CurrentUICulture.Name; + content = await _client.GetGameContentAsync(LauncherId.FromGameId(gameId)!, lang, gameId); + _gameContent[gameId] = content; + } + return content; + } + + + + public async Task GetGamePackageAsync(GameId gameId) + { + if (!_gamePackage.TryGetValue(gameId, out GamePackage? package)) + { + string lang = CultureInfo.CurrentUICulture.Name; + var list = await _client.GetGamePackageAsync(LauncherId.FromGameId(gameId)!, lang); + foreach (var item in list) + { + _gamePackage[item.GameId] = item; + } + package = list.First(x => x.GameId == gameId); + } + return package; + } + + + + public async Task GetGameConfigAsync(GameId gameId) + { + if (!_gameConfig.TryGetValue(gameId, out GameConfig? config)) + { + string lang = CultureInfo.CurrentUICulture.Name; + var list = await _client.GetGameConfigAsync(LauncherId.FromGameId(gameId)!, lang); + foreach (var item in list) + { + _gameConfig[item.GameId] = item; + } + config = list.FirstOrDefault(x => x.GameId == gameId); + } + return config; + } + + + + public async Task> GetGameDeprecatedFilesAsync(GameId gameId) + { + var launcherId = LauncherId.FromGameId(gameId); + if (launcherId is not null) + { + var fileConfig = await _client.GetGameDeprecatedFileConfigAsync(launcherId, "en-us", gameId); + if (fileConfig != null) + { + return fileConfig.DeprecatedFiles; + } + } + return []; + } + + + + public async Task GetGameChannelSDKAsync(GameId gameId) + { + var launcherId = LauncherId.FromGameId(gameId); + if (launcherId is not null) + { + return await _client.GetGameChannelSDKAsync(launcherId, "en-us", gameId); + } + return null; + } + + +} \ No newline at end of file