diff --git a/TaikoLocalServer/Common/Utils/DanOdaiDataManager.cs b/TaikoLocalServer/Common/Utils/DanOdaiDataManager.cs deleted file mode 100644 index 111b2d1..0000000 --- a/TaikoLocalServer/Common/Utils/DanOdaiDataManager.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Immutable; -using System.Text.Json; -using SharedProject.Models; -using Swan.Mapping; - -namespace TaikoLocalServer.Common.Utils; - -public class DanOdaiDataManager -{ - public ImmutableDictionary OdaiDataList { get; } - - static DanOdaiDataManager() {} - - private DanOdaiDataManager() - { - var dataPath = PathHelper.GetDataPath(); - var filePath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME); - var jsonString = File.ReadAllText(filePath); - - var result = JsonSerializer.Deserialize>(jsonString); - - if (result is null) - { - throw new ApplicationException("Cannot parse dan data json!"); - } - - OdaiDataList = result.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData); - } - private GetDanOdaiResponse.OdaiData ToResponseOdaiData(DanData data) - { - var responseOdaiData = new GetDanOdaiResponse.OdaiData - { - DanId = data.DanId, - Title = data.Title, - VerupNo = data.VerupNo - }; - - var odaiSongs = data.OdaiSongList.Select(song => song.CopyPropertiesToNew()); - responseOdaiData.AryOdaiSongs.AddRange(odaiSongs); - - var odaiBorders = data.OdaiBorderList.Select(border => border.CopyPropertiesToNew()); - responseOdaiData.AryOdaiBorders.AddRange(odaiBorders); - - return responseOdaiData; - } - - public static DanOdaiDataManager Instance { get; } = new(); -} \ No newline at end of file diff --git a/TaikoLocalServer/Common/Utils/MusicAttributeManager.cs b/TaikoLocalServer/Common/Utils/MusicAttributeManager.cs deleted file mode 100644 index a7dadda..0000000 --- a/TaikoLocalServer/Common/Utils/MusicAttributeManager.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Text.Json; - -namespace TaikoLocalServer.Common.Utils; - -public class MusicAttributeManager -{ - public readonly Dictionary MusicAttributes; - - static MusicAttributeManager() - { - } - - private MusicAttributeManager() - { - var dataPath = PathHelper.GetDataPath(); - var filePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_FILE_NAME); - var jsonString = File.ReadAllText(filePath); - - var result = JsonSerializer.Deserialize(jsonString); - if (result is null) - { - throw new ApplicationException("Cannot parse music attribute json!"); - } - - MusicAttributes = result.MusicAttributeEntries.ToDictionary(attribute => attribute.MusicId); - - Musics = MusicAttributes.Select(pair => pair.Key) - .ToList(); - Musics.Sort(); - - MusicsWithUra = MusicAttributes.Where(attribute => attribute.Value.HasUra) - .Select(pair => pair.Key) - .ToList(); - MusicsWithUra.Sort(); - } - - public static MusicAttributeManager Instance { get; } = new(); - - public readonly List Musics; - - public readonly List MusicsWithUra; -} \ No newline at end of file diff --git a/TaikoLocalServer/Common/Utils/SongIntroductionDataManager.cs b/TaikoLocalServer/Common/Utils/SongIntroductionDataManager.cs deleted file mode 100644 index abbb329..0000000 --- a/TaikoLocalServer/Common/Utils/SongIntroductionDataManager.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Immutable; -using System.Text.Json; -using SharedProject.Models; - -namespace TaikoLocalServer.Common.Utils; - -public class SongIntroductionDataManager -{ - public ImmutableDictionary IntroDataList { get; } - - static SongIntroductionDataManager() { } - - private SongIntroductionDataManager() - { - var dataPath = PathHelper.GetDataPath(); - var filePath = Path.Combine(dataPath, Constants.INTRO_DATA_FILE_NAME); - var jsonString = File.ReadAllText(filePath); - - var result = JsonSerializer.Deserialize>(jsonString); - - if (result is null) - { - throw new ApplicationException("Cannot parse intro data json!"); - } - - IntroDataList = result.ToImmutableDictionary(data => data.SetId, ToResponseIntroData); - } - private GetSongIntroductionResponse.SongIntroductionData ToResponseIntroData(SongIntroductionData data) - { - var responseOdaiData = new GetSongIntroductionResponse.SongIntroductionData - { - SetId = data.SetId, - VerupNo = data.VerupNo, - MainSongNo = data.MainSongNo, - SubSongNoes = data.SubSongNo - }; - - return responseOdaiData; - } - - public static SongIntroductionDataManager Instance { get; } = new(); -} \ No newline at end of file diff --git a/TaikoLocalServer/Controllers/Game/GetDanOdaiController.cs b/TaikoLocalServer/Controllers/Game/GetDanOdaiController.cs index 6fd7915..beae85c 100644 --- a/TaikoLocalServer/Controllers/Game/GetDanOdaiController.cs +++ b/TaikoLocalServer/Controllers/Game/GetDanOdaiController.cs @@ -1,9 +1,18 @@ -namespace TaikoLocalServer.Controllers.Game; +using TaikoLocalServer.Services.Interfaces; + +namespace TaikoLocalServer.Controllers.Game; [Route("/v12r03/chassis/getdanodai.php")] [ApiController] public class GetDanOdaiController : BaseController { + private readonly IGameDataService gameDataService; + + public GetDanOdaiController(IGameDataService gameDataService) + { + this.gameDataService = gameDataService; + } + [HttpPost] [Produces("application/protobuf")] public IActionResult GetDanOdai([FromBody] GetDanOdaiRequest request) @@ -19,11 +28,10 @@ public class GetDanOdaiController : BaseController { return Ok(response); } - - var manager = DanOdaiDataManager.Instance; + foreach (var danId in request.DanIds) { - manager.OdaiDataList.TryGetValue(danId, out var odaiData); + gameDataService.GetDanDataDictionary().TryGetValue(danId, out var odaiData); if (odaiData is null) { Logger.LogWarning("Requested dan id {Id} does not exist!", danId); diff --git a/TaikoLocalServer/Controllers/Game/GetSongIntroductionController.cs b/TaikoLocalServer/Controllers/Game/GetSongIntroductionController.cs index 9353c8a..b17c4ee 100644 --- a/TaikoLocalServer/Controllers/Game/GetSongIntroductionController.cs +++ b/TaikoLocalServer/Controllers/Game/GetSongIntroductionController.cs @@ -1,9 +1,18 @@ -namespace TaikoLocalServer.Controllers.Game; +using TaikoLocalServer.Services.Interfaces; + +namespace TaikoLocalServer.Controllers.Game; [Route("/v12r03/chassis/getsongintroduction.php")] [ApiController] public class GetSongIntroductionController : BaseController { + private readonly IGameDataService gameDataService; + + public GetSongIntroductionController(IGameDataService gameDataService) + { + this.gameDataService = gameDataService; + } + [HttpPost] [Produces("application/protobuf")] public IActionResult GetSongIntroduction([FromBody] GetSongIntroductionRequest request) @@ -14,11 +23,10 @@ public class GetSongIntroductionController : BaseController { + private readonly IGameDataService gameDataService; + + public InitialDataCheckController(IGameDataService gameDataService) + { + this.gameDataService = gameDataService; + } + [HttpPost] [Produces("application/protobuf")] public IActionResult InitialDataCheck([FromBody] InitialdatacheckRequest request) { Logger.LogInformation("Initial data check request: {Request}", request.Stringify()); - var musicAttributeManager = MusicAttributeManager.Instance; - var enabledArray = new byte[Constants.MUSIC_FLAG_ARRAY_SIZE]; var bitSet = new BitArray(Constants.MUSIC_ID_MAX); - foreach (var music in musicAttributeManager.Musics) + foreach (var music in gameDataService.GetMusicList()) { bitSet.Set((int)music, true); } @@ -31,10 +37,9 @@ public class InitialDataCheckController : BaseController(); - for (var setId = 1; setId <= manager.IntroDataList.Count; setId++) + for (var setId = 1; setId <= gameDataService.GetSongIntroDictionary().Count; setId++) { introData.Add(new InitialdatacheckResponse.InformationData { diff --git a/TaikoLocalServer/Controllers/Game/SelfBestController.cs b/TaikoLocalServer/Controllers/Game/SelfBestController.cs index 6c514d8..8b1e259 100644 --- a/TaikoLocalServer/Controllers/Game/SelfBestController.cs +++ b/TaikoLocalServer/Controllers/Game/SelfBestController.cs @@ -8,10 +8,13 @@ namespace TaikoLocalServer.Controllers.Game; public class SelfBestController : BaseController { private readonly ISongBestDatumService songBestDatumService; + + private readonly IGameDataService gameDataService; - public SelfBestController(ISongBestDatumService songBestDatumService) + public SelfBestController(ISongBestDatumService songBestDatumService, IGameDataService gameDataService) { this.songBestDatumService = songBestDatumService; + this.gameDataService = gameDataService; } [HttpPost] @@ -25,8 +28,6 @@ public class SelfBestController : BaseController Result = 1, Level = request.Level }; - - var manager = MusicAttributeManager.Instance; var requestDifficulty = (Difficulty)request.Level; requestDifficulty.Throw().IfOutOfRange(); @@ -38,7 +39,7 @@ public class SelfBestController : BaseController .ToList(); foreach (var songNo in request.ArySongNoes) { - if (!manager.MusicAttributes.ContainsKey(songNo)) + if (!gameDataService.GetMusicAttributes().ContainsKey(songNo)) { Logger.LogWarning("Music no {No} is missing!", songNo); continue; diff --git a/TaikoLocalServer/Controllers/Game/UserDataController.cs b/TaikoLocalServer/Controllers/Game/UserDataController.cs index 554a51c..0c007a6 100644 --- a/TaikoLocalServer/Controllers/Game/UserDataController.cs +++ b/TaikoLocalServer/Controllers/Game/UserDataController.cs @@ -12,11 +12,14 @@ public class UserDataController : BaseController private readonly IUserDatumService userDatumService; private readonly ISongPlayDatumService songPlayDatumService; + + private readonly IGameDataService gameDataService; - public UserDataController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService) + public UserDataController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService, IGameDataService gameDataService) { this.userDatumService = userDatumService; this.songPlayDatumService = songPlayDatumService; + this.gameDataService = gameDataService; } [HttpPost] @@ -25,13 +28,11 @@ public class UserDataController : BaseController { Logger.LogInformation("UserData request : {Request}", request.Stringify()); - var musicAttributeManager = MusicAttributeManager.Instance; - var releaseSongArray = - FlagCalculator.GetBitArrayFromIds(musicAttributeManager.Musics, Constants.MUSIC_ID_MAX, Logger); + FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicList(), Constants.MUSIC_ID_MAX, Logger); var uraSongArray = - FlagCalculator.GetBitArrayFromIds(musicAttributeManager.MusicsWithUra, Constants.MUSIC_ID_MAX, Logger); + FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicWithUraList(), Constants.MUSIC_ID_MAX, Logger); var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid); diff --git a/TaikoLocalServer/Program.cs b/TaikoLocalServer/Program.cs index a0cb9bc..e2bef2b 100644 --- a/TaikoLocalServer/Program.cs +++ b/TaikoLocalServer/Program.cs @@ -6,6 +6,7 @@ using TaikoLocalServer.Services; using TaikoLocalServer.Services.Extentions; using TaikoLocalServer.Services.Interfaces; using TaikoLocalServer.Settings; +using Throw; var builder = WebApplication.CreateBuilder(args); // Manually enable tls 1.0 @@ -19,6 +20,7 @@ builder.WebHost.UseKestrel(kestrelOptions => // Add services to the container. builder.Services.AddOptions(); +builder.Services.AddSingleton(); builder.Services.Configure(builder.Configuration.GetSection(nameof(UrlSettings))); builder.Services.AddControllers().AddProtoBufNet(); builder.Services.AddDbContext(option => @@ -58,6 +60,10 @@ using (var scope = app.Services.CreateScope()) db.Database.Migrate(); } +var gameDataService = app.Services.GetService(); +gameDataService.ThrowIfNull(); +await gameDataService.InitializeAsync(); + // For reverse proxy app.UseForwardedHeaders(new ForwardedHeadersOptions { diff --git a/TaikoLocalServer/Services/GameDataService.cs b/TaikoLocalServer/Services/GameDataService.cs new file mode 100644 index 0000000..f7fd748 --- /dev/null +++ b/TaikoLocalServer/Services/GameDataService.cs @@ -0,0 +1,130 @@ +using System.Collections.Immutable; +using System.Text.Json; +using SharedProject.Models; +using Swan.Mapping; +using TaikoLocalServer.Services.Interfaces; +using Throw; + +namespace TaikoLocalServer.Services; + +public class GameDataService : IGameDataService +{ + private ImmutableDictionary danDataDictionary = + ImmutableDictionary.Empty; + + private ImmutableDictionary introDataDictionary = + ImmutableDictionary.Empty; + + private ImmutableDictionary musicAttributes = + ImmutableDictionary.Empty; + + private List musics = new(); + + private List musicsWithUra = new(); + + public List GetMusicList() + { + return musics; + } + + public List GetMusicWithUraList() + { + return musicsWithUra; + } + + public ImmutableDictionary GetMusicAttributes() + { + return musicAttributes; + } + + public ImmutableDictionary GetDanDataDictionary() + { + return danDataDictionary; + } + + public ImmutableDictionary GetSongIntroDictionary() + { + return introDataDictionary; + } + + public async Task InitializeAsync() + { + var dataPath = PathHelper.GetDataPath(); + var musicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_FILE_NAME); + var danDataPath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME); + var songIntroDataPath = Path.Combine(dataPath, Constants.INTRO_DATA_FILE_NAME); + + await using var musicAttributeFile = File.OpenRead(musicAttributePath); + await using var danDataFile = File.OpenRead(danDataPath); + await using var songIntroDataFile = File.OpenRead(songIntroDataPath); + + var attributesData = await JsonSerializer.DeserializeAsync(musicAttributeFile); + var danData = await JsonSerializer.DeserializeAsync>(danDataFile); + var introData = await JsonSerializer.DeserializeAsync>(songIntroDataFile); + + InitializeMusicAttributes(attributesData); + + InitializeDanData(danData); + + InitializeIntroData(introData); + } + + private void InitializeIntroData(List? introData) + { + introData.ThrowIfNull("Shouldn't happen!"); + introDataDictionary = introData.ToImmutableDictionary(data => data.SetId, ToResponseIntroData); + } + + private void InitializeDanData(List? danData) + { + danData.ThrowIfNull("Shouldn't happen!"); + danDataDictionary = danData.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData); + } + + private void InitializeMusicAttributes(MusicAttributes? attributesData) + { + attributesData.ThrowIfNull("Shouldn't happen!"); + + musicAttributes = attributesData.MusicAttributeEntries.ToImmutableDictionary(attribute => attribute.MusicId); + + musics = musicAttributes.Select(pair => pair.Key) + .ToList(); + musics.Sort(); + + musicsWithUra = musicAttributes.Where(attribute => attribute.Value.HasUra) + .Select(pair => pair.Key) + .ToList(); + musicsWithUra.Sort(); + } + + private static GetDanOdaiResponse.OdaiData ToResponseOdaiData(DanData data) + { + var responseOdaiData = new GetDanOdaiResponse.OdaiData + { + DanId = data.DanId, + Title = data.Title, + VerupNo = data.VerupNo + }; + + var odaiSongs = data.OdaiSongList.Select(song => song.CopyPropertiesToNew()); + responseOdaiData.AryOdaiSongs.AddRange(odaiSongs); + + var odaiBorders = data.OdaiBorderList.Select(border => border.CopyPropertiesToNew()); + responseOdaiData.AryOdaiBorders.AddRange(odaiBorders); + + return responseOdaiData; + } + + private static GetSongIntroductionResponse.SongIntroductionData ToResponseIntroData(SongIntroductionData data) + { + var responseOdaiData = new GetSongIntroductionResponse.SongIntroductionData + { + SetId = data.SetId, + VerupNo = data.VerupNo, + MainSongNo = data.MainSongNo, + SubSongNoes = data.SubSongNo + }; + + return responseOdaiData; + } +} \ No newline at end of file diff --git a/TaikoLocalServer/Services/Interfaces/IGameDataService.cs b/TaikoLocalServer/Services/Interfaces/IGameDataService.cs new file mode 100644 index 0000000..f633c33 --- /dev/null +++ b/TaikoLocalServer/Services/Interfaces/IGameDataService.cs @@ -0,0 +1,18 @@ +using System.Collections.Immutable; + +namespace TaikoLocalServer.Services.Interfaces; + +public interface IGameDataService +{ + public Task InitializeAsync(); + + public List GetMusicList(); + + public List GetMusicWithUraList(); + + public ImmutableDictionary GetMusicAttributes(); + + public ImmutableDictionary GetDanDataDictionary(); + + public ImmutableDictionary GetSongIntroDictionary(); +} \ No newline at end of file diff --git a/TaikoLocalServer/TaikoLocalServer.csproj b/TaikoLocalServer/TaikoLocalServer.csproj index fd477fb..76728c3 100644 --- a/TaikoLocalServer/TaikoLocalServer.csproj +++ b/TaikoLocalServer/TaikoLocalServer.csproj @@ -35,10 +35,13 @@ - + PreserveNewest - + + PreserveNewest + + PreserveNewest diff --git a/TaikoLocalServer/wwwroot/dan_data.json b/TaikoLocalServer/wwwroot/data/dan_data.json similarity index 100% rename from TaikoLocalServer/wwwroot/dan_data.json rename to TaikoLocalServer/wwwroot/data/dan_data.json diff --git a/TaikoLocalServer/wwwroot/intro_data.json b/TaikoLocalServer/wwwroot/data/intro_data.json similarity index 100% rename from TaikoLocalServer/wwwroot/intro_data.json rename to TaikoLocalServer/wwwroot/data/intro_data.json