1
0
mirror of synced 2024-11-14 18:38:06 +01:00

Stage initial refactors

This commit is contained in:
asesidaa 2024-03-15 04:37:30 +08:00
parent 0cfa5f2033
commit a88965bdd1
98 changed files with 5376 additions and 1358 deletions

View File

@ -4,35 +4,38 @@ namespace GameDatabase.Entities
{
public partial class UserDatum
{
public uint Baid { get; set; }
public string MyDonName { get; set; } = string.Empty;
public uint MyDonNameLanguage { get; set; }
public string Title { get; set; } = string.Empty;
public uint TitlePlateId { get; set; }
public string FavoriteSongsArray { get; set; } = "[]";
public string ToneFlgArray { get; set; } = "[]";
public string TitleFlgArray { get; set; } = "[]";
public string CostumeFlgArray { get; set; } = "[[],[],[],[],[]]";
public string GenericInfoFlgArray { get; set; } = "[]";
public short OptionSetting { get; set; }
public int NotesPosition { get; set; }
public bool IsVoiceOn { get; set; }
public bool IsSkipOn { get; set; }
public string DifficultyPlayedArray { get; set; } = "[]";
public string DifficultySettingArray { get; set; } = "[]";
public uint SelectedToneId { get; set; }
public DateTime LastPlayDatetime { get; set; }
public uint LastPlayMode { get; set; }
public uint ColorBody { get; set; }
public uint ColorFace { get; set; }
public uint ColorLimb { get; set; }
public string CostumeData { get; set; } = "[]";
public bool DisplayDan { get; set; }
public bool DisplayAchievement { get; set; }
public Difficulty AchievementDisplayDifficulty { get; set; }
public int AiWinCount { get; set; }
public List<Token> Tokens { get; set; } = new();
public string UnlockedSongIdList { get; set; } = "[]";
public bool IsAdmin { get; set; }
public uint Baid { get; set; }
public string MyDonName { get; set; } = string.Empty;
public uint MyDonNameLanguage { get; set; }
public string Title { get; set; } = string.Empty;
public uint TitlePlateId { get; set; }
public uint[] FavoriteSongsArray { get; set; } = Array.Empty<uint>();
public uint[] ToneFlgArray { get; set; } = Array.Empty<uint>();
public uint[] TitleFlgArray { get; set; } = Array.Empty<uint>();
public string CostumeFlgArray { get; set; } = "[[],[],[],[],[]]";
public uint[] GenericInfoFlgArray { get; set; } = Array.Empty<uint>();
public short OptionSetting { get; set; }
public int NotesPosition { get; set; }
public bool IsVoiceOn { get; set; }
public bool IsSkipOn { get; set; }
// TODO: Split into separate fields
public string DifficultyPlayedArray { get; set; } = "[]";
// TODO: Split into separate fields
public string DifficultySettingArray { get; set; } = "[]";
public uint SelectedToneId { get; set; }
public DateTime LastPlayDatetime { get; set; }
public uint LastPlayMode { get; set; }
public uint ColorBody { get; set; }
public uint ColorFace { get; set; }
public uint ColorLimb { get; set; }
// TODO: Split into separate fields
public string CostumeData { get; set; } = "[]";
public bool DisplayDan { get; set; }
public bool DisplayAchievement { get; set; }
public Difficulty AchievementDisplayDifficulty { get; set; }
public int AiWinCount { get; set; }
public List<Token> Tokens { get; set; } = new();
public uint[] UnlockedSongIdList { get; set; } = Array.Empty<uint>();
public bool IsAdmin { get; set; }
}
}

View File

