1
0
mirror of synced 2025-01-31 04:13:50 +01:00

Major refactors including frontend requests refactors, authentication refactors and request compressions

This commit is contained in:
S-Sebb 2024-05-25 18:12:30 +01:00
parent 75e90d9aa2
commit 2069681390
57 changed files with 804 additions and 723 deletions

View File

@ -0,0 +1,25 @@
namespace SharedProject.Models;
public class Costume
{
public uint CostumeId { get; set; }
public string CostumeType { get; init; } = string.Empty;
public string CostumeName { get; init; } = string.Empty;
public override bool Equals(object? obj)
{
if (obj is Costume costume)
{
return costume.CostumeName.Equals(CostumeName) && costume.CostumeType.Equals(CostumeType);
}
return false;
}
public override int GetHashCode()
{
return CostumeName.GetHashCode();
}
}

View File

@ -1,4 +1,6 @@
namespace TaikoWebUI.Shared.Models;
using SharedProject.Enums;
namespace SharedProject.Models;
public class MusicDetail
{

View File

@ -1,4 +1,4 @@
namespace TaikoWebUI.Shared.Models;
namespace SharedProject.Models;
public class Title
{

View File

@ -2,27 +2,27 @@
public static class Constants
{
public const string DATE_TIME_FORMAT = "yyyyMMddHHmmss";
public const string DateTimeFormat = "yyyyMMddHHmmss";
public const int MUSIC_ID_MAX = 1600;
public const int MusicIdMax = 1600;
public const int MUSIC_ID_MAX_EXPANDED = 9000;
public const int MusicIdMaxExpanded = 9000;
public const string DEFAULT_DB_NAME = "taiko.db3";
public const string DefaultDbName = "taiko.db3";
public const string MUSIC_INFO_BASE_NAME = "musicinfo";
public const string WORDLIST_BASE_NAME = "wordlist";
public const string MUSIC_ORDER_BASE_NAME = "music_order";
public const string DON_COS_REWARD_BASE_NAME = "don_cos_reward";
public const string SHOUGOU_BASE_NAME = "shougou";
public const string NEIRO_BASE_NAME = "neiro";
public const string MusicInfoBaseName = "musicinfo";
public const string WordlistBaseName = "wordlist";
public const string MusicOrderBaseName = "music_order";
public const string DonCosRewardBaseName = "don_cos_reward";
public const string ShougouBaseName = "shougou";
public const string NeiroBaseName = "neiro";
public const uint DAN_VERUP_MASTER_TYPE = 101;
public const uint GAIDEN_VERUP_MASTER_TYPE = 102;
public const uint FOLDER_VERUP_MASTER_TYPE = 103;
public const uint INTRO_VERUP_MASTER_TYPE = 105;
public const uint DanVerupMasterType = 101;
public const uint GaidenVerupMasterType = 102;
public const uint FolderVerupMasterType = 103;
public const uint IntroVerupMasterType = 105;
public const uint FUNCTION_ID_DANI_FOLDER_AVAILABLE = 1;
public const uint FUNCTION_ID_DANI_AVAILABLE = 2;
public const uint FUNCTION_ID_AI_BATTLE_AVAILABLE = 3;
public const uint FunctionIdDaniFolderAvailable = 1;
public const uint FunctionIdDaniAvailable = 2;
public const uint FunctionIdAiBattleAvailable = 3;
}

View File

@ -1,6 +1,4 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using SharedProject.Models.Requests;
using TaikoLocalServer.Filters;

View File

@ -0,0 +1,93 @@
using Microsoft.Extensions.Options;
using TaikoLocalServer.Filters;
using TaikoLocalServer.Settings;
namespace TaikoLocalServer.Controllers.Api;
[ApiController]
[Route("api/[controller]")]
public class GameDataController(IGameDataService gameDataService, IAuthService authService,
IOptions<AuthSettings> settings) : BaseController<UsersController>
{
private readonly AuthSettings authSettings = settings.Value;
[HttpGet("MusicDetails")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public IActionResult GetMusicDetails()
{
if (authSettings.LoginRequired)
{
var tokenInfo = authService.ExtractTokenInfo(HttpContext);
if (tokenInfo is null)
{
return Unauthorized();
}
}
return Ok(gameDataService.GetMusicDetailDictionary());
}
[HttpGet("Costumes")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public IActionResult GetCostumes()
{
if (authSettings.LoginRequired)
{
var tokenInfo = authService.ExtractTokenInfo(HttpContext);
if (tokenInfo is null)
{
return Unauthorized();
}
}
return Ok(gameDataService.GetCostumeList());
}
[HttpGet("Titles")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public IActionResult GetTitles()
{
if (authSettings.LoginRequired)
{
var tokenInfo = authService.ExtractTokenInfo(HttpContext);
if (tokenInfo is null)
{
return Unauthorized();
}
}
return Ok(gameDataService.GetTitleDictionary());
}
[HttpGet("LockedCostumes")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public IActionResult GetLockedCostumes()
{
if (authSettings.LoginRequired)
{
var tokenInfo = authService.ExtractTokenInfo(HttpContext);
if (tokenInfo is null)
{
return Unauthorized();
}
}
return Ok(gameDataService.GetLockedCostumeDataDictionary());
}
[HttpGet("LockedTitles")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public IActionResult GetLockedTitles()
{
if (authSettings.LoginRequired)
{
var tokenInfo = authService.ExtractTokenInfo(HttpContext);
if (tokenInfo is null)
{
return Unauthorized();
}
}
return Ok(gameDataService.GetLockedTitleDataDictionary());
}
}

View File

@ -58,7 +58,7 @@ public class CrownsDataController : BaseController<CrownsDataController>
{
var songBestData = await songBestDatumService.GetAllSongBestData(baid);
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
var songIdMax = settings.EnableMoreSongs ? Constants.MusicIdMaxExpanded : Constants.MusicIdMax;
var crown = new ushort[songIdMax + 1];
var dondafulCrown = new byte[songIdMax + 1];

View File

@ -49,7 +49,7 @@ public class GetScoreRankController(ISongBestDatumService songBestDatumService,
private async Task<ScoreRankData> Handle(uint baid)
{
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
var songIdMax = settings.EnableMoreSongs ? Constants.MusicIdMaxExpanded : Constants.MusicIdMax;
var kiwamiScores = new byte[songIdMax + 1];
var miyabiScores = new ushort[songIdMax + 1];
var ikiScores = new ushort[songIdMax + 1];

View File

@ -15,8 +15,8 @@ public class GetTelopController : BaseController<GetTelopController>
var response = new GettelopResponse
{
Result = 1,
StartDatetime = startDateTime.ToString(Constants.DATE_TIME_FORMAT),
EndDatetime = endDateTime.ToString(Constants.DATE_TIME_FORMAT),
StartDatetime = startDateTime.ToString(Constants.DateTimeFormat),
EndDatetime = endDateTime.ToString(Constants.DateTimeFormat),
Telop = "Hello 3906",
VerupNo = 1
};
@ -36,8 +36,8 @@ public class GetTelopController : BaseController<GetTelopController>
var response = new Models.v3209.GettelopResponse
{
Result = 1,
StartDatetime = startDateTime.ToString(Constants.DATE_TIME_FORMAT),
EndDatetime = endDateTime.ToString(Constants.DATE_TIME_FORMAT),
StartDatetime = startDateTime.ToString(Constants.DateTimeFormat),
EndDatetime = endDateTime.ToString(Constants.DateTimeFormat),
Telop = "Hello 3209",
VerupNo = 1
};

View File

@ -127,7 +127,7 @@ public class BaidQueryHandler(
GotDanMax = maxDan,
GotGaidenFlg = gotGaidenFlagArray,
IsDispAchievementOn = userData.DisplayAchievement,
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT),
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DateTimeFormat),
LastPlayMode = userData.LastPlayMode,
SelectedToneId = userData.SelectedToneId,
Title = userData.Title,

View File

@ -17,7 +17,7 @@ public class GetInitialDataQueryHandler(IGameDataService gameDataService,
public Task<CommonInitialDataCheckResponse> Handle(GetInitialDataQuery request, CancellationToken cancellationToken)
{
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
var songIdMax = settings.EnableMoreSongs ? Constants.MusicIdMaxExpanded : Constants.MusicIdMax;
var musicList = gameDataService.GetMusicList();
var lockedSongsList = gameDataService.GetLockedSongsList();
@ -39,7 +39,7 @@ public class GetInitialDataQueryHandler(IGameDataService gameDataService,
DefaultSongFlg = defaultSongFlg,
AchievementSongBit = enabledArray,
UraReleaseBit = uraReleaseBit,
SongIntroductionEndDatetime = DateTime.Now.AddYears(10).ToString(Constants.DATE_TIME_FORMAT),
SongIntroductionEndDatetime = DateTime.Now.AddYears(10).ToString(Constants.DateTimeFormat),
ServerCurrentDatetime = (ulong)DateTimeOffset.Now.ToUnixTimeSeconds()
};
@ -65,18 +65,18 @@ public class GetInitialDataQueryHandler(IGameDataService gameDataService,
CommonInitialDataCheckResponse.VerupNoData2[] verupNo2List =
[
GetVerupNoData2(Constants.DAN_VERUP_MASTER_TYPE, commonDanDataDictionary),
GetVerupNoData2(Constants.GAIDEN_VERUP_MASTER_TYPE, commonGaidenDataDictionary),
GetVerupNoData2(Constants.FOLDER_VERUP_MASTER_TYPE, eventFolderDictionary),
GetVerupNoData2(Constants.INTRO_VERUP_MASTER_TYPE, songIntroDictionary)
GetVerupNoData2(Constants.DanVerupMasterType, commonDanDataDictionary),
GetVerupNoData2(Constants.GaidenVerupMasterType, commonGaidenDataDictionary),
GetVerupNoData2(Constants.FolderVerupMasterType, eventFolderDictionary),
GetVerupNoData2(Constants.IntroVerupMasterType, songIntroDictionary)
];
response.AryVerupNoData2s.AddRange(verupNo2List);
response.AryChassisFunctionIds =
[
Constants.FUNCTION_ID_DANI_AVAILABLE,
Constants.FUNCTION_ID_DANI_FOLDER_AVAILABLE,
Constants.FUNCTION_ID_AI_BATTLE_AVAILABLE
Constants.FunctionIdDaniAvailable,
Constants.FunctionIdDaniFolderAvailable,
Constants.FunctionIdAiBattleAvailable
];
return Task.FromResult(response);

View File

@ -22,12 +22,12 @@ public class UserDataQueryHandler(TaikoDbContext context, IGameDataService gameD
lockedSongsList = lockedSongsList.Except(unlockedSongIdList).ToList();
var enabledMusicList = musicList.Except(lockedSongsList);
var releaseSongArray =
FlagCalculator.GetBitArrayFromIds(enabledMusicList, Constants.MUSIC_ID_MAX, logger);
FlagCalculator.GetBitArrayFromIds(enabledMusicList, Constants.MusicIdMax, logger);
var defaultSongWithUraList = gameDataService.GetMusicWithUraList();
var enabledUraMusicList = defaultSongWithUraList.Except(lockedSongsList);
var uraSongArray =
FlagCalculator.GetBitArrayFromIds(enabledUraMusicList, Constants.MUSIC_ID_MAX, logger);
FlagCalculator.GetBitArrayFromIds(enabledUraMusicList, Constants.MusicIdMax, logger);
if (userData.ToneFlgArray.Count == 0)
{

View File

@ -16,7 +16,7 @@ public class CommonBaidResponse
public List<byte[]> CostumeFlagArrays { get; set; }
= new() { Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>() };
public string LastPlayDatetime { get; set; } = DateTime.Now.ToString(Constants.DATE_TIME_FORMAT);
public string LastPlayDatetime { get; set; } = DateTime.Now.ToString(Constants.DateTimeFormat);
public bool DisplayDan { get; set; }
public uint GotDanMax { get; set; }
public byte[] GotDanFlg { get; set; } = Array.Empty<byte>();

View File

@ -10,7 +10,7 @@ public class CommonInitialDataCheckResponse
public byte[] UraReleaseBit { get; set; } = [];
public string SongIntroductionEndDatetime { get; set; } =
DateTime.Now.AddYears(10).ToString(Constants.DATE_TIME_FORMAT);
DateTime.Now.AddYears(10).ToString(Constants.DateTimeFormat);
public List<MovieData> AryMovieInfoes { get; set; } = [];
public List<AiEventData> AryAiEventDatas { get; set; } = [];

View File

@ -4,9 +4,27 @@ namespace TaikoLocalServer.Models;
public class MusicInfoEntry
{
[JsonPropertyName("id")]
public string Id { get; set; } = string.Empty;
[JsonPropertyName("uniqueId")]
public uint MusicId { get; set; }
[JsonPropertyName("genreNo")]
public SongGenre Genre { get; set; }
[JsonPropertyName("starEasy")]
public int StarEasy { get; set; }
[JsonPropertyName("starNormal")]
public int StarNormal { get; set; }
[JsonPropertyName("starHard")]
public int StarHard { get; set; }
[JsonPropertyName("starMania")]
public int StarOni { get; set; }
[JsonPropertyName("starUra")]
public uint StarUra { get; set; }
public int StarUra { get; set; }
}

View File

@ -15,6 +15,8 @@ using Serilog;
using SharedProject.Utils;
using TaikoLocalServer.Controllers.Api;
using TaikoLocalServer.Filters;
using Microsoft.AspNetCore.ResponseCompression;
using System.IO.Compression;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
@ -29,14 +31,14 @@ Log.Information("Server starting up...");
try
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(options =>
{
options.LoggingFields = HttpLoggingFields.All;
options.RequestBodyLogLimit = 32768;
options.ResponseBodyLogLimit = 32768;
});
const string configurationsDirectory = "Configurations";
builder.Configuration.AddJsonFile($"{configurationsDirectory}/Kestrel.json", optional: true, reloadOnChange: false);
builder.Configuration.AddJsonFile($"{configurationsDirectory}/Logging.json", optional: false, reloadOnChange: false);
@ -44,8 +46,8 @@ try
builder.Configuration.AddJsonFile($"{configurationsDirectory}/ServerSettings.json", optional: false, reloadOnChange: false);
builder.Configuration.AddJsonFile($"{configurationsDirectory}/DataSettings.json", optional: true, reloadOnChange: false);
builder.Configuration.AddJsonFile($"{configurationsDirectory}/AuthSettings.json", optional: true, reloadOnChange: false);
builder.Configuration.AddJsonFile("wwwroot/appsettings.json", optional: true, reloadOnChange: true); // Add appsettings.json
builder.Configuration.AddJsonFile("wwwroot/appsettings.json", optional: false, reloadOnChange: false);
builder.Host.UseSerilog((context, configuration) =>
{
configuration
@ -65,6 +67,19 @@ try
Log.Warning("Song limit expanded! Use at your own risk!");
}
// Add response compression services
builder.Services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<GzipCompressionProvider>();
options.Providers.Add<BrotliCompressionProvider>();
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
options.Level = CompressionLevel.Fastest;
});
// Add services to the container.
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
builder.Services.AddOptions();
@ -73,39 +88,38 @@ try
builder.Services.Configure<DataSettings>(builder.Configuration.GetSection(nameof(DataSettings)));
builder.Services.Configure<AuthSettings>(builder.Configuration.GetSection(nameof(AuthSettings)));
// Read LoginRequired setting from appsettings.json
var loginRequired = builder.Configuration.GetValue<bool>("LoginRequired");
var loginRequired = builder.Configuration.GetSection("WebUiSettings").GetValue<bool>("LoginRequired");
builder.Services.Configure<AuthSettings>(options => { options.LoginRequired = loginRequired; });
// Add Authentication with JWT
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration.GetSection(nameof(AuthSettings))["JwtIssuer"],
ValidAudience = builder.Configuration.GetSection(nameof(AuthSettings))["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection(nameof(AuthSettings))["JwtKey"] ?? throw new InvalidOperationException()))
};
});
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration.GetSection(nameof(AuthSettings))["JwtIssuer"],
ValidAudience = builder.Configuration.GetSection(nameof(AuthSettings))["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration.GetSection(nameof(AuthSettings))["JwtKey"] ?? throw new InvalidOperationException()))
};
});
builder.Services.AddScoped<AuthorizeIfRequiredAttribute>(); // Register the custom attribute
builder.Services.AddControllers().AddProtoBufNet();
builder.Services.AddDbContext<TaikoDbContext>(option =>
{
var dbName = builder.Configuration["DbFileName"];
if (string.IsNullOrEmpty(dbName))
{
dbName = Constants.DEFAULT_DB_NAME;
dbName = Constants.DefaultDbName;
}
var path = Path.Combine(PathHelper.GetRootPath(), dbName);
@ -148,6 +162,9 @@ try
gameDataService.ThrowIfNull();
await gameDataService.InitializeAsync();
// Use response compression
app.UseResponseCompression();
// For reverse proxy
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
@ -159,22 +176,28 @@ try
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
// Enable Authentication and Authorization middleware
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpLogging();
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode >= 400)
if (context.Response.StatusCode == StatusCodes.Status404NotFound)
{
Log.Error("Unknown request from: {RemoteIpAddress} {Method} {Path} {StatusCode}",
context.Connection.RemoteIpAddress, context.Request.Method, context.Request.Path, context.Response.StatusCode);
Log.Error("Request headers: {Headers}", context.Request.Headers);
}
else if (context.Response.StatusCode != StatusCodes.Status200OK)
{
Log.Warning("Unsuccessful request from: {RemoteIpAddress} {Method} {Path} {StatusCode}",
context.Connection.RemoteIpAddress, context.Request.Method, context.Request.Path, context.Response.StatusCode);
Log.Warning("Request headers: {Headers}", context.Request.Headers);
}
});
app.MapControllers();
app.MapFallbackToFile("index.html");

View File

@ -6,11 +6,12 @@ using System.IO.Compression;
using System.Security.Cryptography;
using System.Text.Json;
using TaikoLocalServer.Settings;
using TaikoWebUI.Shared.Models;
using Throw;
namespace TaikoLocalServer.Services;
public class GameDataService : IGameDataService
public class GameDataService(IOptions<DataSettings> dataSettings) : IGameDataService
{
private ImmutableDictionary<uint, DanData> commonDanDataDictionary =
ImmutableDictionary<uint, DanData>.Empty;
@ -18,7 +19,7 @@ public class GameDataService : IGameDataService
private ImmutableDictionary<uint, DanData> commonGaidenDataDictionary =
ImmutableDictionary<uint, DanData>.Empty;
private ImmutableDictionary<uint, MusicInfoEntry> musicInfoes =
private ImmutableDictionary<uint, MusicInfoEntry> musicInfos =
ImmutableDictionary<uint, MusicInfoEntry>.Empty;
private ImmutableDictionary<uint, MovieData> movieDataDictionary =
@ -32,37 +33,44 @@ public class GameDataService : IGameDataService
private ImmutableDictionary<uint, EventFolderData> eventFolderDictionary =
ImmutableDictionary<uint, EventFolderData>.Empty;
private List<ShopFolderData> shopFolderList = new();
private List<ShopFolderData> shopFolderList = [];
private List<uint> musics = new();
private List<uint> musicUniqueIdList = [];
private List<uint> musicsWithUra = new();
private List<uint> musicWithUraUniqueIdList = [];
private List<uint> lockedSongsList = new();
private List<uint> lockedSongsList = [];
private List<int> costumeFlagArraySizes = new();
private List<uint> lockedUraSongsList = [];
private readonly Dictionary<uint, MusicDetail> musicDetailDictionary = new();
private readonly List<Costume> costumeList = [];
private readonly Dictionary<uint, Title> titleDictionary = new();
private Dictionary<string, List<uint>> lockedCostumeDataDictionary = new();
private Dictionary<string, List<uint>> lockedTitleDataDictionary = new();
private List<int> costumeFlagArraySize = [];
private int titleFlagArraySize;
private int toneFlagArraySize;
private Dictionary<string, int> tokenDataDictionary = new();
private readonly DataSettings settings;
public GameDataService(IOptions<DataSettings> settings)
{
this.settings = settings.Value;
}
private readonly DataSettings settings = dataSettings.Value;
public List<uint> GetMusicList()
{
return musics;
return musicUniqueIdList;
}
public List<uint> GetMusicWithUraList()
{
return musicsWithUra;
return musicWithUraUniqueIdList;
}
public ImmutableDictionary<uint, MovieData> GetMovieDataDictionary()
@ -104,10 +112,40 @@ public class GameDataService : IGameDataService
{
return lockedSongsList;
}
public List<uint> GetLockedUraSongsList()
{
return lockedUraSongsList;
}
public Dictionary<uint, MusicDetail> GetMusicDetailDictionary()
{
return musicDetailDictionary;
}
public List<Costume> GetCostumeList()
{
return costumeList;
}
public Dictionary<uint, Title> GetTitleDictionary()
{
return titleDictionary;
}
public Dictionary<string, List<uint>> GetLockedCostumeDataDictionary()
{
return lockedCostumeDataDictionary;
}
public Dictionary<string, List<uint>> GetLockedTitleDataDictionary()
{
return lockedTitleDataDictionary;
}
public List<int> GetCostumeFlagArraySizes()
{
return costumeFlagArraySizes;
return costumeFlagArraySize;
}
public int GetTitleFlagArraySize()
@ -130,23 +168,23 @@ public class GameDataService : IGameDataService
var dataPath = PathHelper.GetDataPath();
var datatablePath = PathHelper.GetDatatablePath();
var musicInfoPath = Path.Combine(datatablePath, $"{Constants.MUSIC_INFO_BASE_NAME}.json");
var encryptedInfo = Path.Combine(datatablePath, $"{Constants.MUSIC_INFO_BASE_NAME}.bin");
var musicInfoPath = Path.Combine(datatablePath, $"{Constants.MusicInfoBaseName}.json");
var encryptedInfo = Path.Combine(datatablePath, $"{Constants.MusicInfoBaseName}.bin");
var wordlistPath = Path.Combine(datatablePath, $"{Constants.WORDLIST_BASE_NAME}.json");
var encryptedWordlist = Path.Combine(datatablePath, $"{Constants.WORDLIST_BASE_NAME}.bin");
var wordlistPath = Path.Combine(datatablePath, $"{Constants.WordlistBaseName}.json");
var encryptedWordlist = Path.Combine(datatablePath, $"{Constants.WordlistBaseName}.bin");
var musicOrderPath = Path.Combine(datatablePath, $"{Constants.MUSIC_ORDER_BASE_NAME}.json");
var encryptedMusicOrder = Path.Combine(datatablePath, $"{Constants.MUSIC_ORDER_BASE_NAME}.bin");
var musicOrderPath = Path.Combine(datatablePath, $"{Constants.MusicOrderBaseName}.json");
var encryptedMusicOrder = Path.Combine(datatablePath, $"{Constants.MusicOrderBaseName}.bin");
var donCosRewardPath = Path.Combine(datatablePath, $"{Constants.DON_COS_REWARD_BASE_NAME}.json");
var encryptedDonCosReward = Path.Combine(datatablePath, $"{Constants.DON_COS_REWARD_BASE_NAME}.bin");
var donCosRewardPath = Path.Combine(datatablePath, $"{Constants.DonCosRewardBaseName}.json");
var encryptedDonCosReward = Path.Combine(datatablePath, $"{Constants.DonCosRewardBaseName}.bin");
var shougouPath = Path.Combine(datatablePath, $"{Constants.SHOUGOU_BASE_NAME}.json");
var encryptedShougou = Path.Combine(datatablePath, $"{Constants.SHOUGOU_BASE_NAME}.bin");
var shougouPath = Path.Combine(datatablePath, $"{Constants.ShougouBaseName}.json");
var encryptedShougou = Path.Combine(datatablePath, $"{Constants.ShougouBaseName}.bin");
var neiroPath = Path.Combine(datatablePath, $"{Constants.NEIRO_BASE_NAME}.json");
var encryptedNeiro = Path.Combine(datatablePath, $"{Constants.NEIRO_BASE_NAME}.bin");
var neiroPath = Path.Combine(datatablePath, $"{Constants.NeiroBaseName}.json");
var encryptedNeiro = Path.Combine(datatablePath, $"{Constants.NeiroBaseName}.bin");
var danDataPath = Path.Combine(dataPath, settings.DanDataFileName);
var gaidenDataPath = Path.Combine(dataPath, settings.GaidenDataFileName);
@ -157,6 +195,8 @@ public class GameDataService : IGameDataService
var tokenDataPath = Path.Combine(dataPath, settings.TokenDataFileName);
var lockedSongsDataPath = Path.Combine(dataPath, settings.LockedSongsDataFileName);
var qrCodeDataPath = Path.Combine(dataPath, settings.QrCodeDataFileName);
var lockedCostumeDataPath = Path.Combine(dataPath, settings.LockedCostumeDataFileName);
var lockedTitleDataPath = Path.Combine(dataPath, settings.LockedTitleDataFileName);
var encryptedFiles = new List<string>
{
@ -186,12 +226,9 @@ public class GameDataService : IGameDataService
}
}
foreach (var filePath in outputPaths)
foreach (var filePath in outputPaths.Where(filePath => !File.Exists(filePath)))
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"{Path.GetFileName(filePath)} file not found!");
}
throw new FileNotFoundException($"{Path.GetFileName(filePath)} file not found!");
}
await using var musicInfoFile = File.OpenRead(musicInfoPath);
@ -207,8 +244,12 @@ public class GameDataService : IGameDataService
await using var shougouFile = File.OpenRead(shougouPath);
await using var neiroFile = File.OpenRead(neiroPath);
await using var qrCodeDataFile = File.OpenRead(qrCodeDataPath);
await using var wordlistFile = File.OpenRead(wordlistPath);
await using var musicOrderFile = File.OpenRead(musicOrderPath);
await using var lockedCostumeDataFile = File.OpenRead(lockedCostumeDataPath);
await using var lockedTitleDataFile = File.OpenRead(lockedTitleDataPath);
var infosData = await JsonSerializer.DeserializeAsync<MusicInfos>(musicInfoFile);
var musicInfoData = await JsonSerializer.DeserializeAsync<MusicInfos>(musicInfoFile);
var danData = await JsonSerializer.DeserializeAsync<List<DanData>>(danDataFile);
var gaidenData = await JsonSerializer.DeserializeAsync<List<DanData>>(gaidenDataFile);
var introData = await JsonSerializer.DeserializeAsync<List<SongIntroductionData>>(songIntroDataFile);
@ -221,8 +262,12 @@ public class GameDataService : IGameDataService
var shougouData = await JsonSerializer.DeserializeAsync<Shougous>(shougouFile);
var neiroData = await JsonSerializer.DeserializeAsync<Neiros>(neiroFile);
var qrCodeData = await JsonSerializer.DeserializeAsync<List<QRCodeData>>(qrCodeDataFile);
var wordlistData = await JsonSerializer.DeserializeAsync<WordList>(wordlistFile);
var musicOrderData = await JsonSerializer.DeserializeAsync<MusicOrder>(musicOrderFile);
var lockedCostumeData = await JsonSerializer.DeserializeAsync<Dictionary<string, uint[]>>(lockedCostumeDataFile);
var lockedTitleData = await JsonSerializer.DeserializeAsync<Dictionary<string, uint[]>>(lockedTitleDataFile);
InitializeMusicInfos(infosData);
InitializeMusicInfos(musicInfoData);
InitializeDanData(danData);
@ -239,10 +284,16 @@ public class GameDataService : IGameDataService
InitializeTokenData(tokenData);
InitializeLockedSongsData(lockedSongsData);
InitializeMusicDetails(musicInfoData, musicOrderData, wordlistData);
InitializeCostumeFlagArraySizes(donCosRewardData);
InitializeCostumes(donCosRewardData, wordlistData);
InitializeTitleFlagArraySize(shougouData);
InitializeTitles(shougouData, wordlistData);
InitializeLockedCostumeData(lockedCostumeData);
InitializeLockedTitleData(lockedTitleData);
InitializeToneFlagArraySize(neiroData);
@ -304,16 +355,16 @@ public class GameDataService : IGameDataService
{
infosData.ThrowIfNull("Shouldn't happen!");
musicInfoes = infosData.MusicInfoEntries.ToImmutableDictionary(info => info.MusicId);
musicInfos = infosData.MusicInfoEntries.ToImmutableDictionary(info => info.MusicId);
musics = musicInfoes.Select(pair => pair.Key)
musicUniqueIdList = musicInfos.Select(pair => pair.Key)
.ToList();
musics.Sort();
musicUniqueIdList.Sort();
musicsWithUra = musicInfoes.Where(info => info.Value.StarUra > 0)
musicWithUraUniqueIdList = musicInfos.Where(info => info.Value.StarUra > 0)
.Select(pair => pair.Key)
.ToList();
musicsWithUra.Sort();
musicWithUraUniqueIdList.Sort();
}
private void InitializeShopFolderData(List<ShopFolderData>? shopFolderData)
@ -332,41 +383,129 @@ public class GameDataService : IGameDataService
{
lockedSongsData.ThrowIfNull("Shouldn't happen!");
lockedSongsList = lockedSongsData["songNo"].ToList();
lockedUraSongsList = lockedSongsData["uraSongNo"].ToList();
}
private void InitializeMusicDetails(MusicInfos? musicInfoData, MusicOrder? musicOrderData, WordList? wordlistData)
{
musicInfoData.ThrowIfNull("Shouldn't happen!");
musicOrderData.ThrowIfNull("Shouldn't happen!");
wordlistData.ThrowIfNull("Shouldn't happen!");
foreach (var musicInfo in musicInfoData.MusicInfoEntries)
{
var musicId = musicInfo.Id;
var musicNameKey = $"song_{musicId}";
var musicArtistKey = $"song_sub_{musicId}";
var musicName = wordlistData.WordListEntries.First(entry => entry.Key == musicNameKey).JapaneseText;
var musicArtist = wordlistData.WordListEntries.First(entry => entry.Key == musicArtistKey).JapaneseText;
var musicNameEn = wordlistData.WordListEntries.First(entry => entry.Key == musicNameKey).EnglishUsText;
var musicArtistEn = wordlistData.WordListEntries.First(entry => entry.Key == musicArtistKey).EnglishUsText;
var musicNameCn = wordlistData.WordListEntries.First(entry => entry.Key == musicNameKey).ChineseTText;
var musicArtistCn = wordlistData.WordListEntries.First(entry => entry.Key == musicArtistKey).ChineseTText;
var musicNameKo = wordlistData.WordListEntries.First(entry => entry.Key == musicNameKey).KoreanText;
var musicArtistKo = wordlistData.WordListEntries.First(entry => entry.Key == musicArtistKey).KoreanText;
var musicUniqueId = musicInfo.MusicId;
var musicGenre = musicInfo.Genre;
var musicStarEasy = musicInfo.StarEasy;
var musicStarNormal = musicInfo.StarNormal;
var musicStarHard = musicInfo.StarHard;
var musicStarOni = musicInfo.StarOni;
var musicStarUra = musicInfo.StarUra;
var musicDetail = new MusicDetail
{
SongId = musicUniqueId,
SongName = musicName,
SongNameEN = musicNameEn,
SongNameCN = musicNameCn,
SongNameKO = musicNameKo,
ArtistName = musicArtist,
ArtistNameEN = musicArtistEn,
ArtistNameCN = musicArtistCn,
ArtistNameKO = musicArtistKo,
Genre = musicGenre,
StarEasy = musicStarEasy,
StarNormal = musicStarNormal,
StarHard = musicStarHard,
StarOni = musicStarOni,
StarUra = musicStarUra
};
musicDetailDictionary.TryAdd(musicUniqueId, musicDetail);
}
for (var index = 0; index < musicOrderData.Order.Count; index++)
{
var musicOrderEntry = musicOrderData.Order[index];
var musicUniqueId = musicOrderEntry.SongId;
if (musicDetailDictionary.TryGetValue(musicUniqueId, out var musicDetail))
{
musicDetail.Index = index;
}
}
}
private void InitializeCostumeFlagArraySizes(DonCosRewards? donCosRewardData)
private void InitializeCostumes(DonCosRewards? donCosRewardData, WordList? wordlistData)
{
donCosRewardData.ThrowIfNull("Shouldn't happen!");
var kigurumiUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "kigurumi")
.Select(entry => entry.UniqueId);
var headUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "head")
.Select(entry => entry.UniqueId);
var bodyUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "body")
.Select(entry => entry.UniqueId);
var faceUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "face")
.Select(entry => entry.UniqueId);
var puchiUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "puchi")
.Select(entry => entry.UniqueId);
wordlistData.ThrowIfNull("Shouldn't happen!");
costumeFlagArraySizes = new List<int>
foreach (var donCosReward in donCosRewardData.DonCosRewardEntries)
{
(int)kigurumiUniqueIdList.Max() + 1,
(int)headUniqueIdList.Max() + 1,
(int)bodyUniqueIdList.Max() + 1,
(int)faceUniqueIdList.Max() + 1,
(int)puchiUniqueIdList.Max() + 1
};
var cosType = donCosReward.CosType;
var costumeId = donCosReward.UniqueId;
var costumeNameKey = $"costume_{cosType}_{costumeId}";
var costumeName = wordlistData.WordListEntries.First(entry => entry.Key == costumeNameKey).JapaneseText;
var costume = new Costume
{
CostumeId = costumeId,
CostumeType = cosType,
CostumeName = costumeName
};
costumeList.Add(costume);
}
var kigurumiMaxArraySize = (int)costumeList.Where(costume => costume.CostumeType == "kigurumi").Max(costume => costume.CostumeId) + 1;
var headMaxArraySize = (int)costumeList.Where(costume => costume.CostumeType == "head").Max(costume => costume.CostumeId) + 1;
var bodyMaxArraySize = (int)costumeList.Where(costume => costume.CostumeType == "body").Max(costume => costume.CostumeId) + 1;
var faceMaxArraySize = (int)costumeList.Where(costume => costume.CostumeType == "face").Max(costume => costume.CostumeId) + 1;
var puchiMaxArraySize = (int)costumeList.Where(costume => costume.CostumeType == "puchi").Max(costume => costume.CostumeId) + 1;
costumeFlagArraySize =
[kigurumiMaxArraySize, headMaxArraySize, bodyMaxArraySize, faceMaxArraySize, puchiMaxArraySize];
}
private void InitializeTitleFlagArraySize(Shougous? shougouData)
private void InitializeTitles(Shougous? shougouData, WordList? wordlistData)
{
shougouData.ThrowIfNull("Shouldn't happen!");
titleFlagArraySize = (int)shougouData.ShougouEntries.Max(entry => entry.UniqueId) + 1;
wordlistData.ThrowIfNull("Shouldn't happen!");
foreach (var shougou in shougouData.ShougouEntries)
{
var titleId = shougou.UniqueId;
var titleNameKey = $"syougou_{titleId}";
var titleName = wordlistData.WordListEntries.First(entry => entry.Key == titleNameKey).JapaneseText;
var title = new Title
{
TitleId = titleId,
TitleName = titleName,
TitleRarity = shougou.Rarity
};
titleDictionary.TryAdd(titleId, title);
}
titleFlagArraySize = (int)titleDictionary.Max(title => title.Key) + 1;
}
private void InitializeLockedCostumeData(Dictionary<string, uint[]>? lockedCostumeData)
{
lockedCostumeData.ThrowIfNull("Shouldn't happen!");
lockedCostumeDataDictionary = lockedCostumeData.ToDictionary(pair => pair.Key, pair => pair.Value.ToList());
}
private void InitializeLockedTitleData(Dictionary<string, uint[]>? lockedTitleData)
{
lockedTitleData.ThrowIfNull("Shouldn't happen!");
lockedTitleDataDictionary = lockedTitleData.ToDictionary(pair => pair.Key, pair => pair.Value.ToList());
}
private void InitializeToneFlagArraySize(Neiros? neiroData)

