using GameDatabase.Context; using Throw; namespace TaikoLocalServer.Handlers; public record BaidQuery(string AccessCode) : IRequest; public class BaidQueryHandler( TaikoDbContext context, ILogger logger, IGameDataService gameDataService) : IRequestHandler { public async Task 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()) { 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()) { if (scoreRank != ScoreRank.None) { scoreRankCount[(int)scoreRank - 2] = scoreRankData.GetValueOrDefault(scoreRank, (uint)0); } } List costumeData = [userData.CurrentKigurumi, userData.CurrentHead, userData.CurrentBody, userData.CurrentFace, String.IsNullOrEmpty(credential.Password) ? 200 : userData.CurrentPuchi]; List> 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 ""; } } }