@ -33,7 +33,7 @@ namespace TaikoLocalServer.Migrations
b.HasKey("Baid", "SongId", "Difficulty");
b.ToTable("AiScoreData");
b.ToTable("AiScoreData", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.AiSectionScoreDatum", b =>
@ -73,7 +73,7 @@ namespace TaikoLocalServer.Migrations
b.HasKey("Baid", "SongId", "Difficulty", "SectionIndex");
b.ToTable("AiSectionScoreData");
b.ToTable("AiSectionScoreData", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.Card", b =>
@ -138,7 +138,7 @@ namespace TaikoLocalServer.Migrations
b.HasKey("Baid", "DanId", "DanType");
b.ToTable("DanScoreData");
b.ToTable("DanScoreData", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.DanStageScoreDatum", b =>
@ -183,7 +183,7 @@ namespace TaikoLocalServer.Migrations
b.HasKey("Baid", "DanId", "DanType", "SongNumber");
b.ToTable("DanStageScoreData");
b.ToTable("DanStageScoreData", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.SongBestDatum", b =>
@ -211,7 +211,7 @@ namespace TaikoLocalServer.Migrations
b.HasKey("Baid", "SongId", "Difficulty");
b.ToTable("SongBestData");
b.ToTable("SongBestData", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.SongPlayDatum", b =>
@ -272,7 +272,7 @@ namespace TaikoLocalServer.Migrations
b.HasIndex("Baid");
b.ToTable("SongPlayData");
b.ToTable("SongPlayData", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.Token", b =>
@ -288,7 +288,7 @@ namespace TaikoLocalServer.Migrations
b.HasKey("Baid", "Id");
b.ToTable("Tokens");
b.ToTable("Tokens", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.UserDatum", b =>
@ -394,7 +394,7 @@ namespace TaikoLocalServer.Migrations
b.HasKey("Baid");
b.ToTable("UserData");
b.ToTable("UserData", (string)null);
});
modelBuilder.Entity("GameDatabase.Entities.AiScoreDatum", b =>

View File

@ -0,0 +1,10 @@
namespace SharedProject.Enums;
public enum NameLanguage : uint
{
Japanese = 0,
English = 1,
Korean = 2,
ChineseTraditional = 3,
ChineseSimplified = 4,
}

View File

@ -2,7 +2,7 @@
namespace SharedProject.Models;
public class DanData
public class DanData : IVerupNo
{
[JsonPropertyName("danId")]
public uint DanId { get; set; }

View File

@ -2,7 +2,7 @@
namespace SharedProject.Models;
public class EventFolderData
public class EventFolderData : IVerupNo
{
[JsonPropertyName("folderId")]
public uint FolderId { get; set; }
@ -14,7 +14,7 @@ public class EventFolderData
public uint Priority { get; set; }
[JsonPropertyName("songNo")]
public uint[]? SongNo { get; set; }
public uint[]? SongNoes { get; set; }
[JsonPropertyName("parentFolderId")]
public uint ParentFolderId { get; set; }

View File

@ -0,0 +1,8 @@
using System.Text.Json.Serialization;
namespace SharedProject.Models;
public interface IVerupNo
{
public uint VerupNo { get; set; }
}

View File

@ -6,5 +6,6 @@ public class ShopFolderData
{
[JsonPropertyName("songNo")] public uint SongNo { get; set; }
public uint Type { get; set; }
[JsonPropertyName("price")] public uint Price { get; set; }
}

View File

@ -2,7 +2,7 @@
namespace SharedProject.Models;
public class SongIntroductionData
public class SongIntroductionData : IVerupNo
{
[JsonPropertyName("setId")]
public uint SetId { get; set; }
@ -14,5 +14,5 @@ public class SongIntroductionData
public uint MainSongNo { get; set; }
[JsonPropertyName("subSongNo")]
public uint[]? SubSongNo { get; set; }
public uint[]? SubSongNoes { get; set; }
}

View File

@ -16,4 +16,13 @@ public static class Constants
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 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 FUNCTION_ID_DANI_FOLDER_AVAILABLE = 1;
public const uint FUNCTION_ID_DANI_AVAILABLE = 2;
public const uint FUNCTION_ID_AI_BATTLE_AVAILABLE = 3;
}

View File

@ -33,7 +33,7 @@ public class UserSettingsController : BaseController<UserSettingsController>
var costumeUnlockData = JsonHelper.GetCostumeUnlockDataFromUserData(user, Logger);
var unlockedTitle = JsonHelper.GetUIntArrayFromJson(user.TitleFlgArray, 0, Logger, nameof(user.TitleFlgArray))
var unlockedTitle = user.TitleFlgArray//JsonHelper.GetUIntArrayFromJson(user.TitleFlgArray, 0, Logger, nameof(user.TitleFlgArray))
.ToList();
for (var i = 0; i < 5; i++)
@ -125,8 +125,8 @@ public class UserSettingsController : BaseController<UserSettingsController>
user.CostumeData = JsonSerializer.Serialize(costumes);
// If a locked tone is selected, unlock it
uint[] toneFlg = { 0u };
try
uint[] toneFlg = user.ToneFlgArray;
/*try
{
toneFlg = JsonSerializer.Deserialize<uint[]>(user.ToneFlgArray)!;
}
@ -134,10 +134,10 @@ public class UserSettingsController : BaseController<UserSettingsController>
{
Logger.LogError(e, "Parsing tone flg json data failed");
}
toneFlg.ThrowIfNull("Tone flg should never be null!");
toneFlg.ThrowIfNull("Tone flg should never be null!");*/
toneFlg = toneFlg.Append(0u).Append(userSetting.ToneId).Distinct().ToArray();
user.ToneFlgArray = JsonSerializer.Serialize(toneFlg);
user.ToneFlgArray = toneFlg;
await userDatumService.UpdateUserDatum(user);

View File

@ -1,8 +1,14 @@
namespace TaikoLocalServer.Controllers;
using MediatR;
namespace TaikoLocalServer.Controllers;
public abstract class BaseController<T> : ControllerBase where T : BaseController<T>
{
private ILogger<T>? logger;
private ISender? mediator;
protected ISender Mediator => (mediator ??= HttpContext.RequestServices.GetService<ISender>()) ?? throw new InvalidOperationException();
protected ILogger<T> Logger => (logger ??= HttpContext.RequestServices.GetService<ILogger<T>>()) ?? throw new InvalidOperationException();
}

View File

@ -1,49 +1,37 @@
using System.Text.Json;
using GameDatabase.Entities;
using Throw;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Models.Application;
using AddTokenCountRequestMapper = TaikoLocalServer.Mappers.AddTokenCountRequestMapper;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/addtokencount_7547j3o4.php")]
[ApiController]
public class AddTokenCountController : BaseController<AddTokenCountController>
{
private readonly IUserDatumService userDatumService;
public AddTokenCountController(IUserDatumService userDatumService)
{
this.userDatumService = userDatumService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/addtokencount_7547j3o4.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> AddTokenCount([FromBody] AddTokenCountRequest request)
{
Logger.LogInformation("AddTokenCount request : {Request}", request.Stringify());
Logger.LogInformation("[3906] AddTokenCount request : {Request}", request.Stringify());
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
var command = new AddTokenCountCommand(AddTokenCountRequestMapper.Map(request));
await Mediator.Send(command);
foreach (var addTokenCountData in request.AryAddTokenCountDatas)
var response = new AddTokenCountResponse
{
var tokenId = addTokenCountData.TokenId;
var addTokenCount = addTokenCountData.AddTokenCount;
var token = user.Tokens.FirstOrDefault(t => t.Id == tokenId);
if (token != null)
{
token.Count += addTokenCount;
}
else
{
user.Tokens.Add(new Token
{
Id = (int)tokenId,
Count = addTokenCount
});
}
}
Result = 1
};
await userDatumService.UpdateUserDatum(user);
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/addtokencount.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> AddTokenCount3209([FromBody] Models.v3209.AddTokenCountRequest request)
{
Logger.LogInformation("[3209] AddTokenCount request : {Request}", request.Stringify());
var command = new AddTokenCountCommand(AddTokenCountRequestMapper.Map(request));
await Mediator.Send(command);
var response = new AddTokenCountResponse
{

View File

@ -1,200 +1,68 @@
using GameDatabase.Entities;
using System.Text.Json;
using Throw;
using TaikoLocalServer.Handlers;
namespace TaikoLocalServer.Controllers.Game;
[ApiController]
[Route("/v12r08_ww/chassis/baidcheck_dcfxit1u.php")]
public class BaidController : BaseController<BaidController>
{
private readonly IUserDatumService userDatumService;
private readonly ICardService cardService;
private readonly ISongBestDatumService songBestDatumService;
private readonly IDanScoreDatumService danScoreDatumService;
private readonly IAiDatumService aiDatumService;
private readonly IGameDataService gameDataService;
public BaidController(IUserDatumService userDatumService, ICardService cardService,
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService,
IGameDataService gameDataService)
{
this.userDatumService = userDatumService;
this.cardService = cardService;
this.songBestDatumService = songBestDatumService;
this.danScoreDatumService = danScoreDatumService;
this.aiDatumService = aiDatumService;
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/baidcheck_dcfxit1u.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetBaid([FromBody] BAIDRequest request)
{
Logger.LogInformation("Baid request: {Request}", request.Stringify());
var commonResponse = await Mediator.Send(new BaidQuery(request.AccessCode));
BAIDResponse response;
var card = await cardService.GetCardByAccessCode(request.AccessCode);
if (card is null)
if (commonResponse.IsNewUser)
{
Logger.LogInformation("New user with access code {AccessCode}", request.AccessCode);
var newId = cardService.GetNextBaid();
response = new BAIDResponse
{
Result = 1,
PlayerType = 1,
Baid = newId,
Baid = commonResponse.Baid,
};
return Ok(response);
}
var baid = card.Baid;
var userData = await userDatumService.GetFirstUserDatumOrDefault(baid);
var songBestData = await songBestDatumService.GetAllSongBestData(baid);
var achievementDisplayDifficulty = userData.AchievementDisplayDifficulty;
if (userData.AchievementDisplayDifficulty == Difficulty.None)
{
achievementDisplayDifficulty = songBestData.Any(datum => datum.BestCrown >= CrownType.Clear) ?
songBestData.Where(datum => datum.BestCrown >= CrownType.Clear).Max(datum => datum.Difficulty) :
Difficulty.Easy;
}
var songCountData = songBestData.Where(datum => achievementDisplayDifficulty != Difficulty.UraOni ?
datum.Difficulty == achievementDisplayDifficulty :
datum.Difficulty is Difficulty.Oni or Difficulty.UraOni).ToList();
var crownCount = CalculateCrownCount(songCountData);
var scoreRankCount = CalculateScoreRankCount(songCountData);
var costumeData = JsonHelper.GetCostumeDataFromUserData(userData, Logger);
var costumeArrays = JsonHelper.GetCostumeUnlockDataFromUserData(userData, Logger);
var costumeFlagArrays = gameDataService.GetCostumeFlagArraySizes()
.Select((size, index) => FlagCalculator.GetBitArrayFromIds(costumeArrays[index], size, Logger))
.ToList();
var danData = await danScoreDatumService.GetDanScoreDataList(baid, DanType.Normal);
var maxDan = danData.Where(datum => datum.ClearState != DanClearState.NotClear)
.Select(datum => datum.DanId)
.DefaultIfEmpty()
.Max();
var danDataDictionary = gameDataService.GetDanDataDictionary();
var danIdList = danDataDictionary.Keys.ToList();
var gotDanFlagArray = FlagCalculator.ComputeGotDanFlags(danData, danIdList);
var gaidenData = await danScoreDatumService.GetDanScoreDataList(baid, DanType.Gaiden);
var gaidenDataDictionary = gameDataService.GetGaidenDataDictionary();
var gaidenIdList = gaidenDataDictionary.Keys.ToList();
var gotGaidenFlagArray = FlagCalculator.ComputeGotDanFlags(gaidenData, gaidenIdList);
var genericInfoFlg = Array.Empty<uint>();
try
{
genericInfoFlg = JsonSerializer.Deserialize<uint[]>(userData.GenericInfoFlgArray);
}
catch (JsonException e)
{
Logger.LogError(e, "Parsing genericinfo flg json data failed");
}
// The only way to get a null is provide string "null" as input,
// which means database content need to be fixed, so better throw
genericInfoFlg.ThrowIfNull("Genericinfo flg should never be null!");
var genericInfoFlgLength = genericInfoFlg.Any() ? genericInfoFlg.Max() + 1 : 0;
var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, Logger);
var aiRank = (uint)(userData.AiWinCount / 10);
if (aiRank > 11)
{
aiRank = 11;
}
response = new BAIDResponse
{
Result = 1,
PlayerType = 0,
Baid = baid,
MydonName = userData.MyDonName,
MydonNameLanguage = userData.MyDonNameLanguage,
Title = userData.Title,
TitleplateId = userData.TitlePlateId,
ColorFace = userData.ColorFace,
ColorBody = userData.ColorBody,
ColorLimb = userData.ColorLimb,
AryCostumedata = new BAIDResponse.CostumeData
{
Costume1 = costumeData[0],
Costume2 = costumeData[1],
Costume3 = costumeData[2],
Costume4 = costumeData[3],
Costume5 = costumeData[4]
},
CostumeFlg1 = costumeFlagArrays[0],
CostumeFlg2 = costumeFlagArrays[1],
CostumeFlg3 = costumeFlagArrays[2],
CostumeFlg4 = costumeFlagArrays[3],
CostumeFlg5 = costumeFlagArrays[4],
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT),
IsDispDanOn = userData.DisplayDan,
GotDanMax = maxDan,
GotDanFlg = gotDanFlagArray,
GotDanextraFlg = gotGaidenFlagArray,
DefaultToneSetting = userData.SelectedToneId,
GenericInfoFlg = genericInfoFlgArray,
AryCrownCounts = crownCount,
AryScoreRankCounts = scoreRankCount,
IsDispAchievementOn = userData.DisplayAchievement,
DispAchievementType = (uint)achievementDisplayDifficulty,
IsDispAchievementTypeSet = true,
LastPlayMode = userData.LastPlayMode,
IsDispSouuchiOn = true
};
response = Mappers.BaidResponseMapper.Map3906WithPostProcess(commonResponse);
response.Result = 1;
response.PlayerType = 0;
response.IsDispAchievementTypeSet = true;
response.IsDispSouuchiOn = true;
return Ok(response);
}
private static uint[] CalculateScoreRankCount(IReadOnlyCollection<SongBestDatum> songCountData)
[HttpPost("/v12r00_cn/chassis/baidcheck.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetBaid3209([FromBody] Models.v3209.BAIDRequest request)
{
var scoreRankCount = new uint[7];
foreach (var scoreRankType in Enum.GetValues<ScoreRank>())
Logger.LogInformation("Baid request: {Request}", request.Stringify());
var commonResponse = await Mediator.Send(new BaidQuery(request.WechatQrStr));
Models.v3209.BAIDResponse response;
if (commonResponse.IsNewUser)
{
if (scoreRankType != ScoreRank.None)
Logger.LogInformation("New user with access code {AccessCode}", request.WechatQrStr);
response = new Models.v3209.BAIDResponse
{
scoreRankCount[(int)scoreRankType - 2] =
(uint)songCountData.Count(datum => datum.BestScoreRank == scoreRankType);
}
Result = 1,
PlayerType = 1,
Baid = commonResponse.Baid,
};
return Ok(response);
}
return scoreRankCount;
}
private static uint[] CalculateCrownCount(IReadOnlyCollection<SongBestDatum> songCountData)
{
var crownCount = new uint[3];
foreach (var crownType in Enum.GetValues<CrownType>())
{
if (crownType != CrownType.None)
{
crownCount[(int)crownType - 1] = (uint)songCountData.Count(datum => datum.BestCrown == crownType);
}
}
return crownCount;
response = Mappers.BaidResponseMapper.Map3209WithPostProcess(commonResponse);
response.Result = 1;
response.PlayerType = 0;
response.IsDispAchievementTypeSet = true;
response.IsDispSouuchiOn = true;
return Ok(response);
}
}

View File

@ -1,14 +1,13 @@
namespace TaikoLocalServer.Controllers.Game;
[ApiController]
[Route("/v12r08_ww/chassis/bookkeeping_s4esi5un.php")]
public class BookkeepingController : BaseController<BookkeepingController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/bookkeeping_s4esi5un.php")]
[Produces("application/protobuf")]
public IActionResult StartupAuth([FromBody] BookKeepingRequest request)
{
Logger.LogInformation("Bookkeeping request: {Request}", request.Stringify());
Logger.LogInformation("[3906] Bookkeeping request: {Request}", request.Stringify());
var response = new BookKeepingResponse
{
Result = 1
@ -18,4 +17,15 @@ public class BookkeepingController : BaseController<BookkeepingController>
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/bookkeeping.php")]
[Produces("application/protobuf")]
public IActionResult StartupAuth3209([FromBody] Models.v3209.BookKeepingRequest request)
{
Logger.LogInformation("[3209] Bookkeeping request: {Request}", request.Stringify());
var response = new BookKeepingResponse
{
Result = 1
};
return Ok(response);
}
}

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/challengecompe.php")]
[ApiController]
public class ChallengeCompetitionController : BaseController<ChallengeCompetitionController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/challengecompe.php")]
[Produces("application/protobuf")]
public IActionResult HandleChallenge([FromBody] ChallengeCompeRequest request)
{
@ -16,6 +15,19 @@ public class ChallengeCompetitionController : BaseController<ChallengeCompetitio
};
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/challengecompe.php")]
[Produces("application/protobuf")]
public IActionResult HandleChallenge3209([FromBody] Models.v3209.ChallengeCompeRequest request)
{
Logger.LogInformation("ChallengeCompe request : {Request}", request.Stringify());
var response = new Models.v3209.ChallengeCompeResponse
{
Result = 1
};
return Ok(response);
}
}

View File

@ -3,7 +3,6 @@ using TaikoLocalServer.Settings;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/crownsdata_oqgqy90s.php")]
[ApiController]
public class CrownsDataController : BaseController<CrownsDataController>
{
@ -17,16 +16,50 @@ public class CrownsDataController : BaseController<CrownsDataController>
this.settings = settings.Value;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/crownsdata_oqgqy90s.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> CrownsData([FromBody] CrownsDataRequest request)
{
Logger.LogInformation("CrownsData request : {Request}", request.Stringify());
var songBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
var crownData = await Handle(request.Baid);
var response = new CrownsDataResponse
{
Result = 1,
CrownFlg = crownData.CrownFlg,
DondafulCrownFlg = crownData.DondafulCrownFlg
};
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/crownsdata.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> CrownsData3209([FromBody] Models.v3209.CrownsDataRequest request)
{
Logger.LogInformation("CrownsData request : {Request}", request.Stringify());
var crownData = await Handle((uint)request.Baid);
var response = new Models.v3209.CrownsDataResponse
{
Result = 1,
CrownFlg = crownData.CrownFlg,
DondafulCrownFlg = crownData.DondafulCrownFlg
};
return Ok(response);
}
public record CrownData(byte[] CrownFlg, byte[] DondafulCrownFlg);
public async Task<CrownData> Handle(uint baid)
{
var songBestData = await songBestDatumService.GetAllSongBestData(baid);
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
var crown = new ushort[songIdMax + 1];
var crown = new ushort[songIdMax + 1];
var dondafulCrown = new byte[songIdMax + 1];
for (var songId = 0; songId < songIdMax; songId++)
@ -34,7 +67,7 @@ public class CrownsDataController : BaseController<CrownsDataController>
var id = songId;
dondafulCrown[songId] = songBestData
// Select song of this song id with dondaful crown
.Where(datum => datum.SongId == id &&
.Where(datum => datum.SongId == id &&
datum.BestCrown == CrownType.Dondaful)
// Calculate flag according to difficulty
.Aggregate((byte)0, (flag, datum) => FlagCalculator.ComputeDondafulCrownFlag(flag, datum.Difficulty));
@ -46,14 +79,7 @@ public class CrownsDataController : BaseController<CrownsDataController>
// Calculate flag according to difficulty
.Aggregate((ushort)0, (flag, datum) => FlagCalculator.ComputeCrownFlag(flag, datum.BestCrown, datum.Difficulty));
}
var response = new CrownsDataResponse
{
Result = 1,
CrownFlg = GZipBytesUtil.GetGZipBytes(crown),
DondafulCrownFlg = GZipBytesUtil.GetGZipBytes(dondafulCrown)
};
return Ok(response);
return new CrownData(GZipBytesUtil.GetGZipBytes(crown), GZipBytesUtil.GetGZipBytes(dondafulCrown));
}
}

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/executeqrcode_rgowsr5m.php")]
[ApiController]
public class ExecuteQrCodeController : BaseController<ExecuteQrCodeController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/executeqrcode_rgowsr5m.php")]
[Produces("application/protobuf")]
public IActionResult ExecuteQrCode([FromBody] ExecuteQrcodeRequest request)
{
@ -18,4 +17,19 @@ public class ExecuteQrCodeController : BaseController<ExecuteQrCodeController>
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/executeqrcode.php")]
[Produces("application/protobuf")]
public IActionResult ExecuteQrCode3209([FromBody] Models.v3209.ExecuteQrcodeRequest request)
{
Logger.LogInformation("ExecuteQrcode request : {Request}", request.Stringify());
var response = new Models.v3209.ExecuteQrcodeResponse
{
QrcodeId = 1,
Result = 1
};
return Ok(response);
}
}

View File

@ -1,8 +1,8 @@
using Throw;
using TaikoLocalServer.Handlers;
using Throw;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getaidata_6x30b9nr.php")]
[ApiController]
public class GetAiDataController : BaseController<GetAiDataController>
{
@ -13,22 +13,27 @@ public class GetAiDataController : BaseController<GetAiDataController>
this.userDatumService = userDatumService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getaidata_6x30b9nr.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetAiData([FromBody] GetAiDataRequest request)
{
Logger.LogInformation("GetAiData request : {Request}", request.Stringify());
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
var response = new GetAiDataResponse
{
Result = 1,
TotalWinnings = (uint)user.AiWinCount,
InputMedian = "1000",
InputVariance = "2000"
};
var commonResponse = await Mediator.Send(new GetAiDataQuery(request.Baid));
var response = Mappers.AiDataResponseMapper.MapTo3906(commonResponse);
response.Result = 1;
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getaidata.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetAiData3209([FromBody] Models.v3209.GetAiDataRequest request)
{
Logger.LogInformation("GetAiData request : {Request}", request.Stringify());
var commonResponse = await Mediator.Send(new GetAiDataQuery((uint)request.Baid));
var response = Mappers.AiDataResponseMapper.MapTo3209(commonResponse);
response.Result = 1;
return Ok(response);
}
}

View File

@ -1,8 +1,9 @@
using Throw;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
using Throw;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getaiscore_lp38po4w.php")]
[ApiController]
public class GetAiScoreController : BaseController<GetAiScoreController>
{
@ -13,93 +14,29 @@ public class GetAiScoreController : BaseController<GetAiScoreController>
this.aiDatumService = aiDatumService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getaiscore_lp38po4w.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetAiScore([FromBody] GetAiScoreRequest request)
{
Logger.LogInformation("GetAiScore request : {Request}", request.Stringify());
var response = new GetAiScoreResponse
{
Result = 1
};
var commonResponse = await Mediator.Send(new GetAiScoreQuery(request.Baid, request.SongNo, request.Level));
var response = AiScoreMappers.MapTo3906(commonResponse);
response.Result = 1;
var difficulty = (Difficulty)request.Level;
difficulty.Throw().IfOutOfRange();
return Ok(response);
}
var aiData = await aiDatumService.GetSongAiScore(request.Baid, request.SongNo, difficulty);
if (aiData is null)
{
return Ok(response);
}
[HttpPost("v12r00_cn/chassis/getaiscore.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetAiScore3209([FromBody] Models.v3209.GetAiScoreRequest request)
{
Logger.LogInformation("GetAiScore request : {Request}", request.Stringify());
for (var index = 0; index < aiData.AiSectionScoreData.Count; index++)
{
var sectionScoreDatum = aiData.AiSectionScoreData[index];
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData
{
Crown = (uint)sectionScoreDatum.Crown,
GoodCnt = sectionScoreDatum.GoodCount,
OkCnt = sectionScoreDatum.OkCount,
NgCnt = sectionScoreDatum.MissCount,
PoundCnt = sectionScoreDatum.DrumrollCount,
Score = sectionScoreDatum.Score,
SectionNo = (uint)index
});
}
// There's either 3 or 5 total sections
// SectionNo doesn't seem to actually affect which section is being assigned to, only the List order matters
/*response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
{
SectionNo = 1,
Crown = (uint)CrownType.Clear,
Score = 100000,
GoodCnt = 100,
OkCnt = 50,
NgCnt = 25,
PoundCnt = 12
});
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
{
SectionNo = 2,
Crown = (uint)CrownType.Gold,
Score = 100001,
GoodCnt = 101,
OkCnt = 50,
NgCnt = 25,
PoundCnt = 12
});
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
{
SectionNo = 3,
Crown = (uint)CrownType.Dondaful,
Score = 100002,
GoodCnt = 102,
OkCnt = 50,
NgCnt = 25,
PoundCnt = 12
});
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
{
SectionNo = 4,
Crown = (uint)CrownType.Gold,
Score = 100003,
GoodCnt = 103,
OkCnt = 50,
NgCnt = 25,
PoundCnt = 12
});
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
{
SectionNo = 5,
Crown = (uint)CrownType.Clear,
Score = 100004,
GoodCnt = 104,
OkCnt = 50,
NgCnt = 25,
PoundCnt = 12
});*/
var commonResponse =
await Mediator.Send(new GetAiScoreQuery((uint)request.Baid, request.SongNo, request.Level));
var response = AiScoreMappers.MapTo3209(commonResponse);
response.Result = 1;
return Ok(response);
}

View File

@ -1,12 +1,10 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getapplicationurl.php")]
[ApiController]
public class GetApplicationUrlController : BaseController<GetApplicationUrlController>
{
private const string APPLICATION_URL = "vsapi.taiko-p.jp";
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getapplicationurl.php")]
[Produces("application/protobuf")]
public IActionResult GetApplicationUrl([FromBody] GetApplicationUrlRequest request)
{
@ -15,9 +13,25 @@ public class GetApplicationUrlController : BaseController<GetApplicationUrlContr
var response = new GetApplicationUrlResponse
{
Result = 1,
ApplicationUrl = APPLICATION_URL
ApplicationUrl = $"{HttpContext.Request.Host.Value}/app"
};
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getapplicationurl.php")]
[Produces("application/protobuf")]
public IActionResult GetApplicationUrl3209([FromBody] Models.v3209.GetApplicationUrlRequest request)
{
Logger.LogInformation("GetApplicationUrl request : {Request}", request.Stringify());
var response = new Models.v3209.GetApplicationUrlResponse
{
Result = 1,
ApplicationUrl = $"{HttpContext.Request.Host.Value}/app"
};
return Ok(response);
}
}

View File

@ -1,19 +1,14 @@
namespace TaikoLocalServer.Controllers.Game;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getdanodai_ela9zu1a.php")]
[ApiController]
public class GetDanOdaiController : BaseController<GetDanOdaiController>
{
private readonly IGameDataService gameDataService;
public GetDanOdaiController(IGameDataService gameDataService)
{
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getdanodai_ela9zu1a.php")]
[Produces("application/protobuf")]
public IActionResult GetDanOdai([FromBody] GetDanOdaiRequest request)
public async Task<IActionResult> GetDanOdai([FromBody] GetDanOdaiRequest request)
{
Logger.LogInformation("GetDanOdai request : {Request}", request.Stringify());
@ -22,34 +17,25 @@ public class GetDanOdaiController : BaseController<GetDanOdaiController>
Result = 1
};
if (request.Type == 1)
{
foreach (var danId in request.DanIds)
{
gameDataService.GetDanDataDictionary().TryGetValue(danId, out var odaiData);
if (odaiData is null)
{
Logger.LogWarning("Requested dan id {Id} does not exist!", danId);
continue;
}
var odaiDataList = await Mediator.Send(new GetDanOdaiQuery(request.DanIds, request.Type));
response.AryOdaiDatas.AddRange(odaiDataList.Select(DanDataMappers.To3906OdaiData));
response.AryOdaiDatas.Add(odaiData);
}
}
else if (request.Type == 2)
{
foreach (var danId in request.DanIds)
{
gameDataService.GetGaidenDataDictionary().TryGetValue(danId, out var odaiData);
if (odaiData is null)
{
Logger.LogWarning("Requested dan id {Id} does not exist!", danId);
continue;
}
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getdanodai.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetDanOdai3209([FromBody] Models.v3209.GetDanOdaiRequest request)
{
Logger.LogInformation("GetDanOdai request : {Request}", request.Stringify());
response.AryOdaiDatas.Add(odaiData);
}
}
var response = new Models.v3209.GetDanOdaiResponse
{
Result = 1
};
var odaiDataList = await Mediator.Send(new GetDanOdaiQuery(request.DanIds, request.Type));
response.AryOdaiDatas.AddRange(odaiDataList.Select(DanDataMappers.To3209OdaiData));
return Ok(response);
}

View File

@ -1,52 +1,33 @@
using GameDatabase.Entities;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
using Throw;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getdanscore_frqhg7q6.php")]
[ApiController]
public class GetDanScoreController : BaseController<GetDanScoreController>
{
private readonly IDanScoreDatumService danScoreDatumService;
public GetDanScoreController(IDanScoreDatumService danScoreDatumService)
{
this.danScoreDatumService = danScoreDatumService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getdanscore_frqhg7q6.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetDanScore([FromBody] GetDanScoreRequest request)
{
Logger.LogInformation("GetDanScore request : {Request}", request.Stringify());
var response = new GetDanScoreResponse
{
Result = 1
};
var commonResponse = await Mediator.Send(new GetDanScoreQuery(request.Baid, request.Type, request.DanIds));
var response = DanScoreMappers.MapTo3906(commonResponse);
var danType = (DanType)request.Type;
danType.Throw().IfOutOfRange();
var danScoreData = await danScoreDatumService.GetDanScoreDataList(request.Baid, danType);
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getdanscore.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetDanScore3209([FromBody] Models.v3209.GetDanScoreRequest request)
{
Logger.LogInformation("GetDanScore3209 request : {Request}", request.Stringify());
foreach (var danId in request.DanIds)
{
var datum = danScoreData.FirstOrDefault(scoreDatum => scoreDatum.DanId == danId, new DanScoreDatum());
var responseData = new GetDanScoreResponse.DanScoreData
{
DanId = danId,
ArrivalSongCnt = datum.ArrivalSongCount,
ComboCntTotal = datum.ComboCountTotal,
SoulGaugeTotal = datum.SoulGaugeTotal
};
foreach (var stageScoreDatum in datum.DanStageScoreData)
{
responseData.AryDanScoreDataStages.Add(ObjectMappers.DanStageDbToResponseMap.Apply(stageScoreDatum));
}
response.AryDanScoreDatas.Add(responseData);
}
var commonResponse = await Mediator.Send(new GetDanScoreQuery((uint)request.Baid, request.Type, request.DanIds));
var response = DanScoreMappers.MapTo3209(commonResponse);
return Ok(response);
}

View File

@ -1,38 +1,28 @@
namespace TaikoLocalServer.Controllers.Game;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getfolder_rffj346i.php")]
[ApiController]
public class GetFolderController : BaseController<GetFolderController>
{
private readonly IGameDataService gameDataService;
public GetFolderController(IGameDataService gameDataService)
{
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getfolder_rffj346i.php")]
[Produces("application/protobuf")]
public IActionResult GetFolder([FromBody] GetfolderRequest request)
public async Task<IActionResult> GetFolder([FromBody] GetfolderRequest request)
{
Logger.LogInformation("GetFolder request : {Request}", request.Stringify());
var response = new GetfolderResponse
{
Result = 1
};
foreach (var folderId in request.FolderIds)
{
gameDataService.GetFolderDictionary().TryGetValue(folderId, out var folderData);
if (folderData is null)
{
Logger.LogWarning("Requested folder id {Id} does not exist!", folderId);
continue;
}
response.AryEventfolderDatas.Add(folderData);
}
var commonResponse = await Mediator.Send(new GetFolderQuery(request.FolderIds));
var response = FolderDataMappers.MapTo3906(commonResponse);
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getfolder.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetFolder([FromBody] Models.v3209.GetfolderRequest request)
{
Logger.LogInformation("GetFolder3209 request : {Request}", request.Stringify());
var commonResponse = await Mediator.Send(new GetFolderQuery(request.FolderIds));
var response = FolderDataMappers.MapTo3209(commonResponse);
return Ok(response);
}
}

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[ApiController]
[Route("/v12r08_ww/chassis/getgenericmaster_ts8om3qd.php")]
public class GetGenericMasterController : BaseController<GetGenericMasterController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getgenericmaster_ts8om3qd.php")]
[Produces("application/protobuf")]
public IActionResult GetGenericMaster([FromBody] GetGenericMasterRequest request)
{
@ -17,6 +16,21 @@ public class GetGenericMasterController : BaseController<GetGenericMasterControl
EnableIdBit = FlagCalculator.GetBitArrayTrue(5000)
};
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getgenericmaster.php")]
[Produces("application/protobuf")]
public IActionResult GetGenericMaster([FromBody] Models.v3209.GetGenericMasterRequest request)
{
Logger.LogInformation("GetGenericMaster3209Request: {Request}", request.Stringify());
var response = new Models.v3209.GetGenericMasterResponse
{
Result = 1,
VerupNo = 2,
EnableIdBit = FlagCalculator.GetBitArrayTrue(5000)
};
return Ok(response);
}

View File

@ -3,36 +3,63 @@ using TaikoLocalServer.Settings;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getscorerank_1c8l7y61.php")]
[ApiController]
public class GetScoreRankController : BaseController<GetScoreRankController>
public class GetScoreRankController(ISongBestDatumService songBestDatumService, IOptions<ServerSettings> settings)
: BaseController<GetScoreRankController>
{
private readonly ISongBestDatumService songBestDatumService;
private readonly ServerSettings settings = settings.Value;
private readonly ServerSettings settings;
public GetScoreRankController(ISongBestDatumService songBestDatumService, IOptions<ServerSettings> settings)
{
this.songBestDatumService = songBestDatumService;
this.settings = settings.Value;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getscorerank_1c8l7y61.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetScoreRank([FromBody] GetScoreRankRequest request)
{
Logger.LogInformation("GetScoreRank request : {Request}", request.Stringify());
var scoreRankData = await Handle(request.Baid);
var response = new GetScoreRankResponse
{
Result = 1,
IkiScoreRankFlg = scoreRankData.IkiScoreRankFlg,
KiwamiScoreRankFlg = scoreRankData.KiwamiScoreRankFlg,
MiyabiScoreRankFlg = scoreRankData.MiyabiScoreRankFlg
};
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getscorerank.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetScoreRank3209([FromBody] Models.v3209.GetScoreRankRequest request)
{
Logger.LogInformation("GetScoreRank request : {Request}", request.Stringify());
var scoreRankData = await Handle((uint)request.Baid);
var response = new Models.v3209.GetScoreRankResponse
{
Result = 1,
IkiScoreRankFlg = scoreRankData.IkiScoreRankFlg,
KiwamiScoreRankFlg = scoreRankData.KiwamiScoreRankFlg,
MiyabiScoreRankFlg = scoreRankData.MiyabiScoreRankFlg
};
return Ok(response);
}
public record ScoreRankData(byte[] IkiScoreRankFlg, byte[] KiwamiScoreRankFlg, byte[] MiyabiScoreRankFlg);
private async Task<ScoreRankData> Handle(uint baid)
{
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
var kiwamiScores = new byte[songIdMax + 1];
var kiwamiScores = new byte[songIdMax + 1];
var miyabiScores = new ushort[songIdMax + 1];
var ikiScores = new ushort[songIdMax + 1];
var songBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
var ikiScores = new ushort[songIdMax + 1];
var songBestData = await songBestDatumService.GetAllSongBestData(baid);
for (var songId = 0; songId < songIdMax; songId++)
{
var id = songId;
kiwamiScores[songId] = songBestData
.Where(datum => datum.SongId == id &&
.Where(datum => datum.SongId == id &&
datum.BestScoreRank == ScoreRank.Dondaful)
.Aggregate((byte)0, (flag, datum) => FlagCalculator.ComputeKiwamiScoreRankFlag(flag, datum.Difficulty));
@ -46,14 +73,11 @@ public class GetScoreRankController : BaseController<GetScoreRankController>
datum.BestScoreRank is ScoreRank.Gold or ScoreRank.Purple or ScoreRank.Sakura)
.Aggregate((ushort)0, (flag, datum) => FlagCalculator.ComputeMiyabiOrIkiScoreRank(flag, datum.BestScoreRank, datum.Difficulty));
}
var response = new GetScoreRankResponse
{
Result = 1,
IkiScoreRankFlg = GZipBytesUtil.GetGZipBytes(ikiScores),
KiwamiScoreRankFlg = GZipBytesUtil.GetGZipBytes(kiwamiScores),
MiyabiScoreRankFlg = GZipBytesUtil.GetGZipBytes(miyabiScores)
};
return Ok(response);
return new ScoreRankData(
GZipBytesUtil.GetGZipBytes(ikiScores),
GZipBytesUtil.GetGZipBytes(kiwamiScores),
GZipBytesUtil.GetGZipBytes(miyabiScores)
);
}
}

View File

@ -1,34 +1,31 @@
namespace TaikoLocalServer.Controllers.Game;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getshopfolder_w4xik0uw.php")]
[ApiController]
public class GetShopFolderController : BaseController<GetShopFolderController>
{
private readonly IGameDataService gameDataService;
public GetShopFolderController(IGameDataService gameDataService)
{
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getshopfolder_w4xik0uw.php")]
[Produces("application/protobuf")]
public IActionResult GetShopFolder([FromBody] GetShopFolderRequest request)
public async Task<IActionResult> GetShopFolder([FromBody] GetShopFolderRequest request)
{
Logger.LogInformation("GetShopFolder request : {Request}", request.Stringify());
gameDataService.GetTokenDataDictionary().TryGetValue("shopTokenId", out var shopTokenId);
var commonResponse = await Mediator.Send(new GetShopFolderQuery());
var response = ShopFolderDataMappers.MapTo3906(commonResponse);
var shopFolderList = gameDataService.GetShopFolderList();
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getshopfolder.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetShopFolder3209([FromBody] Models.v3209.GetShopFolderRequest request)
{
Logger.LogInformation("GetShopFolder request : {Request}", request.Stringify());
var response = new GetShopFolderResponse
{
Result = 1,
TokenId = shopTokenId > 0 ? (uint)shopTokenId : 1,
VerupNo = 2
};
response.AryShopFolderDatas.AddRange(shopFolderList);
var commonResponse = await Mediator.Send(new GetShopFolderQuery());
var response = ShopFolderDataMappers.MapTo3209(commonResponse);
return Ok(response);
}

View File

@ -1,6 +1,8 @@
namespace TaikoLocalServer.Controllers.Game;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/getsongintroduction_66blw6is.php")]
[ApiController]
public class GetSongIntroductionController : BaseController<GetSongIntroductionController>
{
@ -11,29 +13,27 @@ public class GetSongIntroductionController : BaseController<GetSongIntroductionC
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/getsongintroduction_66blw6is.php")]
[Produces("application/protobuf")]
public IActionResult GetSongIntroduction([FromBody] GetSongIntroductionRequest request)
public async Task<IActionResult> GetSongIntroduction([FromBody] GetSongIntroductionRequest request)
{
Logger.LogInformation("GetSongIntroduction request : {Request}", request.Stringify());
var response = new GetSongIntroductionResponse
{
Result = 1
};
foreach (var setId in request.SetIds)
{
gameDataService.GetSongIntroDictionary().TryGetValue(setId, out var introData);
if (introData is null)
{
Logger.LogWarning("Requested set id {Id} does not exist!", setId);
continue;
}
response.ArySongIntroductionDatas.Add(introData);
}
var commonResponse = await Mediator.Send(new GetSongIntroductionQuery(request.SetIds));
var response = SongIntroductionDataMappers.MapTo3906(commonResponse);
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/getsongintroduction.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetSongIntroduction3209([FromBody] Models.v3209.GetSongIntroductionRequest request)
{
Logger.LogInformation("GetSongIntroduction request : {Request}", request.Stringify());
var commonResponse = await Mediator.Send(new GetSongIntroductionQuery(request.SetIds));
var response = SongIntroductionDataMappers.MapTo3209(commonResponse);
return Ok(response);
}
}

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/gettelop_o0cb2z3e.php")]
[ApiController]
public class GetTelopController : BaseController<GetTelopController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/gettelop_o0cb2z3e.php")]
[Produces("application/protobuf")]
public IActionResult GetTelop([FromBody] GettelopRequest request)
{
@ -18,7 +17,28 @@ public class GetTelopController : BaseController<GetTelopController>
Result = 1,
StartDatetime = startDateTime.ToString(Constants.DATE_TIME_FORMAT),
EndDatetime = endDateTime.ToString(Constants.DATE_TIME_FORMAT),
Telop = "Hello world",
Telop = "Hello 3906",
VerupNo = 1
};
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/gettelop.php")]
[Produces("application/protobuf")]
public IActionResult GetTelop3209([FromBody] Models.v3209.GettelopRequest request)
{
Logger.LogInformation("GetTelop request : {Request}", request.Stringify());
var startDateTime = DateTime.Now - TimeSpan.FromDays(999.0);
var endDateTime = DateTime.Now + TimeSpan.FromDays(999.0);
var response = new Models.v3209.GettelopResponse
{
Result = 1,
StartDatetime = startDateTime.ToString(Constants.DATE_TIME_FORMAT),
EndDatetime = endDateTime.ToString(Constants.DATE_TIME_FORMAT),
Telop = "Hello 3209",
VerupNo = 1
};

View File

@ -1,92 +1,34 @@
using System.Text.Json;
using GameDatabase.Entities;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
using Throw;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/gettokencount_iut9g23g.php")]
[ApiController]
public class GetTokenCountController : BaseController<GetTokenCountController>
{
private readonly IGameDataService gameDataService;
private readonly IUserDatumService userDatumService;
public GetTokenCountController(IUserDatumService userDatumService, IGameDataService gameDataService)
{
this.userDatumService = userDatumService;
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/gettokencount_iut9g23g.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetTokenCount([FromBody] GetTokenCountRequest request)
{
Logger.LogInformation("GetTokenCount request : {Request}", request.Stringify());
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
var commonResponse = await Mediator.Send(new GetTokenCountQuery(request.Baid));
var response = TokenCountDataMappers.MapTo3906(commonResponse);
var tokenDataDictionary = gameDataService.GetTokenDataDictionary();
tokenDataDictionary.TryGetValue("shopTokenId", out var shopTokenId);
tokenDataDictionary.TryGetValue("kaTokenId", out var kaTokenId);
tokenDataDictionary.TryGetValue("onePieceTokenId", out var onePieceTokenId);
tokenDataDictionary.TryGetValue("soshinaTokenId", out var soshinaTokenId);
tokenDataDictionary.TryGetValue("Yatsushika1TokenId", out var yatsushika1TokenId);
tokenDataDictionary.TryGetValue("Yatsushika2TokenId", out var yatsushika2TokenId);
tokenDataDictionary.TryGetValue("Yatsushika3TokenId", out var yatsushika3TokenId);
tokenDataDictionary.TryGetValue("Yatsushika4TokenId", out var yatsushika4TokenId);
tokenDataDictionary.TryGetValue("MaskedKid1TokenId", out var maskedKid1TokenId);
tokenDataDictionary.TryGetValue("MaskedKid2TokenId", out var maskedKid2TokenId);
tokenDataDictionary.TryGetValue("MaskedKid3TokenId", out var maskedKid3TokenId);
tokenDataDictionary.TryGetValue("MaskedKid4TokenId", out var maskedKid4TokenId);
tokenDataDictionary.TryGetValue("Kiyoshi1TokenId", out var kiyoshi1TokenId);
tokenDataDictionary.TryGetValue("Kiyoshi2TokenId", out var kiyoshi2TokenId);
tokenDataDictionary.TryGetValue("Kiyoshi3TokenId", out var kiyoshi3TokenId);
tokenDataDictionary.TryGetValue("Kiyoshi4TokenId", out var kiyoshi4TokenId);
tokenDataDictionary.TryGetValue("Amitie1TokenId", out var amitie1TokenId);
tokenDataDictionary.TryGetValue("Amitie2TokenId", out var amitie2TokenId);
tokenDataDictionary.TryGetValue("Amitie3TokenId", out var amitie3TokenId);
tokenDataDictionary.TryGetValue("Amitie4TokenId", out var amitie4TokenId);
tokenDataDictionary.TryGetValue("Machina1TokenId", out var machina1TokenId);
tokenDataDictionary.TryGetValue("Machina2TokenId", out var machina2TokenId);
tokenDataDictionary.TryGetValue("Machina3TokenId", out var machina3TokenId);
tokenDataDictionary.TryGetValue("Machina4TokenId", out var machina4TokenId);
return Ok(response);
}
[HttpPost("v12r00_cn/chassis/gettokencount.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetTokenCount3209([FromBody] Models.v3209.GetTokenCountRequest request)
{
Logger.LogInformation("GetTokenCount request : {Request}", request.Stringify());
int[] tokenDataIdArray =
{
shopTokenId, kaTokenId, onePieceTokenId, soshinaTokenId, yatsushika1TokenId, yatsushika2TokenId,
yatsushika3TokenId, yatsushika4TokenId, maskedKid1TokenId, maskedKid2TokenId, maskedKid3TokenId,
maskedKid4TokenId, kiyoshi1TokenId, kiyoshi2TokenId, kiyoshi3TokenId, kiyoshi4TokenId, amitie1TokenId,
amitie2TokenId, amitie3TokenId, amitie4TokenId, machina1TokenId, machina2TokenId, machina3TokenId,
machina4TokenId
};
var response = new GetTokenCountResponse
{
Result = 1
};
foreach (var tokenDataId in tokenDataIdArray)
{
if (tokenDataId <= 0) continue;
var castedTokenDataId = (uint)tokenDataId;
if (user.Tokens.All(token => token.Id != castedTokenDataId))
{
user.Tokens.Add(new Token
{
Id = (int)castedTokenDataId,
Count = 0
});
}
var tokenCount = user.Tokens.First(token => token.Id == castedTokenDataId).Count;
response.AryTokenCountDatas.Add(new GetTokenCountResponse.TokenCountData
{
TokenCount = tokenCount,
TokenId = castedTokenDataId
});
}
await userDatumService.UpdateUserDatum(user);
var commonResponse = await Mediator.Send(new GetTokenCountQuery((uint)request.Baid));
var response = TokenCountDataMappers.MapTo3209(commonResponse);
return Ok(response);
}

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[ApiController]
[Route("/v12r08_ww/chassis/heartbeat_hcv5akgr.php")]
public class HeartbeatController : BaseController<HeartbeatController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/heartbeat_hcv5akgr.php")]
[Produces("application/protobuf")]
public IActionResult HeartBeat([FromBody] HeartBeatRequest request)
{
@ -18,4 +17,18 @@ public class HeartbeatController : BaseController<HeartbeatController>
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/heartbeat.php")]
[Produces("application/protobuf")]
public IActionResult HeartBeat3209([FromBody] Models.v3209.HeartBeatRequest request)
{
Logger.LogInformation("Heartbeat request: {Request}", request.Stringify());
var response = new Models.v3209.HeartBeatResponse
{
Result = 1,
GameSvrStat = 1
};
return Ok(response);
}
}

View File

@ -1,139 +1,33 @@
using Microsoft.Extensions.Options;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
using TaikoLocalServer.Settings;
namespace TaikoLocalServer.Controllers.Game;
[ApiController]
[Route("/v12r08_ww/chassis/initialdatacheck_vaosv643.php")]
public class InitialDataCheckController : BaseController<InitialDataCheckController>
{
private readonly IGameDataService gameDataService;
private readonly ServerSettings settings;
public InitialDataCheckController(IGameDataService gameDataService, IOptions<ServerSettings> settings)
{
this.gameDataService = gameDataService;
this.settings = settings.Value;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/initialdatacheck_vaosv643.php")]
[Produces("application/protobuf")]
public IActionResult InitialDataCheck([FromBody] InitialdatacheckRequest request)
public async Task<IActionResult> InitialDataCheck([FromBody] InitialdatacheckRequest request)
{
Logger.LogInformation("Initial data check request: {Request}", request.Stringify());
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
var commonResponse = await Mediator.Send(new GetInitialDataQuery());
var response = InitialDataMappers.MapTo3906(commonResponse);
var musicList = gameDataService.GetMusicList();
var lockedSongsList = gameDataService.GetLockedSongsList();
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/initialdatacheck.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> InitialDataCheckCN([FromBody] Models.v3209.InitialdatacheckRequest request)
{
Logger.LogInformation("Initial data check request: {Request}", request.Stringify());
var enabledArray =
FlagCalculator.GetBitArrayFromIds(musicList, songIdMax, Logger);
var defaultSongList = musicList.Except(lockedSongsList);
var defaultSongFlg =
FlagCalculator.GetBitArrayFromIds(defaultSongList, songIdMax, Logger);
var defaultSongWithUraList = gameDataService.GetMusicWithUraList();
var uraReleaseBit =
FlagCalculator.GetBitArrayFromIds(defaultSongWithUraList, songIdMax, Logger);
var response = new InitialdatacheckResponse
{
Result = 1,
DefaultSongFlg = defaultSongFlg,
AchievementSongBit = enabledArray,
UraReleaseBit = uraReleaseBit,
SongIntroductionEndDatetime = DateTime.Now.AddYears(10).ToString(Constants.DATE_TIME_FORMAT),
};
var movieDataDictionary = gameDataService.GetMovieDataDictionary();
foreach (var movieData in movieDataDictionary) response.AryMovieInfoes.Add(movieData.Value);
var verupNo1 = new uint[] { 2, 3, 4, 5, 6, 7, 8, 13, 15, 24, 25, 26, 27, 28, 29, 30, 31, 104 };
var aryVerUp = verupNo1.Select(i => new InitialdatacheckResponse.VerupNoData1
{
MasterType = i,
VerupNo = 1
})
.ToList();
response.AryVerupNoData1s.AddRange(aryVerUp);
var danData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
var danDataDictionary = gameDataService.GetDanDataDictionary();
foreach (var danId in danDataDictionary.Keys)
{
gameDataService.GetDanDataDictionary().TryGetValue(danId, out var odaiData);
danData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
{
InfoId = danId,
VerupNo = odaiData?.VerupNo ?? 1
});
}
var verUp2Type101 = new InitialdatacheckResponse.VerupNoData2
{
MasterType = 101,
};
verUp2Type101.AryInformationDatas.AddRange(danData);
response.AryVerupNoData2s.Add(verUp2Type101);
var gaidenData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
var gaidenDataDictionary = gameDataService.GetGaidenDataDictionary();
foreach (var gaidenId in gaidenDataDictionary.Keys)
{
gaidenDataDictionary.TryGetValue(gaidenId, out var odaiData);
gaidenData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
{
InfoId = gaidenId,
VerupNo = odaiData?.VerupNo ?? 1
});
}
var verUp2Type102 = new InitialdatacheckResponse.VerupNoData2
{
MasterType = 102,
};
verUp2Type102.AryInformationDatas.AddRange(gaidenData);
response.AryVerupNoData2s.Add(verUp2Type102);
var eventFolderData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
var eventFolderDictionary = gameDataService.GetFolderDictionary();
foreach (var folderId in eventFolderDictionary.Keys)
{
eventFolderDictionary.TryGetValue(folderId, out var folderData);
eventFolderData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
{
InfoId = folderId,
VerupNo = folderData?.VerupNo ?? 1
});
}
var verUp2Type103 = new InitialdatacheckResponse.VerupNoData2
{
MasterType = 103,
};
verUp2Type103.AryInformationDatas.AddRange(eventFolderData);
response.AryVerupNoData2s.Add(verUp2Type103);
var songIntroData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
var songIntroDictionary = gameDataService.GetSongIntroDictionary();
foreach (var setId in songIntroDictionary.Select(item => item.Value.SetId))
{
songIntroDictionary.TryGetValue(setId, out var introData);
songIntroData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
{
InfoId = setId,
VerupNo = introData?.VerupNo ?? 1
});
}
var verUp2Type105 = new InitialdatacheckResponse.VerupNoData2
{
MasterType = 105,
};
verUp2Type105.AryInformationDatas.AddRange(songIntroData);
response.AryVerupNoData2s.Add(verUp2Type105);
response.AryChassisFunctionIds = new uint[] { 1, 2, 3 };
var commonResponse = await Mediator.Send(new GetInitialDataQuery());
var response = InitialDataMappers.MapTo3209(commonResponse);
return Ok(response);
}

View File

@ -1,73 +1,31 @@
using GameDatabase.Entities;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/mydonentry_3nrd7kwk.php")]
[ApiController]
public class MyDonEntryController : BaseController<MyDonEntryController>
{
private readonly IUserDatumService userDatumService;
private readonly ICardService cardService;
private readonly ICredentialService credentialService;
public MyDonEntryController(IUserDatumService userDatumService, ICardService cardService, ICredentialService credentialService)
{
this.userDatumService = userDatumService;
this.cardService = cardService;
this.credentialService = credentialService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/mydonentry_3nrd7kwk.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetMyDonEntry([FromBody] MydonEntryRequest request)
{
Logger.LogInformation("MyDonEntry request : {Request}", request.Stringify());
var newId = cardService.GetNextBaid();
var newUser = new UserDatum
{
Baid = newId,
MyDonName = request.MydonName,
MyDonNameLanguage = 0,
DisplayDan = true,
DisplayAchievement = true,
AchievementDisplayDifficulty = Difficulty.None,
ColorFace = 0,
ColorBody = 1,
ColorLimb = 3,
FavoriteSongsArray = "[]",
ToneFlgArray = "[0]",
TitleFlgArray = "[]",
CostumeFlgArray = "[[0],[0],[0],[0],[0]]",
GenericInfoFlgArray = "[]",
UnlockedSongIdList = "[]"
};
await userDatumService.InsertUserDatum(newUser);
await cardService.AddCard(new Card
{
AccessCode = request.AccessCode,
Baid = newId
});
await credentialService.AddCredential(new Credential
{
Baid = newId,
Password = "",
Salt = ""
});
var response = new MydonEntryResponse
{
Result = 1,
Baid = newId,
MydonName = request.MydonName,
MydonNameLanguage = 0
};
var commonResponse = await Mediator.Send(new AddMyDonEntryCommand(request.AccessCode, request.MydonName, request.MydonNameLanguage));
var response = MyDonEntryMappers.MapTo3906(commonResponse);
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/mydonentry.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> GetMyDonEntry3209([FromBody] Models.v3209.MydonEntryRequest request)
{
Logger.LogInformation("MyDonEntry request : {Request}", request.Stringify());
var commonResponse = await Mediator.Send(new AddMyDonEntryCommand(request.WechatQrStr, request.MydonName, request.MydonNameLanguage));
var response = MyDonEntryMappers.MapTo3209(commonResponse);
return Ok(response);
}
}

View File

@ -222,11 +222,16 @@ public class PlayResultController : BaseController<PlayResultController>
userdata.LastPlayDatetime = lastPlayDatetime;
userdata.LastPlayMode = playResultData.PlayMode;
userdata.ToneFlgArray =
UpdateJsonUintFlagArray(userdata.ToneFlgArray, playResultData.GetToneNoes, nameof(userdata.ToneFlgArray));
var tones = userdata.ToneFlgArray.ToList();
tones.AddRange(playResultData.GetToneNoes);
userdata.ToneFlgArray = tones.ToArray();
//UpdateJsonUintFlagArray(userdata.ToneFlgArray, playResultData.GetToneNoes, nameof(userdata.ToneFlgArray));
userdata.TitleFlgArray =
UpdateJsonUintFlagArray(userdata.TitleFlgArray, playResultData.GetTitleNoes, nameof(userdata.TitleFlgArray));
/*userdata.TitleFlgArray =
UpdateJsonUintFlagArray(userdata.TitleFlgArray, playResultData.GetTitleNoes, nameof(userdata.TitleFlgArray));*/
var titles = userdata.TitleFlgArray.ToList();
titles.AddRange(playResultData.GetTitleNoes);
userdata.TitleFlgArray = titles.ToArray();
userdata.CostumeFlgArray = UpdateJsonCostumeFlagArray(userdata.CostumeFlgArray,
new[]
@ -238,8 +243,11 @@ public class PlayResultController : BaseController<PlayResultController>
playResultData.GetCostumeNo5s
});
userdata.GenericInfoFlgArray =
UpdateJsonUintFlagArray(userdata.GenericInfoFlgArray, playResultData.GetGenericInfoNoes, nameof(userdata.GenericInfoFlgArray));
/*userdata.GenericInfoFlgArray =
UpdateJsonUintFlagArray(userdata.GenericInfoFlgArray, playResultData.GetGenericInfoNoes, nameof(userdata.GenericInfoFlgArray));*/
var genericInfo = userdata.GenericInfoFlgArray.ToList();
genericInfo.AddRange(playResultData.GetGenericInfoNoes);
userdata.GenericInfoFlgArray = genericInfo.ToArray();
var difficultyPlayedArray = new List<uint>
{

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/rewarditem.php")]
[ApiController]
public class RewardItemController : BaseController<RewardItemController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/rewarditem.php")]
[Produces("application/protobuf")]
public IActionResult RewardItem([FromBody] RewardItemRequest request)
{
@ -17,4 +16,18 @@ public class RewardItemController : BaseController<RewardItemController>
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/rewarditem.php")]
[Produces("application/protobuf")]
public IActionResult RewardItem3209([FromBody] Models.v3209.RewardItemRequest request)
{
Logger.LogInformation("RewardItem request : {Request}", request.Stringify());
var response = new Models.v3209.RewardItemResponse
{
Result = 1
};
return Ok(response);
}
}

View File

@ -1,81 +1,36 @@
using GameDatabase.Entities;
using TaikoLocalServer.Handlers;
using TaikoLocalServer.Mappers;
using Throw;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/selfbest_5nz47auu.php")]
[ApiController]
public class SelfBestController : BaseController<SelfBestController>
{
private readonly ISongBestDatumService songBestDatumService;
private readonly IGameDataService gameDataService;
public SelfBestController(ISongBestDatumService songBestDatumService, IGameDataService gameDataService)
{
this.songBestDatumService = songBestDatumService;
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/selfbest_5nz47auu.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> SelfBest([FromBody] SelfBestRequest request)
{
Logger.LogInformation("SelfBest request : {Request}", request.Stringify());
var response = new SelfBestResponse
{
Result = 1,
Level = request.Level
};
var requestDifficulty = (Difficulty)request.Level;
requestDifficulty.Throw().IfOutOfRange();
var playerBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
playerBestData = playerBestData
.Where(datum => datum.Difficulty == requestDifficulty ||
(datum.Difficulty == Difficulty.UraOni && requestDifficulty == Difficulty.Oni))
.ToList();
foreach (var songNo in request.ArySongNoes)
{
if (!gameDataService.GetMusicList().Contains(songNo))
{
Logger.LogWarning("Music no {No} is missing!", songNo);
continue;
}
var selfBestData = GetSongSelfBestData(playerBestData, songNo);
response.ArySelfbestScores.Add(selfBestData);
}
response.ArySelfbestScores.Sort((data, otherData) => data.SongNo.CompareTo(otherData.SongNo));
var commonResponse =
await Mediator.Send(new GetSelfBestQuery(request.Baid, request.Level, request.ArySongNoes));
var response = SelfBestMappers.MapTo3906(commonResponse);
return Ok(response);
}
private static SelfBestResponse.SelfBestData GetSongSelfBestData(IEnumerable<SongBestDatum> playerBestData, uint songNo)
[HttpPost("/v12r00_cn/chassis/selfbest.php")]
[Produces("application/protobuf")]
public async Task<IActionResult> SelfBest3209([FromBody] Models.v3209.SelfBestRequest request)
{
var songBestDatum = playerBestData.Where(datum => datum.SongId == songNo);
Logger.LogInformation("SelfBest3209 request : {Request}", request.Stringify());
var selfBestData = new SelfBestResponse.SelfBestData
{
SongNo = songNo,
};
var commonResponse =
await Mediator.Send(new GetSelfBestQuery((uint)request.Baid, request.Level, request.ArySongNoes));
var response = SelfBestMappers.MapTo3209(commonResponse);
foreach (var datum in songBestDatum)
{
if (datum.Difficulty == Difficulty.UraOni)
{
selfBestData.UraBestScore = datum.BestScore;
selfBestData.UraBestScoreRate = datum.BestRate;
continue;
}
selfBestData.SelfBestScore = datum.BestScore;
selfBestData.SelfBestScoreRate = datum.BestRate;
}
return selfBestData;
return Ok(response);
}
}

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/setanystring_mssxf3bo.php")]
[ApiController]
public class SetAnyStringController : BaseController<SetAnyStringController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/setanystring_mssxf3bo.php")]
[Produces("application/protobuf")]
public IActionResult SetAnyString([FromBody] SetAnyStringRequest request)
{
@ -17,4 +16,18 @@ public class SetAnyStringController : BaseController<SetAnyStringController>
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/setanystring.php")]
[Produces("application/protobuf")]
public IActionResult SetAnyString3209([FromBody] Models.v3209.SetAnyStringRequest request)
{
Logger.LogInformation("SetAnyString request : {Request}", request.Stringify());
var response = new Models.v3209.SetAnyStringResponse
{
Result = 1,
};
return Ok(response);
}
}

View File

@ -23,37 +23,9 @@ public class SongPurchaseController : BaseController<SongPurchaseController>
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
/*var tokenCountDict = new Dictionary<uint, int>();
try
{
tokenCountDict = !string.IsNullOrEmpty(user.TokenCountDict)
? JsonSerializer.Deserialize<Dictionary<uint, int>>(user.TokenCountDict)
: new Dictionary<uint, int>();
}
catch (JsonException e)
{
Logger.LogError(e, "Parsing TokenCountDict data for user with baid {Request} failed!", request.Baid);
}
tokenCountDict.ThrowIfNull("TokenCountDict should never be null");*/
Logger.LogInformation("Original UnlockedSongIdList: {UnlockedSongIdList}", user.UnlockedSongIdList);
var unlockedSongIdList = new List<uint>();
try
{
unlockedSongIdList = !string.IsNullOrEmpty(user.UnlockedSongIdList)
? JsonSerializer.Deserialize<List<uint>>(user.UnlockedSongIdList)
: new List<uint>();
}
catch (JsonException e)
{
Logger.LogError(e, "Parsing UnlockedSongIdList data for user with baid {Request} failed!", request.Baid);
}
unlockedSongIdList.ThrowIfNull("UnlockedSongIdList should never be null");
//if (tokenCountDict.ContainsKey(request.TokenId)) tokenCountDict[request.TokenId] -= (int)request.Price;
var unlockedSongIdList = user.UnlockedSongIdList.ToList();
var token = user.Tokens.FirstOrDefault(t => t.Id == request.TokenId);
if (token is not null && token.Count >= request.Price)
@ -68,7 +40,7 @@ public class SongPurchaseController : BaseController<SongPurchaseController>
if (!unlockedSongIdList.Contains(request.SongNo)) unlockedSongIdList.Add(request.SongNo);
user.UnlockedSongIdList = JsonSerializer.Serialize(unlockedSongIdList);
user.UnlockedSongIdList = unlockedSongIdList.ToArray();
Logger.LogInformation("Updated UnlockedSongIdList: {UnlockedSongIdList}", user.UnlockedSongIdList);

View File

@ -1,10 +1,9 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/tournamentcheck.php")]
[ApiController]
public class TournamentCheckController : BaseController<TournamentCheckController>
{
[HttpPost]
[HttpPost("/v12r08_ww/chassis/tournamentcheck.php")]
[Produces("application/protobuf")]
public IActionResult TournamentCheck([FromBody] TournamentcheckRequest request)
{
@ -17,4 +16,18 @@ public class TournamentCheckController : BaseController<TournamentCheckControlle
return Ok(response);
}
[HttpPost("/v12r00_cn/chassis/tournamentcheck.php")]
[Produces("application/protobuf")]
public IActionResult TournamentCheck3209([FromBody] Models.v3209.TournamentcheckRequest request)
{
Logger.LogInformation("TournamentCheck request : {Request}", request.Stringify());
var response = new Models.v3209.TournamentcheckResponse
{
Result = 1,
};
return Ok(response);
}
}

View File

@ -37,7 +37,7 @@ public class UserDataController : BaseController<UserDataController>
var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid);
var unlockedSongIdList = new List<uint>();
var unlockedSongIdList = userData.UnlockedSongIdList;/*new List<uint>();
try
{
unlockedSongIdList = !string.IsNullOrEmpty(userData.UnlockedSongIdList)
@ -49,7 +49,7 @@ public class UserDataController : BaseController<UserDataController>
Logger.LogError(e, "Parsing UnlockedSongIdList data for user with baid {Request} failed!", request.Baid);
}
unlockedSongIdList.ThrowIfNull("UnlockedSongIdList should never be null");
unlockedSongIdList.ThrowIfNull("UnlockedSongIdList should never be null");*/
var musicList = gameDataService.GetMusicList();
var lockedSongsList = gameDataService.GetLockedSongsList();
@ -63,7 +63,7 @@ public class UserDataController : BaseController<UserDataController>
var uraSongArray =
FlagCalculator.GetBitArrayFromIds(enabledUraMusicList, songIdMax, Logger);
var toneFlg = Array.Empty<uint>();
var toneFlg = userData.ToneFlgArray;/*Array.Empty<uint>();
try
{
toneFlg = JsonSerializer.Deserialize<uint[]>(userData.ToneFlgArray);
@ -75,19 +75,19 @@ public class UserDataController : BaseController<UserDataController>
// The only way to get a null is provide string "null" as input,
// which means database content need to be fixed, so better throw
toneFlg.ThrowIfNull("Tone flg should never be null!");
toneFlg.ThrowIfNull("Tone flg should never be null!");*/
// If toneFlg is empty, add 0 to it
if (toneFlg.Length == 0)
{
toneFlg = new uint[] { 0 };
userData.ToneFlgArray = JsonSerializer.Serialize(toneFlg);
userData.ToneFlgArray = toneFlg;//JsonSerializer.Serialize(toneFlg);
await userDatumService.UpdateUserDatum(userData);
}
var toneArray = FlagCalculator.GetBitArrayFromIds(toneFlg, gameDataService.GetToneFlagArraySize(), Logger);
var titleFlg = Array.Empty<uint>();
var titleFlg = userData.TitleFlgArray;/*Array.Empty<uint>();
try
{
titleFlg = JsonSerializer.Deserialize<uint[]>(userData.TitleFlgArray);
@ -99,7 +99,7 @@ public class UserDataController : BaseController<UserDataController>
// The only way to get a null is provide string "null" as input,
// which means database content need to be fixed, so better throw
titleFlg.ThrowIfNull("Title flg should never be null!");
titleFlg.ThrowIfNull("Title flg should never be null!");*/
var titleArray = FlagCalculator.GetBitArrayFromIds(titleFlg, gameDataService.GetTitleFlagArraySize(), Logger);
@ -123,7 +123,7 @@ public class UserDataController : BaseController<UserDataController>
recentSongs = recentSet.ToArray();
var favoriteSongs = Array.Empty<uint>();
var favoriteSongs = userData.FavoriteSongsArray;/*Array.Empty<uint>();
try
{
favoriteSongs = JsonSerializer.Deserialize<uint[]>(userData.FavoriteSongsArray);
@ -135,7 +135,7 @@ public class UserDataController : BaseController<UserDataController>
// The only way to get a null is provide string "null" as input,
// which means database content need to be fixed, so better throw
favoriteSongs.ThrowIfNull("Favorite song should never be null!");
favoriteSongs.ThrowIfNull("Favorite song should never be null!");*/
var defaultOptions = new byte[2];
BinaryPrimitives.WriteInt16LittleEndian(defaultOptions, userData.OptionSetting);

View File

@ -1,6 +1,5 @@
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r08_ww/chassis/verifyqrcode_ku5ra5q7.php")]
[ApiController]
public class VerifyQrCodeController : BaseController<VerifyQrCodeController>
{
@ -11,27 +10,39 @@ public class VerifyQrCodeController : BaseController<VerifyQrCodeController>
this.gameDataService = gameDataService;
}
[HttpPost]
[HttpPost("/v12r08_ww/chassis/verifyqrcode_ku5ra5q7.php")]
[Produces("application/protobuf")]
public IActionResult VerifyQrCode([FromBody] VerifyQrcodeRequest request)
{
Logger.LogInformation("VerifyQrCode request : {Request}", request.Stringify());
var qrCodeDataDictionary = gameDataService.GetQRCodeDataDictionary();
qrCodeDataDictionary.TryGetValue(request.QrcodeSerial, out var qrCodeId);
if (qrCodeId == 0)
{
Logger.LogWarning("Requested QR code serial {Serial} does not exist!", request.QrcodeSerial);
}
var qrCodeId = VerifyQr(request.QrcodeSerial);
var response = new VerifyQrcodeResponse
{
Result = 1,
QrcodeId = qrCodeId
QrcodeId = (uint)qrCodeId
};
if (qrCodeId == -1)
{
response.Result = 51;
}
return Ok(response);
}
private int VerifyQr(string serial)
{
var qrCodeDataDictionary = gameDataService.GetQRCodeDataDictionary();
qrCodeDataDictionary.TryGetValue(serial, out var qrCodeId);
if (qrCodeId == 0)
{
Logger.LogWarning("Requested QR code serial {Serial} does not exist!", serial);
return -1;
}
return (int)qrCodeId;
}
}

View File

@ -2,9 +2,10 @@
global using Microsoft.AspNetCore.Mvc;
global using Microsoft.EntityFrameworkCore;
global using ProtoBuf;
global using MediatR;
global using Swan.Formatters;
global using SharedProject.Enums;
global using taiko.game;
global using TaikoLocalServer.Models.v3906;
global using TaikoLocalServer.Common;
global using TaikoLocalServer.Common.Utils;
global using TaikoLocalServer.Models;

View File

@ -0,0 +1,64 @@
using GameDatabase.Context;
using GameDatabase.Entities;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Handlers;
public record AddMyDonEntryCommand(string AccessCode, string Name, uint Language) : IRequest<CommonMyDonEntryResponse>;
public class AddMyDonEntryCommandHandler(TaikoDbContext context, ILogger<AddMyDonEntryCommandHandler> logger)
: IRequestHandler<AddMyDonEntryCommand, CommonMyDonEntryResponse>
{
public async Task<CommonMyDonEntryResponse> Handle(AddMyDonEntryCommand request, CancellationToken cancellationToken)
{
var nextBaid = await context.Cards.Select(card => card.Baid)
.DefaultIfEmpty(0u)
.MaxAsync(cancellationToken) + 1;
var newUser = new UserDatum
{
Baid = nextBaid,
MyDonName = request.Name,
MyDonNameLanguage = request.Language,
DisplayDan = true,
DisplayAchievement = true,
AchievementDisplayDifficulty = Difficulty.None,
ColorFace = 0,
ColorBody = 1,
ColorLimb = 3,
FavoriteSongsArray = [],
ToneFlgArray = [0],
TitleFlgArray = [],
CostumeFlgArray = "[[0],[0],[0],[0],[0]]",
GenericInfoFlgArray = [],
UnlockedSongIdList = []
};
context.UserData.Add(newUser);
var newCard = new Card
{
AccessCode = request.AccessCode,
Baid = nextBaid
};
context.Cards.Add(newCard);
var newCredential = new Credential
{
Baid = nextBaid,
Password = "",
Salt = ""
};
context.Credentials.Add(newCredential);
await context.SaveChangesAsync(cancellationToken);
var response = new CommonMyDonEntryResponse
{
Result = 1,
Baid = nextBaid,
MydonName = request.Name,
MydonNameLanguage = request.Language,
ComSvrResult = 1,
AccessCode = request.AccessCode
};
return response;
}
}

View File

@ -0,0 +1,53 @@
using GameDatabase.Context;
using GameDatabase.Entities;
using MediatR;
using TaikoLocalServer.Models.Application;
using Throw;
namespace TaikoLocalServer.Handlers;
public record AddTokenCountCommand(CommonAddTokenCountRequest Request) : IRequest;
public class AddTokenCountCommandHandler : IRequestHandler<AddTokenCountCommand>
{
private readonly TaikoDbContext context;
private readonly ILogger<AddTokenCountCommandHandler> logger;
public AddTokenCountCommandHandler(TaikoDbContext context, ILogger<AddTokenCountCommandHandler> logger)
{
this.context = context;
this.logger = logger;
}
public async Task Handle(AddTokenCountCommand command, CancellationToken cancellationToken)
{
var request = command.Request;
var user = await context.UserData
.Include(userDatum => userDatum.Tokens)
.FirstOrDefaultAsync(datum => datum.Baid == request.Baid, cancellationToken);
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
foreach (var addTokenCountData in request.AryAddTokenCountDatas)
{
var tokenId = addTokenCountData.TokenId;
var addTokenCount = addTokenCountData.AddTokenCount;
var token = user.Tokens.FirstOrDefault(t => t.Id == tokenId);
if (token is not null)
{
token.Count += addTokenCount;
}
else
{
user.Tokens.Add(new Token
{
Id = (int)tokenId,
Count = addTokenCount
});
}
}
context.Update(user);
await context.SaveChangesAsync(cancellationToken);
}
}

View File

@ -0,0 +1,146 @@
using System.Text.Json;
using GameDatabase.Context;
using MediatR;
using TaikoLocalServer.Models.Application;
using Throw;
namespace TaikoLocalServer.Handlers;
public record BaidQuery(string AccessCode) : IRequest<CommonBaidResponse>;
public class BaidQueryHandler : IRequestHandler<BaidQuery, CommonBaidResponse>
{
private readonly TaikoDbContext context;
private readonly ILogger<BaidQueryHandler> logger;
private readonly IGameDataService gameDataService;
public BaidQueryHandler(TaikoDbContext context, ILogger<BaidQueryHandler> logger, IGameDataService gameDataService)
{
this.context = context;
this.logger = logger;
this.gameDataService = gameDataService;
}
public async Task<CommonBaidResponse> Handle(BaidQuery request, CancellationToken cancellationToken)
{
var card = await context.Cards.FindAsync(request.AccessCode);
if (card is null)
{
logger.LogInformation("New user with access code {AccessCode}", request.AccessCode);
return new CommonBaidResponse
{
IsNewUser = true,
Baid = context.Cards.Any() ? context.Cards.ToList().Max(c => c.Baid) + 1 : 1
};
}
var baid = card.Baid;
var userData = await context.UserData.FindAsync(baid);
userData.ThrowIfNull($"User not found for card with Baid {baid}!");
var songBestData = context.SongBestData.Where(datum => datum.Baid == baid);
var achievementDisplayDifficulty = userData.AchievementDisplayDifficulty;
if (achievementDisplayDifficulty == Difficulty.None)
{
achievementDisplayDifficulty = songBestData
.Where(datum => datum.BestCrown >= CrownType.Clear)
.Select(datum => datum.Difficulty)
.DefaultIfEmpty(Difficulty.Easy)
.Max();
}
// For each crown type, calculate how many songs have that crown type
var crownCountData = await songBestData
.Where(datum => datum.BestCrown >= CrownType.Clear)
.GroupBy(datum => datum.BestCrown)
.ToDictionaryAsync(datums => datums.Key, datums => (uint)datums.Count(), cancellationToken);
var crownCount = new uint[3];
foreach (var crownType in Enum.GetValues<CrownType>())
{
if (crownType != CrownType.None)
{
crownCount[(int)crownType - 1] = crownCountData.GetValueOrDefault(crownType, (uint)0);
}
}
var scoreRankData = await songBestData
.Where(datum => datum.BestCrown >= CrownType.Clear)
.GroupBy(datum => datum.BestScoreRank)
.ToDictionaryAsync(datums => datums.Key, datums => (uint)datums.Count(), cancellationToken);
var scoreRankCount = new uint[7];
foreach (var scoreRank in Enum.GetValues<ScoreRank>())
{
if (scoreRank != ScoreRank.None)
{
scoreRankCount[(int)scoreRank - 2] = scoreRankData.GetValueOrDefault(scoreRank, (uint)0);
}
}
var costumeData = JsonHelper.GetCostumeDataFromUserData(userData, logger);
var costumeArrays = JsonHelper.GetCostumeUnlockDataFromUserData(userData, logger);
var costumeFlagArrays = gameDataService.GetCostumeFlagArraySizes()
.Select((size, index) => FlagCalculator.GetBitArrayFromIds(costumeArrays[index], size, logger))
.ToList();
var danData = await context.DanScoreData
.Where(datum => datum.Baid == baid && datum.DanType == DanType.Normal)
.Include(datum => datum.DanStageScoreData).ToListAsync(cancellationToken);
var gaidenData = await context.DanScoreData
.Where(datum => datum.Baid == baid && datum.DanType == DanType.Gaiden)
.Include(datum => datum.DanStageScoreData).ToListAsync(cancellationToken);
var maxDan = danData.Where(datum => datum.ClearState != DanClearState.NotClear)
.Select(datum => datum.DanId)
.DefaultIfEmpty()
.Max();
var danDataDictionary = gameDataService.GetCommonDanDataDictionary();
var danIdList = danDataDictionary.Keys.ToList();
var gotDanFlagArray = FlagCalculator.ComputeGotDanFlags(danData, danIdList);
var gaidenDataDictionary = gameDataService.GetCommonGaidenDataDictionary();
var gaidenIdList = gaidenDataDictionary.Keys.ToList();
var gotGaidenFlagArray = FlagCalculator.ComputeGotDanFlags(gaidenData, gaidenIdList);
var genericInfoFlg = userData.GenericInfoFlgArray;
var genericInfoFlgLength = genericInfoFlg.Any() ? genericInfoFlg.Max() + 1 : 0;
var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, logger);
var aiRank = (uint)(userData.AiWinCount / 10);
if (aiRank > 11)
{
aiRank = 11;
}
return new CommonBaidResponse
{
IsNewUser = false,
Baid = baid,
MyDonName = userData.MyDonName,
MyDonNameLanguage = userData.MyDonNameLanguage,
AryCrownCounts = crownCount,
AryScoreRankCounts = scoreRankCount,
ColorBody = userData.ColorBody,
ColorFace = userData.ColorFace,
ColorLimb = userData.ColorLimb,
CostumeData = costumeData,
CostumeFlagArrays = costumeFlagArrays,
DisplayDan = userData.DisplayDan,
DispAchievementType = (uint)achievementDisplayDifficulty,
GenericInfoFlg = genericInfoFlgArray,
GotDanFlg = gotDanFlagArray,
GotDanMax = maxDan,
GotGaidenFlg = gotGaidenFlagArray,
IsDispAchievementOn = userData.DisplayAchievement,
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT),
LastPlayMode = userData.LastPlayMode,
SelectedToneId = userData.SelectedToneId,
Title = userData.Title,
TitlePlateId = userData.TitlePlateId
};
}
}

View File

@ -0,0 +1,35 @@
using GameDatabase.Context;
using MediatR;
using TaikoLocalServer.Models.Application;
using Throw;
namespace TaikoLocalServer.Handlers;
public record GetAiDataQuery(uint Baid) : IRequest<CommonAiDataResponse>;
public class GetAiDataQueryHandler : IRequestHandler<GetAiDataQuery, CommonAiDataResponse>
{
private readonly TaikoDbContext context;
private readonly ILogger<GetAiDataQueryHandler> logger;
public GetAiDataQueryHandler(TaikoDbContext context, ILogger<GetAiDataQueryHandler> logger)
{
this.context = context;
this.logger = logger;
}
public async Task<CommonAiDataResponse> Handle(GetAiDataQuery request, CancellationToken cancellationToken)
{
var user = await context.UserData.FirstOrDefaultAsync(datum => datum.Baid == request.Baid);
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
var response = new CommonAiDataResponse
{
TotalWinnings = (uint)user.AiWinCount,
InputMedian = "1",
InputVariance = "0"
};
return response;
}
}

View File

@ -0,0 +1,34 @@
using GameDatabase.Context;
using TaikoLocalServer.Mappers;
using TaikoLocalServer.Models.Application;
using Throw;
namespace TaikoLocalServer.Handlers;
public record GetAiScoreQuery(uint Baid, uint SongId, uint Level) : IRequest<CommonAiScoreResponse>;
public class GetAiScoreQueryHandler : IRequestHandler<GetAiScoreQuery, CommonAiScoreResponse>
{
private readonly TaikoDbContext context;
private readonly ILogger<GetAiScoreQueryHandler> logger;
public GetAiScoreQueryHandler(TaikoDbContext context, ILogger<GetAiScoreQueryHandler> logger)
{
this.context = context;
this.logger = logger;
}
public async Task<CommonAiScoreResponse> Handle(GetAiScoreQuery request, CancellationToken cancellationToken)
{
var difficulty = (Difficulty)request.Level;
difficulty.Throw().IfOutOfRange();
var aiData = await context.AiScoreData.Where(datum => datum.Baid == request.Baid &&
datum.SongId == request.SongId &&
datum.Difficulty == difficulty)
.Include(datum => datum.AiSectionScoreData)
.FirstOrDefaultAsync(cancellationToken);
return aiData is null ? new CommonAiScoreResponse() : AiScoreMappers.MapToCommonAiScoreResponse(aiData);
}
}

View File

@ -0,0 +1,52 @@
using SharedProject.Models;
using Throw;
namespace TaikoLocalServer.Handlers;
public record GetDanOdaiQuery(uint[] DanIds, uint Type) : IRequest<List<DanData>>;
public class GetDanOdaiQueryHandler : IRequestHandler<GetDanOdaiQuery, List<DanData>>
{
private readonly IGameDataService gameDataService;
public GetDanOdaiQueryHandler(IGameDataService gameDataService)
{
this.gameDataService = gameDataService;
}
public Task<List<DanData>> Handle(GetDanOdaiQuery request, CancellationToken cancellationToken)
{
var type = (DanType)request.Type;
type.Throw().IfOutOfRange();
var danDataList = new List<DanData>();
switch (type)
{
case DanType.Normal:
var danDataDictionary = gameDataService.GetCommonDanDataDictionary();
foreach (var danId in request.DanIds)
{
if (danDataDictionary.TryGetValue(danId, out var danData))
{
danDataList.Add(danData);
}
}
break;
case DanType.Gaiden:
var gaidenDataDictionary = gameDataService.GetCommonGaidenDataDictionary();
foreach (var danId in request.DanIds)
{
if (gaidenDataDictionary.TryGetValue(danId, out var danData))
{
danDataList.Add(danData);
}
}
break;
default:
throw new ApplicationException("Impossible");
}
return Task.FromResult(danDataList);
}
}

View File

@ -0,0 +1,73 @@
using GameDatabase.Context;
using GameDatabase.Entities;
using TaikoLocalServer.Models.Application;
using Throw;
namespace TaikoLocalServer.Handlers;
public record GetDanScoreQuery(uint Baid, uint Type, uint[] DanIds) : IRequest<CommonDanScoreDataResponse>;
public class GetDanScoreQueryHandler : IRequestHandler<GetDanScoreQuery, CommonDanScoreDataResponse>
{
private readonly ILogger<GetDanScoreQueryHandler> logger;
private readonly TaikoDbContext context;
public GetDanScoreQueryHandler(ILogger<GetDanScoreQueryHandler> logger, TaikoDbContext context)
{
this.logger = logger;
this.context = context;
}
public async Task<CommonDanScoreDataResponse> Handle(GetDanScoreQuery request, CancellationToken cancellationToken)
{
var danType = (DanType)request.Type;
danType.Throw().IfOutOfRange();
var idList = request.DanIds.ToList();
// Select the dan score data from the database where baid and type matches and danid is in the list of danids
var danScoreData = await context.DanScoreData
.Where(d => d.Baid == request.Baid && d.DanType == danType &&idList.Contains(d.DanId))
.Include(d => d.DanStageScoreData)
.ToListAsync(cancellationToken);
var response = new CommonDanScoreDataResponse
{
Result = 1
};
foreach (var danScoreDatum in danScoreData)
{
var responseData = new CommonDanScoreDataResponse.DanScoreData
{
DanId = danScoreDatum.DanId,
ArrivalSongCnt = danScoreDatum.ArrivalSongCount,
ComboCntTotal = danScoreDatum.ComboCountTotal,
SoulGaugeTotal = danScoreDatum.SoulGaugeTotal
};
for (int i = 0; i < danScoreDatum.ArrivalSongCount; i++)
{
var songNumber = i;
var stageScoreDatum = danScoreDatum.DanStageScoreData.FirstOrDefault(d => d.SongNumber == songNumber);
if (stageScoreDatum is null)
{
logger.LogWarning("Stage score data for dan {DanId} song number {SongNumber} not found", danScoreDatum.DanId, songNumber);
stageScoreDatum = new DanStageScoreDatum();
}
responseData.AryDanScoreDataStages.Add(new CommonDanScoreDataResponse.DanScoreDataStage
{
PlayScore = stageScoreDatum.PlayScore,
GoodCnt = stageScoreDatum.GoodCount,
OkCnt = stageScoreDatum.OkCount,
NgCnt = stageScoreDatum.BadCount,
PoundCnt = stageScoreDatum.DrumrollCount,
HitCnt = stageScoreDatum.TotalHitCount,
ComboCnt = stageScoreDatum.ComboCount,
HighScore = stageScoreDatum.HighScore
});
}
response.AryDanScoreDatas.Add(responseData);
}
return response;
}
}

View File

@ -0,0 +1,29 @@
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Handlers;
public record GetFolderQuery(uint[] FolderIds) : IRequest<CommonGetFolderResponse>;
public class GetFolderQueryHandler(ILogger<GetFolderQueryHandler> logger, IGameDataService gameDataService)
: IRequestHandler<GetFolderQuery, CommonGetFolderResponse>
{
public Task<CommonGetFolderResponse> Handle(GetFolderQuery request, CancellationToken cancellationToken)
{
var response = new CommonGetFolderResponse
{
Result = 1
};
var eventFolders = gameDataService.GetEventFolderDictionary();
foreach (var folderId in request.FolderIds)
{
eventFolders.TryGetValue(folderId, out var folderData);
if (folderData is null)
{
logger.LogWarning("Folder data for folder {FolderId} not found", folderId);
continue;
}
response.AryEventfolderDatas.Add(folderData);
}
return Task.FromResult(response);
}
}

View File

@ -0,0 +1,102 @@
using System.Collections.Immutable;
using Microsoft.Extensions.Options;
using SharedProject.Models;
using TaikoLocalServer.Models.Application;
using TaikoLocalServer.Settings;
namespace TaikoLocalServer.Handlers;
public record GetInitialDataQuery : IRequest<CommonInitialDataCheckResponse>;
public class GetInitialDataQueryHandler(IGameDataService gameDataService,
ILogger<GetInitialDataQueryHandler> logger,
IOptions<ServerSettings> settings)
: IRequestHandler<GetInitialDataQuery, CommonInitialDataCheckResponse>
{
private readonly ServerSettings settings = settings.Value;
public Task<CommonInitialDataCheckResponse> Handle(GetInitialDataQuery request, CancellationToken cancellationToken)
{
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
var musicList = gameDataService.GetMusicList();
var lockedSongsList = gameDataService.GetLockedSongsList();
var enabledArray =
FlagCalculator.GetBitArrayFromIds(musicList, songIdMax, logger);
var defaultSongList = musicList.Except(lockedSongsList);
var defaultSongFlg =
FlagCalculator.GetBitArrayFromIds(defaultSongList, songIdMax, logger);
var defaultSongWithUraList = gameDataService.GetMusicWithUraList();
var uraReleaseBit =
FlagCalculator.GetBitArrayFromIds(defaultSongWithUraList, songIdMax, logger);
var response = new CommonInitialDataCheckResponse
{
Result = 1,
DefaultSongFlg = defaultSongFlg,
AchievementSongBit = enabledArray,
UraReleaseBit = uraReleaseBit,
SongIntroductionEndDatetime = DateTime.Now.AddYears(10).ToString(Constants.DATE_TIME_FORMAT),
ServerCurrentDatetime = (ulong)DateTimeOffset.Now.ToUnixTimeSeconds()
};
var movieDataDictionary = gameDataService.GetMovieDataDictionary();
foreach (var movieData in movieDataDictionary)
{
response.AryMovieInfoes.Add(movieData.Value);
}
// TODO: Figure out what they are individually
var verupNo1 = new uint[] { 2, 3, 4, 5, 6, 7, 8, 13, 15, 24, 25, 26, 27, 28, 29, 30, 31, 104 };
var aryVerUp = verupNo1.Select(i => new CommonInitialDataCheckResponse.VerupNoData1
{
MasterType = i,
VerupNo = 1
}).ToList();
response.AryVerupNoData1s.AddRange(aryVerUp);
var commonDanDataDictionary = gameDataService.GetCommonDanDataDictionary();
var commonGaidenDataDictionary = gameDataService.GetCommonGaidenDataDictionary();
var eventFolderDictionary = gameDataService.GetEventFolderDictionary();
var songIntroDictionary = gameDataService.GetSongIntroductionDictionary();
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)
];
response.AryVerupNoData2s.AddRange(verupNo2List);
response.AryChassisFunctionIds =
[
Constants.FUNCTION_ID_DANI_AVAILABLE,
Constants.FUNCTION_ID_DANI_FOLDER_AVAILABLE,
Constants.FUNCTION_ID_AI_BATTLE_AVAILABLE
];
return Task.FromResult(response);
}
private CommonInitialDataCheckResponse.VerupNoData2 GetVerupNoData2<T>(uint masterType, ImmutableDictionary<uint, T> dictionary)
where T:IVerupNo
{
var infoData = dictionary.Select(pair => new CommonInitialDataCheckResponse.VerupNoData2.InformationData
{
InfoId = pair.Key,
VerupNo = pair.Value.VerupNo
}).ToList();
return new CommonInitialDataCheckResponse.VerupNoData2
{
MasterType = masterType,
AryInformationDatas = infoData
};
}
}

View File

@ -0,0 +1,52 @@
using GameDatabase.Context;
using MudBlazor.Extensions;
using SharedProject.Models;
using TaikoLocalServer.Models.Application;
using Throw;
namespace TaikoLocalServer.Handlers;
public record GetSelfBestQuery(uint Baid, uint Difficulty, uint[] SongIdList) : IRequest<CommonSelfBestResponse>;
public class GetSelfBestQueryHandler(IGameDataService gameDataService, TaikoDbContext context, ILogger<GetSelfBestQueryHandler> logger)
: IRequestHandler<GetSelfBestQuery, CommonSelfBestResponse>
{
public async Task<CommonSelfBestResponse> Handle(GetSelfBestQuery request, CancellationToken cancellationToken)
{
var requestDifficulty = (Difficulty)request.Difficulty;
requestDifficulty.Throw().IfOutOfRange();
var allSongSet = gameDataService.GetMusicList().ToHashSet();
var requestSet = request.SongIdList.ToHashSet();
if (!requestSet.IsSubsetOf(allSongSet))
{
var invalidSongIds = requestSet.Except(allSongSet);
logger.LogWarning("Invalid song IDs: {InvalidSongIds}", invalidSongIds.Stringify());
requestSet.ExceptWith(invalidSongIds);
}
var selfbestScores = await context.SongBestData
.Where(datum => datum.Baid == request.Baid &&
requestSet.Contains(datum.SongId) &&
(datum.Difficulty == requestDifficulty ||
(datum.Difficulty == Difficulty.UraOni && requestDifficulty == Difficulty.Oni)))
.OrderBy(datum => datum.SongId)
.ToListAsync(cancellationToken);
var response = new CommonSelfBestResponse
{
Result = 1,
Level = request.Difficulty,
ArySelfbestScores = selfbestScores.ConvertAll(datum => new CommonSelfBestResponse.SelfBestData
{
SongNo = datum.SongId,
SelfBestScore = datum.BestScore,
UraBestScore = datum.Difficulty == Difficulty.UraOni ? datum.BestScore : 0,
SelfBestScoreRate = datum.BestRate,
UraBestScoreRate = datum.Difficulty == Difficulty.UraOni ? datum.BestRate : 0
})
};
return response;
}
}

View File

@ -0,0 +1,26 @@
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Handlers;
public record GetShopFolderQuery : IRequest<CommonGetShopFolderResponse>;
public class GetShopFolderHandler(IGameDataService gameDataService)
: IRequestHandler<GetShopFolderQuery, CommonGetShopFolderResponse>
{
public Task<CommonGetShopFolderResponse> Handle(GetShopFolderQuery request, CancellationToken cancellationToken)
{
gameDataService.GetTokenDataDictionary().TryGetValue("shopTokenId", out var shopTokenId);
var shopFolderList = gameDataService.GetShopFolderList();
var response = new CommonGetShopFolderResponse
{
Result = 1,
TokenId = shopTokenId > 0 ? (uint)shopTokenId : 1,
VerupNo = 2,
AryShopFolderDatas = shopFolderList
};
return Task.FromResult(response);
}
}

View File

@ -0,0 +1,32 @@
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Handlers;
public record GetSongIntroductionQuery(uint[] SetIds) : IRequest<CommonGetSongIntroductionResponse>;
public class GetSongIntroductionQueryHandler(IGameDataService gameDataService, ILogger<GetSongIntroductionQueryHandler> logger)
: IRequestHandler<GetSongIntroductionQuery, CommonGetSongIntroductionResponse>
{
public Task<CommonGetSongIntroductionResponse> Handle(GetSongIntroductionQuery request, CancellationToken cancellationToken)
{
var response = new CommonGetSongIntroductionResponse
{
Result = 1
};
foreach (var setId in request.SetIds)
{
gameDataService.GetSongIntroductionDictionary().TryGetValue(setId, out var introData);
if (introData is null)
{
logger.LogWarning("Requested set id {Id} does not exist!", setId);
continue;
}
response.ArySongIntroductionDatas.Add(introData);
}
return Task.FromResult(response);
}
}

View File

@ -0,0 +1,50 @@
using GameDatabase.Context;
using GameDatabase.Entities;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Handlers;
public record GetTokenCountQuery(uint Baid) : IRequest<CommonGetTokenCountResponse>;
public class GetTokenCountQueryHandler(IGameDataService gameDataService,
TaikoDbContext context,
ILogger<GetTokenCountQueryHandler> logger)
: IRequestHandler<GetTokenCountQuery, CommonGetTokenCountResponse>
{
public async Task<CommonGetTokenCountResponse> Handle(GetTokenCountQuery request, CancellationToken cancellationToken)
{
var response = new CommonGetTokenCountResponse
{
Result = 1
};
string[] tokenNames = ["shopTokenId", "kaTokenId", "onePieceTokenId", "soshinaTokenId", "Yatsushika1TokenId", "Yatsushika2TokenId",
"Yatsushika3TokenId", "Yatsushika4TokenId", "MaskedKid1TokenId", "MaskedKid2TokenId", "MaskedKid3TokenId", "MaskedKid4TokenId",
"Kiyoshi1TokenId", "Kiyoshi2TokenId", "Kiyoshi3TokenId", "Kiyoshi4TokenId", "Amitie1TokenId", "Amitie2TokenId", "Amitie3TokenId",
"Amitie4TokenId", "Machina1TokenId", "Machina2TokenId", "Machina3TokenId", "Machina4TokenId"];
var tokenDataDictionary = gameDataService.GetTokenDataDictionary();
foreach (var tokenName in tokenNames)
{
tokenDataDictionary.TryGetValue(tokenName, out var tokenId);
if (tokenId <= 0) continue;
var token = await context.Tokens.FirstOrDefaultAsync(t => t.Baid == request.Baid &&
t.Id == tokenId,
cancellationToken) ?? new Token
{
Id = tokenId,
Count = 0
};
response.AryTokenCountDatas.Add(new CommonTokenCountData
{
TokenId = (uint)token.Id,
TokenCount = token.Count
});
context.Tokens.Update(token);
}
await context.SaveChangesAsync(cancellationToken);
return response;
}
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class AddTokenCountRequestMapper
{
public static partial CommonAddTokenCountRequest Map(AddTokenCountRequest request);
public static partial CommonAddTokenCountRequest Map(Models.v3209.AddTokenCountRequest request);
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class AiDataResponseMapper
{
public static partial GetAiDataResponse MapTo3906(CommonAiDataResponse response);
public static partial Models.v3209.GetAiDataResponse MapTo3209(CommonAiDataResponse response);
}

View File

@ -0,0 +1,16 @@
using GameDatabase.Entities;
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class AiScoreMappers
{
[MapProperty(nameof(AiScoreDatum.AiSectionScoreData), nameof(CommonAiScoreResponse.AryBestSectionDatas))]
public static partial CommonAiScoreResponse MapToCommonAiScoreResponse(AiScoreDatum datum);
public static partial GetAiScoreResponse MapTo3906(CommonAiScoreResponse response);
public static partial Models.v3209.GetAiScoreResponse MapTo3209(CommonAiScoreResponse response);
}

View File

@ -0,0 +1,50 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class BaidResponseMapper
{
public static partial BAIDResponse MapTo3906(CommonBaidResponse commonBaidResponse);
public static BAIDResponse Map3906WithPostProcess(CommonBaidResponse commonBaidResponse)
{
var response = MapTo3906(commonBaidResponse);
response.AryCostumedata = new BAIDResponse.CostumeData
{
Costume1 = commonBaidResponse.CostumeData[0],
Costume2 = commonBaidResponse.CostumeData[1],
Costume3 = commonBaidResponse.CostumeData[2],
Costume4 = commonBaidResponse.CostumeData[3],
Costume5 = commonBaidResponse.CostumeData[4]
};
response.CostumeFlg1 = commonBaidResponse.CostumeFlagArrays[0];
response.CostumeFlg2 = commonBaidResponse.CostumeFlagArrays[1];
response.CostumeFlg3 = commonBaidResponse.CostumeFlagArrays[2];
response.CostumeFlg4 = commonBaidResponse.CostumeFlagArrays[3];
response.CostumeFlg5 = commonBaidResponse.CostumeFlagArrays[4];
return response;
}
public static partial Models.v3209.BAIDResponse MapTo3209(CommonBaidResponse commonBaidResponse);
public static Models.v3209.BAIDResponse Map3209WithPostProcess(CommonBaidResponse commonBaidResponse)
{
var response = MapTo3209(commonBaidResponse);
response.AryCostumedata = new Models.v3209.BAIDResponse.CostumeData
{
Costume1 = commonBaidResponse.CostumeData[0],
Costume2 = commonBaidResponse.CostumeData[1],
Costume3 = commonBaidResponse.CostumeData[2],
Costume4 = commonBaidResponse.CostumeData[3],
Costume5 = commonBaidResponse.CostumeData[4]
};
response.CostumeFlg1 = commonBaidResponse.CostumeFlagArrays[0];
response.CostumeFlg2 = commonBaidResponse.CostumeFlagArrays[1];
response.CostumeFlg3 = commonBaidResponse.CostumeFlagArrays[2];
response.CostumeFlg4 = commonBaidResponse.CostumeFlagArrays[3];
response.CostumeFlg5 = commonBaidResponse.CostumeFlagArrays[4];
return response;
}
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using SharedProject.Models;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class DanDataMappers
{
public static partial GetDanOdaiResponse.OdaiData To3906OdaiData(DanData data);
public static partial Models.v3209.GetDanOdaiResponse.OdaiData To3209OdaiData(DanData data);
}

View File

@ -0,0 +1,11 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class DanScoreMappers
{
public static partial GetDanScoreResponse MapTo3906(CommonDanScoreDataResponse response);
public static partial Models.v3209.GetDanScoreResponse MapTo3209(CommonDanScoreDataResponse response);
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class FolderDataMappers
{
public static partial GetfolderResponse MapTo3906(CommonGetFolderResponse response);
public static partial Models.v3209.GetfolderResponse MapTo3209(CommonGetFolderResponse response);
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class InitialDataMappers
{
public static partial InitialdatacheckResponse MapTo3906(CommonInitialDataCheckResponse response);
public static partial Models.v3209.InitialdatacheckResponse MapTo3209(CommonInitialDataCheckResponse response);
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class MyDonEntryMappers
{
public static partial MydonEntryResponse MapTo3906(CommonMyDonEntryResponse response);
public static partial Models.v3209.MydonEntryResponse MapTo3209(CommonMyDonEntryResponse response);
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class SelfBestMappers
{
public static partial SelfBestResponse MapTo3906(CommonSelfBestResponse response);
public static partial Models.v3209.SelfBestResponse MapTo3209(CommonSelfBestResponse response);
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class ShopFolderDataMappers
{
public static partial GetShopFolderResponse MapTo3906(CommonGetShopFolderResponse response);
public static partial Models.v3209.GetShopFolderResponse MapTo3209(CommonGetShopFolderResponse response);
}

View File

@ -0,0 +1,13 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class SongIntroductionDataMappers
{
public static partial GetSongIntroductionResponse MapTo3906(CommonGetSongIntroductionResponse response);
public static partial Models.v3209.GetSongIntroductionResponse
MapTo3209(CommonGetSongIntroductionResponse response);
}

View File

@ -0,0 +1,12 @@
using Riok.Mapperly.Abstractions;
using TaikoLocalServer.Models.Application;
namespace TaikoLocalServer.Mappers;
[Mapper]
public static partial class TokenCountDataMappers
{
public static partial GetTokenCountResponse MapTo3906(CommonGetTokenCountResponse response);
public static partial Models.v3209.GetTokenCountResponse MapTo3209(CommonGetTokenCountResponse response);
}

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
#region Designer generated code
#pragma warning disable CS0612, CS0618, CS1591, CS3021, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
namespace taiko.game
namespace TaikoLocalServer.Models.v3906
{
[global::ProtoBuf.ProtoContract()]
public partial class HeartBeatRequest : global::ProtoBuf.IExtensible
@ -657,10 +657,10 @@ namespace taiko.game
public string Title { get; set; }
[global::ProtoBuf.ProtoMember(4, Name = @"ary_odai_song")]
public global::System.Collections.Generic.List<OdaiSong> AryOdaiSongs { get; } = new global::System.Collections.Generic.List<OdaiSong>();
public global::System.Collections.Generic.List<OdaiSong> OdaiSongList { get; } = new global::System.Collections.Generic.List<OdaiSong>();
[global::ProtoBuf.ProtoMember(5, Name = @"ary_odai_border")]
public global::System.Collections.Generic.List<OdaiBorder> AryOdaiBorders { get; } = new global::System.Collections.Generic.List<OdaiBorder>();
public global::System.Collections.Generic.List<OdaiBorder> OdaiBorderList { get; } = new global::System.Collections.Generic.List<OdaiBorder>();
[global::ProtoBuf.ProtoContract()]
public partial class OdaiSong : global::ProtoBuf.IExtensible
@ -1260,7 +1260,7 @@ namespace taiko.game
[global::ProtoBuf.ProtoMember(12, Name = @"mydon_name")]
[global::System.ComponentModel.DefaultValue("")]
public string MydonName
public string MyDonName
{
get => __pbn__MydonName ?? "";
set => __pbn__MydonName = value;
@ -1270,7 +1270,7 @@ namespace taiko.game
private string __pbn__MydonName;
[global::ProtoBuf.ProtoMember(13, Name = @"mydon_name_language")]
public uint MydonNameLanguage
public uint MyDonNameLanguage
{
get => __pbn__MydonNameLanguage.GetValueOrDefault();
set => __pbn__MydonNameLanguage = value;
@ -1291,7 +1291,7 @@ namespace taiko.game
private string __pbn__Title;
[global::ProtoBuf.ProtoMember(15, Name = @"titleplate_id")]
public uint TitleplateId
public uint TitlePlateId
{
get => __pbn__TitleplateId.GetValueOrDefault();
set => __pbn__TitleplateId = value;
@ -1395,7 +1395,7 @@ namespace taiko.game
private string __pbn__LastPlayDatetime;
[global::ProtoBuf.ProtoMember(26, Name = @"is_disp_dan_on")]
public bool IsDispDanOn
public bool DisplayDan
{
get => __pbn__IsDispDanOn.GetValueOrDefault();
set => __pbn__IsDispDanOn = value;
@ -1425,17 +1425,17 @@ namespace taiko.game
private byte[] __pbn__GotDanFlg;
[global::ProtoBuf.ProtoMember(29, Name = @"got_danextra_flg")]
public byte[] GotDanextraFlg
public byte[] GotGaidenFlg
{
get => __pbn__GotDanextraFlg;
set => __pbn__GotDanextraFlg = value;
get => pbnGotGaidenFlg;
set => pbnGotGaidenFlg = value;
}
public bool ShouldSerializeGotDanextraFlg() => __pbn__GotDanextraFlg != null;
public void ResetGotDanextraFlg() => __pbn__GotDanextraFlg = null;
private byte[] __pbn__GotDanextraFlg;
public bool ShouldSerializeGotDanextraFlg() => pbnGotGaidenFlg != null;
public void ResetGotDanextraFlg() => pbnGotGaidenFlg = null;
private byte[] pbnGotGaidenFlg;
[global::ProtoBuf.ProtoMember(30, Name = @"default_tone_setting")]
public uint DefaultToneSetting
public uint SelectedToneId
{
get => __pbn__DefaultToneSetting.GetValueOrDefault();
set => __pbn__DefaultToneSetting = value;
@ -2867,7 +2867,7 @@ namespace taiko.game
=> global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
[global::ProtoBuf.ProtoMember(1, Name = @"section_no", IsRequired = true)]
public uint SectionNo { get; set; }
public uint SectionIndex { get; set; }
[global::ProtoBuf.ProtoMember(2, Name = @"crown")]
public uint Crown
@ -2890,16 +2890,16 @@ namespace taiko.game
private uint? __pbn__Score;
[global::ProtoBuf.ProtoMember(4, Name = @"good_cnt", IsRequired = true)]
public uint GoodCnt { get; set; }
public uint GoodCount { get; set; }
[global::ProtoBuf.ProtoMember(5, Name = @"ok_cnt", IsRequired = true)]
public uint OkCnt { get; set; }
public uint OkCount { get; set; }
[global::ProtoBuf.ProtoMember(6, Name = @"ng_cnt", IsRequired = true)]
public uint NgCnt { get; set; }
public uint MissCount { get; set; }
[global::ProtoBuf.ProtoMember(7, Name = @"pound_cnt", IsRequired = true)]
public uint PoundCnt { get; set; }
public uint DrumrollCount { get; set; }
}

View File

@ -0,0 +1,13 @@
namespace TaikoLocalServer.Models.Application;
public class CommonAddTokenCountRequest
{
public uint Baid { get; set; }
public List<AddTokenCountData> AryAddTokenCountDatas { get; set; } = new();
}
public class AddTokenCountData
{
public uint TokenId { get; set; }
public int AddTokenCount { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace TaikoLocalServer.Models.Application;
public class CommonAiDataResponse
{
public uint TotalWinnings { get; set; }
public string InputMedian { get; set; } = "1";
public string InputVariance { get; set; } = "0";
}

View File

@ -0,0 +1,17 @@
namespace TaikoLocalServer.Models.Application;
public class CommonAiScoreResponse
{
public List<CommonAiBestSectionData> AryBestSectionDatas { get; set; } = new();
}
public class CommonAiBestSectionData
{
public uint SectionIndex { get; set; }
public uint Crown { get; set; }
public uint Score { get; set; }
public uint GoodCount { get; set; }
public uint OkCount { get; set; }
public uint MissCount { get; set; }
public uint DrumrollCount { get; set; }
}

View File

@ -0,0 +1,32 @@
using GameDatabase.Entities;
namespace TaikoLocalServer.Models.Application;
public class CommonBaidResponse
{
public bool IsNewUser { get; set; }
public uint Baid { get; set; }
public string MyDonName { get; set; } = string.Empty;
public uint MyDonNameLanguage { get; set; }
public string Title { get; set; } = string.Empty;
public uint TitlePlateId { get; set; }
public uint ColorFace { get; set; }
public uint ColorBody { get; set; }
public uint ColorLimb { get; set; }
public List<uint> CostumeData { get; set; } = new() { 0, 0, 0, 0, 0 };
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 bool DisplayDan { get; set; }
public uint GotDanMax { get; set; }
public byte[] GotDanFlg { get; set; } = Array.Empty<byte>();
public byte[] GotGaidenFlg { get; set; } = Array.Empty<byte>();
public uint SelectedToneId { get; set; }
public byte[] GenericInfoFlg { get; set; } = Array.Empty<byte>();
public uint[] AryCrownCounts { get; set; } = Array.Empty<uint>();
public uint[] AryScoreRankCounts { get; set; } = Array.Empty<uint>();
public bool IsDispAchievementOn { get; set; }
public uint DispAchievementType { get; set; }
public uint LastPlayMode { get; set; }
}

View File

@ -0,0 +1,28 @@
namespace TaikoLocalServer.Models.Application;
public class CommonDanScoreDataResponse
{
public uint Result { get; set; }
public List<DanScoreData> AryDanScoreDatas { get; set; } = [];
public class DanScoreData
{
public uint DanId { get; set; }
public uint ArrivalSongCnt { get; set; }
public uint SoulGaugeTotal { get; set; }
public uint ComboCntTotal { get; set; }
public List<DanScoreDataStage> AryDanScoreDataStages { get; set; } = [];
}
public class DanScoreDataStage
{
public uint PlayScore { get; set; }
public uint GoodCnt { get; set; }
public uint OkCnt { get; set; }
public uint NgCnt { get; set; }
public uint PoundCnt { get; set; }
public uint HitCnt { get; set; }
public uint ComboCnt { get; set; }
public uint HighScore { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using SharedProject.Models;
namespace TaikoLocalServer.Models.Application;
public class CommonGetFolderResponse
{
public uint Result { get; set; }
public List<EventFolderData> AryEventfolderDatas { get; set; } = [];
}

View File

@ -0,0 +1,11 @@
using SharedProject.Models;
namespace TaikoLocalServer.Models.Application;
public class CommonGetShopFolderResponse
{
public uint Result { get; set; }
public uint VerupNo { get; set; }
public uint TokenId { get; set; }
public List<ShopFolderData> AryShopFolderDatas { get; set; } = [];
}

View File

@ -0,0 +1,10 @@
using SharedProject.Models;
namespace TaikoLocalServer.Models.Application;
public class CommonGetSongIntroductionResponse
{
public uint Result { get; set; }
public List<SongIntroductionData> ArySongIntroductionDatas { get; set; } = [];
}

View File

@ -0,0 +1,14 @@
namespace TaikoLocalServer.Models.Application;
public class CommonGetTokenCountResponse
{
public uint Result { get; set; }
public List<CommonTokenCountData> AryTokenCountDatas { get; set; } = [];
}
public class CommonTokenCountData
{
public uint TokenId { get; set; }
public int TokenCount { get; set; }
}

View File

@ -0,0 +1,46 @@
using SharedProject.Models;
namespace TaikoLocalServer.Models.Application;
public class CommonInitialDataCheckResponse
{
public uint Result { get; set; }
public byte[] DefaultSongFlg { get; set; } = [];
public byte[] AchievementSongBit { get; set; } = [];
public byte[] UraReleaseBit { get; set; } = [];
public string SongIntroductionEndDatetime { get; set; } =
DateTime.Now.AddYears(10).ToString(Constants.DATE_TIME_FORMAT);
public List<MovieData> AryMovieInfoes { get; set; } = [];
public List<AiEventData> AryAiEventDatas { get; set; } = [];
public List<VerupNoData1> AryVerupNoData1s { get; set; } = [];
public List<VerupNoData2> AryVerupNoData2s { get; set; } = [];
public uint[] AryChassisFunctionIds { get; set; } = [1, 2, 3];
public ulong ServerCurrentDatetime { get; set; } = (ulong)DateTimeOffset.Now.ToUnixTimeSeconds();
public class AiEventData
{
public uint AiEventId { get; set; }
public uint TokenId { get; set; }
}
public class VerupNoData1
{
public uint MasterType { get; set; }
public uint VerupNo { get; set; }
}
public class VerupNoData2
{
public uint MasterType { get; set; }
public List<InformationData> AryInformationDatas { get; set; } = [];
public class InformationData
{
public uint InfoId { get; set; }
public uint VerupNo { get; set; }
}
}
}

View File

@ -0,0 +1,11 @@
namespace TaikoLocalServer.Models.Application;
public class CommonMyDonEntryResponse
{
public uint Result { get; set; }
public uint Baid { get; set; }
public string MydonName { get; set; } = string.Empty;
public uint MydonNameLanguage { get; set; }
public string AccessCode { get; set; } = string.Empty;
public uint ComSvrResult { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace TaikoLocalServer.Models.Application;
public class CommonSelfBestResponse
{
public uint Result { get; set; }
public uint Level { get; set; }
public List<SelfBestData> ArySelfbestScores { get; set; } = [];
public class SelfBestData
{
public uint SongNo { get; set; }
public uint SelfBestScore { get; set; }
public uint UraBestScore { get; set; }
public uint SelfBestScoreRate { get; set; }
public uint UraBestScoreRate { get; set; }
}
}

View File

@ -60,6 +60,7 @@ try
}
// Add services to the container.
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
builder.Services.AddOptions();
builder.Services.AddSingleton<IGameDataService, GameDataService>();
builder.Services.Configure<ServerSettings>(builder.Configuration.GetSection(nameof(ServerSettings)));

View File

@ -18,11 +18,6 @@ public class CardService : ICardService
return await context.Cards.FindAsync(accessCode);
}
public uint GetNextBaid()
{
return context.Cards.Any() ? context.Cards.ToList().Max(card => card.Baid) + 1 : 1;
}
public async Task<List<User>> GetUsersFromCards()
{
var cardEntries = await context.Cards.ToListAsync();

View File

@ -1,5 +1,4 @@
using ICSharpCode.SharpZipLib.GZip;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Options;
using SharedProject.Models;
using SharedProject.Utils;
using Swan.Mapping;
@ -14,461 +13,377 @@ namespace TaikoLocalServer.Services;
public class GameDataService : IGameDataService
{
private ImmutableDictionary<uint, MusicInfoEntry> musicInfoes =
ImmutableDictionary<uint, MusicInfoEntry>.Empty;
private ImmutableDictionary<uint, DanData> commonDanDataDictionary =
ImmutableDictionary<uint, DanData>.Empty;
private ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> danDataDictionary =
ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData>.Empty;
private ImmutableDictionary<uint, DanData> commonGaidenDataDictionary =
ImmutableDictionary<uint, DanData>.Empty;
private ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> gaidenDataDictionary =
ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData>.Empty;
private ImmutableDictionary<uint, MusicInfoEntry> musicInfoes =
ImmutableDictionary<uint, MusicInfoEntry>.Empty;
private ImmutableDictionary<uint, MovieData> movieDataDictionary =
ImmutableDictionary<uint, MovieData>.Empty;
private ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData> introDataDictionary =
ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData>.Empty;
private ImmutableDictionary<uint, SongIntroductionData> songIntroductionDictionary =
ImmutableDictionary<uint, SongIntroductionData>.Empty;
private ImmutableDictionary<string, uint> qrCodeDataDictionary = ImmutableDictionary<string, uint>.Empty;
private ImmutableDictionary<uint, EventFolderData> eventFolderDictionary =
ImmutableDictionary<uint, EventFolderData>.Empty;
private ImmutableDictionary<uint, InitialdatacheckResponse.MovieData> movieDataDictionary =
ImmutableDictionary<uint, InitialdatacheckResponse.MovieData>.Empty;
private List<ShopFolderData> shopFolderList = new();
private ImmutableDictionary<uint, GetfolderResponse.EventfolderData> folderDictionary =
ImmutableDictionary<uint, GetfolderResponse.EventfolderData>.Empty;
private List<uint> musics = new();
private ImmutableDictionary<string, uint> qrCodeDataDictionary = ImmutableDictionary<string, uint>.Empty;
private List<GetShopFolderResponse.ShopFolderData> shopFolderList = new();
private List<uint> musicsWithUra = new();
private List<uint> musics = new();
private List<uint> lockedSongsList = new();
private List<uint> musicsWithUra = new();
private List<int> costumeFlagArraySizes = new();
private List<uint> lockedSongsList = new();
private List<int> costumeFlagArraySizes = new();
private int titleFlagArraySize;
private int toneFlagArraySize;
private int titleFlagArraySize;
private Dictionary<string, int> tokenDataDictionary = new();
private int toneFlagArraySize;
private readonly DataSettings settings;
private Dictionary<string, int> tokenDataDictionary = new();
public GameDataService(IOptions<DataSettings> settings)
{
this.settings = settings.Value;
}
private readonly DataSettings settings;
public List<uint> GetMusicList()
{
return musics;
}
public GameDataService(IOptions<DataSettings> settings)
{
this.settings = settings.Value;
}
public List<uint> GetMusicWithUraList()
{
return musicsWithUra;
}
public List<uint> GetMusicList()
{
return musics;
}
public ImmutableDictionary<uint, MusicInfoEntry> GetMusicInfoes()
{
return musicInfoes;
}
public List<uint> GetMusicWithUraList()
{
return musicsWithUra;
}
public ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> GetDanDataDictionary()
{
return danDataDictionary;
}
public ImmutableDictionary<uint, MusicInfoEntry> GetMusicInfoes()
{
return musicInfoes;
}
public ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> GetGaidenDataDictionary()
{
return gaidenDataDictionary;
}
public ImmutableDictionary<uint, MovieData> GetMovieDataDictionary()
{
return movieDataDictionary;
}
public ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData> GetSongIntroDictionary()
{
return introDataDictionary;
}
public ImmutableDictionary<uint, EventFolderData> GetEventFolderDictionary()
{
return eventFolderDictionary;
}
public ImmutableDictionary<uint, InitialdatacheckResponse.MovieData> GetMovieDataDictionary()
{
return movieDataDictionary;
}
public ImmutableDictionary<uint, DanData> GetCommonDanDataDictionary()
{
return commonDanDataDictionary;
}
public ImmutableDictionary<uint, GetfolderResponse.EventfolderData> GetFolderDictionary()
{
return folderDictionary;
}
public ImmutableDictionary<uint, DanData> GetCommonGaidenDataDictionary()
{
return commonGaidenDataDictionary;
}
public ImmutableDictionary<uint, SongIntroductionData> GetSongIntroductionDictionary()
{
return songIntroductionDictionary;
}
public List<GetShopFolderResponse.ShopFolderData> GetShopFolderList()
{
return shopFolderList;
}
public List<ShopFolderData> GetShopFolderList()
{
return shopFolderList;
}
public Dictionary<string, int> GetTokenDataDictionary()
{
return tokenDataDictionary;
}
public Dictionary<string, int> GetTokenDataDictionary()
{
return tokenDataDictionary;
}
public List<uint> GetLockedSongsList()
{
return lockedSongsList;
}
public List<int> GetCostumeFlagArraySizes()
{
return costumeFlagArraySizes;
}
public int GetTitleFlagArraySize()
{
return titleFlagArraySize;
}
public int GetToneFlagArraySize()
{
return toneFlagArraySize;
}
public List<uint> GetLockedSongsList()
{
return lockedSongsList;
}
public ImmutableDictionary<string, uint> GetQRCodeDataDictionary()
{
return qrCodeDataDictionary;
}
public List<int> GetCostumeFlagArraySizes()
{
return costumeFlagArraySizes;
}
public async Task InitializeAsync()
{
var dataPath = PathHelper.GetDataPath();
var datatablePath = PathHelper.GetDatatablePath();
public int GetTitleFlagArraySize()
{
return titleFlagArraySize;
}
var musicInfoPath = Path.Combine(datatablePath, $"{Constants.MUSIC_INFO_BASE_NAME}.json");
var encryptedInfo = Path.Combine(datatablePath, $"{Constants.MUSIC_INFO_BASE_NAME}.bin");
var wordlistPath = Path.Combine(datatablePath, $"{Constants.WORDLIST_BASE_NAME}.json");
var encryptedWordlist = Path.Combine(datatablePath, $"{Constants.WORDLIST_BASE_NAME}.bin");
var musicOrderPath = Path.Combine(datatablePath, $"{Constants.MUSIC_ORDER_BASE_NAME}.json");
var encryptedMusicOrder = Path.Combine(datatablePath, $"{Constants.MUSIC_ORDER_BASE_NAME}.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 shougouPath = Path.Combine(datatablePath, $"{Constants.SHOUGOU_BASE_NAME}.json");
var encryptedShougou = Path.Combine(datatablePath, $"{Constants.SHOUGOU_BASE_NAME}.bin");
var neiroPath = Path.Combine(datatablePath, $"{Constants.NEIRO_BASE_NAME}.json");
var encryptedNeiro = Path.Combine(datatablePath, $"{Constants.NEIRO_BASE_NAME}.bin");
var danDataPath = Path.Combine(dataPath, settings.DanDataFileName);
var gaidenDataPath = Path.Combine(dataPath, settings.GaidenDataFileName);
var songIntroDataPath = Path.Combine(dataPath, settings.IntroDataFileName);
var movieDataPath = Path.Combine(dataPath, settings.MovieDataFileName);
var eventFolderDataPath = Path.Combine(dataPath, settings.EventFolderDataFileName);
var shopFolderDataPath = Path.Combine(dataPath, settings.ShopFolderDataFileName);
var tokenDataPath = Path.Combine(dataPath, settings.TokenDataFileName);
var lockedSongsDataPath = Path.Combine(dataPath, settings.LockedSongsDataFileName);
var qrCodeDataPath = Path.Combine(dataPath, settings.QRCodeDataFileName);
public int GetToneFlagArraySize()
{
return toneFlagArraySize;
}
if (File.Exists(encryptedInfo))
{
DecryptDataTable(encryptedInfo, musicInfoPath);
}
if (File.Exists(encryptedWordlist))
{
DecryptDataTable(encryptedWordlist, wordlistPath);
}
if (File.Exists(encryptedMusicOrder))
{
DecryptDataTable(encryptedMusicOrder, musicOrderPath);
}
if (File.Exists(encryptedDonCosReward))
{
DecryptDataTable(encryptedDonCosReward, donCosRewardPath);
}
if (File.Exists(encryptedShougou))
{
DecryptDataTable(encryptedShougou, shougouPath);
}
if (File.Exists(encryptedNeiro))
{
DecryptDataTable(encryptedNeiro, neiroPath);
}
if (!File.Exists(musicInfoPath))
{
throw new FileNotFoundException("Music info file not found!");
}
if (!File.Exists(wordlistPath))
{
throw new FileNotFoundException("Wordlist file not found!");
}
if (!File.Exists(musicOrderPath))
{
throw new FileNotFoundException("Music order file not found!");
}
if (!File.Exists(donCosRewardPath))
{
throw new FileNotFoundException("Don cos reward file not found!");
}
if (!File.Exists(shougouPath))
{
throw new FileNotFoundException("Shougou file not found!");
}
if (!File.Exists(neiroPath))
{
throw new FileNotFoundException("Neiro file not found!");
}
await using var musicInfoFile = File.OpenRead(musicInfoPath);
await using var danDataFile = File.OpenRead(danDataPath);
await using var gaidenDataFile = File.OpenRead(gaidenDataPath);
await using var songIntroDataFile = File.OpenRead(songIntroDataPath);
await using var movieDataFile = File.OpenRead(movieDataPath);
await using var eventFolderDataFile = File.OpenRead(eventFolderDataPath);
await using var shopFolderDataFile = File.OpenRead(shopFolderDataPath);
await using var tokenDataFile = File.OpenRead(tokenDataPath);
await using var lockedSongsDataFile = File.OpenRead(lockedSongsDataPath);
await using var donCosRewardFile = File.OpenRead(donCosRewardPath);
await using var shougouFile = File.OpenRead(shougouPath);
await using var neiroFile = File.OpenRead(neiroPath);
await using var qrCodeDataFile = File.OpenRead(qrCodeDataPath);
public ImmutableDictionary<string, uint> GetQRCodeDataDictionary()
{
return qrCodeDataDictionary;
}
var infoesData = await JsonSerializer.DeserializeAsync<MusicInfoes>(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);
var movieData = await JsonSerializer.DeserializeAsync<List<MovieData>>(movieDataFile);
var eventFolderData = await JsonSerializer.DeserializeAsync<List<EventFolderData>>(eventFolderDataFile);
var shopFolderData = await JsonSerializer.DeserializeAsync<List<ShopFolderData>>(shopFolderDataFile);
var tokenData = await JsonSerializer.DeserializeAsync<Dictionary<string, int>>(tokenDataFile);
var lockedSongsData = await JsonSerializer.DeserializeAsync<Dictionary<string, uint[]>>(lockedSongsDataFile);
var donCosRewardData = await JsonSerializer.DeserializeAsync<DonCosRewards>(donCosRewardFile);
var shougouData = await JsonSerializer.DeserializeAsync<Shougous>(shougouFile);
var neiroData = await JsonSerializer.DeserializeAsync<Neiros>(neiroFile);
var qrCodeData = await JsonSerializer.DeserializeAsync<List<QRCodeData>>(qrCodeDataFile);
public async Task InitializeAsync()
{
var dataPath = PathHelper.GetDataPath();
var datatablePath = PathHelper.GetDatatablePath();
InitializeMusicInfoes(infoesData);
var musicInfoPath = Path.Combine(datatablePath, $"{Constants.MUSIC_INFO_BASE_NAME}.json");
var encryptedInfo = Path.Combine(datatablePath, $"{Constants.MUSIC_INFO_BASE_NAME}.bin");
InitializeDanData(danData);
var wordlistPath = Path.Combine(datatablePath, $"{Constants.WORDLIST_BASE_NAME}.json");
var encryptedWordlist = Path.Combine(datatablePath, $"{Constants.WORDLIST_BASE_NAME}.bin");
InitializeGaidenData(gaidenData);
var musicOrderPath = Path.Combine(datatablePath, $"{Constants.MUSIC_ORDER_BASE_NAME}.json");
var encryptedMusicOrder = Path.Combine(datatablePath, $"{Constants.MUSIC_ORDER_BASE_NAME}.bin");
InitializeIntroData(introData);
var donCosRewardPath = Path.Combine(datatablePath, $"{Constants.DON_COS_REWARD_BASE_NAME}.json");
var encryptedDonCosReward = Path.Combine(datatablePath, $"{Constants.DON_COS_REWARD_BASE_NAME}.bin");
InitializeMovieData(movieData);
var shougouPath = Path.Combine(datatablePath, $"{Constants.SHOUGOU_BASE_NAME}.json");
var encryptedShougou = Path.Combine(datatablePath, $"{Constants.SHOUGOU_BASE_NAME}.bin");
InitializeEventFolderData(eventFolderData);
var neiroPath = Path.Combine(datatablePath, $"{Constants.NEIRO_BASE_NAME}.json");
var encryptedNeiro = Path.Combine(datatablePath, $"{Constants.NEIRO_BASE_NAME}.bin");
InitializeShopFolderData(shopFolderData);
var danDataPath = Path.Combine(dataPath, settings.DanDataFileName);
var gaidenDataPath = Path.Combine(dataPath, settings.GaidenDataFileName);
var songIntroDataPath = Path.Combine(dataPath, settings.IntroDataFileName);
var movieDataPath = Path.Combine(dataPath, settings.MovieDataFileName);
var eventFolderDataPath = Path.Combine(dataPath, settings.EventFolderDataFileName);
var shopFolderDataPath = Path.Combine(dataPath, settings.ShopFolderDataFileName);
var tokenDataPath = Path.Combine(dataPath, settings.TokenDataFileName);
var lockedSongsDataPath = Path.Combine(dataPath, settings.LockedSongsDataFileName);
var qrCodeDataPath = Path.Combine(dataPath, settings.QRCodeDataFileName);
InitializeTokenData(tokenData);
var encryptedFiles = new List<string>
{
encryptedInfo,
encryptedWordlist,
encryptedMusicOrder,
encryptedDonCosReward,
encryptedShougou,
encryptedNeiro
};
InitializeLockedSongsData(lockedSongsData);
InitializeCostumeFlagArraySizes(donCosRewardData);
InitializeTitleFlagArraySize(shougouData);
InitializeToneFlagArraySize(neiroData);
var outputPaths = new List<string>
{
musicInfoPath,
wordlistPath,
musicOrderPath,
donCosRewardPath,
shougouPath,
neiroPath
};
InitializeQrCodeData(qrCodeData);
}
for (var i = 0; i < encryptedFiles.Count; i++)
{
if (File.Exists(encryptedFiles[i]))
{
DecryptDataTable(encryptedFiles[i], outputPaths[i]);
}
}
private static void DecryptDataTable(string inputFileName, string outputFileName)
{
var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Convert.FromHexString("3530304242323633353537423431384139353134383346433246464231354534");
var iv = new byte[16];
using var fs = File.OpenRead(inputFileName);
var count = fs.Read(iv, 0, 16);
count.Throw("Read IV for datatable failed!").IfNotEquals(16);
aes.IV = iv;
using var cs = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read);
using var ms = new MemoryStream();
cs.CopyTo(ms);
ms.Position = 0;
using var gz = new GZipStream(ms, CompressionMode.Decompress);
using var output = File.Create(outputFileName);
gz.CopyTo(output);
}
foreach (var filePath in outputPaths)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"{Path.GetFileName(filePath)} file not found!");
}
}
private void InitializeIntroData(List<SongIntroductionData>? introData)
{
introData.ThrowIfNull("Shouldn't happen!");
introDataDictionary = introData.ToImmutableDictionary(data => data.SetId, ToResponseIntroData);
}
await using var musicInfoFile = File.OpenRead(musicInfoPath);
await using var danDataFile = File.OpenRead(danDataPath);
await using var gaidenDataFile = File.OpenRead(gaidenDataPath);
await using var songIntroDataFile = File.OpenRead(songIntroDataPath);
await using var movieDataFile = File.OpenRead(movieDataPath);
await using var eventFolderDataFile = File.OpenRead(eventFolderDataPath);
await using var shopFolderDataFile = File.OpenRead(shopFolderDataPath);
await using var tokenDataFile = File.OpenRead(tokenDataPath);
await using var lockedSongsDataFile = File.OpenRead(lockedSongsDataPath);
await using var donCosRewardFile = File.OpenRead(donCosRewardPath);
await using var shougouFile = File.OpenRead(shougouPath);
await using var neiroFile = File.OpenRead(neiroPath);
await using var qrCodeDataFile = File.OpenRead(qrCodeDataPath);
private void InitializeMovieData(List<MovieData>? movieData)
{
movieData.ThrowIfNull("Shouldn't happen!");
movieDataDictionary = movieData.ToImmutableDictionary(data => data.MovieId, ToResponseMovieData);
}
var infoesData = await JsonSerializer.DeserializeAsync<MusicInfoes>(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);
var movieData = await JsonSerializer.DeserializeAsync<List<MovieData>>(movieDataFile);
var eventFolderData = await JsonSerializer.DeserializeAsync<List<EventFolderData>>(eventFolderDataFile);
var shopFolderData = await JsonSerializer.DeserializeAsync<List<ShopFolderData>>(shopFolderDataFile);
var tokenData = await JsonSerializer.DeserializeAsync<Dictionary<string, int>>(tokenDataFile);
var lockedSongsData = await JsonSerializer.DeserializeAsync<Dictionary<string, uint[]>>(lockedSongsDataFile);
var donCosRewardData = await JsonSerializer.DeserializeAsync<DonCosRewards>(donCosRewardFile);
var shougouData = await JsonSerializer.DeserializeAsync<Shougous>(shougouFile);
var neiroData = await JsonSerializer.DeserializeAsync<Neiros>(neiroFile);
var qrCodeData = await JsonSerializer.DeserializeAsync<List<QRCodeData>>(qrCodeDataFile);
private void InitializeDanData(List<DanData>? danData)
{
danData.ThrowIfNull("Shouldn't happen!");
danDataDictionary = danData.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData);
}
InitializeMusicInfoes(infoesData);
private void InitializeGaidenData(List<DanData>? gaidenData)
{
gaidenData.ThrowIfNull("Shouldn't happen!");
gaidenDataDictionary = gaidenData.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData);
}
InitializeDanData(danData);
private void InitializeEventFolderData(List<EventFolderData>? eventFolderData)
{
eventFolderData.ThrowIfNull("Shouldn't happen!");
folderDictionary = eventFolderData.ToImmutableDictionary(data => data.FolderId, ToResponseEventFolderData);
}
InitializeGaidenData(gaidenData);
private void InitializeMusicInfoes(MusicInfoes? infoesData)
{
infoesData.ThrowIfNull("Shouldn't happen!");
InitializeIntroData(introData);
musicInfoes = infoesData.MusicInfoEntries.ToImmutableDictionary(info => info.MusicId);
InitializeMovieData(movieData);
musics = musicInfoes.Select(pair => pair.Key)
.ToList();
musics.Sort();
musicsWithUra = musicInfoes.Where(info => info.Value.StarUra > 0)
.Select(pair => pair.Key)
.ToList();
musicsWithUra.Sort();
}
InitializeEventFolderData(eventFolderData);
private void InitializeShopFolderData(List<ShopFolderData>? shopFolderData)
{
shopFolderData.ThrowIfNull("Shouldn't happen!");
foreach (var folderData in shopFolderData)
{
shopFolderList.Add(ToResponseShopFolderData(folderData));
}
}
InitializeShopFolderData(shopFolderData);
private void InitializeTokenData(Dictionary<string, int>? tokenData)
{
tokenData.ThrowIfNull("Shouldn't happen!");
tokenDataDictionary = tokenData;
}
InitializeTokenData(tokenData);
private void InitializeLockedSongsData(Dictionary<string, uint[]>? lockedSongsData)
{
lockedSongsData.ThrowIfNull("Shouldn't happen!");
lockedSongsList = lockedSongsData["songNo"].ToList();
}
private void InitializeCostumeFlagArraySizes(DonCosRewards? donCosRewardData)
{
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);
costumeFlagArraySizes = new List<int>
{
(int)kigurumiUniqueIdList.Max() + 1,
(int)headUniqueIdList.Max() + 1,
(int)bodyUniqueIdList.Max() + 1,
(int)faceUniqueIdList.Max() + 1,
(int)puchiUniqueIdList.Max() + 1
};
}
private void InitializeTitleFlagArraySize(Shougous? shougouData)
{
shougouData.ThrowIfNull("Shouldn't happen!");
titleFlagArraySize = (int)shougouData.ShougouEntries.Max(entry => entry.UniqueId) + 1;
}
private void InitializeToneFlagArraySize(Neiros? neiroData)
{
neiroData.ThrowIfNull("Shouldn't happen!");
toneFlagArraySize = (int)neiroData.NeiroEntries.Max(entry => entry.UniqueId) + 1;
}
private void InitializeQrCodeData(List<QRCodeData>? qrCodeData)
{
qrCodeData.ThrowIfNull("Shouldn't happen!");
qrCodeDataDictionary = qrCodeData.ToImmutableDictionary(data => data.Serial, data => data.Id);
}
InitializeLockedSongsData(lockedSongsData);
private static GetDanOdaiResponse.OdaiData ToResponseOdaiData(DanData data)
{
var responseOdaiData = new GetDanOdaiResponse.OdaiData
{
DanId = data.DanId,
Title = data.Title,
VerupNo = data.VerupNo
};
InitializeCostumeFlagArraySizes(donCosRewardData);
var odaiSongs = data.OdaiSongList.Select(song => song.CopyPropertiesToNew<GetDanOdaiResponse.OdaiData.OdaiSong>());
responseOdaiData.AryOdaiSongs.AddRange(odaiSongs);
InitializeTitleFlagArraySize(shougouData);
var odaiBorders = data.OdaiBorderList.Select(border => border.CopyPropertiesToNew<GetDanOdaiResponse.OdaiData.OdaiBorder>());
responseOdaiData.AryOdaiBorders.AddRange(odaiBorders);
InitializeToneFlagArraySize(neiroData);
return responseOdaiData;
}
InitializeQrCodeData(qrCodeData);
}
private static GetSongIntroductionResponse.SongIntroductionData ToResponseIntroData(SongIntroductionData data)
{
var responseIntroData = new GetSongIntroductionResponse.SongIntroductionData
{
SetId = data.SetId,
VerupNo = data.VerupNo,
MainSongNo = data.MainSongNo,
SubSongNoes = data.SubSongNo
};
private static void DecryptDataTable(string inputFileName, string outputFileName)
{
var aes = Aes.Create();
aes.Mode = CipherMode.CBC;
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Key = Convert.FromHexString("3530304242323633353537423431384139353134383346433246464231354534");
var iv = new byte[16];
using var fs = File.OpenRead(inputFileName);
var count = fs.Read(iv, 0, 16);
count.Throw("Read IV for datatable failed!").IfNotEquals(16);
aes.IV = iv;
using var cs = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read);
using var ms = new MemoryStream();
cs.CopyTo(ms);
ms.Position = 0;
using var gz = new GZipStream(ms, CompressionMode.Decompress);
using var output = File.Create(outputFileName);
gz.CopyTo(output);
}
return responseIntroData;
}
private void InitializeIntroData(List<SongIntroductionData>? introData)
{
introData.ThrowIfNull("Shouldn't happen!");
songIntroductionDictionary = introData.ToImmutableDictionary(data => data.SetId);
}
private static InitialdatacheckResponse.MovieData ToResponseMovieData(MovieData data)
{
var responseMovieData = new InitialdatacheckResponse.MovieData
{
MovieId = data.MovieId,
EnableDays = data.EnableDays
};
private void InitializeMovieData(List<MovieData>? movieData)
{
movieData.ThrowIfNull("Shouldn't happen!");
movieDataDictionary = movieData.ToImmutableDictionary(data => data.MovieId);
}
return responseMovieData;
}
private void InitializeDanData(List<DanData>? danData)
{
danData.ThrowIfNull("Shouldn't happen!");
commonDanDataDictionary = danData.ToImmutableDictionary(data => data.DanId);
}
private static GetfolderResponse.EventfolderData ToResponseEventFolderData(EventFolderData data)
{
var responseEventFolderData = new GetfolderResponse.EventfolderData
{
FolderId = data.FolderId,
VerupNo = data.VerupNo,
Priority = data.Priority,
SongNoes = data.SongNo,
ParentFolderId = data.ParentFolderId
};
private void InitializeGaidenData(List<DanData>? gaidenData)
{
gaidenData.ThrowIfNull("Shouldn't happen!");
commonGaidenDataDictionary = gaidenData.ToImmutableDictionary(data => data.DanId);
}
return responseEventFolderData;
}
private void InitializeEventFolderData(List<EventFolderData>? eventFolderData)
{
eventFolderData.ThrowIfNull("Shouldn't happen!");
eventFolderDictionary = eventFolderData.ToImmutableDictionary(d => d.FolderId);
}
private static GetShopFolderResponse.ShopFolderData ToResponseShopFolderData(ShopFolderData data)
{
var responseShopFolderData = new GetShopFolderResponse.ShopFolderData
{
SongNo = data.SongNo,
Price = data.Price
};
private void InitializeMusicInfoes(MusicInfoes? infoesData)
{
infoesData.ThrowIfNull("Shouldn't happen!");
return responseShopFolderData;
}
musicInfoes = infoesData.MusicInfoEntries.ToImmutableDictionary(info => info.MusicId);
musics = musicInfoes.Select(pair => pair.Key)
.ToList();
musics.Sort();
musicsWithUra = musicInfoes.Where(info => info.Value.StarUra > 0)
.Select(pair => pair.Key)
.ToList();
musicsWithUra.Sort();
}
private void InitializeShopFolderData(List<ShopFolderData>? shopFolderData)
{
shopFolderData.ThrowIfNull("Shouldn't happen!");
shopFolderList = shopFolderData;
}
private void InitializeTokenData(Dictionary<string, int>? tokenData)
{
tokenData.ThrowIfNull("Shouldn't happen!");
tokenDataDictionary = tokenData;
}
private void InitializeLockedSongsData(Dictionary<string, uint[]>? lockedSongsData)
{
lockedSongsData.ThrowIfNull("Shouldn't happen!");
lockedSongsList = lockedSongsData["songNo"].ToList();
}
private void InitializeCostumeFlagArraySizes(DonCosRewards? donCosRewardData)
{
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);
costumeFlagArraySizes = new List<int>
{
(int)kigurumiUniqueIdList.Max() + 1,
(int)headUniqueIdList.Max() + 1,
(int)bodyUniqueIdList.Max() + 1,
(int)faceUniqueIdList.Max() + 1,
(int)puchiUniqueIdList.Max() + 1
};
}
private void InitializeTitleFlagArraySize(Shougous? shougouData)
{
shougouData.ThrowIfNull("Shouldn't happen!");
titleFlagArraySize = (int)shougouData.ShougouEntries.Max(entry => entry.UniqueId) + 1;
}
private void InitializeToneFlagArraySize(Neiros? neiroData)
{
neiroData.ThrowIfNull("Shouldn't happen!");
toneFlagArraySize = (int)neiroData.NeiroEntries.Max(entry => entry.UniqueId) + 1;
}
private void InitializeQrCodeData(List<QRCodeData>? qrCodeData)
{
qrCodeData.ThrowIfNull("Shouldn't happen!");
qrCodeDataDictionary = qrCodeData.ToImmutableDictionary(data => data.Serial, data => data.Id);
}
}

View File

@ -7,8 +7,6 @@ public interface ICardService
{
public Task<Card?> GetCardByAccessCode(string accessCode);
public uint GetNextBaid();
public Task<List<User>> GetUsersFromCards();
public Task AddCard(Card card);

View File

@ -12,18 +12,18 @@ public interface IGameDataService
public List<uint> GetMusicWithUraList();
public ImmutableDictionary<uint, MusicInfoEntry> GetMusicInfoes();
public ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> GetDanDataDictionary();
public ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> GetGaidenDataDictionary();
public ImmutableDictionary<uint, SongIntroductionData> GetSongIntroductionDictionary();
public ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData> GetSongIntroDictionary();
public ImmutableDictionary<uint, MovieData> GetMovieDataDictionary();
public ImmutableDictionary<uint, InitialdatacheckResponse.MovieData> GetMovieDataDictionary();
public ImmutableDictionary<uint, EventFolderData> GetEventFolderDictionary();
public ImmutableDictionary<uint, DanData> GetCommonDanDataDictionary();
public ImmutableDictionary<uint, DanData> GetCommonGaidenDataDictionary();
public ImmutableDictionary<uint, GetfolderResponse.EventfolderData> GetFolderDictionary();
public List<GetShopFolderResponse.ShopFolderData> GetShopFolderList();
public List<ShopFolderData> GetShopFolderList();
public Dictionary<string, int> GetTokenDataDictionary();

View File

@ -75,8 +75,9 @@ public class UserDatumService : IUserDatumService
{
var userDatum = await context.UserData.FindAsync(baid);
userDatum.ThrowIfNull($"User with baid: {baid} not found!");
return userDatum.FavoriteSongsArray.ToList();
using var stringStream = GZipBytesUtil.GenerateStreamFromString(userDatum.FavoriteSongsArray);
/*using var stringStream = GZipBytesUtil.GenerateStreamFromString(userDatum.FavoriteSongsArray);
List<uint>? result;
try
{
@ -88,7 +89,7 @@ public class UserDatumService : IUserDatumService
result = new List<uint>();
}
result.ThrowIfNull("Song favorite array should never be null!");
return result;
return result;*/
}
public async Task UpdateFavoriteSong(uint baid, uint songId, bool isFavorite)
@ -96,7 +97,7 @@ public class UserDatumService : IUserDatumService
var userDatum = await context.UserData.FindAsync(baid);
userDatum.ThrowIfNull($"User with baid: {baid} not found!");
using var stringStream = GZipBytesUtil.GenerateStreamFromString(userDatum.FavoriteSongsArray);
/*using var stringStream = GZipBytesUtil.GenerateStreamFromString(userDatum.FavoriteSongsArray);
List<uint>? favoriteSongIds;
try
{
@ -107,8 +108,8 @@ public class UserDatumService : IUserDatumService
logger.LogError(e, "Parse favorite song array json failed! Is the user initialized correctly?");
favoriteSongIds = new List<uint>();
}
favoriteSongIds.ThrowIfNull("Song favorite array should never be null!");
var favoriteSet = new HashSet<uint>(favoriteSongIds);
favoriteSongIds.ThrowIfNull("Song favorite array should never be null!");*/
var favoriteSet = new HashSet<uint>(userDatum.FavoriteSongsArray);
if (isFavorite)
{
favoriteSet.Add(songId);
@ -123,8 +124,8 @@ public class UserDatumService : IUserDatumService
newFavoriteSongStream.Position = 0;
using var reader = new StreamReader(newFavoriteSongStream);
userDatum.FavoriteSongsArray = await reader.ReadToEndAsync();
logger.LogInformation("Favorite songs are: {Favorite}", userDatum.FavoriteSongsArray);
userDatum.FavoriteSongsArray = favoriteSet.ToArray();//await reader.ReadToEndAsync();
//logger.LogInformation("Favorite songs are: {Favorite}", userDatum.FavoriteSongsArray);
context.Update(userDatum);
await context.SaveChangesAsync();
}

View File

@ -5,7 +5,7 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>1.0.0-beta</Version>
<LangVersion>11</LangVersion>
<LangVersion>12</LangVersion>
<EnableConfigurationBindingGenerator>false</EnableConfigurationBindingGenerator>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
@ -15,6 +15,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="12.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.0-rc.1.23421.29" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0-rc.2.23480.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-rc.2.23480.1" />
@ -22,9 +23,10 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="protobuf-net" Version="3.2.26" />
<PackageReference Include="protobuf-net" Version="3.2.30" />
<PackageReference Include="protobuf-net.AspNetCore" Version="3.2.12" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Riok.Mapperly" Version="3.5.0-next.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2-dev-00334" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0-dev-00137" />
<PackageReference Include="Serilog.Sinks.File.Header" Version="1.0.2" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />

View File

@ -1,6 +1,6 @@
[
{
"serial": "",
"id": 0
"serial": "TOURNAMENT_2021_01",
"id": 12345
}
]