View File

@ -26,9 +26,21 @@ public interface IGameDataService
public Dictionary<string, int> GetTokenDataDictionary();
public List<uint> GetLockedSongsList();
public List<uint> GetLockedUraSongsList();
public Dictionary<uint, MusicDetail> GetMusicDetailDictionary();
public List<Costume> GetCostumeList();
public Dictionary<uint, Title> GetTitleDictionary();
public Dictionary<string, List<uint>> GetLockedCostumeDataDictionary();
public Dictionary<string, List<uint>> GetLockedTitleDataDictionary();
public List<int> GetCostumeFlagArraySizes();
public int GetTitleFlagArraySize();
public int GetToneFlagArraySize();

View File

@ -19,6 +19,7 @@
<PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.4" />
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.4">

View File

@ -1,17 +1,17 @@
{
"Kigurumi": [
"kigurumi": [
],
"Head": [
"head": [
],
"Body": [
"body": [
],
"Face": [
"face": [
],
"Puchi": [
"puchi": [
]
}

View File

@ -1,8 +1,8 @@
{
"TitleNo": [
"title": [
],
"TitlePlateNo": [
"titlePlate": [
]
}

View File

@ -53,7 +53,7 @@
isDarkMode = await LocalStorage.GetItemAsync<bool>("isDarkMode");
}
if (AuthService.LoginRequired)
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
// If not logged in, attempt to use JwtToken from local storage to log in
await AuthService.LoginWithAuthToken();

View File

@ -19,6 +19,12 @@ public partial class AccessCode
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
await InitializeUser();
userSetting = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");

View File

@ -11,6 +11,11 @@ public partial class ChangePassword
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
}
private async Task OnChangePassword()

View File

@ -143,13 +143,13 @@ else
</MudTooltip>
<MudStack Row="true" Spacing="1" Justify="Justify.Center" AlignItems="AlignItems.Center">
<MudIcon Icon="@Icons.Material.Filled.Star" Size="Size.Small" />
<MudText Typo="Typo.caption" Style="line-height:1;margin-top:2px;margin-right:2px;">@GameDataService.GetMusicStarLevel(danDataOdaiSong.SongNo, difficulty)</MudText>
<MudText Typo="Typo.caption" Style="line-height:1;margin-top:2px;margin-right:2px;">@GameDataService.GetMusicStarLevel(musicDetailDictionary, danDataOdaiSong.SongNo, difficulty)</MudText>
</MudStack>
</MudItem>
<MudItem xs="9" md="4" Style="display:flex;flex-direction:column;" Class="pl-4">
<MudText Typo="Typo.body1" Style="font-weight: bold;">@GameDataService.GetMusicNameBySongId(danDataOdaiSong.SongNo, @CurrentLanguage)</MudText>
<MudText Typo="Typo.caption">@GameDataService.GetMusicArtistBySongId(danDataOdaiSong.SongNo, @CurrentLanguage)</MudText>
<MudText Typo="Typo.body1" Style="font-weight: bold;">@GameDataService.GetMusicNameBySongId(musicDetailDictionary, danDataOdaiSong.SongNo, @CurrentLanguage)</MudText>
<MudText Typo="Typo.caption">@GameDataService.GetMusicArtistBySongId(musicDetailDictionary, danDataOdaiSong.SongNo, @CurrentLanguage)</MudText>
</MudItem>

View File

@ -12,12 +12,18 @@ public partial class DaniDojo
private UserSetting? userSetting;
private static Dictionary<uint, DanBestData> bestDataMap = new();
private Dictionary<uint, MusicDetail> musicDetailDictionary = new();
private readonly List<BreadcrumbItem> breadcrumbs = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
response = await Client.GetFromJsonAsync<DanBestDataResponse>($"api/DanBestData/{Baid}");
response.ThrowIfNull();
@ -28,6 +34,8 @@ public partial class DaniDojo
CurrentLanguage = await JsRuntime.InvokeAsync<string>("blazorCulture.get");
userSetting = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");
musicDetailDictionary = await GameDataService.GetMusicDetailDictionary();
if (AuthService.IsLoggedIn && !AuthService.IsAdmin)
{

View File

@ -1,11 +1,10 @@
@using TaikoWebUI.Shared.Models
@using System.Collections.Immutable
@using System.Collections.Immutable
@inject IGameDataService GameDataService
@inject IJSRuntime Js
<MudDialog>
<DialogContent>
<MudTable Items="@titles" Filter="@Filter" @bind-SelectedItem="@selectedTitle" Height="40vh" Hover="true">
<MudTable Items="@Titles" Filter="@Filter" @bind-SelectedItem="@selectedTitle" Height="40vh" Hover="true">
<ColGroup>
<col style="width: 50px;" />
<col />
@ -66,9 +65,7 @@
public bool AllowFreeProfileEditing { get; set; }
[Parameter]
public List<uint> TitleUniqueIdList { get; set; } = new();
private IEnumerable<Title> titles = new List<Title>();
public List<Title> Titles { get; set; } = new();
private Title? selectedTitle;
@ -77,28 +74,13 @@
protected override void OnInitialized()
{
base.OnInitialized();
var titleSet = GameDataService.GetTitles();
if (!AllowFreeProfileEditing)
{
var unlockedTitle = UserSetting.UnlockedTitle;
titleSet = titleSet.Where(title => unlockedTitle.Contains(title.TitleId)).ToImmutableHashSet();
}
else
{
// Only allow titles in titleUniqueIdList
titleSet = titleSet.Where(title => TitleUniqueIdList.Contains(title.TitleId)).ToImmutableHashSet();
}
titles = titleSet.ToImmutableList().Sort((title, title1) => title.TitleId.CompareTo(title1.TitleId));
var currentTitle = new Title
{
TitleName = UserSetting.Title
};
if (titleSet.Contains(currentTitle))
if (Titles.Contains(currentTitle))
{
titleSet.TryGetValue(new Title
{
TitleName = UserSetting.Title
}, out selectedTitle);
selectedTitle = currentTitle;
}
}

View File

@ -22,14 +22,7 @@
{
@if (AuthService.LoginRequired && (!AuthService.IsLoggedIn || (AuthService.GetLoggedInBaid() != Baid && !AuthService.IsAdmin)))
{
if (!AuthService.IsLoggedIn)
{
NavigationManager.NavigateTo("/Login");
}
else
{
NavigationManager.NavigateTo("/");
}
NavigationManager.NavigateTo(AuthService.IsLoggedIn ? "/" : "/Login");
}
else
{
@ -50,7 +43,7 @@
<MudText Typo="Typo.body2" Style="font-weight:bold">@Localizer["Song Name"]</MudText>
</MudTh>
<MudTh>
<MudTableSortLabel T="SongBestData" SortBy="x => GameDataService.GetMusicStarLevel(x.SongId, difficulty)">
<MudTableSortLabel T="SongBestData" SortBy="x => GameDataService.GetMusicStarLevel(musicDetailDictionary, x.SongId, difficulty)">
<MudText>@Localizer["Level"]</MudText>
</MudTableSortLabel>
</MudTh>
@ -106,7 +99,7 @@
</MudTh>
<MudTh>
<MudTableSortLabel T="SongBestData" SortBy="x => x.PlayCount">
<MudText>@Localizer["Total Plays"]</MudText>
<MudText>@Localizer["Total Credits Played"]</MudText>
</MudTableSortLabel>
</MudTh>
<MudTh>
@ -148,7 +141,7 @@
<MudTd>
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
<MudIcon Icon="@Icons.Material.Filled.Star" Size="Size.Small" />
<MudText Typo="Typo.caption" Style="line-height:1;margin-top:2px;margin-right:2px;">@GameDataService.GetMusicStarLevel(context.SongId, difficulty)</MudText>
<MudText Typo="Typo.caption" Style="line-height:1;margin-top:2px;margin-right:2px;">@GameDataService.GetMusicStarLevel(musicDetailDictionary, context.SongId, difficulty)</MudText>
</MudStack>
</MudTd>
<MudTd>

View File

@ -1,7 +1,4 @@
using static MudBlazor.Colors;
using System;
using Microsoft.JSInterop;
using Microsoft.JSInterop;
namespace TaikoWebUI.Pages;
@ -15,24 +12,32 @@ public partial class HighScores
private Dictionary<Difficulty, List<SongBestData>> songBestDataMap = new();
private readonly List<BreadcrumbItem> breadcrumbs = new();
private int selectedDifficultyTab = 0;
private int selectedDifficultyTab;
private Dictionary<uint, MusicDetail> musicDetailDictionary = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
response = await Client.GetFromJsonAsync<SongBestResponse>($"api/PlayData/{Baid}");
response.ThrowIfNull();
userSetting = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");
var language = await JsRuntime.InvokeAsync<string>("blazorCulture.get");
musicDetailDictionary = await GameDataService.GetMusicDetailDictionary();
response.SongBestData.ForEach(data =>
{
var songId = data.SongId;
data.Genre = GameDataService.GetMusicGenreBySongId(songId);
data.MusicName = GameDataService.GetMusicNameBySongId(songId, string.IsNullOrEmpty(language) ? "ja" : language);
data.MusicArtist = GameDataService.GetMusicArtistBySongId(songId, string.IsNullOrEmpty(language) ? "ja" : language);
data.Genre = GameDataService.GetMusicGenreBySongId(musicDetailDictionary, songId);
data.MusicName = GameDataService.GetMusicNameBySongId(musicDetailDictionary, songId, string.IsNullOrEmpty(language) ? "ja" : language);
data.MusicArtist = GameDataService.GetMusicArtistBySongId(musicDetailDictionary, songId, string.IsNullOrEmpty(language) ? "ja" : language);
});
songBestDataMap = response.SongBestData.GroupBy(data => data.Difficulty)
@ -40,8 +45,8 @@ public partial class HighScores
data => data.ToList());
foreach (var songBestDataList in songBestDataMap.Values)
{
songBestDataList.Sort((data1, data2) => GameDataService.GetMusicIndexBySongId(data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(data2.SongId)));
songBestDataList.Sort((data1, data2) => GameDataService.GetMusicIndexBySongId(musicDetailDictionary, data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(musicDetailDictionary, data2.SongId)));
}
// Set last selected tab from local storage

View File

@ -9,6 +9,11 @@ public partial class Login
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
}
private async Task OnLogin()

View File

@ -1,11 +1,8 @@
@inject IGameDataService GameDataService
@inject HttpClient Client
@inject AuthService AuthService
@inject IJSRuntime JSRuntime
@inject IJSRuntime JsRuntime
@inject NavigationManager NavigationManager
@using TaikoWebUI.Utilities;
@using TaikoWebUI.Shared.Models;
@using SharedProject.Enums;
@page "/Users/{baid:int}/PlayHistory"
@ -113,7 +110,7 @@
ToggledSize="Size.Small"
Title="Add to favorites" ToggledTitle="Remove from favorites" />
</div>
</MudStack>
</MudStack>
</MudTd>
@* Genre display *@

View File

@ -1,9 +1,5 @@
using static MudBlazor.Colors;
using System;
using static MudBlazor.CategoryTypes;
using System.Globalization;
using System.Globalization;
using Microsoft.JSInterop;
using TaikoWebUI.Shared.Models;
namespace TaikoWebUI.Pages;
@ -24,22 +20,32 @@ public partial class PlayHistory
private readonly List<BreadcrumbItem> breadcrumbs = new();
private Dictionary<uint, MusicDetail> musicDetailDictionary = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
response = await Client.GetFromJsonAsync<SongHistoryResponse>($"api/PlayHistory/{(uint)Baid}");
response.ThrowIfNull();
currentLanguage = await JSRuntime.InvokeAsync<string>("blazorCulture.get");
currentLanguage = await JsRuntime.InvokeAsync<string>("blazorCulture.get");
musicDetailDictionary = await GameDataService.GetMusicDetailDictionary();
response.SongHistoryData.ForEach(data =>
{
var songId = data.SongId;
data.Genre = GameDataService.GetMusicGenreBySongId(songId);
data.MusicName = GameDataService.GetMusicNameBySongId(songId, string.IsNullOrEmpty(currentLanguage) ? "ja" : currentLanguage);
data.MusicArtist = GameDataService.GetMusicArtistBySongId(songId, string.IsNullOrEmpty(currentLanguage) ? "ja" : currentLanguage);
data.Stars = GameDataService.GetMusicStarLevel(songId, data.Difficulty);
data.Genre = GameDataService.GetMusicGenreBySongId(musicDetailDictionary, songId);
data.MusicName = GameDataService.GetMusicNameBySongId(musicDetailDictionary, songId, string.IsNullOrEmpty(currentLanguage) ? "ja" : currentLanguage);
data.MusicArtist = GameDataService.GetMusicArtistBySongId(musicDetailDictionary, songId, string.IsNullOrEmpty(currentLanguage) ? "ja" : currentLanguage);
data.Stars = GameDataService.GetMusicStarLevel(musicDetailDictionary, songId, data.Difficulty);
data.ShowDetails = false;
});

View File

@ -126,35 +126,35 @@
<MudSelect @bind-Value="@response.Head" Label=@Localizer["Head"]>
@foreach (var index in headUniqueIdList)
{
var costumeTitle = GameDataService.GetHeadTitle(index);
var costumeTitle = GameDataService.GetHeadTitle(costumeList, index);
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
}
</MudSelect>
<MudSelect @bind-Value="@response.Body" Label=@Localizer["Body"]>
@foreach (var index in bodyUniqueIdList)
{
var costumeTitle = GameDataService.GetBodyTitle(index);
var costumeTitle = GameDataService.GetBodyTitle(costumeList, index);
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
}
</MudSelect>
<MudSelect @bind-Value="@response.Face" Label=@Localizer["Face"]>
@foreach (var index in faceUniqueIdList)
{
var costumeTitle = GameDataService.GetFaceTitle(index);
var costumeTitle = GameDataService.GetFaceTitle(costumeList, index);
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
}
</MudSelect>
<MudSelect @bind-Value="@response.Kigurumi" Label=@Localizer["Kigurumi"]>
@foreach (var index in kigurumiUniqueIdList)
{
var costumeTitle = GameDataService.GetKigurumiTitle(index);
var costumeTitle = GameDataService.GetKigurumiTitle(costumeList, index);
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
}
</MudSelect>
<MudSelect @bind-Value="@response.Puchi" Label=@Localizer["Puchi"]>
@foreach (var index in puchiUniqueIdList)
{
var costumeTitle = GameDataService.GetPuchiTitle(index);
var costumeTitle = GameDataService.GetPuchiTitle(costumeList, index);
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
}
</MudSelect>

View File

@ -177,15 +177,31 @@ public partial class Profile
private List<uint> puchiUniqueIdList = new();
private List<uint> titleUniqueIdList = new();
private List<uint> titlePlateIdList = new();
private List<Costume> costumeList = new();
private Dictionary<uint, Title> titleDictionary = new();
private Dictionary<string, List<uint>> lockedCostumeDataDictionary = new();
private Dictionary<string, List<uint>> lockedTitleDataDictionary = new();
private List<Title> unlockedTitles = new();
private int[] scoresArray = new int[10];
private Dictionary<uint, MusicDetail> musicDetailDictionary = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
isSavingOptions = false;
response = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");
response.ThrowIfNull();
musicDetailDictionary = await GameDataService.GetMusicDetailDictionary();
if (AuthService.IsLoggedIn && !AuthService.IsAdmin)
{
@ -194,10 +210,15 @@ public partial class Profile
else
{
breadcrumbs.Add(new BreadcrumbItem(Localizer["Users"], href: "/Users"));
};
}
breadcrumbs.Add(new BreadcrumbItem($"{response.MyDonName}", href: null, disabled: true));
breadcrumbs.Add(new BreadcrumbItem(Localizer["Profile"], href: $"/Users/{Baid}/Profile", disabled: false));
costumeList = await GameDataService.GetCostumeList();
titleDictionary = await GameDataService.GetTitleDictionary();
lockedCostumeDataDictionary = await GameDataService.GetLockedCostumeDataDictionary();
lockedTitleDataDictionary = await GameDataService.GetLockedTitleDataDictionary();
InitializeAvailableCostumes();
InitializeAvailableTitles();
@ -207,9 +228,9 @@ public partial class Profile
songresponse.SongBestData.ForEach(data =>
{
var songId = data.SongId;
data.Genre = GameDataService.GetMusicGenreBySongId(songId);
data.MusicName = GameDataService.GetMusicNameBySongId(songId);
data.MusicArtist = GameDataService.GetMusicArtistBySongId(songId);
data.Genre = GameDataService.GetMusicGenreBySongId(musicDetailDictionary, songId);
data.MusicName = GameDataService.GetMusicNameBySongId(musicDetailDictionary, songId);
data.MusicArtist = GameDataService.GetMusicArtistBySongId(musicDetailDictionary, songId);
});
songBestDataMap = songresponse.SongBestData.GroupBy(data => data.Difficulty)
@ -217,12 +238,12 @@ public partial class Profile
data => data.ToList());
foreach (var songBestDataList in songBestDataMap.Values)
{
songBestDataList.Sort((data1, data2) => GameDataService.GetMusicIndexBySongId(data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(data2.SongId)));
songBestDataList.Sort((data1, data2) => GameDataService.GetMusicIndexBySongId(musicDetailDictionary, data1.SongId)
.CompareTo(GameDataService.GetMusicIndexBySongId(musicDetailDictionary, data2.SongId)));
}
for (var i = 0; i < (int)Difficulty.UraOni; i++)
if (songBestDataMap.TryGetValue((Difficulty)i, out var values))
if (songBestDataMap.ContainsKey((Difficulty)i) && songBestDataMap[(Difficulty)i].Count > 0)
{
highestDifficulty = (Difficulty)i;
}
@ -240,18 +261,30 @@ public partial class Profile
if (AuthService.AllowFreeProfileEditing)
{
kigurumiUniqueIdList = GameDataService.GetKigurumiUniqueIdList();
headUniqueIdList = GameDataService.GetHeadUniqueIdList();
bodyUniqueIdList = GameDataService.GetBodyUniqueIdList();
faceUniqueIdList = GameDataService.GetFaceUniqueIdList();
puchiUniqueIdList = GameDataService.GetPuchiUniqueIdList();
kigurumiUniqueIdList = costumeList.Where(costume => costume.CostumeType == "kigurumi").Select(costume => costume.CostumeId).ToList();
headUniqueIdList = costumeList.Where(costume => costume.CostumeType == "head").Select(costume => costume.CostumeId).ToList();
bodyUniqueIdList = costumeList.Where(costume => costume.CostumeType == "body").Select(costume => costume.CostumeId).ToList();
faceUniqueIdList = costumeList.Where(costume => costume.CostumeType == "face").Select(costume => costume.CostumeId).ToList();
puchiUniqueIdList = costumeList.Where(costume => costume.CostumeType == "puchi").Select(costume => costume.CostumeId).ToList();
// Lock costumes in LockedCostumesList but not in UnlockedCostumesList
var lockedKigurumiUniqueIdList = GameDataService.GetLockedKigurumiUniqueIdList().Except(unlockedKigurumi).ToList();
var lockedHeadUniqueIdList = GameDataService.GetLockedHeadUniqueIdList().Except(unlockedHead).ToList();
var lockedBodyUniqueIdList = GameDataService.GetLockedBodyUniqueIdList().Except(unlockedBody).ToList();
var lockedFaceUniqueIdList = GameDataService.GetLockedFaceUniqueIdList().Except(unlockedFace).ToList();
var lockedPuchiUniqueIdList = GameDataService.GetLockedPuchiUniqueIdList().Except(unlockedPuchi).ToList();
lockedCostumeDataDictionary.TryGetValue("kigurumi", out var lockedKigurumiUniqueIdList);
lockedCostumeDataDictionary.TryGetValue("head", out var lockedHeadUniqueIdList);
lockedCostumeDataDictionary.TryGetValue("body", out var lockedBodyUniqueIdList);
lockedCostumeDataDictionary.TryGetValue("face", out var lockedFaceUniqueIdList);
lockedCostumeDataDictionary.TryGetValue("puchi", out var lockedPuchiUniqueIdList);
lockedKigurumiUniqueIdList ??= new List<uint>();
lockedHeadUniqueIdList ??= new List<uint>();
lockedBodyUniqueIdList ??= new List<uint>();
lockedFaceUniqueIdList ??= new List<uint>();
lockedPuchiUniqueIdList ??= new List<uint>();
unlockedKigurumi.ForEach(id => kigurumiUniqueIdList.Add(id));
unlockedHead.ForEach(id => headUniqueIdList.Add(id));
unlockedBody.ForEach(id => bodyUniqueIdList.Add(id));
unlockedFace.ForEach(id => faceUniqueIdList.Add(id));
unlockedPuchi.ForEach(id => puchiUniqueIdList.Add(id));
lockedKigurumiUniqueIdList.ForEach(id => kigurumiUniqueIdList.Remove(id));
lockedHeadUniqueIdList.ForEach(id => headUniqueIdList.Remove(id));
@ -262,19 +295,33 @@ public partial class Profile
else
{
// Only unlock costumes that are in both UnlockedCostumesList and CostumeList
kigurumiUniqueIdList = GameDataService.GetKigurumiUniqueIdList().Intersect(unlockedKigurumi).ToList();
headUniqueIdList = GameDataService.GetHeadUniqueIdList().Intersect(unlockedHead).ToList();
bodyUniqueIdList = GameDataService.GetBodyUniqueIdList().Intersect(unlockedBody).ToList();
faceUniqueIdList = GameDataService.GetFaceUniqueIdList().Intersect(unlockedFace).ToList();
puchiUniqueIdList = GameDataService.GetPuchiUniqueIdList().Intersect(unlockedPuchi).ToList();
kigurumiUniqueIdList = costumeList.Where(costume => costume.CostumeType == "kigurumi").Select(costume => costume.CostumeId).Intersect(unlockedKigurumi).ToList();
headUniqueIdList = costumeList.Where(costume => costume.CostumeType == "head").Select(costume => costume.CostumeId).Intersect(unlockedHead).ToList();
bodyUniqueIdList = costumeList.Where(costume => costume.CostumeType == "body").Select(costume => costume.CostumeId).Intersect(unlockedBody).ToList();
faceUniqueIdList = costumeList.Where(costume => costume.CostumeType == "face").Select(costume => costume.CostumeId).Intersect(unlockedFace).ToList();
puchiUniqueIdList = costumeList.Where(costume => costume.CostumeType == "puchi").Select(costume => costume.CostumeId).Intersect(unlockedPuchi).ToList();
}
// Take unique values and sort
kigurumiUniqueIdList = kigurumiUniqueIdList.Distinct().OrderBy(id => id).ToList();
headUniqueIdList = headUniqueIdList.Distinct().OrderBy(id => id).ToList();
bodyUniqueIdList = bodyUniqueIdList.Distinct().OrderBy(id => id).ToList();
faceUniqueIdList = faceUniqueIdList.Distinct().OrderBy(id => id).ToList();
puchiUniqueIdList = puchiUniqueIdList.Distinct().OrderBy(id => id).ToList();
}
private void InitializeAvailableTitlePlates()
{
titlePlateIdList = GameDataService.GetTitlePlateIdList().ToList();
titlePlateIdList = titleDictionary.Values.Select(title => title.TitleRarity).ToList();
lockedTitleDataDictionary.TryGetValue("titlePlate", out var lockedTitlePlateIdList);
lockedTitlePlateIdList ??= new List<uint>();
// Cut off ids longer than TitlePlateStrings
titlePlateIdList = titlePlateIdList.Where(id => id < TitlePlateStrings.Length).Except(GameDataService.GetLockedTitlePlateIdList()).ToList();
titlePlateIdList = titlePlateIdList.Where(id => id < TitlePlateStrings.Length).Except(lockedTitlePlateIdList).ToList();
// Take unique values and sort
titlePlateIdList = titlePlateIdList.Distinct().OrderBy(id => id).ToList();
}
private void InitializeAvailableTitles()
@ -285,23 +332,32 @@ public partial class Profile
if (AuthService.AllowFreeProfileEditing)
{
titleUniqueIdList = GameDataService.GetTitleUniqueIdList();
titleUniqueIdList = titleDictionary.Values.Select(title => title.TitleId).ToList();
var titles = GameDataService.GetTitles();
// Lock titles in LockedTitlesList but not in UnlockedTitle
var lockedTitleUniqueIdList = GameDataService.GetLockedTitleUniqueIdList().ToList();
var lockedTitlePlateIdList = GameDataService.GetLockedTitlePlateIdList().ToList();
lockedTitleDataDictionary.TryGetValue("title", out var lockedTitleUniqueIdList);
lockedTitleDataDictionary.TryGetValue("titlePlate", out var lockedTitlePlateIdList);
lockedTitleUniqueIdList ??= new List<uint>();
lockedTitlePlateIdList ??= new List<uint>();
// Unlock titles in UnlockedTitlesList
lockedTitleUniqueIdList = lockedTitleUniqueIdList.Except(unlockedTitle).ToList();
// Find uniqueIds of titles with rarity in lockedTitlePlateIdList
lockedTitleUniqueIdList.AddRange(titles.Where(title => lockedTitlePlateIdList.Contains(title.TitleRarity)).Select(title => title.TitleId));
lockedTitleUniqueIdList.AddRange(titleDictionary.Values.Where(title => lockedTitlePlateIdList.Contains(title.TitleRarity)).Select(title => title.TitleId));
titleUniqueIdList = titleUniqueIdList.Except(lockedTitleUniqueIdList).ToList();
}
else
{
// Only unlock titles that are in both UnlockedTitlesList and TitleList
titleUniqueIdList = GameDataService.GetTitleUniqueIdList().Intersect(unlockedTitle).ToList();
titleUniqueIdList = titleDictionary.Values.Select(title => title.TitleId).ToList();
titleUniqueIdList = titleUniqueIdList.Intersect(unlockedTitle).ToList();
}
unlockedTitles = titleDictionary.Values.Where(title => titleUniqueIdList.Contains(title.TitleId)).ToList();
// Take unique values and sort
titleUniqueIdList = titleUniqueIdList.Distinct().OrderBy(id => id).ToList();
}
private async Task SaveOptions()
@ -323,11 +379,22 @@ public partial class Profile
response.AchievementDisplayDifficulty = difficulty;
scoresArray = new int[10];
if (difficulty is Difficulty.None) difficulty = highestDifficulty;
if (difficulty == Difficulty.None) difficulty = highestDifficulty;
if (!songBestDataMap.TryGetValue(difficulty, out var values)) return;
foreach (var value in values)
var valuesList = new List<SongBestData>(values);
if (difficulty == Difficulty.UraOni)
{
// Also include Oni scores
if (songBestDataMap.TryGetValue(Difficulty.Oni, out var oniValues))
{
valuesList.AddRange(oniValues);
}
}
foreach (var value in valuesList)
{
switch (value.BestScoreRank)
{
@ -368,6 +435,7 @@ public partial class Profile
}
}
}
public static string CostumeOrDefault(string file, uint id, string defaultfile)
{
var path = "/images/Costumes/";
@ -391,7 +459,7 @@ public partial class Profile
{
{x => x.UserSetting, response},
{x => x.AllowFreeProfileEditing, AuthService.AllowFreeProfileEditing},
{x => x.TitleUniqueIdList, titleUniqueIdList}
{x => x.Titles, unlockedTitles},
};
var dialog = DialogService.Show<ChooseTitleDialog>("Player Titles", parameters, options);
var result = await dialog.Result;

View File

@ -1,5 +1,4 @@
@inject HttpClient Client
@inject IDialogService DialogService
@inject IDialogService DialogService
@inject AuthService AuthService
@inject NavigationManager NavigationManager

View File

@ -16,6 +16,11 @@ public partial class Register
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
}
private async Task OnRegister()

View File

@ -21,6 +21,11 @@ namespace TaikoWebUI.Pages
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
response = await Client.GetFromJsonAsync<SongHistoryResponse>($"api/PlayHistory/{(uint)Baid}");
response.ThrowIfNull();
@ -29,11 +34,13 @@ namespace TaikoWebUI.Pages
// Get user settings
userSetting = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");
var musicDetailDictionary = await GameDataService.GetMusicDetailDictionary();
// Get song title and artist
var language = await JsRuntime.InvokeAsync<string>("blazorCulture.get");
songTitle = GameDataService.GetMusicNameBySongId((uint)SongId, string.IsNullOrEmpty(language) ? "ja" : language);
songArtist = GameDataService.GetMusicArtistBySongId((uint)SongId, string.IsNullOrEmpty(language) ? "ja" : language);
songTitle = GameDataService.GetMusicNameBySongId(musicDetailDictionary, (uint)SongId, string.IsNullOrEmpty(language) ? "ja" : language);
songArtist = GameDataService.GetMusicArtistBySongId(musicDetailDictionary, (uint)SongId, string.IsNullOrEmpty(language) ? "ja" : language);
// Breadcrumbs
var formattedSongTitle = songTitle;

View File

@ -4,7 +4,6 @@
@inject IJSRuntime JsRuntime
@inject NavigationManager NavigationManager
@using TaikoWebUI.Utilities;
@using TaikoWebUI.Shared.Models;
@page "/Users/{baid:int}/Songs"
@ -27,7 +26,7 @@
else
{
<MudItem xs="12">
<MudTable Items="musicMap" Elevation="0" Outlined="true" Filter="@FilterSongs">
<MudTable Items="musicDetailDictionary.Values" Elevation="0" Outlined="true" Filter="@FilterSongs">
<ToolBarContent>
<MudGrid Spacing="2">
<MudItem xs="12" md="8">
@ -56,7 +55,7 @@
</ToolBarContent>
<HeaderContent>
<MudTh>
<MudTableSortLabel T="MusicDetail" SortBy="context => GameDataService.GetMusicNameBySongId(context.SongId, CurrentLanguage)">
<MudTableSortLabel T="MusicDetail" SortBy="context => GameDataService.GetMusicNameBySongId(musicDetailDictionary, context.SongId, CurrentLanguage)">
@Localizer["Song Title / Artist"]
</MudTableSortLabel>
</MudTh>
@ -70,7 +69,7 @@
@if (difficulty is not Difficulty.None)
{
<MudTh>
<MudTableSortLabel T="MusicDetail" SortBy="context => GameDataService.GetMusicStarLevel(context.SongId, difficulty)">
<MudTableSortLabel T="MusicDetail" SortBy="context => GameDataService.GetMusicStarLevel(musicDetailDictionary, context.SongId, difficulty)">
<img src="@ScoreUtils.GetDifficultyIcon(difficulty)" alt="@ScoreUtils.GetDifficultyTitle(difficulty)" style="@Constants.ICON_STYLE" />
</MudTableSortLabel>
</MudTh>
@ -83,10 +82,10 @@
<div>
<a href="@($"/Users/{Baid}/Songs/{context.SongId}")">
<MudText Typo="Typo.body2" Style="font-weight:bold">
@GameDataService.GetMusicNameBySongId(context.SongId, CurrentLanguage)
@GameDataService.GetMusicNameBySongId(musicDetailDictionary, context.SongId, CurrentLanguage)
</MudText>
<MudText Typo="Typo.caption">
@GameDataService.GetMusicArtistBySongId(context.SongId, CurrentLanguage)
@GameDataService.GetMusicArtistBySongId(musicDetailDictionary, context.SongId, CurrentLanguage)
</MudText>
</a>
</div>
@ -110,7 +109,7 @@
{
@if (difficulty is not Difficulty.None)
{
var starLevel = GameDataService.GetMusicStarLevel(context.SongId, difficulty);
var starLevel = GameDataService.GetMusicStarLevel(musicDetailDictionary, context.SongId, difficulty);
<MudTd>
@if (starLevel > 0)
{

View File

@ -1,7 +1,4 @@
using System.Reflection.Emit;
using Microsoft.JSInterop;
using TaikoWebUI.Shared.Models;
using Microsoft.JSInterop;
namespace TaikoWebUI.Pages;
@ -18,17 +15,23 @@ public partial class SongList
private UserSetting? userSetting;
private readonly List<BreadcrumbItem> breadcrumbs = new();
private List<MusicDetail> musicMap = new();
private Dictionary<uint, MusicDetail> musicDetailDictionary = new();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
response = await Client.GetFromJsonAsync<SongBestResponse>($"api/PlayData/{Baid}");
response.ThrowIfNull();
userSetting = await Client.GetFromJsonAsync<UserSetting>($"api/UserSettings/{Baid}");
musicMap = GameDataService.GetMusicList();
musicDetailDictionary = await GameDataService.GetMusicDetailDictionary();
CurrentLanguage = await JsRuntime.InvokeAsync<string>("blazorCulture.get");

View File

@ -7,6 +7,11 @@ public partial class Users
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
if (AuthService.LoginRequired && !AuthService.IsLoggedIn)
{
await AuthService.LoginWithAuthToken();
}
if (AuthService.IsAdmin || !AuthService.LoginRequired)
{
users = await Client.GetFromJsonAsync<List<User>>("api/Users");

View File

@ -10,6 +10,16 @@ var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
// Create a temporary HttpClient to fetch the appsettings.json file
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
var configurationStream = await httpClient.GetStreamAsync("appsettings.json");
// Load the configuration from the stream
var configuration = new ConfigurationBuilder()
.AddJsonStream(configurationStream)
.Build();
builder.Services.AddSingleton(sp => new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
@ -17,18 +27,16 @@ builder.Services.AddSingleton(sp => new HttpClient
builder.Services.AddMudServices();
builder.Services.AddSingleton<IGameDataService, GameDataService>();
builder.Services.Configure<WebUiSettings>(builder.Configuration.GetSection(nameof(WebUiSettings)));
// Configure WebUiSettings using the loaded configuration
builder.Services.Configure<WebUiSettings>(configuration.GetSection(nameof(WebUiSettings)));
builder.Services.AddScoped<AuthService>();
builder.Services.AddLocalization();
builder.Services.AddSingleton<MudLocalizer, ResXMudLocalizer>();
builder.Services.AddSingleton<ScoreUtils>();
builder.Services.AddSingleton<StringUtil>();
builder.Services.AddBlazoredLocalStorage();
var host = builder.Build();
var gameDataService = host.Services.GetRequiredService<IGameDataService>();
@ -51,4 +59,4 @@ else
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
await host.RunAsync();
await host.RunAsync();

View File

@ -1,5 +1,4 @@
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Json;
@ -23,8 +22,10 @@ public sealed class AuthService
public bool IsAdmin { get; private set; }
private readonly ILocalStorageService localStorage;
private readonly HttpClient client;
private readonly NavigationManager navigationManager;
public AuthService(IOptions<WebUiSettings> settings, ILocalStorageService localStorage, HttpClient client)
public AuthService(IOptions<WebUiSettings> settings, ILocalStorageService localStorage, HttpClient client,
NavigationManager navigationManager)
{
this.localStorage = localStorage;
IsLoggedIn = false;
@ -37,6 +38,7 @@ public sealed class AuthService
AllowUserDelete = webUiSettings.AllowUserDelete;
AllowFreeProfileEditing = webUiSettings.AllowFreeProfileEditing;
this.client = client;
this.navigationManager = navigationManager;
}
private void OnLoginStatusChanged()
@ -105,11 +107,19 @@ public sealed class AuthService
public async Task LoginWithAuthToken()
{
var hasAuthToken = await localStorage.ContainKeyAsync("authToken");
if (!hasAuthToken) return;
if (!hasAuthToken)
{
navigationManager.NavigateTo("/Login");
return;
}
// Attempt to get JWT token from local storage
var authToken = await localStorage.GetItemAsync<string>("authToken");
if (authToken == null) return;
if (authToken == null)
{
navigationManager.NavigateTo("/Login");
return;
}
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
var responseMessage = await client.PostAsync("api/Auth/LoginWithToken", null);
@ -117,6 +127,7 @@ public sealed class AuthService
{
// Clear JWT token
await localStorage.RemoveItemAsync("authToken");
navigationManager.NavigateTo("/Login");
return;
}

View File

@ -1,38 +1,21 @@
using System.Collections.Immutable;
using Swan.Mapping;
using TaikoWebUI.Shared.Models;
namespace TaikoWebUI.Services;
public class GameDataService : IGameDataService
{
private readonly HttpClient client;
private readonly Dictionary<uint, MusicDetail> musicMap = new();
private ImmutableDictionary<uint, DanData> danMap = ImmutableDictionary<uint, DanData>.Empty;
private ImmutableHashSet<Title> titles = ImmutableHashSet<Title>.Empty;
private string[] bodyTitles = { };
private string[] faceTitles = { };
private string[] headTitles = { };
private string[] kigurumiTitles = { };
private string[] puchiTitles = { };
private Dictionary<uint, MusicDetail>? musicDetailDictionary = new();
private List<Costume>? costumeList;
private Dictionary<uint,Title>? titleDictionary = new();
private List<uint> kigurumiUniqueIdList = new();
private List<uint> headUniqueIdList = new();
private List<uint> bodyUniqueIdList = new();
private List<uint> faceUniqueIdList = new();
private List<uint> puchiUniqueIdList = new();
private List<uint> titleUniqueIdList = new();
private List<uint> titlePlateIdList = new();
private bool musicDetailInitialized;
private bool costumesInitialized;
private bool titlesInitialized;
private List<uint> lockedKigurumiUniqueIdList = new();
private List<uint> lockedHeadUniqueIdList = new();
private List<uint> lockedBodyUniqueIdList = new();
private List<uint> lockedFaceUniqueIdList = new();
private List<uint> lockedPuchiUniqueIdList = new();
private List<uint> lockedTitleUniqueIdList = new();
private List<uint> lockedTitlePlateIdList = new();
private Dictionary<string, List<uint>>? lockedCostumeDataDictionary = new();
private Dictionary<string, List<uint>>? lockedTitleDataDictionary = new();
public GameDataService(HttpClient client)
{
@ -42,58 +25,64 @@ public class GameDataService : IGameDataService
public async Task InitializeAsync(string dataBaseUrl)
{
dataBaseUrl = dataBaseUrl.TrimEnd('/');
var musicInfo = await GetData<MusicInfo>(dataBaseUrl, Constants.MUSIC_INFO_BASE_NAME);
var wordList = await GetData<WordList>(dataBaseUrl, Constants.WORDLIST_BASE_NAME);
var musicOrder = await GetData<MusicOrder>(dataBaseUrl, Constants.MUSIC_ORDER_BASE_NAME);
var donCosRewardData = await GetData<DonCosRewards>(dataBaseUrl, Constants.DON_COS_REWARD_BASE_NAME);
var shougouData = await GetData<Shougous>(dataBaseUrl, Constants.SHOUGOU_BASE_NAME);
var danData = await client.GetFromJsonAsync<List<DanData>>($"{dataBaseUrl}/data/dan_data.json");
danData.ThrowIfNull();
danMap = danData.ToImmutableDictionary(data => data.DanId);
}
public async Task<Dictionary<uint, MusicDetail>> GetMusicDetailDictionary()
{
if (!musicDetailInitialized)
{
await InitializeMusicDetailAsync();
}
// To prevent duplicate entries in wordlist
var wordlistDict = wordList.WordListEntries.GroupBy(entry => entry.Key)
.ToImmutableDictionary(group => group.Key, group => group.First());
await Task.Run(() => InitializeMusicMap(musicInfo, wordlistDict, musicOrder));
return musicDetailDictionary ?? new Dictionary<uint, MusicDetail>();
}
public async Task<List<Costume>> GetCostumeList()
{
if (!costumesInitialized)
{
await InitializeCostumesAsync();
}
await Task.Run(() => InitializeCostumeIdLists(donCosRewardData));
await Task.Run(() => InitializeTitleIdList(shougouData));
await Task.Run(() => InitializeHeadTitles(wordlistDict));
await Task.Run(() => InitializeFaceTitles(wordlistDict));
await Task.Run(() => InitializeBodyTitles(wordlistDict));
await Task.Run(() => InitializePuchiTitles(wordlistDict));
await Task.Run(() => InitializeKigurumiTitles(wordlistDict));
await Task.Run(() => InitializeTitles(wordlistDict, shougouData));
return costumeList ?? new List<Costume>();
}
public async Task<Dictionary<uint, Title>> GetTitleDictionary()
{
if (!titlesInitialized)
{
await InitializeTitlesAsync();
}
var lockedCostumeDataDictionary = await client.GetFromJsonAsync<Dictionary<string, List<uint>>>($"{dataBaseUrl}/data/locked_costume_data.json") ?? throw new InvalidOperationException();
lockedKigurumiUniqueIdList = lockedCostumeDataDictionary.GetValueOrDefault("Kigurumi") ?? new List<uint>();
lockedHeadUniqueIdList = lockedCostumeDataDictionary.GetValueOrDefault("Head") ?? new List<uint>();
lockedBodyUniqueIdList = lockedCostumeDataDictionary.GetValueOrDefault("Body") ?? new List<uint>();
lockedFaceUniqueIdList = lockedCostumeDataDictionary.GetValueOrDefault("Face") ?? new List<uint>();
lockedPuchiUniqueIdList = lockedCostumeDataDictionary.GetValueOrDefault("Puchi") ?? new List<uint>();
return titleDictionary ?? new Dictionary<uint, Title>();
}
public async Task<Dictionary<string, List<uint>>> GetLockedCostumeDataDictionary()
{
if (!costumesInitialized)
{
await InitializeCostumesAsync();
}
var lockedTitleDataDictionary = await client.GetFromJsonAsync<Dictionary<string, List<uint>>>($"{dataBaseUrl}/data/locked_title_data.json") ?? throw new InvalidOperationException();
lockedTitleUniqueIdList = lockedTitleDataDictionary.GetValueOrDefault("TitleNo") ?? new List<uint>();
lockedTitlePlateIdList = lockedTitleDataDictionary.GetValueOrDefault("TitlePlateNo") ?? new List<uint>();
return lockedCostumeDataDictionary ?? new Dictionary<string, List<uint>>();
}
public async Task<Dictionary<string, List<uint>>> GetLockedTitleDataDictionary()
{
if (!titlesInitialized)
{
await InitializeTitlesAsync();
}
return lockedTitleDataDictionary ?? new Dictionary<string, List<uint>>();
}
private async Task<T> GetData<T>(string dataBaseUrl, string fileBaseName) where T : notnull
public string GetMusicNameBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId, string? language = "ja")
{
var data = await client.GetFromJsonAsync<T>($"{dataBaseUrl}/data/datatable/{fileBaseName}.json");
data.ThrowIfNull();
return data;
}
public List<MusicDetail> GetMusicList()
{
return musicMap.Values.Where(musicDetail => musicDetail.SongId != 0).ToList();
}
public string GetMusicNameBySongId(uint songId, string? language = "ja")
{
return musicMap.TryGetValue(songId, out var musicDetail) ? language switch
return musicDetails.TryGetValue(songId, out var musicDetail) ? language switch
{
"ja" => musicDetail.SongName,
"en-US" => musicDetail.SongNameEN,
@ -104,9 +93,9 @@ public class GameDataService : IGameDataService
} : string.Empty;
}
public string GetMusicArtistBySongId(uint songId, string? language = "ja")
public string GetMusicArtistBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId, string? language = "ja")
{
return musicMap.TryGetValue(songId, out var musicDetail) ? language switch
return musicDetails.TryGetValue(songId, out var musicDetail) ? language switch
{
"jp" => musicDetail.ArtistName,
"en-US" => musicDetail.ArtistNameEN,
@ -117,14 +106,14 @@ public class GameDataService : IGameDataService
} : string.Empty;
}
public SongGenre GetMusicGenreBySongId(uint songId)
public SongGenre GetMusicGenreBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId)
{
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Genre : SongGenre.Variety;
return musicDetails.TryGetValue(songId, out var musicDetail) ? musicDetail.Genre : SongGenre.Variety;
}
public int GetMusicIndexBySongId(uint songId)
public int GetMusicIndexBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId)
{
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Index : int.MaxValue;
return musicDetails.TryGetValue(songId, out var musicDetail) ? musicDetail.Index : int.MaxValue;
}
public DanData GetDanDataById(uint danId)
@ -132,9 +121,9 @@ public class GameDataService : IGameDataService
return danMap.GetValueOrDefault(danId, new DanData());
}
public int GetMusicStarLevel(uint songId, Difficulty difficulty)
public int GetMusicStarLevel(Dictionary<uint, MusicDetail> musicDetails, uint songId, Difficulty difficulty)
{
var success = musicMap.TryGetValue(songId, out var musicDetail);
var success = musicDetails.TryGetValue(songId, out var musicDetail);
return difficulty switch
{
Difficulty.None => throw new ArgumentException("Difficulty cannot be none"),
@ -147,261 +136,48 @@ public class GameDataService : IGameDataService
};
}
public string GetHeadTitle(uint index)
public string GetHeadTitle(IEnumerable<Costume> costumes, uint index)
{
return index < headTitles.Length ? headTitles[index] : string.Empty;
return costumes.FirstOrDefault(costume => costume.CostumeType == "head" && costume.CostumeId == index)?.CostumeName ?? string.Empty;
}
public string GetKigurumiTitle(uint index)
public string GetKigurumiTitle(IEnumerable<Costume> costumes, uint index)
{
return index < kigurumiTitles.Length ? kigurumiTitles[index] : string.Empty;
return costumes.FirstOrDefault(costume => costume.CostumeType == "kigurumi" && costume.CostumeId == index)?.CostumeName ?? string.Empty;
}
public string GetBodyTitle(uint index)
public string GetBodyTitle(IEnumerable<Costume> costumes, uint index)
{
return index < bodyTitles.Length ? bodyTitles[index] : string.Empty;
return costumes.FirstOrDefault(costume => costume.CostumeType == "body" && costume.CostumeId == index)?.CostumeName ?? string.Empty;
}
public string GetFaceTitle(uint index)
public string GetFaceTitle(IEnumerable<Costume> costumes, uint index)
{
return index < faceTitles.Length ? faceTitles[index] : string.Empty;
return costumes.FirstOrDefault(costume => costume.CostumeType == "face" && costume.CostumeId == index)?.CostumeName ?? string.Empty;
}
public string GetPuchiTitle(uint index)
public string GetPuchiTitle(IEnumerable<Costume> costumes, uint index)
{
return index < puchiTitles.Length ? puchiTitles[index] : string.Empty;
}
public ImmutableHashSet<Title> GetTitles()
{
return titles;
return costumes.FirstOrDefault(costume => costume.CostumeType == "puchi" && costume.CostumeId == index)?.CostumeName ?? string.Empty;
}
public List<uint> GetKigurumiUniqueIdList()
private async Task InitializeMusicDetailAsync()
{
return kigurumiUniqueIdList;
musicDetailDictionary = await client.GetFromJsonAsync<Dictionary<uint, MusicDetail>>("api/GameData/MusicDetails");
musicDetailInitialized = true;
}
public List<uint> GetHeadUniqueIdList()
private async Task InitializeCostumesAsync()
{
return headUniqueIdList;
costumeList = await client.GetFromJsonAsync<List<Costume>>("api/GameData/Costumes");
lockedCostumeDataDictionary = await client.GetFromJsonAsync<Dictionary<string, List<uint>>>("api/GameData/LockedCostumes");
costumesInitialized = true;
}
public List<uint> GetBodyUniqueIdList()
private async Task InitializeTitlesAsync()
{
return bodyUniqueIdList;
}
public List<uint> GetFaceUniqueIdList()
{
return faceUniqueIdList;
}
public List<uint> GetPuchiUniqueIdList()
{
return puchiUniqueIdList;
}
public List<uint> GetTitleUniqueIdList()
{
return titleUniqueIdList;
}
public List<uint> GetTitlePlateIdList()
{
return titlePlateIdList;
}
private void InitializeTitleIdList(Shougous? shougouData)
{
shougouData.ThrowIfNull("Shouldn't happen!");
titleUniqueIdList = shougouData.ShougouEntries.Select(entry => entry.UniqueId).ToList();
}
private void InitializeTitles(ImmutableDictionary<string, WordListEntry> dict, Shougous? shougouData)
{
shougouData.ThrowIfNull("Shouldn't happen!");
var set = ImmutableHashSet.CreateBuilder<Title>();
foreach (var i in titleUniqueIdList)
{
var key = $"syougou_{i}";
var titleWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
var titleRarity = shougouData.ShougouEntries
.Where(entry => entry.UniqueId == i)
.Select(entry => entry.Rarity)
.FirstOrDefault();
if (!titlePlateIdList.Contains(titleRarity))
{
titlePlateIdList.Add(titleRarity);
}
set.Add(new Title
{
TitleName = titleWordlistItem.JapaneseText,
TitleId = i,
TitleRarity = titleRarity
});
}
titles = set.ToImmutable();
}
private void InitializeCostumeIdLists(DonCosRewards? donCosRewardData)
{
donCosRewardData.ThrowIfNull("Shouldn't happen!");
kigurumiUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "kigurumi")
.Select(entry => entry.UniqueId).ToList();
headUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "head")
.Select(entry => entry.UniqueId).ToList();
bodyUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "body")
.Select(entry => entry.UniqueId).ToList();
faceUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "face")
.Select(entry => entry.UniqueId).ToList();
puchiUniqueIdList = donCosRewardData.DonCosRewardEntries
.Where(entry => entry.CosType == "puchi")
.Select(entry => entry.UniqueId).ToList();
}
private void InitializeKigurumiTitles(ImmutableDictionary<string, WordListEntry> dict)
{
kigurumiTitles = new string[kigurumiUniqueIdList.Max() + 1];
foreach (var i in kigurumiUniqueIdList)
{
var key = $"costume_kigurumi_{i}";
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
kigurumiTitles[i] = costumeWordlistItem.JapaneseText;
}
}
private void InitializeHeadTitles(ImmutableDictionary<string, WordListEntry> dict)
{
headTitles = new string[headUniqueIdList.Max() + 1];
foreach (var i in headUniqueIdList)
{
var key = $"costume_head_{i}";
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
headTitles[i] = costumeWordlistItem.JapaneseText;
}
}
private void InitializeBodyTitles(ImmutableDictionary<string, WordListEntry> dict)
{
bodyTitles = new string[bodyUniqueIdList.Max() + 1];
foreach (var i in bodyUniqueIdList)
{
var key = $"costume_body_{i}";
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
bodyTitles[i] = costumeWordlistItem.JapaneseText;
}
}
private void InitializeFaceTitles(ImmutableDictionary<string, WordListEntry> dict)
{
faceTitles = new string[faceUniqueIdList.Max() + 1];
foreach (var i in faceUniqueIdList)
{
var key = $"costume_face_{i}";
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
faceTitles[i] = costumeWordlistItem.JapaneseText;
}
}
private void InitializePuchiTitles(ImmutableDictionary<string, WordListEntry> dict)
{
puchiTitles = new string[puchiUniqueIdList.Max() + 1];
foreach (var i in puchiUniqueIdList)
{
var key = $"costume_puchi_{i}";
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
puchiTitles[i] = costumeWordlistItem.JapaneseText;
}
}
private void InitializeMusicMap(MusicInfo musicInfo, ImmutableDictionary<string, WordListEntry> dict,
MusicOrder musicOrder)
{
foreach (var music in musicInfo.Items)
{
var songNameKey = $"song_{music.Id}";
var songArtistKey = $"song_sub_{music.Id}";
var musicName = dict.GetValueOrDefault(songNameKey, new WordListEntry());
var musicArtist = dict.GetValueOrDefault(songArtistKey, new WordListEntry());
var musicSongId = music.SongId;
var musicDetail = music.CopyPropertiesToNew<MusicDetail>();
musicDetail.SongName = musicName.JapaneseText;
musicDetail.ArtistName = musicArtist.JapaneseText;
// Add localized names
musicDetail.SongNameEN = musicName.EnglishUsText;
musicDetail.ArtistNameEN = musicArtist.EnglishUsText;
musicDetail.SongNameCN = musicName.ChineseTText;
musicDetail.ArtistNameCN = musicArtist.ChineseTText;
musicDetail.SongNameKO = musicName.KoreanText;
musicDetail.ArtistNameKO = musicArtist.KoreanText;
musicMap.TryAdd(musicSongId, musicDetail);
}
for (var index = 0; index < musicOrder.Order.Count; index++)
{
var musicOrderEntry = musicOrder.Order[index];
var songId = musicOrderEntry.SongId;
if (musicMap.TryGetValue(songId, out var value))
{
value.Index = index;
}
}
}
public List<uint> GetLockedKigurumiUniqueIdList()
{
return lockedKigurumiUniqueIdList;
}
public List<uint> GetLockedHeadUniqueIdList()
{
return lockedHeadUniqueIdList;
}
public List<uint> GetLockedBodyUniqueIdList()
{
return lockedBodyUniqueIdList;
}
public List<uint> GetLockedFaceUniqueIdList()
{
return lockedFaceUniqueIdList;
}
public List<uint> GetLockedPuchiUniqueIdList()
{
return lockedPuchiUniqueIdList;
}
public List<uint> GetLockedTitleUniqueIdList()
{
return lockedTitleUniqueIdList;
}
public List<uint> GetLockedTitlePlateIdList()
{
return lockedTitlePlateIdList;
titleDictionary = await client.GetFromJsonAsync<Dictionary<uint, Title>>("api/GameData/Titles");
lockedTitleDataDictionary = await client.GetFromJsonAsync<Dictionary<string, List<uint>>>("api/GameData/LockedTitles");
titlesInitialized = true;
}
}

View File

@ -1,47 +1,34 @@
using System.Collections.Immutable;
using TaikoWebUI.Shared.Models;
namespace TaikoWebUI.Services;
namespace TaikoWebUI.Services;
public interface IGameDataService
{
public Task InitializeAsync(string dataBaseUrl);
public List<MusicDetail> GetMusicList();
public Task<Dictionary<uint, MusicDetail>> GetMusicDetailDictionary();
public Task<Dictionary<uint, Title>> GetTitleDictionary();
public Task<List<Costume>> GetCostumeList();
public Task<Dictionary<string, List<uint>>> GetLockedCostumeDataDictionary();
public Task<Dictionary<string, List<uint>>> GetLockedTitleDataDictionary();
public string GetMusicNameBySongId(uint songId, string? language = null);
public string GetMusicNameBySongId(Dictionary<uint, MusicDetail> musicDetails,uint songId, string? language = null);
public string GetMusicArtistBySongId(uint songId, string? language = null);
public string GetMusicArtistBySongId(Dictionary<uint, MusicDetail> musicDetails,uint songId, string? language = null);
public SongGenre GetMusicGenreBySongId(uint songId);
public SongGenre GetMusicGenreBySongId(Dictionary<uint, MusicDetail> musicDetails,uint songId);
public int GetMusicIndexBySongId(uint songId);
public int GetMusicIndexBySongId(Dictionary<uint, MusicDetail> musicDetails,uint songId);
public DanData GetDanDataById(uint danId);
public int GetMusicStarLevel(uint songId, Difficulty difficulty);
public int GetMusicStarLevel(Dictionary<uint, MusicDetail> musicDetails, uint songId, Difficulty difficulty);
public string GetHeadTitle(uint index);
public string GetKigurumiTitle(uint index);
public string GetBodyTitle(uint index);
public string GetFaceTitle(uint index);
public string GetPuchiTitle(uint index);
public List<uint> GetKigurumiUniqueIdList();
public List<uint> GetHeadUniqueIdList();
public List<uint> GetBodyUniqueIdList();
public List<uint> GetFaceUniqueIdList();
public List<uint> GetPuchiUniqueIdList();
public List<uint> GetTitleUniqueIdList();
public List<uint> GetTitlePlateIdList();
public List<uint> GetLockedKigurumiUniqueIdList();
public List<uint> GetLockedHeadUniqueIdList();
public List<uint> GetLockedBodyUniqueIdList();
public List<uint> GetLockedFaceUniqueIdList();
public List<uint> GetLockedPuchiUniqueIdList();
public List<uint> GetLockedTitleUniqueIdList();
public List<uint> GetLockedTitlePlateIdList();
public ImmutableHashSet<Title> GetTitles();
public string GetHeadTitle(IEnumerable<Costume> costumes, uint index);
public string GetKigurumiTitle(IEnumerable<Costume> costumes, uint index);
public string GetBodyTitle(IEnumerable<Costume> costumes, uint index);
public string GetFaceTitle(IEnumerable<Costume> costumes, uint index);
public string GetPuchiTitle(IEnumerable<Costume> costumes, uint index);
}

View File

@ -1,12 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class DonCosRewardEntry
{
[JsonPropertyName("cosType")]
public string CosType { get; set; } = null!;
[JsonPropertyName("uniqueId")]
public uint UniqueId { get; set; }
}

View File

@ -1,9 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class DonCosRewards
{
[JsonPropertyName("items")]
public List<DonCosRewardEntry> DonCosRewardEntries { get; set; } = new();
}

View File

@ -1,9 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class MusicInfo
{
[JsonPropertyName("items")]
public List<MusicInfoEntry> Items { get; set; } = new();
}

View File

@ -1,30 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class MusicInfoEntry
{
[JsonPropertyName("id")]
public string Id { get; set; } = string.Empty;
[JsonPropertyName("uniqueId")]
public uint SongId { get; set; }
[JsonPropertyName("genreNo")]
public SongGenre Genre { get; set; }
[JsonPropertyName("starEasy")]
public int StarEasy { get; set; }
[JsonPropertyName("starNormal")]
public int StarNormal { get; set; }
[JsonPropertyName("starHard")]
public int StarHard { get; set; }
[JsonPropertyName("starMania")]
public int StarOni { get; set; }
[JsonPropertyName("starUra")]
public int StarUra { get; set; }
}

View File

@ -1,9 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class NeiroEntry
{
[JsonPropertyName("uniqueId")]
public uint UniqueId { get; set; }
}

View File

@ -1,9 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class Neiros
{
[JsonPropertyName("items")]
public List<NeiroEntry> NeiroEntries { get; set; } = new();
}

View File

@ -1,12 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class ShougouEntry
{
[JsonPropertyName("uniqueId")]
public uint UniqueId { get; set; }
[JsonPropertyName("rarity")]
public uint Rarity { get; set; }
}

View File

@ -1,9 +0,0 @@
using System.Text.Json.Serialization;
namespace TaikoWebUI.Shared.Models;
public class Shougous
{
[JsonPropertyName("items")]
public List<ShougouEntry> ShougouEntries { get; set; } = new();
}

View File

@ -1,6 +0,0 @@
namespace TaikoWebUI.Shared.Models
{
public class SongListEntry
{
}
}