224 lines
8.9 KiB
C#
224 lines
8.9 KiB
C#
|
using GameDatabase.Context;
|
|||
|
using Throw;
|
|||
|
|
|||
|
namespace TaikoLocalServer.Handlers;
|
|||
|
|
|||
|
public record BaidQuery(string AccessCode) : IRequest<CommonBaidResponse>;
|
|||
|
|
|||
|
public class BaidQueryHandler(
|
|||
|
TaikoDbContext context,
|
|||
|
ILogger<BaidQueryHandler> logger,
|
|||
|
IGameDataService gameDataService)
|
|||
|
: IRequestHandler<BaidQuery, CommonBaidResponse>
|
|||
|
{
|
|||
|
public async Task<CommonBaidResponse> Handle(BaidQuery request, CancellationToken cancellationToken)
|
|||
|
{
|
|||
|
var card = await context.Cards.FindAsync(request.AccessCode);
|
|||
|
|
|||
|
var oldAccesscode = ConvertOldUID(request.AccessCode);
|
|||
|
var oldcard = await context.Cards.FindAsync(oldAccesscode);
|
|||
|
if (oldcard is not null)
|
|||
|
{
|
|||
|
logger.LogInformation("{AccessCode} converts to {Converted}, Updating...", request.AccessCode, oldcard.AccessCode);
|
|||
|
if (card is null)
|
|||
|
{
|
|||
|
logger.LogInformation("{newCard} wasn't bound !, Updating...", request.AccessCode);
|
|||
|
|
|||
|
Card newCard = new Card();
|
|||
|
newCard.Baid = oldcard.Baid;
|
|||
|
newCard.AccessCode = request.AccessCode;
|
|||
|
|
|||
|
await context.Cards.AddAsync(newCard);
|
|||
|
card = newCard;
|
|||
|
}
|
|||
|
else logger.LogInformation("{newCard} was already bound !", request.AccessCode);
|
|||
|
|
|||
|
context.Cards.Remove(oldcard);
|
|||
|
await context.SaveChangesAsync(cancellationToken);
|
|||
|
}
|
|||
|
|
|||
|
if (card is null)
|
|||
|
{
|
|||
|
logger.LogInformation("New user with access code {AccessCode}", request.AccessCode);
|
|||
|
return new CommonBaidResponse
|
|||
|
{
|
|||
|
Result = 1,
|
|||
|
IsNewUser = true,
|
|||
|
Baid = context.Cards.Any() ? context.Cards.AsEnumerable().Max(c => c.Baid) + 1 : 1
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
var baid = card.Baid;
|
|||
|
var credential = context.Credentials.Where(cred => cred.Baid == baid).First();
|
|||
|
var userData = await context.UserData.FindAsync(baid, cancellationToken);
|
|||
|
userData.ThrowIfNull($"User not found for card with Baid {baid}!");
|
|||
|
|
|||
|
var songBestData = context.SongBestData.Where(datum => datum.Baid == baid).ToList();
|
|||
|
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 = songBestData
|
|||
|
.Where(datum => datum.BestCrown >= CrownType.Clear)
|
|||
|
.GroupBy(datum => datum.BestCrown)
|
|||
|
.ToDictionary(datums => datums.Key, datums => (uint)datums.Count());
|
|||
|
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 = songBestData
|
|||
|
.Where(datum => datum.BestCrown >= CrownType.Clear)
|
|||
|
.GroupBy(datum => datum.BestScoreRank)
|
|||
|
.ToDictionary(datums => datums.Key, datums => (uint)datums.Count());
|
|||
|
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);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
List<uint> costumeData = [userData.CurrentKigurumi, userData.CurrentHead, userData.CurrentBody, userData.CurrentFace, String.IsNullOrEmpty(credential.Password) ? 200 : userData.CurrentPuchi];
|
|||
|
|
|||
|
List<List<uint>> costumeArrays =
|
|||
|
[userData.UnlockedKigurumi, userData.UnlockedHead, userData.UnlockedBody, userData.UnlockedFace, userData.UnlockedPuchi];
|
|||
|
|
|||
|
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 > 10)
|
|||
|
{
|
|||
|
aiRank = 10;
|
|||
|
}
|
|||
|
|
|||
|
return new CommonBaidResponse
|
|||
|
{
|
|||
|
Result = 1,
|
|||
|
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.DateTimeFormat),
|
|||
|
LastPlayMode = userData.LastPlayMode,
|
|||
|
SelectedToneId = userData.SelectedToneId,
|
|||
|
Title = String.IsNullOrEmpty(credential.Password) ? FormatStringInGroupsOfFour(request.AccessCode) : userData.Title,
|
|||
|
TitlePlateId = userData.TitlePlateId,
|
|||
|
AiTotalWin = (uint)userData.AiWinCount,
|
|||
|
AiRank = aiRank
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
static string FormatStringInGroupsOfFour(string input)
|
|||
|
{
|
|||
|
if (string.IsNullOrEmpty(input))
|
|||
|
return input;
|
|||
|
|
|||
|
// Use a StringBuilder for efficient string manipulation
|
|||
|
var stringBuilder = new System.Text.StringBuilder();
|
|||
|
int length = input.Length;
|
|||
|
|
|||
|
for (int i = 0; i < length; i++)
|
|||
|
{
|
|||
|
if (i > 0 && i % 4 == 0)
|
|||
|
{
|
|||
|
stringBuilder.Append(' ');
|
|||
|
}
|
|||
|
stringBuilder.Append(input[i]);
|
|||
|
}
|
|||
|
|
|||
|
return stringBuilder.ToString();
|
|||
|
}
|
|||
|
|
|||
|
string PadLeftWithZeros(string input, int desiredLength)
|
|||
|
{
|
|||
|
int zerosToAdd = Math.Max(0, desiredLength - input.Length);
|
|||
|
return new string('0', zerosToAdd) + input;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
string ConvertOldUID(string inputCardNum)
|
|||
|
{
|
|||
|
// Convert hexadecimal string to a byte array
|
|||
|
inputCardNum = inputCardNum.ToUpper().Trim();
|
|||
|
//Console.WriteLine(inputCardNum);
|
|||
|
try
|
|||
|
{
|
|||
|
byte[] byteArray = new byte[inputCardNum.Length / 2];
|
|||
|
for (int i = 0; i < inputCardNum.Length; i += 2)
|
|||
|
{
|
|||
|
byteArray[i / 2] = Convert.ToByte(inputCardNum.Substring(i, 2), 16);
|
|||
|
}
|
|||
|
|
|||
|
// Reverse the array if needed (depends on endianness)
|
|||
|
Array.Reverse(byteArray);
|
|||
|
|
|||
|
// Convert byte array to an unsigned long integer
|
|||
|
string convertedNumber = PadLeftWithZeros(BitConverter.ToUInt64(byteArray, 0).ToString(), 20);
|
|||
|
|
|||
|
//Console.WriteLine($"Hexadecimal: {inputCardNum}");
|
|||
|
//Console.WriteLine($"Decimal: {convertedNumber}");
|
|||
|
|
|||
|
return convertedNumber;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
catch { }
|
|||
|
return "";
|
|||
|
}
|
|||
|
}
|
|||
|
}
|