Compare commits

...
52 Commits
Author SHA1 Message Date
asesidaa 82e0ecd9c3 Update card_detail.md 2024-01-16 23:42:54 +08:00
asesidaa bd2cb39b63 Add stub for writewmusic 2023-11-09 02:46:24 +08:00
asesidaa 22eff77e88 Merge remote-tracking branch 'origin/master' 2023-11-05 18:42:51 +08:00
asesidaa 449cd579d4 Update dbs to include calc flag 2023-11-05 18:38:26 +08:00
asesidaa 0896e92598 Fix new and calc flag for music
Add column to music.db3
Minor fix in Program.cs
2023-11-05 18:35:26 +08:00
asesidaa e159a427a0 Update to test play count support on older versions 2023-10-14 20:32:20 +08:00
asesidaa 7edcd07723 Test temp fix for cond 2023-10-12 02:20:43 +08:00
asesidaa dbc776ddda Add validation to rank options 2023-10-08 15:49:55 +08:00
asesidaa 66aaf027e2 Fix tenpo rank when read card 2023-10-08 15:00:07 +08:00
asesidaa 48956e758f Update to support custom rank update interval.
Add support for accurate tenpo score rank.
2023-10-08 02:12:10 +08:00
asesidaa 64da380363 Update to support custom shop name and id
Bump dependency
2023-09-30 16:33:21 +08:00
asesidaa 5997452412 Merge remote-tracking branch 'origin/master' 2023-07-03 02:05:59 +08:00
asesidaa c19437baf6 Update final data 2023-06-25 15:59:38 +08:00
asesidaa eebbf05b10 Update README.md 2023-03-03 19:10:57 +08:00
asesidaa a0eefdc3a9 Merge remote-tracking branch 'origin/master' 2023-03-03 19:03:25 +08:00
asesidaa cd5c06c97d Fix unlock music and saving time 2023-03-03 19:03:13 +08:00
asesidaa f0a1dbc5ce Update README.md 2023-03-03 16:19:03 +08:00
asesidaa 0fd5fc7227 Remove old version 2023-03-03 12:18:46 +08:00
asesidaa 32a1886cf4 Update and rename Certifications.md to ImportCertificatesInX0.md 2023-03-01 18:39:21 +08:00
asesidaa 5afd885a00 Update README.md 2023-02-27 12:20:48 +08:00
asesidaa ae2fe531a6 Update README.md 2023-02-27 02:48:21 +08:00
asesidaa addee9ccdd Revert defaults 2023-02-27 02:44:21 +08:00
asesidaa f1edf55883 Delete temp files 2023-02-27 02:43:38 +08:00
asesidaa 46d9a63a45 Migrate online matching 2023-02-27 02:43:13 +08:00
asesidaa 728415902b Add entities for online matching 2023-02-26 00:57:03 +08:00
asesidaa fe6ff27ffa Add import script 2023-02-26 00:10:53 +08:00
asesidaa c16b083b99 Add bundled certificates, only manage certificate on windows.
Update README.md
2023-02-23 17:41:15 +08:00
asesidaa a911a9054c Update README.md 2023-02-23 02:39:11 +08:00
asesidaa 57e379817f Do not initialize when not using events 2023-02-23 02:31:48 +08:00
asesidaa a6dbf26bdf Update README.md, update default values for release 2023-02-23 02:22:04 +08:00
asesidaa 7c5c2fafb7 Minor fixes and prepare for release 2023-02-23 01:15:53 +08:00
asesidaa eb4380b6d5 Add Play Records page and apis, finish migration (except for online matching) 2023-02-23 00:35:59 +08:00
asesidaa e5eee66ff2 Finish total result and options page, add unlock all music function 2023-02-22 02:55:32 +08:00
asesidaa 4d0e351abd Update WebUI 2023-02-21 22:45:51 +08:00
asesidaa 8900cca29e Update on web ui 2023-02-18 01:29:20 +08:00
asesidaa 2ff32b9693 Start API and WebUI rework 2023-02-17 00:38:01 +08:00
asesidaa 6de389cf2b Use global using in application 2023-02-16 16:53:02 +08:00
asesidaa ef11e98da7 Add single rank 2023-02-16 16:04:38 +08:00
asesidaa 7b3fae51b7 Rank and unlock fixes 2023-02-16 15:26:13 +08:00
asesidaa 9d58e71e06 Basically finish migration for server, implement play number rank, wait for testing 2023-02-15 02:14:19 +08:00
asesidaa 02a241567c Fix alive, migrate write, add online matching stub, start rank 2023-02-14 02:47:08 +08:00
asesidaa 5c85e83c3e Migrate read 2023-02-14 00:38:19 +08:00
asesidaa 1fccca42c6 Implement music read, fix artist nullable 2023-02-13 02:40:40 +08:00
asesidaa 1db9fdf6d3 Update commands, add option to aggregate 2023-02-13 02:12:26 +08:00
asesidaa 14f171792e Add read stubs, implement card detail and avatar 2023-02-13 01:46:08 +08:00
asesidaa 805611027c Add card read, use service error for not registered and not reissue 2023-02-12 01:09:29 +08:00
asesidaa 7093997658 Add stubs for online mathcing and writing 2023-02-11 01:24:24 +08:00
asesidaa aefc9ca326 Include music db files, update link for event files, fix .evt and .cmp static file extension unknown 2023-02-11 00:46:44 +08:00
asesidaa 6eb71437cf Start card read migration 2023-02-09 23:19:14 +08:00
asesidaa 9c759c3b1a Start implementing card 2023-02-09 17:25:42 +08:00
asesidaa f7761f200f Start migration to asp .net core + entity framework core 2023-02-08 21:33:22 +08:00
asesidaa aea09c2c8b Test linux cert, change event download url 2022-08-06 14:30:42 +08:00
343 changed files with 54831 additions and 16673 deletions
+18
View File
@@ -0,0 +1,18 @@
namespace Application.Api;
public record GetCardsQuery() : IRequestWrapper<List<ClientCardDto>>;
public class GetCardsQueryHandler : RequestHandlerBase<GetCardsQuery, List<ClientCardDto>>
{
public GetCardsQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override async Task<ServiceResult<List<ClientCardDto>>> Handle(GetCardsQuery request, CancellationToken cancellationToken)
{
var cards = await CardDbContext.CardMains.ToListAsync(cancellationToken: cancellationToken);
var dtoList = cards.Select(card => card.CardMainToClientDto()).ToList();
return new ServiceResult<List<ClientCardDto>>(dtoList);
}
}
+43
View File
@@ -0,0 +1,43 @@
using Shared.Models;
namespace Application.Api;
public record GetPlayOptionQuery(long CardId) : IRequestWrapper<PlayOptionData>;
public class GetPlayOptionQueryHandler : RequestHandlerBase<GetPlayOptionQuery, PlayOptionData>
{
public GetPlayOptionQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override async Task<ServiceResult<PlayOptionData>> Handle(GetPlayOptionQuery request,
CancellationToken cancellationToken)
{
var optionDetail1 = await CardDbContext.CardDetails.FirstOrDefaultAsync(detail =>
detail.CardId == request.CardId &&
detail.Pcol1 == 0 &&
detail.Pcol2 == 0 &&
detail.Pcol3 == 0,
cancellationToken: cancellationToken);
var optionDetail2 = await CardDbContext.CardDetails.FirstOrDefaultAsync(detail =>
detail.CardId == request.CardId &&
detail.Pcol1 == 1 &&
detail.Pcol2 == 0 &&
detail.Pcol3 == 0,
cancellationToken: cancellationToken);
if (optionDetail1 is null ||
optionDetail2 is null)
{
return ServiceResult.Failed<PlayOptionData>(ServiceError.CustomMessage("At least one of the play option records not found"));
}
var result = new PlayOptionData
{
CardId = request.CardId,
OptionPart1 = optionDetail1.CardDetailToFirstOption(),
OptionPart2 = optionDetail2.CardDetailToSecondOption()
};
return new ServiceResult<PlayOptionData>(result);
}
}
+125
View File
@@ -0,0 +1,125 @@
using System.Diagnostics.CodeAnalysis;
using Domain.Entities;
using Domain.Enums;
namespace Application.Api;
public record GetSongPlayRecordsQuery(long cardId) : IRequestWrapper<List<SongPlayRecord>>;
public class GetSongPlayRecordsQueryHandler : RequestHandlerBase<GetSongPlayRecordsQuery, List<SongPlayRecord>>
{
public GetSongPlayRecordsQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records")]
public override async Task<ServiceResult<List<SongPlayRecord>>> Handle(GetSongPlayRecordsQuery request,
CancellationToken cancellationToken)
{
var exists = await CardDbContext.CardDetails.AnyAsync(detail => detail.CardId == request.cardId);
if (!exists)
{
return ServiceResult.Failed<List<SongPlayRecord>>(ServiceError.CustomMessage("No play record"));
}
var results = new List<SongPlayRecord>();
var musics = await MusicDbContext.MusicUnlocks.ToDictionaryAsync(unlock => unlock.MusicId, cancellationToken);
var playCounts = await CardDbContext.CardDetails
.Where(detail => detail.CardId == request.cardId &&
detail.Pcol1 == 20)
.Select(detail => new
{
MusicId = detail.Pcol2,
Difficulty = (Difficulty)detail.Pcol3,
Detail = detail
})
.ToDictionaryAsync(arg => new { arg.MusicId, arg.Difficulty }, cancellationToken: cancellationToken);
var stageDetails = await CardDbContext.CardDetails
.Where(detail => detail.CardId == request.cardId &&
detail.Pcol1 == 21)
.Select(detail => new
{
MusicId = detail.Pcol2,
Difficulty = (Difficulty)detail.Pcol3,
Score = detail.ScoreUi1,
MaxChain = detail.ScoreUi3,
})
.ToDictionaryAsync(arg => new { arg.MusicId, arg.Difficulty }, cancellationToken);
var favorites = await CardDbContext.CardDetails
.Where(detail => detail.CardId == request.cardId &&
detail.Pcol1 == 10)
.Select(detail => new { MusicId = detail.Pcol2, IsFavorite = detail.Fcol1 == 1 })
.ToListAsync(cancellationToken);
foreach (var song in favorites)
{
var musicId = song.MusicId;
var music = musics.GetValueOrDefault(musicId);
var songPlayRecord = new SongPlayRecord
{
MusicId = (int)musicId,
Title = music?.Title ?? string.Empty,
Artist = music?.Artist ?? string.Empty,
IsFavorite = song.IsFavorite
};
foreach (var difficulty in DifficultyExtensions.GetValues())
{
var key = new { MusicId = musicId, Difficulty = difficulty };
if (!playCounts.ContainsKey(key) || !stageDetails.ContainsKey(key)) continue;
var playCountDetail = playCounts[key].Detail;
var playCount = playCountDetail.ScoreUi1;
var stageDetail = stageDetails[key];
var score = stageDetail.Score;
var maxChain = stageDetail.MaxChain;
var clearState = GetClearState(playCountDetail);
var stagePlayRecord = new StagePlayRecord
{
Difficulty = difficulty,
ClearState = clearState,
PlayCount = (int)playCount,
Score = (int)score,
MaxChain = (int)maxChain,
LastPlayTime = playCountDetail?.LastPlayTime ?? DateTime.MinValue
};
songPlayRecord.StagePlayRecords.Add(stagePlayRecord);
}
songPlayRecord.TotalPlayCount = songPlayRecord.StagePlayRecords.Sum(record => record.PlayCount);
if (songPlayRecord.StagePlayRecords.Count > 0)
{
results.Add(songPlayRecord);
}
}
return new ServiceResult<List<SongPlayRecord>>(results);
}
private static ClearState GetClearState(CardDetail detail)
{
var result = ClearState.Failed;
if (detail.ScoreUi2 > 0)
{
result = ClearState.Clear;
}
if (detail.ScoreUi3 > 0)
{
result = ClearState.NoMiss;
}
if (detail.ScoreUi4 > 0)
{
result = ClearState.FullChain;
}
if (detail.ScoreUi6 > 0)
{
result = ClearState.Perfect;
}
return result;
}
}
+80
View File
@@ -0,0 +1,80 @@
using Microsoft.Extensions.Logging;
using Shared.Models;
namespace Application.Api;
public record GetTotalResultQuery(long CardId) : IRequestWrapper<TotalResultData>;
public class GetTotalResultQueryHandler : RequestHandlerBase<GetTotalResultQuery, TotalResultData>
{
private const int S_SCORE = 900000;
private const int SS_SCORE = 950000;
private const int SSS_SCORE = 990000;
private readonly ILogger<GetTotalResultQueryHandler> logger;
public GetTotalResultQueryHandler(ICardDependencyAggregate aggregate, ILogger<GetTotalResultQueryHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<TotalResultData>> Handle(GetTotalResultQuery request, CancellationToken cancellationToken)
{
var card = await CardDbContext.CardMains.FirstOrDefaultAsync(card => card.CardId == request.CardId, cancellationToken);
if (card is null)
{
logger.LogWarning("Trying to get total result for non existing card: {CardId}", request.CardId);
return ServiceResult.Failed<TotalResultData>(ServiceError.UserNotFound);
}
var result = new TotalResultData
{
CardId = card.CardId,
PlayerName = card.PlayerName
};
var totalSongCount = await MusicDbContext.MusicUnlocks.CountAsync(cancellationToken: cancellationToken);
var totalExtraCount = await MusicDbContext.MusicExtras.CountAsync(cancellationToken: cancellationToken);
var totalStageCount = totalSongCount * 3 + totalExtraCount;
result.PlayerData.TotalSongCount = totalSongCount;
result.StageCountData.Total = totalStageCount;
var playedStageDetails = await CardDbContext.CardDetails.Where(detail =>
detail.CardId == request.CardId &&
detail.Pcol1 == 20).ToListAsync(cancellationToken: cancellationToken);
var playedStageScores = await CardDbContext.CardDetails.Where(detail =>
detail.CardId == request.CardId &&
detail.Pcol1 == 21).ToListAsync(cancellationToken: cancellationToken);
var playedSongCount = playedStageDetails.DistinctBy(detail => detail.Pcol2).Count();
var playedStageCount = playedStageDetails.Count;
var clearedStageCount = playedStageDetails.Count(detail => detail.ScoreUi2 > 0);
var noMissStageCount = playedStageDetails.Count(detail => detail.ScoreUi3 > 0);
var fullChainStageCount = playedStageDetails.Count(detail => detail.ScoreUi4 > 0);
var perfectStageCount = playedStageDetails.Count(detail => detail.ScoreUi6 > 0);
var sStageCount = playedStageScores.Count(detail => detail.ScoreUi1 > S_SCORE);
var ssStageCount = playedStageScores.Count(detail => detail.ScoreUi1 > SS_SCORE);
var sssStageCount = playedStageScores.Count(detail => detail.ScoreUi1 > SSS_SCORE);
result.PlayerData.PlayedSongCount = playedSongCount;
result.StageCountData.Cleared = clearedStageCount;
result.StageCountData.NoMiss = noMissStageCount;
result.StageCountData.FullChain = fullChainStageCount;
result.StageCountData.Perfect = perfectStageCount;
result.StageCountData.S = sStageCount;
result.StageCountData.Ss = ssStageCount;
result.StageCountData.Sss = sssStageCount;
var totalScore = playedStageScores.Sum(detail => detail.ScoreUi1);
var averageScore = playedStageCount == 0 ? 0 : totalScore / playedStageCount;
result.PlayerData.TotalScore = totalScore;
result.PlayerData.AverageScore = (int)averageScore;
var rank = await CardDbContext.GlobalScoreRanks.FirstOrDefaultAsync(rank => rank.CardId == request.CardId,
cancellationToken: cancellationToken);
result.PlayerData.Rank = (int)(rank?.Rank ?? -1);
return new ServiceResult<TotalResultData>(result);
}
}
@@ -0,0 +1,39 @@
using Microsoft.Extensions.Logging;
namespace Application.Api;
public record SetFavoriteMusicCommand(MusicFavoriteDto Data) : IRequestWrapper<bool>;
public class SetFavoriteMusicCommandHandler : RequestHandlerBase<SetFavoriteMusicCommand, bool>
{
private readonly ILogger<SetFavoriteMusicCommandHandler> logger;
public SetFavoriteMusicCommandHandler(ICardDependencyAggregate aggregate, ILogger<SetFavoriteMusicCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<bool>> Handle(SetFavoriteMusicCommand request,
CancellationToken cancellationToken)
{
var musicDetail = await CardDbContext.CardDetails.FirstOrDefaultAsync(detail =>
detail.CardId == request.Data.CardId &&
detail.Pcol1 == 10 &&
detail.Pcol2 == request.Data.MusicId &&
detail.Pcol3 == 0,
cancellationToken);
if (musicDetail is null)
{
logger.LogWarning("Attempt to set favorite for non existing music, card id: {CardId}, music id: {MusicId}",
request.Data.CardId, request.Data.MusicId);
return ServiceResult.Failed<bool>(ServiceError.CustomMessage("Music record not found"));
}
musicDetail.Fcol1 = request.Data.IsFavorite ? 1 : 0;
CardDbContext.CardDetails.Update(musicDetail);
var count = await CardDbContext.SaveChangesAsync(cancellationToken);
return count == 1 ? new ServiceResult<bool>(true) : ServiceResult.Failed<bool>(ServiceError.DatabaseSaveFailed);
}
}
+49
View File
@@ -0,0 +1,49 @@
using Microsoft.Extensions.Logging;
using Shared.Models;
namespace Application.Api;
public record SetPlayOptionCommand(PlayOptionData Data) : IRequestWrapper<bool>;
public class SetPlayOptionCommandHandler : RequestHandlerBase<SetPlayOptionCommand, bool>
{
private readonly ILogger<SetPlayOptionCommandHandler> logger;
public SetPlayOptionCommandHandler(ICardDependencyAggregate aggregate, ILogger<SetPlayOptionCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<bool>> Handle(SetPlayOptionCommand request, CancellationToken cancellationToken)
{
var optionDetail1 = await CardDbContext.CardDetails.FirstOrDefaultAsync(detail =>
detail.CardId == request.Data.CardId &&
detail.Pcol1 == 0 &&
detail.Pcol2 == 0 &&
detail.Pcol3 == 0,
cancellationToken: cancellationToken);
var optionDetail2 = await CardDbContext.CardDetails.FirstOrDefaultAsync(detail =>
detail.CardId == request.Data.CardId &&
detail.Pcol1 == 1 &&
detail.Pcol2 == 0 &&
detail.Pcol3 == 0,
cancellationToken: cancellationToken);
if (optionDetail1 is null ||
optionDetail2 is null)
{
logger.LogWarning("Attempt to set play options for card id {CardId} failed due to missing data",
request.Data.CardId);
return ServiceResult.Failed<bool>(ServiceError.CustomMessage("At least one of the play option records not found"));
}
request.Data.OptionPart1.MapFirstOptionDetail(optionDetail1);
request.Data.OptionPart2.MapSecondOptionDetail(optionDetail2);
CardDbContext.CardDetails.Update(optionDetail1);
CardDbContext.CardDetails.Update(optionDetail2);
var count = await CardDbContext.SaveChangesAsync(cancellationToken);
return count == 1 ? new ServiceResult<bool>(true) : ServiceResult.Failed<bool>(ServiceError.DatabaseSaveFailed);
}
}
+33
View File
@@ -0,0 +1,33 @@
using Application.Common.Helpers;
using Microsoft.Extensions.Logging;
namespace Application.Api;
public record SetPlayerNameCommand(ClientCardDto Card) : IRequestWrapper<bool>;
public class SetPlayerNameCommandHandler : RequestHandlerBase<SetPlayerNameCommand, bool>
{
private readonly ILogger<SetPlayerNameCommandHandler> logger;
public SetPlayerNameCommandHandler(ICardDependencyAggregate aggregate, ILogger<SetPlayerNameCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<bool>> Handle(SetPlayerNameCommand request, CancellationToken cancellationToken)
{
var card = await CardDbContext.CardMains.FirstOrDefaultAsync(card => card.CardId == request.Card.CardId, cancellationToken: cancellationToken);
if (card is null)
{
logger.LogWarning("Attempt to set name for a non existing card {CardId}", request.Card.CardId);
return ServiceResult.Failed<bool>(ServiceError.UserNotFound);
}
card.PlayerName = request.Card.PlayerName;
card.Modified = TimeHelper.CurrentTimeToString();
CardDbContext.CardMains.Update(card);
var count = await CardDbContext.SaveChangesAsync(cancellationToken);
return count == 1 ? new ServiceResult<bool>(true) : ServiceResult.Failed<bool>(ServiceError.DatabaseSaveFailed);
}
}
+52
View File
@@ -0,0 +1,52 @@
using System.Diagnostics.CodeAnalysis;
using ChoETL;
using Domain.Enums;
using Microsoft.Extensions.Logging;
namespace Application.Api;
public record UnlockAllMusicCommand(long CardId) : IRequestWrapper<bool>;
public class UnlockAllMusicCommandHandler : RequestHandlerBase<UnlockAllMusicCommand, bool>
{
private readonly ILogger<UnlockAllMusicCommandHandler> logger;
public UnlockAllMusicCommandHandler(ICardDependencyAggregate aggregate,
ILogger<UnlockAllMusicCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records")]
[SuppressMessage("ReSharper.DPA", "DPA0006: Large number of DB commands")]
public override async Task<ServiceResult<bool>> Handle(UnlockAllMusicCommand request, CancellationToken cancellationToken)
{
var exists = await CardDbContext.CardDetails.AnyAsync(
detail => detail.CardId == request.CardId, cancellationToken);
if (!exists)
{
logger.LogWarning("Attempt to unlock for card {Card} that does not exist or is empty!", request.CardId);
return ServiceResult.Failed<bool>(ServiceError.CustomMessage("Unlock failed"));
}
var unlockables = Config.UnlockRewards
.Where(config => config.RewardType == RewardType.Music)
.Select(config => new CardDetailDto
{
CardId = request.CardId,
Pcol1 = 10,
Pcol2 = config.TargetId,
Pcol3 = 0,
LastPlayTenpoId = "1337",
LastPlayTime = DateTime.Now,
ScoreUi2 = 1,
ScoreUi6 = 1
}.DtoToCardDetail());
await CardDbContext.CardDetails.UpsertRange(unlockables).RunAsync(cancellationToken);
await CardDbContext.SaveChangesAsync(cancellationToken);
return new ServiceResult<bool>(true);
}
}
+27
View File
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Domain\Domain.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ChoETL" Version="1.2.1.64" />
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="7.0.0" />
<PackageReference Include="MediatR" Version="12.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.0" />
<PackageReference Include="Microsoft.NETCore.Targets" Version="6.0.0-preview.4.21253.7" />
<PackageReference Include="Quartz.AspNetCore" Version="3.6.0" />
<PackageReference Include="Riok.Mapperly" Version="2.7.0-next.2" />
<PackageReference Include="Throw" Version="1.3.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,21 @@
using Domain.Config;
namespace Application.Common.Base;
public abstract class RequestHandlerBase<TIn, TOut>: IRequestHandlerWrapper<TIn, TOut>
where TIn : IRequestWrapper<TOut>
{
protected ICardDbContext CardDbContext { get; }
protected IMusicDbContext MusicDbContext { get; }
protected GameConfig Config { get; }
public RequestHandlerBase(ICardDependencyAggregate aggregate)
{
CardDbContext = aggregate.CardDbContext;
MusicDbContext = aggregate.MusicDbContext;
Config = aggregate.Options.Value;
}
public abstract Task<ServiceResult<TOut>> Handle(TIn request, CancellationToken cancellationToken);
}
@@ -0,0 +1,24 @@
using MediatR.Pipeline;
using Microsoft.Extensions.Logging;
namespace Application.Common.Behaviours;
public class LoggingBehaviour<TRequest> : IRequestPreProcessor<TRequest> where TRequest : notnull
{
private readonly ILogger<TRequest> logger;
// ReSharper disable once ContextualLoggerProblem
public LoggingBehaviour(ILogger<TRequest> logger)
{
this.logger = logger;
}
public Task Process(TRequest request, CancellationToken cancellationToken)
{
var requestName = typeof(TRequest).Name;
logger.LogInformation("Received request: {RequestName}, content: {Request}", requestName, request);
return Task.CompletedTask;
}
}
@@ -0,0 +1,33 @@
using MediatR;
using Microsoft.Extensions.Logging;
namespace Application.Common.Behaviours;
public class UnhandledExceptionBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly ILogger<TRequest> logger;
// ReSharper disable once ContextualLoggerProblem
public UnhandledExceptionBehaviour(ILogger<TRequest> logger)
{
this.logger = logger;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
{
try
{
return await next();
}
catch (Exception ex)
{
var requestName = typeof(TRequest).Name;
logger.LogError(ex, "Unhandled Exception for Request {Name} {@Request}", requestName, request);
throw;
}
}
}
@@ -0,0 +1,8 @@
namespace Application.Common.Exceptions;
public class CardExistsException : Exception
{
public CardExistsException(string? message) : base(message)
{
}
}
@@ -0,0 +1,57 @@
using System.Text;
using ChoETL;
using Throw;
namespace Application.Common.Extensions;
public static class XmlSerializationExtensions
{
public static T DeserializeCardData<T>(this string source) where T : class
{
using var reader = new ChoXmlReader<T>(new StringReader(source)).WithXPath("/root/data");
reader.Configuration.IgnoreFieldValueMode = ChoIgnoreFieldValueMode.Any;
var result = reader.Read();
result.ThrowIfNull();
return result;
}
public static string SerializeCardData<T>(this T source, string xpath) where T : class
{
var buffer = new StringBuilder();
using (var writer = new ChoXmlWriter<T>(buffer).WithXPath(xpath).UseXmlSerialization())
{
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.DoNotEmitXmlNamespace = true;
writer.Write(source);
}
return buffer.ToString();
}
public static string SerializeCardData<T>(this T source) where T : class
{
var buffer = new StringBuilder();
using (var writer = new ChoXmlWriter<T>(buffer).UseXmlSerialization())
{
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.DoNotEmitXmlNamespace = true;
writer.Configuration.IgnoreRootName = true;
writer.Write(source);
}
return buffer.ToString();
}
public static string SerializeCardDataList<T>(this IEnumerable<T> source, string xpath) where T : class
{
var buffer = new StringBuilder();
using (var writer = new ChoXmlWriter<T>(buffer).WithXPath(xpath).UseXmlSerialization())
{
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.DoNotEmitXmlNamespace = true;
writer.Write(source);
}
return buffer.ToString();
}
}
+14
View File
@@ -0,0 +1,14 @@
namespace Application.Common.Helpers;
public static class TimeHelper
{
public static string CurrentTimeToString()
{
return DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss");
}
public static string DateToString(DateTime time)
{
return time.ToString("yyyy/MM/dd hh:mm:ss");
}
}
+77
View File
@@ -0,0 +1,77 @@
using System.Reflection;
using Application.Common.Behaviours;
using Application.Game.Card;
using Application.Jobs;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
namespace Application;
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services, int refreshIntervalHours = 24)
{
services.AddMediatR(configuration => configuration.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()));
services.AddScoped<ICardDependencyAggregate, CardDependencyAggregate>();
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
q.AddJob<UpdatePlayNumRankJob>(options => options.WithIdentity(UpdatePlayNumRankJob.KEY));
q.AddTrigger(options =>
{
options.ForJob(UpdatePlayNumRankJob.KEY)
.WithIdentity("UpdatePlayNumRankJob-trigger")
.StartNow()
.WithSimpleSchedule(x =>
{
x.WithIntervalInHours(refreshIntervalHours).RepeatForever();
});
});
q.AddJob<UpdateGlobalScoreRankJob>(options => options.WithIdentity(UpdateGlobalScoreRankJob.KEY));
q.AddTrigger(options =>
{
options.ForJob(UpdateGlobalScoreRankJob.KEY)
.WithIdentity("UpdateGlobalScoreRankJob-trigger")
.StartNow()
.WithSimpleSchedule(x =>
{
x.WithIntervalInHours(refreshIntervalHours).RepeatForever();
});
});
q.AddJob<UpdateMonthlyScoreRankJob>(options => options.WithIdentity(UpdateMonthlyScoreRankJob.KEY));
q.AddTrigger(options =>
{
options.ForJob(UpdateMonthlyScoreRankJob.KEY)
.WithIdentity("UpdateMonthlyScoreRankJob-trigger")
.StartNow()
.WithSimpleSchedule(x =>
{
x.WithIntervalInHours(refreshIntervalHours).RepeatForever();
});
});
q.AddJob<MaintainNullValuesJob>(options => options.WithIdentity(MaintainNullValuesJob.KEY));
q.AddTrigger(options =>
{
options.ForJob(MaintainNullValuesJob.KEY)
.WithIdentity("MaintainNullValuesJob-trigger")
.StartNow()
.WithSimpleSchedule(x =>
{
x.WithIntervalInHours(refreshIntervalHours).RepeatForever();
});
});
});
services.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
});
return services;
}
}
+29
View File
@@ -0,0 +1,29 @@
using System.ComponentModel;
namespace Application.Dto.Game;
public class AvatarDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "avatar_id")]
public int AvatarId { get; set; }
[XmlElement("created")]
[DefaultValue("")]
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
[DefaultValue("")]
public string Modified { get; set; } = string.Empty;
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; }
}
+13
View File
@@ -0,0 +1,13 @@
namespace Application.Dto.Game;
public class CardBDatumDto
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "bdata")]
public string CardBdata { get; set; } = string.Empty;
[XmlElement(ElementName = "bdata_size")]
public int BDataSize { get; set; }
}
+66
View File
@@ -0,0 +1,66 @@
using System.ComponentModel;
namespace Application.Dto.Game;
public class CardDetailDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; } = -1;
public bool ShouldSerializeId()
{
return Id != -1;
}
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "pcol1")]
public int Pcol1 { get; set; }
[XmlElement(ElementName = "pcol2")]
public int Pcol2 { get; set; }
[XmlElement(ElementName = "pcol3")]
public int Pcol3 { get; set; }
[XmlElement(ElementName = "score_i1")]
public long ScoreI1 { get; set; }
[XmlElement(ElementName = "score_ui1")]
public long ScoreUi1 { get; set; }
[XmlElement(ElementName = "score_ui2")]
public long ScoreUi2 { get; set; }
[XmlElement(ElementName = "score_ui3")]
public long ScoreUi3 { get; set; }
[XmlElement(ElementName = "score_ui4")]
public long ScoreUi4 { get; set; }
[XmlElement(ElementName = "score_ui5")]
public long ScoreUi5 { get; set; }
[XmlElement(ElementName = "score_ui6")]
public long ScoreUi6 { get; set; }
[XmlElement(ElementName = "score_bi1")]
public long ScoreBi1 { get; set; }
[XmlElement(ElementName = "last_play_tenpo_id")]
[DefaultValue("1337")]
public string LastPlayTenpoId { get; set; } = "1337";
[XmlElement("fcol1")]
public int Fcol1 { get; set; }
[XmlElement("fcol2")]
public int Fcol2 { get; set; }
[XmlElement("fcol3")]
public int Fcol3 { get; set; }
[XmlIgnore]
public DateTime LastPlayTime { get; set; } = DateTime.MinValue;
}
+37
View File
@@ -0,0 +1,37 @@
using System.ComponentModel;
namespace Application.Dto.Game;
public class CardDto
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "player_name")]
[DefaultValue("")]
public string PlayerName { get; set; } = string.Empty;
[XmlElement("score_i1")]
public long ScoreI1 { get; set; }
[XmlElement("fcol1")]
public long Fcol1 { get; set; }
[XmlElement("fcol2")]
public long Fcol2 { get; set; }
[XmlElement("fcol3")]
public long Fcol3 { get; set; }
[XmlElement("achieve_status")]
[DefaultValue("")]
public string AchieveStatus { get; set; } = string.Empty;
[XmlElement("created")]
[DefaultValue("")]
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
[DefaultValue("")]
public string Modified { get; set; } = string.Empty;
}
@@ -1,8 +1,6 @@
using System.Xml.Serialization;
namespace Application.Dto.Game;
namespace GCLocalServerRewrite.models;
public class Coin
public class CoinDto
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
@@ -17,8 +15,8 @@ public class Coin
public int MonthlyCoins { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
public string Modified { get; set; } = "1";
public string Modified { get; set; } = string.Empty;
}
@@ -1,9 +1,10 @@
using System.Xml.Serialization;
namespace Application.Dto.Game;
namespace GCLocalServerRewrite.models;
public class Item : Record, IIdModel, ICardIdModel
public class ItemDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
@@ -24,14 +25,4 @@ public class Item : Record, IIdModel, ICardIdModel
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public void SetId(int id)
{
ItemId = id;
}
public void SetCardId(long cardId)
{
CardId = cardId;
}
}
+13
View File
@@ -0,0 +1,13 @@
namespace Application.Dto.Game;
public class MusicAouDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "music_id")]
public int MusicId { get; set; }
[XmlElement(ElementName = "use_flag")]
public int UseFlag { get; set; }
}
+31
View File
@@ -0,0 +1,31 @@
namespace Application.Dto.Game;
public class MusicDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement("music_id")]
public int MusicId { get; set; }
[XmlElement(ElementName = "title")]
public string Title { get; set; } = string.Empty;
[XmlElement(ElementName = "artist")]
public string Artist { get; set; } = string.Empty;
[XmlElement(ElementName = "release_date")]
public string ReleaseDate { get; set; } = "2013-01-01 08:00:00";
[XmlElement(ElementName = "end_date")]
public string EndDate { get; set; } = "2030-01-01 08:00:00";
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; }
[XmlElement("calc_flag")]
public int CalcFlag { get; set; }
}
+13
View File
@@ -0,0 +1,13 @@
namespace Application.Dto.Game;
public class MusicExtraDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "music_id")]
public int MusicId { get; set; }
[XmlElement(ElementName = "use_flag")]
public int UseFlag { get; set; }
}
+25
View File
@@ -0,0 +1,25 @@
namespace Application.Dto.Game;
public class NavigatorDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "navigator_id")]
public int NavigatorId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
public string Modified { get; set; } = string.Empty;
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; }
}
@@ -0,0 +1,67 @@
namespace Application.Dto.Game;
public class OnlineMatchEntryDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "machine_id")]
public long MachineId { get; set; }
[XmlElement(ElementName = "event_id")]
public long EventId { get; set; }
[XmlElement(ElementName = "matching_id")]
public long MatchId { get; set; }
[XmlElement(ElementName = "entry_no")]
public long EntryId { get; set; }
[XmlElement(ElementName = "entry_start")]
public string StartTime { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
[XmlElement(ElementName = "status")]
public long Status { get; set; } = 1;
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "player_name")]
public string PlayerName { get; set; } = string.Empty;
[XmlElement(ElementName = "avatar_id")]
public long AvatarId { get; set; }
[XmlElement(ElementName = "title_id")]
public long TitleId { get; set; }
[XmlElement(ElementName = "class_id")]
public long ClassId { get; set; }
[XmlElement(ElementName = "group_id")]
public long GroupId { get; set; }
[XmlElement(ElementName = "tenpo_id")]
public long TenpoId { get; set; } = 1337;
[XmlElement(ElementName = "tenpo_name")]
public string TenpoName { get; set; } = "GCLocalServer";
[XmlElement(ElementName = "pref_id")]
public long PrefId { get; set; }
[XmlElement(ElementName = "pref")]
public string Pref { get; set; } = "nesys";
[XmlElement(ElementName = "message_id")]
public long MessageId { get; set; }
[XmlElement(ElementName = "matching_timeout")]
public long MatchTimeout { get; set; } = 99;
[XmlElement(ElementName = "matching_wait_time")]
public long MatchWaitTime { get; set; } = 10;
[XmlElement(ElementName = "matching_remaining_time")]
public long MatchRemainingTime { get; set; } = 89;
}
@@ -1,9 +1,6 @@
using System.Xml.Serialization;
namespace Application.Dto.Game;
namespace GCLocalServerRewrite.models;
[XmlType("record")]
public class PlayNumRankRecord
public class PlayNumRankDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
@@ -26,9 +23,9 @@ public class PlayNumRankRecord
[XmlElement("score_bi1")]
public int ScoreBi1 { get; set; }
[XmlElement("title")]
public string? Title { get; set; }
[XmlElement("title")]
public string Title { get; set; } = string.Empty;
[XmlElement("artist")]
public string? Artist { get; set; }
[XmlElement("artist")]
public string Artist { get; set; } = string.Empty;
}
+52
View File
@@ -0,0 +1,52 @@
namespace Application.Dto.Game;
public class ScoreRankDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "player_name")]
public string PlayerName { get; set; } = string.Empty;
[XmlElement(ElementName = "rank")]
public long Rank { get; set; }
[XmlElement(ElementName = "rank2")]
public long Rank2 { get; set; }
[XmlElement(ElementName = "score_bi1")]
public long TotalScore { get; set; }
[XmlElement(ElementName = "score_i1")]
public int AvatarId { get; set; }
[XmlElement(ElementName = "fcol2")]
public long TitleId { get; set; }
[XmlElement(ElementName = "fcol1")]
public long Fcol1 { get; set; }
[XmlElement(ElementName = "pref_id")]
public int PrefId { get; set; }
[XmlElement(ElementName = "pref")]
public string Pref { get; set; } = string.Empty;
[XmlElement(ElementName = "area_id")]
public int AreaId { get; set; }
[XmlElement(ElementName = "area")]
public string Area { get; set; } = string.Empty;
[XmlElement(ElementName = "last_play_tenpo_id")]
public int LastPlayTenpoId { get; set; }
[XmlElement(ElementName = "tenpo_name")]
public string TenpoName { get; set; } = string.Empty;
[XmlElement(ElementName = "title")]
public string Title { get; set; } = string.Empty;
}
@@ -1,8 +1,6 @@
using System.Xml.Serialization;
namespace Application.Dto.Game;
namespace GCLocalServerRewrite.models;
public class Session
public class SessionDto
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
+25
View File
@@ -0,0 +1,25 @@
namespace Application.Dto.Game;
public class SkinDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "skin_id")]
public int SkinId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
public string Modified { get; set; } = string.Empty;
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; }
}
+25
View File
@@ -0,0 +1,25 @@
namespace Application.Dto.Game;
public class SoundEffectDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "sound_effect_id")]
public int SoundEffectId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
public string Modified { get; set; } = string.Empty;
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; }
}
+25
View File
@@ -0,0 +1,25 @@
namespace Application.Dto.Game;
public class TitleDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "title_id")]
public int TitleId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
public string Modified { get; set; } = string.Empty;
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; }
}
@@ -1,8 +1,6 @@
using System.Xml.Serialization;
namespace Application.Dto.Game;
namespace GCLocalServerRewrite.models;
public class TotalTrophy
public class TotalTrophyDto
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
@@ -1,9 +1,10 @@
using System.Xml.Serialization;
namespace Application.Dto.Game;
namespace GCLocalServerRewrite.models;
public class UnlockKeynum : Record
public class UnlockKeyNumDto
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
@@ -20,14 +21,14 @@ public class UnlockKeynum : Record
public int ExpiredFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public int UseFlag { get; set; }
[XmlElement("cash_flag")]
public int CashFlag { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
public string Modified { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = string.Empty;
}
@@ -0,0 +1,19 @@
using Domain.Config;
using Microsoft.Extensions.Options;
namespace Application.Game.Card;
public class CardDependencyAggregate : ICardDependencyAggregate
{
public CardDependencyAggregate(ICardDbContext cardDbContext, IMusicDbContext musicDbContext, IOptions<GameConfig> options)
{
CardDbContext = cardDbContext;
MusicDbContext = musicDbContext;
Options = options;
}
public ICardDbContext CardDbContext { get; }
public IMusicDbContext MusicDbContext { get; }
public IOptions<GameConfig> Options { get; }
}
+24
View File
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
namespace Application.Game.Card;
public class CardRequest
{
[ModelBinder(Name = "mac_addr")]
public string Mac { get; set; } = string.Empty;
[ModelBinder(Name = "cmd_str")]
public int CardCommandType { get; set; }
[ModelBinder(Name = "type")]
public int CardRequestType { get; set; }
[ModelBinder(Name = "card_no")]
public long CardId { get; set; }
[ModelBinder(Name = "tenpo_id")]
public string TenpoId { get; set; } = "1337";
[ModelBinder(Name = "data")]
public string Data { get; set; } = string.Empty;
}
@@ -0,0 +1,34 @@
using Application.Common.Helpers;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Management;
public record CardRegisterCommand(long CardId, string Data) : IRequestWrapper<string>;
public class RegisterCommandHandler : RequestHandlerBase<CardRegisterCommand, string>
{
private readonly ILogger<RegisterCommandHandler> logger;
public RegisterCommandHandler(ICardDependencyAggregate aggregate, ILogger<RegisterCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(CardRegisterCommand request, CancellationToken cancellationToken)
{
var exists = CardDbContext.CardMains.Any(card => card.CardId == request.CardId);
if (exists)
{
return ServiceResult.Failed<string>(ServiceError.CustomMessage($"Card {request.CardId} already exists!"));
}
var card = request.Data.DeserializeCardData<CardDto>().CardDtoToCardMain();
card.CardId = request.CardId;
card.Created = TimeHelper.CurrentTimeToString();
card.Modified = card.Created;
logger.LogInformation("New card {{Id: {Id}, Player Name: {Name}}} registered", card.CardId, card.PlayerName);
CardDbContext.CardMains.Add(card);
await CardDbContext.SaveChangesAsync(cancellationToken);
return new ServiceResult<string>(request.Data);
}
}
@@ -0,0 +1,16 @@
namespace Application.Game.Card.Management;
public record CardReissueCommand(long CardId) : IRequestWrapper<string>;
public class ReissueCommandHandler : RequestHandlerBase<CardReissueCommand, string>
{
public ReissueCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(CardReissueCommand request, CancellationToken cancellationToken)
{
// TODO: Support actual reissue
return Task.FromResult(ServiceResult.Failed<string>(ServiceError.NotReissue));
}
}
@@ -0,0 +1,92 @@
using Application.Common.Helpers;
using Domain.Entities;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.OnlineMatching;
public record StartOnlineMatchingCommand(long CardId, string Data) : IRequestWrapper<string>;
public class StartOnlineMatchingCommandHandler : RequestHandlerBase<StartOnlineMatchingCommand, string>
{
private const int MAX_RETRY = 5;
private const string MATCH_ENRTY_XPATH = "/root/online_matching";
private const string RECORD_XPATH = $"{MATCH_ENRTY_XPATH}/record";
private readonly ILogger<StartOnlineMatchingCommandHandler> logger;
public StartOnlineMatchingCommandHandler(ICardDependencyAggregate aggregate, ILogger<StartOnlineMatchingCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(StartOnlineMatchingCommand request, CancellationToken cancellationToken)
{
var dto = request.Data.DeserializeCardData<OnlineMatchEntryDto>();
dto.CardId = request.CardId;
dto.StartTime = TimeHelper.CurrentTimeToString();
dto.MatchTimeout = 20;
dto.MatchRemainingTime = 5;
dto.MatchWaitTime = 5;
dto.Status = 1;
var entry = dto.DtoToOnlineMatchEntry();
var matchId = await CardDbContext.OnlineMatches.CountAsync(cancellationToken);
for (int i = 0; i < MAX_RETRY; i++)
{
try
{
var onlineMatch = await CardDbContext.OnlineMatches
.Include(match => match.Entries)
.FirstOrDefaultAsync(match => match.IsOpen && match.Entries.Count < 4, cancellationToken);
string result;
if (onlineMatch is not null)
{
entry.EntryId = onlineMatch.Entries.Count;
onlineMatch.Entries.Add(entry);
onlineMatch.Guid = Guid.NewGuid();
await CardDbContext.SaveChangesAsync(cancellationToken);
result = onlineMatch.Entries.Select((matchEntry, id) =>
{
var entryDto = matchEntry.OnlineMatchEntryToDto();
entryDto.Id = id;
return entryDto;
}).SerializeCardDataList(RECORD_XPATH);
return new ServiceResult<string>(result);
}
entry.EntryId = 0;
onlineMatch = new OnlineMatch
{
MatchId = matchId,
Entries = { entry },
Guid = Guid.NewGuid(),
IsOpen = true
};
CardDbContext.OnlineMatches.Add(onlineMatch);
await CardDbContext.SaveChangesAsync(cancellationToken);
result = onlineMatch.Entries.Select((matchEntry, id) =>
{
var entryDto = matchEntry.OnlineMatchEntryToDto();
entryDto.Id = id;
return entryDto;
}).SerializeCardDataList(RECORD_XPATH);
return new ServiceResult<string>(result);
}
catch (DbUpdateConcurrencyException e)
{
logger.LogWarning(e, "Concurrent DB update when starting online match");
}
catch (DbUpdateException e)
when (e.InnerException != null
&& e.InnerException.Message.StartsWith("Cannot insert duplicate key row in object"))
{
logger.LogWarning(e, "Concurrent insert when starting online match");
}
}
logger.LogError("Cannot update DB after {Number} trials for online match!", MAX_RETRY);
return ServiceResult.Failed<string>(ServiceError.DatabaseSaveFailed);
}
}
@@ -0,0 +1,78 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.OnlineMatching;
public record UpdateOnlineMatchingCommand(long CardId, string Data) : IRequestWrapper<string>;
public class UpdateOnlineMatchingCommandHandler : RequestHandlerBase<UpdateOnlineMatchingCommand, string>
{
private readonly ILogger<UpdateOnlineMatchingCommandHandler> logger;
private const string MATCH_ENRTY_XPATH = "/root/online_matching";
private const string RECORD_XPATH = $"{MATCH_ENRTY_XPATH}/record";
private const int MAX_RETRY = 5;
public UpdateOnlineMatchingCommandHandler(ICardDependencyAggregate aggregate,
ILogger<UpdateOnlineMatchingCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
[SuppressMessage("ReSharper.DPA", "DPA0006: Large number of DB commands")]
public override async Task<ServiceResult<string>> Handle(UpdateOnlineMatchingCommand request,
CancellationToken cancellationToken)
{
var data = request.Data.DeserializeCardData<OnlineMatchEntryDto>();
for (int i = 0; i < MAX_RETRY; i++)
{
try
{
var match = await CardDbContext.OnlineMatches
.Include(onlineMatch => onlineMatch.Entries)
.FirstOrDefaultAsync(onlineMatch =>
onlineMatch.MatchId == data.MatchId, cancellationToken);
if (match is null)
{
logger.LogWarning("Match id {MatchId} not found", data.MatchId);
return ServiceResult.Failed<string>(ServiceError.CustomMessage("Match with this id does not exist"));
}
match.Entries.ForEach(entry =>
{
if (entry.CardId == request.CardId)
{
entry.MessageId = data.MessageId;
}
entry.MatchRemainingTime--;
});
if (match.Entries.TrueForAll(entry => entry.MatchRemainingTime <= 0))
{
match.Entries.ForEach(entry => entry.Status = 3);
match.IsOpen = false;
}
match.Guid = Guid.NewGuid();
await CardDbContext.SaveChangesAsync(cancellationToken);
var result = match.Entries.Select((matchEntry, id) =>
{
var entryDto = matchEntry.OnlineMatchEntryToDto();
entryDto.Id = id;
return entryDto;
}).SerializeCardDataList(RECORD_XPATH);
return new ServiceResult<string>(result);
}
catch (DbUpdateConcurrencyException e)
{
logger.LogWarning(e, "Concurrent DB update when starting online match");
}
}
logger.LogError("Cannot update DB after {Number} trials for online match!", MAX_RETRY);
return ServiceResult.Failed<string>(ServiceError.DatabaseSaveFailed);
}
}
@@ -0,0 +1,24 @@
namespace Application.Game.Card.OnlineMatching;
public record UploadOnlineMatchingResultCommand(long CardId, string Data) : IRequestWrapper<string>;
public class UploadOnlineMatchingResultCommandHandler : RequestHandlerBase<UploadOnlineMatchingResultCommand, string>
{
private const string XPATH = "/root/online_battle_result";
public UploadOnlineMatchingResultCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(UploadOnlineMatchingResultCommand request, CancellationToken cancellationToken)
{
var result = new OnlineMatchingResult { Status = 1 }.SerializeCardData(XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
public class OnlineMatchingResult
{
[XmlElement(ElementName = "status")]
public int Status { get; set; }
}
@@ -0,0 +1,56 @@
using System.Diagnostics.CodeAnalysis;
using Domain.Enums;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Read;
public record ReadAllCardDetailsQuery(long CardId) : IRequestWrapper<string>;
public class ReadAllDetailsQueryHandler : RequestHandlerBase<ReadAllCardDetailsQuery, string>
{
private const string CARD_DETAILS_XPATH = "/root/card_detail";
private const string RECORD_XPATH = $"{CARD_DETAILS_XPATH}/record";
private readonly ILogger<ReadAllDetailsQueryHandler> logger;
public ReadAllDetailsQueryHandler(ICardDependencyAggregate aggregate, ILogger<ReadAllDetailsQueryHandler> logger) : base(aggregate)
{
this.logger = logger;
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records",
Justification = "Card details will return all records by design, which results in a large number of DB records")]
public override async Task<ServiceResult<string>> Handle(ReadAllCardDetailsQuery request, CancellationToken cancellationToken)
{
var exists = await CardDbContext.CardMains.AnyAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (!exists)
{
logger.LogWarning("Card id: {CardId} does not exist!", request.CardId);
return ServiceResult.Failed<string>(
new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
}
var cardDetails = await CardDbContext.CardDetails
.Where(detail => detail.CardId == request.CardId)
.ToListAsync(cancellationToken: cancellationToken);
string result;
if (cardDetails.Count == 0)
{
result = new object().SerializeCardData(CARD_DETAILS_XPATH);
}
else
{
var dtoList = cardDetails.Select((detail, i) =>
{
var dto = detail.CardDetailToDto();
dto.Id = i;
return dto;
});
result = dtoList.SerializeCardDataList(RECORD_XPATH);
}
return new ServiceResult<string>(result);
}
}
@@ -0,0 +1,37 @@
namespace Application.Game.Card.Read;
public record ReadAvatarQuery(long CardId) : IRequestWrapper<string>;
public class ReadAvatarQueryHandler : RequestHandlerBase<ReadAvatarQuery, string>
{
private const string AVATAR_XPATH = "/root/avatar/record";
public ReadAvatarQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadAvatarQuery request, CancellationToken cancellationToken)
{
var count = Config.AvatarCount;
var list = new List<AvatarDto>();
for (int i = 0; i < count; i++)
{
var avatar = new AvatarDto
{
Id = i,
CardId = request.CardId,
AvatarId = i + 1,
Created = "2013-01-01 08:00:00",
Modified = "2013-01-01 08:00:00",
NewFlag = 0,
UseFlag = 1
};
list.Add(avatar);
}
var result = list.SerializeCardDataList(AVATAR_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,38 @@
using Domain.Enums;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Read;
public record ReadCardBDataQuery(long CardId) : IRequestWrapper<string>;
public class ReadBDataQueryHandler : RequestHandlerBase<ReadCardBDataQuery, string>
{
private const string CARD_BDATA_XPATH = "/root/card_bdata";
private readonly ILogger<ReadBDataQueryHandler> logger;
public ReadBDataQueryHandler(ICardDependencyAggregate aggregate, ILogger<ReadBDataQueryHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(ReadCardBDataQuery request, CancellationToken cancellationToken)
{
var exists = await CardDbContext.CardMains.AnyAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (!exists)
{
logger.LogWarning("Card id: {CardId} does not exist!", request.CardId);
return ServiceResult.Failed<string>(
new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
}
var bdata = await CardDbContext.CardBdata.FirstOrDefaultAsync(
card => card.CardId == request.CardId, cancellationToken: cancellationToken);
var result = bdata?.CardBDatumToDto().SerializeCardData(CARD_BDATA_XPATH)
?? new object().SerializeCardData(CARD_BDATA_XPATH);
return new ServiceResult<string>(result);
}
}
@@ -0,0 +1,44 @@
using Domain.Enums;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Read;
public record ReadCardDetailQuery(long CardId, string Data) : IRequestWrapper<string>;
public class ReadDetailQueryHandler : RequestHandlerBase<ReadCardDetailQuery, string>
{
private const string CARD_DETAILS_XPATH = "/root/card_detail";
private readonly ILogger<ReadDetailQueryHandler> logger;
public ReadDetailQueryHandler(ICardDependencyAggregate aggregate, ILogger<ReadDetailQueryHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(ReadCardDetailQuery request, CancellationToken cancellationToken)
{
var exists = await CardDbContext.CardMains.AnyAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (!exists)
{
logger.LogWarning("Card id: {CardId} does not exist!", request.CardId);
return ServiceResult.Failed<string>(
new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
}
var queryCondition = request.Data.DeserializeCardData<CardDetailDto>();
var detail = await CardDbContext.CardDetails.FirstOrDefaultAsync(cardDetail =>
cardDetail.CardId == request.CardId &&
cardDetail.Pcol1 == queryCondition.Pcol1 &&
cardDetail.Pcol2 == queryCondition.Pcol2 &&
cardDetail.Pcol3 == queryCondition.Pcol3, cancellationToken: cancellationToken);
var dto = detail?.CardDetailToDto();
var result = dto?.SerializeCardData(CARD_DETAILS_XPATH) ??
new object().SerializeCardData(CARD_DETAILS_XPATH);
return new ServiceResult<string>(result);
}
}
@@ -0,0 +1,29 @@
using Domain.Enums;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Read;
public record ReadCardQuery(long CardId) : IRequestWrapper<string>;
public class ReadQueryHandler : RequestHandlerBase<ReadCardQuery, string>
{
private readonly ILogger<ReadQueryHandler> logger;
public ReadQueryHandler(ICardDependencyAggregate aggregate, ILogger<ReadQueryHandler> logger) : base(aggregate) {
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(ReadCardQuery request, CancellationToken cancellationToken)
{
var card = await CardDbContext.CardMains.FirstOrDefaultAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (card is null)
{
logger.LogInformation("Card of id: {CardId} does not exist! Registering a new one...", request.CardId);
return ServiceResult.Failed<string>(new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
}
var result = card.CardMainToCardDto().SerializeCardData("/root/card");
return new ServiceResult<string>(result);
}
}
@@ -0,0 +1,30 @@
namespace Application.Game.Card.Read;
public record ReadCoinQuery(long CardId) : IRequestWrapper<string>;
public class ReadCoinQueryHandler : RequestHandlerBase<ReadCoinQuery, string>
{
private const string COIN_XPATH = "/root/coin";
public ReadCoinQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadCoinQuery request, CancellationToken cancellationToken)
{
var dto = new CoinDto
{
CardId = request.CardId,
CurrentCoins = 900000,
MonthlyCoins = 900000,
TotalCoins = 900000,
Created = "2013-01-01 08:00:00",
Modified = "2013-01-01 08:00:00"
};
var result = dto.SerializeCardData(COIN_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,20 @@
namespace Application.Game.Card.Read;
public record ReadCondQuery(long CardId) : IRequestWrapper<string>;
public class ReadCondQueryHandler : RequestHandlerBase<ReadCondQuery, string>
{
private const string COND_XPATH = "/root/cond";
public ReadCondQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadCondQuery request, CancellationToken cancellationToken)
{
var result = new object().SerializeCardData(COND_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,20 @@
namespace Application.Game.Card.Read;
public record ReadEventRewardQuery(long CardId) : IRequestWrapper<string>;
public class ReadEventRewardQueryHandler : RequestHandlerBase<ReadEventRewardQuery, string>
{
private const string EVENT_REWARD_XPATH = "/root/event_reward";
public ReadEventRewardQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadEventRewardQuery request, CancellationToken cancellationToken)
{
var result = new object().SerializeCardData(EVENT_REWARD_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,20 @@
namespace Application.Game.Card.Read;
public record ReadGetMessageQuery(long CardId) : IRequestWrapper<string>;
public class ReadGetMessageQueryHandler : RequestHandlerBase<ReadGetMessageQuery, string>
{
private const string GET_MESSAGE_XPATH = "/root/get_message";
public ReadGetMessageQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadGetMessageQuery request, CancellationToken cancellationToken)
{
var result = new object().SerializeCardData(GET_MESSAGE_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,37 @@
namespace Application.Game.Card.Read;
public record ReadItemQuery(long CardId) : IRequestWrapper<string>;
public class ReadItemQueryHandler : RequestHandlerBase<ReadItemQuery, string>
{
private const string ITEM_XPATH = "/root/item/record";
public ReadItemQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadItemQuery request, CancellationToken cancellationToken)
{
var count = Config.ItemCount;
var list = new List<ItemDto>();
for (int i = 0; i < count; i++)
{
var item = new ItemDto
{
Id = i,
CardId = request.CardId,
ItemId = i + 1,
ItemNum = 90,
Created = "2013-01-01",
Modified = "2013-01-01",
NewFlag = 0,
UseFlag = 1
};
list.Add(item);
}
var result = list.SerializeCardDataList(ITEM_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,35 @@
using System.Diagnostics.CodeAnalysis;
namespace Application.Game.Card.Read;
public record ReadMusicAouQuery(long CardId) : IRequestWrapper<string>;
public class ReadMusicAouQueryHandler : RequestHandlerBase<ReadMusicAouQuery, string>
{
private const string MUSIC_AOU_XPATH = "/root/music_aou";
private const string RECORD_XPATH = $"{MUSIC_AOU_XPATH}/record";
public ReadMusicAouQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records",
Justification = "To return all musics, the whole table need to be returned")]
public override async Task<ServiceResult<string>> Handle(ReadMusicAouQuery request, CancellationToken cancellationToken)
{
var musics = await MusicDbContext.MusicAous.ToListAsync(cancellationToken: cancellationToken);
var dtoList = musics.Select((aou, i) =>
{
var dto = aou.MusicAouToDto();
dto.Id = i;
return dto;
}).ToList();
var result = dtoList.Count == 0 ? new object().SerializeCardData(MUSIC_AOU_XPATH)
: dtoList.SerializeCardDataList(RECORD_XPATH);
return new ServiceResult<string>(result);
}
}
@@ -0,0 +1,31 @@
namespace Application.Game.Card.Read;
public record ReadMusicExtraQuery(long CardId) : IRequestWrapper<string>;
public class ReadMusicExtraQueryHandler : RequestHandlerBase<ReadMusicExtraQuery, string>
{
private const string MUSIC_EXTRA_XPATH = "/root/music_extra";
private const string RECORD_XPATH = $"{MUSIC_EXTRA_XPATH}/record";
public ReadMusicExtraQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override async Task<ServiceResult<string>> Handle(ReadMusicExtraQuery request, CancellationToken cancellationToken)
{
var musics = await MusicDbContext.MusicExtras.ToListAsync(cancellationToken: cancellationToken);
var dtoList = musics.Select((aou, i) =>
{
var dto = aou.MusicExtraToDto();
dto.Id = i;
return dto;
}).ToList();
var result = dtoList.Count == 0 ? new object().SerializeCardData(MUSIC_EXTRA_XPATH)
: dtoList.SerializeCardDataList(RECORD_XPATH);
return new ServiceResult<string>(result);
}
}
@@ -0,0 +1,31 @@
using System.Diagnostics.CodeAnalysis;
namespace Application.Game.Card.Read;
public record ReadMusicQuery(long CardId) : IRequestWrapper<string>;
public class ReadMusicQueryHandler : RequestHandlerBase<ReadMusicQuery, string>
{
private const string MUSIC_XPATH = "/root/music/record";
public ReadMusicQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records",
Justification = "To return all musics, the whole table need to be returned")]
public override async Task<ServiceResult<string>> Handle(ReadMusicQuery request, CancellationToken cancellationToken)
{
var musics = await MusicDbContext.MusicUnlocks.ToListAsync(cancellationToken: cancellationToken);
var dtoList = musics.Select((unlock, i) =>
{
var dto = unlock.MusicToDto();
dto.Id = i;
return dto;
});
var result = dtoList.SerializeCardDataList(MUSIC_XPATH);
return new ServiceResult<string>(result);
}
}
@@ -0,0 +1,38 @@
namespace Application.Game.Card.Read;
public record ReadNavigatorQuery(long CardId) : IRequestWrapper<string>;
public class ReadNavigatorQueryHandler : RequestHandlerBase<ReadNavigatorQuery, string>
{
private const string NAVIGATOR_XPATH = "/root/navigator/record";
public ReadNavigatorQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadNavigatorQuery request, CancellationToken cancellationToken)
{
var count = Config.NavigatorCount;
var list = new List<NavigatorDto>();
for (int i = 0; i < count; i++)
{
var navigator = new NavigatorDto
{
Id = i,
CardId = request.CardId,
NavigatorId = i + 1,
Created = "2013-01-01 08:00:00",
Modified = "2013-01-01 08:00:00",
NewFlag = 0,
UseFlag = 1
};
list.Add(navigator);
}
var result = list.SerializeCardDataList(NAVIGATOR_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,38 @@
namespace Application.Game.Card.Read;
public record ReadSkinQuery(long CardId) : IRequestWrapper<string>;
public class ReadSkinQueryHandler : RequestHandlerBase<ReadSkinQuery, string>
{
private const string SKIN_XPATH = "/root/skin/record";
public ReadSkinQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadSkinQuery request, CancellationToken cancellationToken)
{
var count = Config.SkinCount;
var list = new List<SkinDto>();
for (int i = 0; i < count; i++)
{
var skin = new SkinDto
{
Id = i,
CardId = request.CardId,
SkinId = i + 1,
Created = "2013-01-01 08:00:00",
Modified = "2013-01-01 08:00:00",
NewFlag = 0,
UseFlag = 1
};
list.Add(skin);
}
var result = list.SerializeCardDataList(SKIN_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,37 @@
namespace Application.Game.Card.Read;
public record ReadSoundEffectQuery(long CardId) : IRequestWrapper<string>;
public class ReadSoundEffectQueryHandler : RequestHandlerBase<ReadSoundEffectQuery, string>
{
private const string SOUND_EFFECT_XPATH = "/root/sound_effect/record";
public ReadSoundEffectQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadSoundEffectQuery request, CancellationToken cancellationToken)
{
var count = Config.SeCount;
var list = new List<SoundEffectDto>();
for (int i = 0; i < count; i++)
{
var soundEffect = new SoundEffectDto
{
Id = i,
CardId = request.CardId,
SoundEffectId = i + 1,
Created = "2013-01-01 08:00:00",
Modified = "2013-01-01 08:00:00",
NewFlag = 0,
UseFlag = 1
};
list.Add(soundEffect);
}
var result = list.SerializeCardDataList(SOUND_EFFECT_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,38 @@
namespace Application.Game.Card.Read;
public record ReadTitleQuery(long CardId) : IRequestWrapper<string>;
public class ReadTitleQueryHandler : RequestHandlerBase<ReadTitleQuery, string>
{
private const string TITLE_XPATH = "/root/title/record";
public ReadTitleQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadTitleQuery request, CancellationToken cancellationToken)
{
var count = Config.TitleCount;
var list = new List<TitleDto>();
for (int i = 0; i < count; i++)
{
var soundEffect = new TitleDto
{
Id = i,
CardId = request.CardId,
TitleId = i + 1,
Created = "2013-01-01 08:00:00",
Modified = "2013-01-01 08:00:00",
NewFlag = 0,
UseFlag = 1
};
list.Add(soundEffect);
}
var result = list.SerializeCardDataList(TITLE_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,26 @@
namespace Application.Game.Card.Read;
public record ReadTotalTrophyQuery(long CardId) : IRequestWrapper<string>;
public class ReadTotalTrophyQueryHandler : RequestHandlerBase<ReadTotalTrophyQuery, string>
{
private const string TOTAL_TROPHY_XPATH = "/root/total_trophy";
public ReadTotalTrophyQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadTotalTrophyQuery request, CancellationToken cancellationToken)
{
var trophy = new TotalTrophyDto
{
CardId = request.CardId,
TrophyNum = 8
};
var result = trophy.SerializeCardData(TOTAL_TROPHY_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,35 @@
namespace Application.Game.Card.Read;
public record ReadUnlockKeynumQuery(long CardId) : IRequestWrapper<string>;
public class ReadUnlockKeynumQueryHandler : RequestHandlerBase<ReadUnlockKeynumQuery, string>
{
private const string UNLOCK_KEYNUM_XPATH = "/root/unlock_keynum/record";
public ReadUnlockKeynumQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadUnlockKeynumQuery request, CancellationToken cancellationToken)
{
var unlockables = Config.UnlockRewards;
var list = unlockables.Select((unlockable, index) => new UnlockKeyNumDto
{
Id = index,
CardId = request.CardId,
RewardId = unlockable.RewardId,
KeyNum = 0,
RewardCount = 0,
CashFlag = 0,
ExpiredFlag = 0,
UseFlag = 1,
Created = "2013-01-01 08:00:00",
Modified = "2020-01-01 08:00:00"
})
.ToList();
var result = list.SerializeCardDataList(UNLOCK_KEYNUM_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
@@ -0,0 +1,100 @@
namespace Application.Game.Card.Read;
public record ReadUnlockRewardQuery(long CardId) : IRequestWrapper<string>;
public class ReadUnlockRewardQueryHandler : RequestHandlerBase<ReadUnlockRewardQuery, string>
{
private const string UNLOCK_REWARD_XPATH = "/root/unlock_reward/record";
public ReadUnlockRewardQueryHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(ReadUnlockRewardQuery request, CancellationToken cancellationToken)
{
var config = Config.UnlockRewards;
var list = config.Select((rewardConfig, i) => new UnlockRewardModel
{
Id = i,
CardId = request.CardId,
RewardId = rewardConfig.RewardId,
RewardType = (int)rewardConfig.RewardType,
TargetId = rewardConfig.TargetId,
TargetNum = rewardConfig.TargetNum,
KeyNum = rewardConfig.KeyNum,
DisplayFlag = 1,
UseFlag = 1,
LimitedFlag = 0,
Created = "2013-01-01 08:00:00",
Modified = "2013-01-01 08:00:00",
OpenDate = "2013-01-01",
CloseDate = "2030-01-01",
OpenTime = "00:00:01",
CloseTime = "23:59:59",
OpenUnixTime = new DateTimeOffset(2013, 1, 1, 0, 0, 1, TimeSpan.Zero).ToUnixTimeSeconds(),
CloseUnixTime = new DateTimeOffset(2030, 1, 1, 23, 59, 59, TimeSpan.Zero).ToUnixTimeSeconds()
});
var result = list.SerializeCardDataList(UNLOCK_REWARD_XPATH);
return Task.FromResult(new ServiceResult<string>(result));
}
}
public class UnlockRewardModel
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "reward_id")]
public int RewardId { get; set; }
[XmlElement(ElementName = "reward_type")]
public int RewardType { get; set; }
[XmlElement(ElementName = "open_date")]
public string OpenDate { get; set; } = "2013-01-01";
[XmlElement(ElementName = "close_date")]
public string CloseDate { get; set; } = "2030-01-01";
[XmlElement(ElementName = "open_time")]
public string OpenTime { get; set; } = "00:00:01";
[XmlElement(ElementName = "close_time")]
public string CloseTime { get; set; } = "23:59:59";
[XmlElement(ElementName = "target_id")]
public int TargetId { get; set; }
[XmlElement(ElementName = "target_num")]
public int TargetNum { get; set; }
[XmlElement(ElementName = "key_num")]
public int KeyNum { get; set; }
[XmlElement("display_flag")]
public int DisplayFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; }
[XmlElement("limited_flag")]
public int LimitedFlag { get; set; }
[XmlElement("open_unixtime")]
public long OpenUnixTime { get; set; }
[XmlElement("close_unixtime")]
public long CloseUnixTime { get; set; }
[XmlElement("created")]
public string Created { get; set; } = string.Empty;
[XmlElement("modified")]
public string Modified { get; set; } = string.Empty;
}
@@ -0,0 +1,25 @@
namespace Application.Game.Card.Session;
public record GetSessionCommand(long CardId, string Mac) : IRequestWrapper<string>;
public class GetSessionCommandHandler : RequestHandlerBase<GetSessionCommand, string>
{
private const string SESSION_XPATH = "/root/session";
public GetSessionCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate)
{
}
public override Task<ServiceResult<string>> Handle(GetSessionCommand request, CancellationToken cancellationToken)
{
var session = new SessionDto
{
CardId = request.CardId,
Mac = request.Mac,
PlayerId = 1,
Expires = 9999,
SessionId = "12345678901234567890123456789012"
};
return Task.FromResult(new ServiceResult<string>(session.SerializeCardData(SESSION_XPATH)));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteAvatarCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteAvatarCommandHandler : RequestHandlerBase<WriteAvatarCommand, string>
{
public WriteAvatarCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteAvatarCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,52 @@
using Domain.Entities;
using Domain.Enums;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Write;
public record WriteCardBDataCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteBDataCommandHandler : RequestHandlerBase<WriteCardBDataCommand, string>
{
private readonly ILogger<WriteBDataCommandHandler> logger;
public WriteBDataCommandHandler(ICardDependencyAggregate aggregate, ILogger<WriteBDataCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(WriteCardBDataCommand request, CancellationToken cancellationToken)
{
var exists = await CardDbContext.CardMains.AnyAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (!exists)
{
logger.LogWarning("Card id: {CardId} does not exist!", request.CardId);
return ServiceResult.Failed<string>(
new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
}
var dto = request.Data.DeserializeCardData<CardBDatumDto>();
var data = dto.DtoToCardBDatum();
data.CardId = request.CardId;
await CardDbContext.CardBdata.Upsert(data).RunAsync(cancellationToken);
var cardPlayCount = await CardDbContext.CardPlayCounts
.FirstOrDefaultAsync(count => count.CardId == request.CardId, cancellationToken);
if (cardPlayCount is null)
{
cardPlayCount = new CardPlayCount
{
CardId = request.CardId,
PlayCount = 0,
LastPlayedTime = DateTime.Now
};
}
cardPlayCount.PlayCount++;
cardPlayCount.LastPlayedTime = DateTime.Now;
await CardDbContext.CardPlayCounts.Upsert(cardPlayCount).RunAsync(cancellationToken);
await CardDbContext.SaveChangesAsync(cancellationToken);
return new ServiceResult<string>(request.Data);
}
}
@@ -0,0 +1,47 @@
using Application.Common.Helpers;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Write;
public record WriteCardCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteCommandHandler : RequestHandlerBase<WriteCardCommand, string>
{
private readonly ILogger<WriteCommandHandler> logger;
public WriteCommandHandler(ICardDependencyAggregate aggregate, ILogger<WriteCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(WriteCardCommand request, CancellationToken cancellationToken)
{
var dto = request.Data.DeserializeCardData<CardDto>();
dto.CardId = request.CardId;
var card = await CardDbContext.CardMains.FirstOrDefaultAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (card is null)
{
logger.LogInformation("Creating new card {CardId}", request.CardId);
card = dto.CardDtoToCardMain();
card.Created = TimeHelper.CurrentTimeToString();
card.Modified = TimeHelper.CurrentTimeToString();
CardDbContext.CardMains.Add(card);
}
else
{
logger.LogInformation("Updating {CardId}", request.CardId);
card.Fcol1 = dto.Fcol1;
card.Fcol2 = dto.Fcol2;
card.Fcol3 = dto.Fcol3;
card.ScoreI1 = dto.ScoreI1;
card.Modified = TimeHelper.CurrentTimeToString();
CardDbContext.CardMains.Update(card);
}
await CardDbContext.SaveChangesAsync(cancellationToken);
return new ServiceResult<string>(request.Data);
}
}
@@ -0,0 +1,38 @@
using Domain.Enums;
using Microsoft.Extensions.Logging;
namespace Application.Game.Card.Write;
public record WriteCardDetailCommand(long CardId, string TenpoId, string Data) : IRequestWrapper<string>;
public class WriteDetailCommandHandler : RequestHandlerBase<WriteCardDetailCommand, string>
{
private readonly ILogger<WriteDetailCommandHandler> logger;
public WriteDetailCommandHandler(ICardDependencyAggregate aggregate, ILogger<WriteDetailCommandHandler> logger) : base(aggregate)
{
this.logger = logger;
}
public override async Task<ServiceResult<string>> Handle(WriteCardDetailCommand request, CancellationToken cancellationToken)
{
var exists = await CardDbContext.CardMains.AnyAsync(card => card.CardId == request.CardId, cancellationToken: cancellationToken);
if (!exists)
{
logger.LogWarning("Card id: {CardId} does not exist!", request.CardId);
return ServiceResult.Failed<string>(
new ServiceError($"Card id: {request.CardId} does not exist!", (int)CardReturnCode.CardNotRegistered));
}
var dto = request.Data.DeserializeCardData<CardDetailDto>();
var detail = dto.DtoToCardDetail();
detail.CardId = request.CardId;
detail.LastPlayTime = DateTime.Now;
detail.LastPlayTenpoId = request.TenpoId;
await CardDbContext.CardDetails.Upsert(detail).RunAsync(cancellationToken);
await CardDbContext.SaveChangesAsync(cancellationToken);
return new ServiceResult<string>(request.Data);
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteCoinCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteCoinCommandHandler : RequestHandlerBase<WriteCoinCommand, string>
{
public WriteCoinCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteCoinCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteCondCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteCondCommandHandler : RequestHandlerBase<WriteCondCommand, string>
{
public WriteCondCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteCondCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteItemCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteItemCommandHandler : RequestHandlerBase<WriteItemCommand, string>
{
public WriteItemCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteItemCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteMusicDetailCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteMusicDetailCommandHandler : RequestHandlerBase<WriteMusicDetailCommand, string>
{
public WriteMusicDetailCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteMusicDetailCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteNavigatorCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteNavigatorCommandHandler : RequestHandlerBase<WriteNavigatorCommand, string>
{
public WriteNavigatorCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteNavigatorCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteSkinCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteSkinCommandHandler : RequestHandlerBase<WriteSkinCommand, string>
{
public WriteSkinCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteSkinCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteSoundEffectCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteSoundEffectCommandHandler : RequestHandlerBase<WriteSoundEffectCommand, string>
{
public WriteSoundEffectCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteSoundEffectCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteTitleCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteTitleCommandHandler : RequestHandlerBase<WriteTitleCommand, string>
{
public WriteTitleCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteTitleCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
@@ -0,0 +1,14 @@
namespace Application.Game.Card.Write;
public record WriteUnlockKeynumCommand(long CardId, string Data) : IRequestWrapper<string>;
public class WriteUnlockKeynumCommandHandler : RequestHandlerBase<WriteUnlockKeynumCommand, string>
{
public WriteUnlockKeynumCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) {}
public override Task<ServiceResult<string>> Handle(WriteUnlockKeynumCommand request, CancellationToken cancellationToken)
{
// TODO: Add proper implementation
return Task.FromResult(new ServiceResult<string>(request.Data));
}
}
+60
View File
@@ -0,0 +1,60 @@
using MediatR;
using Microsoft.Extensions.Logging;
namespace Application.Game.Option;
public record PlayCountQuery(long CardId) : IRequest<long>;
public class PlayCountQueryHandler : IRequestHandler<PlayCountQuery, long>
{
private readonly ICardDbContext context;
private readonly ILogger<PlayCountQueryHandler> logger;
public PlayCountQueryHandler(ICardDbContext context, ILogger<PlayCountQueryHandler> logger)
{
this.context = context;
this.logger = logger;
}
public async Task<long> Handle(PlayCountQuery request, CancellationToken cancellationToken)
{
return await GetPlayCount(request.CardId);
}
private async Task<long> GetPlayCount(long cardId)
{
var record = await context.CardPlayCounts.FirstOrDefaultAsync(count => count.CardId == cardId);
if (record is null)
{
return 0;
}
var now = DateTime.Now;
var lastPlayedTime = record.LastPlayedTime;
if (now <= lastPlayedTime)
{
logger.LogWarning("Clock skew detected! " +
"Current time: {Now}," +
"Last Play Time: {Last}", now, lastPlayedTime);
return 0;
}
DateTime start;
DateTime end;
if (now.Hour >= 8)
{
start = DateTime.Today.AddHours(8);
end = start.AddHours(24);
}
else
{
end = DateTime.Today.AddHours(8);
start = end.AddHours(-24);
}
var inBetween = lastPlayedTime >= start && lastPlayedTime <= end;
return inBetween ? record.PlayCount : 0;
}
}
@@ -0,0 +1,39 @@
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetEventRankQuery() : IRequestWrapper<string>;
public class GetEventRankQueryHandler : IRequestHandlerWrapper<GetEventRankQuery, string>
{
public Task<ServiceResult<string>> Handle(GetEventRankQuery request, CancellationToken cancellationToken)
{
var container = new EventRankContainer
{
Ranks = new List<object>(),
Status = new RankStatus
{
TableName = "EventRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 0
}
};
return Task.FromResult(new ServiceResult<string>(container.SerializeCardData()));
}
}
[XmlRoot("root")]
public class EventRankContainer
{
[XmlArray(ElementName = "event_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<object> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
@@ -0,0 +1,134 @@
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetGlobalScoreRankQuery(string Param) : IRequestWrapper<string>;
public class GetGlobalScoreRankQueryHandler : IRequestHandlerWrapper<GetGlobalScoreRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetGlobalScoreRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetGlobalScoreRankQuery request, CancellationToken cancellationToken)
{
var param = request.Param.DeserializeCardData<RankParam>();
return param switch
{
{ CardId: 0, TenpoId: 0 } => await GetAllRanks(cancellationToken),
{ CardId: > 0, TenpoId: 0 } => await GetCardRank(param.CardId, cancellationToken),
{ CardId: 0, TenpoId: > 0 } => await GetTenpoRanks(param.TenpoId, cancellationToken),
_ => ServiceResult.Failed<string>(ServiceError.ValidationFormat)
};
}
private async Task<ServiceResult<string>> GetCardRank(long cardId,
CancellationToken cancellationToken)
{
var rank = await cardDbContext.GlobalScoreRanks.FirstOrDefaultAsync(scoreRank => scoreRank.CardId == cardId,
cancellationToken: cancellationToken);
var container = new GlobalScoreRankContainer
{
Ranks = new List<ScoreRankDto>(),
Status = new RankStatus
{
TableName = "GlobalScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 1
}
};
if (rank is null)
{
return new ServiceResult<string>(container.SerializeCardData());
}
var dto = rank.ScoreRankToDto();
dto.Id = 0;
container.Ranks.Add(dto);
container.Status.Rows++;
return new ServiceResult<string>(container.SerializeCardData());
}
private async Task<ServiceResult<string>> GetAllRanks(CancellationToken cancellationToken)
{
var ranks = await cardDbContext.GlobalScoreRanks.OrderBy(rank => rank.Rank)
.Take(30).ToListAsync(cancellationToken: cancellationToken);
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.ScoreRankToDto();
dto.Id = i;
dto.Rank2 = dto.Rank;
return dto;
}).ToList();
var container = new GlobalScoreRankContainer
{
Ranks = dtoList,
Status = new RankStatus
{
TableName = "GlobalScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = dtoList.Count,
Status = 1
}
};
return new ServiceResult<string>(container.SerializeCardData());
}
private async Task<ServiceResult<string>> GetTenpoRanks(int tenpoId, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.GlobalScoreRanks.Where(rank => rank.LastPlayTenpoId == tenpoId)
.OrderByDescending(rank => rank.TotalScore)
.Take(30)
.ToListAsync(cancellationToken: cancellationToken);
ranks = ranks.Select((rank, i) =>
{
rank.Rank = i + 1;
return rank;
}).ToList();
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.ScoreRankToDto();
dto.Id = i;
dto.Rank2 = dto.Rank;
return dto;
}).ToList();
var container = new GlobalScoreRankContainer
{
Ranks = dtoList,
Status = new RankStatus
{
TableName = "TenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = dtoList.Count,
Status = 1
}
};
return new ServiceResult<string>(container.SerializeCardData());
}
}
[XmlRoot("root")]
public class GlobalScoreRankContainer
{
[XmlArray(ElementName = "score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<ScoreRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
@@ -0,0 +1,57 @@
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetMonthlyScoreRankQuery() : IRequestWrapper<string>;
public class GetMonthlyScoreRankQueryHandler : IRequestHandlerWrapper<GetMonthlyScoreRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetMonthlyScoreRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetMonthlyScoreRankQuery request, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.MonthlyScoreRanks.OrderBy(rank => rank.Rank)
.Take(30).ToListAsync(cancellationToken: cancellationToken);
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.ScoreRankToDto();
dto.Id = i;
dto.Rank2 = dto.Rank;
return dto;
}).ToList();
var container = new MonthlyScoreRankContainer
{
Ranks = dtoList,
Status = new RankStatus
{
TableName = "MonthlyScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = dtoList.Count,
Status = 1
}
};
return new ServiceResult<string>(container.SerializeCardData());
}
}
[XmlRoot("root")]
public class MonthlyScoreRankContainer
{
[XmlArray(ElementName = "m_score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<ScoreRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
@@ -0,0 +1,61 @@
using System.Diagnostics;
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetPlayNumRankQuery(): IRequestWrapper<string>;
public class GetPlayNumRankQueryHandler : IRequestHandlerWrapper<GetPlayNumRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetPlayNumRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetPlayNumRankQuery request, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.PlayNumRanks.OrderBy(rank => rank.Rank)
.Take(30).ToListAsync(cancellationToken: cancellationToken);
var status = new RankStatus
{
TableName = "PlayNumRank",
StartDate = TimeHelper.DateToString(Process.GetCurrentProcess().StartTime.Date),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = ranks.Count,
Status = 1
};
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.PlayNumRankToDto();
dto.Id = i;
return dto;
}).ToList();
var container = new PlayNumRankContainer
{
Ranks = dtoList,
Status = status
};
var result = container.SerializeCardData();
return new ServiceResult<string>(result);
}
}
[XmlRoot("root")]
public class PlayNumRankContainer
{
[XmlArray(ElementName = "play_num_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<PlayNumRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
@@ -0,0 +1,98 @@
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetTenpoScoreRankQuery(int TenpoId, string Param) : IRequestWrapper<string>;
public class GetTenpoScoreRankQueryHandler : IRequestHandlerWrapper<GetTenpoScoreRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetTenpoScoreRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetTenpoScoreRankQuery request, CancellationToken cancellationToken)
{
var param = request.Param.DeserializeCardData<RankParam>();
if (param.CardId == 0)
{
return await GetAllRanks(request.TenpoId, cancellationToken);
}
return await GetCardRank(param.CardId, request.TenpoId, cancellationToken);
}
private async Task<ServiceResult<string>> GetCardRank(long cardId, int tenpoId, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.GlobalScoreRanks.Where(rank => rank.LastPlayTenpoId == tenpoId)
.OrderByDescending(rank => rank.TotalScore)
.ToListAsync(cancellationToken: cancellationToken);
ranks = ranks.Select((rank, i) =>
{
rank.Rank = i + 1;
return rank;
}).ToList();
var rank = ranks.FirstOrDefault(rank => rank.CardId == cardId);
var container = new TenpoScoreRankContainer
{
Ranks = new List<ScoreRankDto>(),
Status = new RankStatus
{
TableName = "CardTenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 1
}
};
if (rank is null)
{
return new ServiceResult<string>(container.SerializeCardData());
}
var dto = rank.ScoreRankToDto();
dto.Id = 0;
container.Ranks.Add(dto);
container.Status.Rows++;
return new ServiceResult<string>(container.SerializeCardData());
}
private async Task<ServiceResult<string>> GetAllRanks(int tenpoId, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.GlobalScoreRanks.Where(rank => rank.LastPlayTenpoId == tenpoId)
.OrderByDescending(rank => rank.TotalScore)
.Take(30)
.ToListAsync(cancellationToken: cancellationToken);
ranks = ranks.Select((rank, i) =>
{
rank.Rank = i + 1;
return rank;
}).ToList();
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.ScoreRankToDto();
dto.Id = i;
dto.Rank2 = dto.Rank;
return dto;
}).ToList();
var container = new TenpoScoreRankContainer
{
Ranks = dtoList,
Status = new RankStatus
{
TableName = "CardTenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = dtoList.Count,
Status = 1
}
};
return new ServiceResult<string>(container.SerializeCardData());
}
}
@@ -0,0 +1,61 @@
using System.Diagnostics;
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetWPlayNumRankQuery(): IRequestWrapper<string>;
public class GetWPlayNumRankQueryHandler : IRequestHandlerWrapper<GetWPlayNumRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetWPlayNumRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetWPlayNumRankQuery request, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.PlayNumRanks.OrderBy(rank => rank.Rank)
.Take(30).ToListAsync(cancellationToken: cancellationToken);
var status = new RankStatus
{
TableName = "PlayNumRank",
StartDate = TimeHelper.DateToString(Process.GetCurrentProcess().StartTime.Date),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = ranks.Count,
Status = 1
};
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.PlayNumRankToDto();
dto.Id = i;
return dto;
}).ToList();
var container = new WPlayNumRankContainer
{
Ranks = dtoList,
Status = status
};
var result = container.SerializeCardData();
return new ServiceResult<string>(result);
}
}
[XmlRoot("root")]
public class WPlayNumRankContainer
{
[XmlArray(ElementName = "w_play_num_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<PlayNumRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
@@ -0,0 +1,96 @@
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetWScoreRankQuery(string Param) : IRequestWrapper<string>;
public class GetWScoreRankQueryHandler : IRequestHandlerWrapper<GetWScoreRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetWScoreRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetWScoreRankQuery request, CancellationToken cancellationToken)
{
var param = request.Param.DeserializeCardData<RankParam>();
if (param.CardId == 0)
{
return await GetAllRanks(cancellationToken);
}
return await GetCardRank(param.CardId, cancellationToken);
}
private async Task<ServiceResult<string>> GetCardRank(long cardId,
CancellationToken cancellationToken)
{
var rank = await cardDbContext.GlobalScoreRanks.FirstOrDefaultAsync(scoreRank => scoreRank.CardId == cardId,
cancellationToken: cancellationToken);
var container = new GlobalScoreRankContainer
{
Ranks = new List<ScoreRankDto>(),
Status = new RankStatus
{
TableName = "GlobalScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 1
}
};
if (rank is null)
{
return new ServiceResult<string>(container.SerializeCardData());
}
var dto = rank.ScoreRankToDto();
dto.Id = 0;
container.Ranks.Add(dto);
container.Status.Rows++;
return new ServiceResult<string>(container.SerializeCardData());
}
private async Task<ServiceResult<string>> GetAllRanks(CancellationToken cancellationToken)
{
var ranks = await cardDbContext.GlobalScoreRanks.OrderBy(rank => rank.Rank)
.Take(30).ToListAsync(cancellationToken: cancellationToken);
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.ScoreRankToDto();
dto.Id = i;
dto.Rank2 = dto.Rank;
return dto;
}).ToList();
var container = new WScoreRankContainer
{
Ranks = dtoList,
Status = new RankStatus
{
TableName = "GlobalScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = dtoList.Count,
Status = 1
}
};
return new ServiceResult<string>(container.SerializeCardData());
}
}
[XmlRoot("root")]
public class WScoreRankContainer
{
[XmlArray(ElementName = "w_score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<ScoreRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
@@ -0,0 +1,102 @@
using Application.Common.Helpers;
namespace Application.Game.Rank;
public record GetWTenpoScoreRankQuery(int TenpoId, string Param) : IRequestWrapper<string>;
public class GetWTenpoScoreRankQueryHandler : IRequestHandlerWrapper<GetWTenpoScoreRankQuery, string>
{
private readonly ICardDbContext cardDbContext;
public GetWTenpoScoreRankQueryHandler(ICardDbContext cardDbContext)
{
this.cardDbContext = cardDbContext;
}
public async Task<ServiceResult<string>> Handle(GetWTenpoScoreRankQuery request, CancellationToken cancellationToken)
{
var param = request.Param.DeserializeCardData<RankParam>();
if (param.CardId == 0)
{
return await GetAllRanks(request.TenpoId, cancellationToken);
}
return await GetCardRank(param.CardId, request.TenpoId, cancellationToken);
}
private async Task<ServiceResult<string>> GetCardRank(long cardId, int tenpoId, CancellationToken cancellationToken)
{
var rank = await cardDbContext.GlobalScoreRanks.FirstOrDefaultAsync(scoreRank => scoreRank.CardId == cardId &&
scoreRank.LastPlayTenpoId == tenpoId,
cancellationToken: cancellationToken);
var container = new TenpoScoreRankContainer
{
Ranks = new List<ScoreRankDto>(),
Status = new RankStatus
{
TableName = "TenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = 0,
Status = 1
}
};
if (rank is null)
{
return new ServiceResult<string>(container.SerializeCardData());
}
var dto = rank.ScoreRankToDto();
dto.Id = 0;
container.Ranks.Add(dto);
container.Status.Rows++;
return new ServiceResult<string>(container.SerializeCardData());
}
private async Task<ServiceResult<string>> GetAllRanks(int tenpoId, CancellationToken cancellationToken)
{
var ranks = await cardDbContext.GlobalScoreRanks.Where(rank => rank.LastPlayTenpoId == tenpoId)
.OrderByDescending(rank => rank.TotalScore)
.Take(30)
.ToListAsync(cancellationToken: cancellationToken);
ranks = ranks.Select((rank, i) =>
{
rank.Rank = i + 1;
return rank;
}).ToList();
var dtoList = ranks.Select((rank, i) =>
{
var dto = rank.ScoreRankToDto();
dto.Id = i;
dto.Rank2 = dto.Rank;
return dto;
}).ToList();
var container = new WTenpoScoreRankContainer
{
Ranks = dtoList,
Status = new RankStatus
{
TableName = "TenpoScoreRank",
StartDate = TimeHelper.DateToString(DateTime.Today),
EndDate = TimeHelper.DateToString(DateTime.Today),
Rows = dtoList.Count,
Status = 1
}
};
return new ServiceResult<string>(container.SerializeCardData());
}
}
[XmlRoot("root")]
public class WTenpoScoreRankContainer
{
[XmlArray(ElementName = "w_t_score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<ScoreRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
+14
View File
@@ -0,0 +1,14 @@
using System.ComponentModel;
namespace Application.Game.Rank;
public class RankParam
{
[XmlElement(ElementName = "card_id")]
[DefaultValue("0")]
public long CardId { get; set; }
[XmlElement(ElementName = "tenpo_id")]
[DefaultValue("0")]
public int TenpoId { get; set; }
}
+19
View File
@@ -0,0 +1,19 @@
namespace Application.Game.Rank;
public class RankStatus
{
[XmlElement("table_name")]
public string TableName { get; set; } = string.Empty;
[XmlElement("start_date")]
public string StartDate { get; set; } = string.Empty;
[XmlElement("end_date")]
public string EndDate { get; set; } = string.Empty;
[XmlElement("status")]
public int Status { get; set; }
[XmlElement("rows")]
public int Rows { get; set; }
}
@@ -0,0 +1,13 @@
namespace Application.Game.Rank;
[XmlRoot("root")]
public class TenpoScoreRankContainer
{
[XmlArray(ElementName = "t_score_rank")]
[XmlArrayItem(ElementName = "record")]
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public List<ScoreRankDto> Ranks { get; init; } = new();
[XmlElement("ranking_status")]
public RankStatus Status { get; set; } = new();
}
+117
View File
@@ -0,0 +1,117 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Domain.Config;
using MediatR;
using Microsoft.Extensions.Options;
namespace Application.Game.Server;
public record CertifyCommand(string? Gid, string? Mac, string? Random, string? Md5, string Host) : IRequest<string>;
public partial class CertifyCommandHandler : IRequestHandler<CertifyCommand, string>
{
private readonly RelayConfig relayConfig;
private readonly AuthConfig authConfig;
public CertifyCommandHandler(IOptions<RelayConfig> relayOptions, IOptions<AuthConfig> authOptions)
{
relayConfig = relayOptions.Value;
authConfig = authOptions.Value;
}
public Task<string> Handle(CertifyCommand request, CancellationToken cancellationToken)
{
if (request.Gid == null)
{
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoGid));
}
if (request.Mac == null)
{
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoMac));
}
if (request.Random == null)
{
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoRandom));
}
if (request.Md5 == null)
{
return Task.FromResult(QuitWithError(ErrorCode.ErrorNoHash));
}
if (!MacValid(request.Mac) )
{
return Task.FromResult(QuitWithError(ErrorCode.ErrorInvalidMac));
}
if (!Md5Valid(request.Md5))
{
return Task.FromResult(QuitWithError(ErrorCode.ErrorInvalidHash));
}
var machine = new Machine
{
TenpoId = "1337",
TenpoName = "GCLocalServer",
Pref = "nesys",
Location = "Local",
Mac = request.Mac
};
if (authConfig.Enabled)
{
machine = authConfig.Machines.FirstOrDefault(m => m.Mac == request.Mac);
if (machine is null)
{
return Task.FromResult(QuitWithError(ErrorCode.ErrorInvalidMac));
}
}
var ticket = string.Join(string.Empty,
MD5.HashData(Encoding.UTF8.GetBytes(request.Gid)).Select(b => b.ToString("x2")));
var response = $"host=card_id=7020392000147361,relay_addr={relayConfig.RelayServer},relay_port={relayConfig.RelayPort}\n" +
$"no={machine.TenpoId}\n" +
$"name={machine.TenpoName}\n" +
$"pref={machine.Pref}\n" +
$"addr={machine.Location}\n" +
"x-next-time=15\n" +
$"x-img=http://{request.Host}/news.png\n" +
$"x-ranking=http://{request.Host}/ranking/ranking.php\n" +
$"ticket={ticket}";
return Task.FromResult(response);
}
private static bool MacValid(string mac)
{
return MacRegex().IsMatch(mac);
}
private static bool Md5Valid(string md5)
{
return Md5Regex().IsMatch(md5);
}
private static string QuitWithError(ErrorCode errorCode)
{
return $"error={(int)errorCode}";
}
private enum ErrorCode
{
ErrorNoGid,
ErrorNoMac,
ErrorNoRandom,
ErrorNoHash,
ErrorInvalidMac,
ErrorInvalidHash
}
[GeneratedRegex("^[a-fA-F0-9]{12}$")]
private static partial Regex MacRegex();
[GeneratedRegex("^[a-fA-F0-9]{32}$")]
private static partial Regex Md5Regex();
}
+46
View File
@@ -0,0 +1,46 @@
using System.Text;
using MediatR;
namespace Application.Game.Server;
public record GetDataQuery(string Host, string Scheme) : IRequest<string>;
public class GetDataQueryHandler : IRequestHandler<GetDataQuery, string>
{
private readonly IEventManagerService eventManagerService;
public GetDataQueryHandler(IEventManagerService eventManagerService)
{
this.eventManagerService = eventManagerService;
}
public Task<string> Handle(GetDataQuery request, CancellationToken cancellationToken)
{
var response = "count=0\n" +
"nexttime=180";
if (!eventManagerService.UseEvents())
{
return Task.FromResult(response);
}
var urlBase = $"{request.Scheme}://{request.Host}/events/";
var dataString = new StringBuilder();
var events = eventManagerService.GetEvents();
var count = 0;
foreach (var pair in events.Select((@event, i) => new {Value = @event, Index = i}))
{
var value = pair.Value;
var index = pair.Index;
var fileUrl = $"{urlBase}{value.Name}";
var eventString = $"{index},{fileUrl},{value.NotBefore},{value.NotAfter},{value.Md5},{value.Index}";
dataString.Append(eventString).Append('\n');
count++;
}
response = $"count={count}\n" +
"nexttime=1\n" +
$"{dataString}";
return Task.FromResult(response);
}
}
+11
View File
@@ -0,0 +1,11 @@
// Global using directives
global using Microsoft.EntityFrameworkCore;
global using System.Xml.Serialization;
global using Application.Common.Base;
global using Application.Common.Extensions;
global using Shared.Dto.Api;
global using Shared.Models;
global using Application.Dto.Game;
global using Application.Interfaces;
global using Application.Mappers;
+30
View File
@@ -0,0 +1,30 @@
using Domain.Entities;
namespace Application.Interfaces;
public interface ICardDbContext
{
public DbSet<CardBdatum> CardBdata { get; set; }
public DbSet<CardDetail> CardDetails { get; set; }
public DbSet<CardMain> CardMains { get; set; }
public DbSet<CardPlayCount> CardPlayCounts { get; set; }
public DbSet<PlayNumRank> PlayNumRanks { get; set; }
public DbSet<GlobalScoreRank> GlobalScoreRanks { get; set; }
public DbSet<MonthlyScoreRank> MonthlyScoreRanks { get; set; }
public DbSet<ShopScoreRank> ShopScoreRanks { get; set; }
public DbSet<OnlineMatch> OnlineMatches { get; set; }
public DbSet<OnlineMatchEntry> OnlineMatchEntries { get; set; }
public Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}
@@ -0,0 +1,12 @@
using Domain.Config;
using Microsoft.Extensions.Options;
namespace Application.Interfaces;
public interface ICardDependencyAggregate
{
ICardDbContext CardDbContext { get; }
IMusicDbContext MusicDbContext { get; }
IOptions<GameConfig> Options { get; }
}
@@ -0,0 +1,12 @@
using Domain.Models;
namespace Application.Interfaces;
public interface IEventManagerService
{
public void InitializeEvents();
public bool UseEvents();
public IEnumerable<Event> GetEvents();
}
+12
View File
@@ -0,0 +1,12 @@
using Domain.Entities;
namespace Application.Interfaces;
public interface IMusicDbContext
{
public DbSet<MusicAou> MusicAous { get; set; }
public DbSet<MusicExtra> MusicExtras { get; set; }
public DbSet<MusicUnlock> MusicUnlocks { get; set; }
}
+13
View File
@@ -0,0 +1,13 @@
using MediatR;
namespace Application.Interfaces;
public interface IRequestWrapper<T> : IRequest<ServiceResult<T>>
{
}
public interface IRequestHandlerWrapper<TIn, TOut> : IRequestHandler<TIn, ServiceResult<TOut>> where TIn : IRequestWrapper<TOut>
{
}
+74
View File
@@ -0,0 +1,74 @@
using System.Diagnostics.CodeAnalysis;
using Domain.Config;
using Domain.Enums;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Quartz;
namespace Application.Jobs;
public class MaintainNullValuesJob : IJob
{
private readonly ILogger<MaintainNullValuesJob> logger;
private readonly ICardDbContext cardDbContext;
private readonly GameConfig config;
public static readonly JobKey KEY = new("MaintainNullValuesJob");
public MaintainNullValuesJob(ILogger<MaintainNullValuesJob> logger, ICardDbContext cardDbContext, IOptions<GameConfig> options)
{
this.logger = logger;
this.cardDbContext = cardDbContext;
config = options.Value;
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records",
Justification = "All details might be read")]
[SuppressMessage("ReSharper.DPA", "DPA0006: Large number of DB commands")]
public async Task Execute(IJobExecutionContext context)
{
logger.LogInformation("Starting changing null values in card detail table");
var details = await cardDbContext.CardDetails.Where(detail => detail.LastPlayTenpoId == null ||
detail.LastPlayTenpoId == "GC local server"
|| detail.LastPlayTime == null).ToListAsync();
details.ForEach(detail =>
{
detail.LastPlayTenpoId = "1337";
detail.LastPlayTime = DateTime.MinValue;
});
cardDbContext.CardDetails.UpdateRange(details);
var count = await cardDbContext.SaveChangesAsync(new CancellationToken());
logger.LogInformation("Updated {Count} entries in card detail table", count);
logger.LogInformation("Starting closing unfinished matches");
var matches = await cardDbContext.OnlineMatches.Where(match => match.IsOpen == true).ToListAsync();
matches.ForEach(match => match.IsOpen = false);
cardDbContext.OnlineMatches.UpdateRange(matches);
count = await cardDbContext.SaveChangesAsync(new CancellationToken());
logger.LogInformation("Closed {Count} matches", count);
logger.LogInformation("Starting to remove previously new songs");
var unlockables = config.UnlockRewards
.Where(c => c.RewardType == RewardType.Music).ToDictionary(rewardConfig => rewardConfig.TargetId);
var targets = await cardDbContext.CardDetails.Where(detail => detail.Pcol1 == 10).ToListAsync();
foreach (var target in targets)
{
if (unlockables.ContainsKey((int)target.Pcol2))
{
continue;
}
target.ScoreUi2 = 0;
target.ScoreUi6 = 0;
}
cardDbContext.CardDetails.UpdateRange(targets);
count = await cardDbContext.SaveChangesAsync(new CancellationToken());
logger.LogInformation("Fixed {Count} records", count);
}
}
@@ -0,0 +1,139 @@
using System.Diagnostics.CodeAnalysis;
using Domain.Config;
using Domain.Entities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Quartz;
namespace Application.Jobs;
public class UpdateGlobalScoreRankJob : IJob
{
private readonly ILogger<UpdateGlobalScoreRankJob> logger;
private readonly ICardDbContext cardDbContext;
private readonly AuthConfig authConfig;
public static readonly JobKey KEY = new("UpdateGlobalScoreRankJob");
public UpdateGlobalScoreRankJob(ILogger<UpdateGlobalScoreRankJob> logger,
ICardDbContext cardDbContext,
IOptions<AuthConfig> authConfig)
{
this.logger = logger;
this.cardDbContext = cardDbContext;
this.authConfig = authConfig.Value;
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records",
Justification = "All play record will be read")]
public async Task Execute(IJobExecutionContext context)
{
logger.LogInformation("Starting update global rank");
var cardMains = await cardDbContext.CardMains.ToListAsync();
var totalScoresByCardId = await cardDbContext.CardDetails.Where(detail => detail.Pcol1 == 21)
.GroupBy(detail => detail.CardId)
.Select(detailGroup => new
{
CardId = detailGroup.Key,
TotalScore = detailGroup.Sum(detail => detail.ScoreUi1)
})
.ToListAsync();
var avatarAndTitles = await cardDbContext.CardDetails.Where(detail => detail.Pcol1 == 0 &&
detail.Pcol2 == 0 &&
detail.Pcol3 == 0).ToListAsync();
var ranks = new List<GlobalScoreRank>();
foreach (var record in totalScoresByCardId)
{
var cardId = record.CardId;
var score = record.TotalScore;
var card = cardMains.FirstOrDefault(card => card.CardId == cardId);
if (card is null)
{
logger.LogWarning("Card id {CardId} missing in main card table!", cardId);
continue;
}
var detail = avatarAndTitles.First(detail => detail.CardId == cardId);
var pref = "nesys";
var lastPlayTenpoId = 1337;
var tenpoName = "GCLocalServer";
if (authConfig.Enabled)
{
var result = int.TryParse(detail.LastPlayTenpoId, out lastPlayTenpoId);
if (!result)
{
lastPlayTenpoId = 1337;
}
pref = authConfig.Machines.FirstOrDefault(m => m.TenpoId == detail.LastPlayTenpoId)?.Pref ?? "nesys";
tenpoName = authConfig.Machines.FirstOrDefault(m => m.TenpoId == detail.LastPlayTenpoId)?.TenpoName ?? "GCLocalServer";
}
var globalRank = new GlobalScoreRank
{
CardId = cardId,
PlayerName = card.PlayerName,
Fcol1 = detail.Fcol1,
Area = "Local",
AreaId = 1,
Pref = pref,
PrefId = 1337,
LastPlayTenpoId = lastPlayTenpoId,
TenpoName = tenpoName,
AvatarId = (int)detail.ScoreI1,
Title = "Title",
TitleId = detail.Fcol2,
TotalScore = score
};
ranks.Add(globalRank);
}
ranks.AddRange(GetFakeRanks());
ranks.Sort((rank, other) => -rank.TotalScore.CompareTo(other.TotalScore));
ranks = ranks.Select((rank, i) =>
{
rank.Rank = i + 1;
return rank;
}).ToList();
await cardDbContext.GlobalScoreRanks.UpsertRange(ranks).RunAsync();
await cardDbContext.SaveChangesAsync(new CancellationToken());
logger.LogInformation("Updating global score rank done");
}
private static IEnumerable<GlobalScoreRank> GetFakeRanks()
{
var fakeList = new List<GlobalScoreRank>();
for (int i = 0; i < 5; i++)
{
var rank = new GlobalScoreRank
{
CardId = 1020392010281502 + i,
PlayerName = $"Fake{i}",
Fcol1 = 0,
Area = "Local",
AreaId = 1,
Pref = "nesys",
PrefId = 1337,
LastPlayTenpoId = 1337,
TenpoName = "GCLocalServer",
AvatarId = i + 10,
Title = "Title",
TitleId = i + 100,
TotalScore = (i + 1) * 1000000
};
fakeList.Add(rank);
}
return fakeList;
}
}
@@ -0,0 +1,136 @@
using Domain.Config;
using Domain.Entities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Quartz;
namespace Application.Jobs;
public class UpdateMonthlyScoreRankJob : IJob
{
private readonly ILogger<UpdateMonthlyScoreRankJob> logger;
private readonly ICardDbContext cardDbContext;
private readonly AuthConfig authConfig;
public static readonly JobKey KEY = new JobKey("UpdateMonthlyScoreRankJob");
public UpdateMonthlyScoreRankJob(ILogger<UpdateMonthlyScoreRankJob> logger,
ICardDbContext cardDbContext,
IOptions<AuthConfig> authConfig)
{
this.logger = logger;
this.cardDbContext = cardDbContext;
this.authConfig = authConfig.Value;
}
public async Task Execute(IJobExecutionContext context)
{
logger.LogInformation("Starting update montly global rank");
var cardMains = await cardDbContext.CardMains.ToListAsync();
var totalScoresByCardId = await cardDbContext.CardDetails.Where(detail => detail.Pcol1 == 21 && detail.LastPlayTime >= DateTime.Today.AddDays(-30))
.GroupBy(detail => detail.CardId)
.Select(detailGroup => new
{
CardId = detailGroup.Key,
TotalScore = detailGroup.Sum(detail => detail.ScoreUi1)
})
.ToListAsync();
var avatarAndTitles = await cardDbContext.CardDetails.Where(detail => detail.Pcol1 == 0 &&
detail.Pcol2 == 0 &&
detail.Pcol3 == 0).ToListAsync();
var ranks = new List<MonthlyScoreRank>();
foreach (var record in totalScoresByCardId)
{
var cardId = record.CardId;
var score = record.TotalScore;
var card = cardMains.FirstOrDefault(card => card.CardId == cardId);
if (card is null)
{
logger.LogWarning("Card id {CardId} missing in main card table!", cardId);
continue;
}
var detail = avatarAndTitles.First(detail => detail.CardId == cardId);
var pref = "nesys";
var lastPlayTenpoId = 1337;
var tenpoName = "GCLocalServer";
if (authConfig.Enabled)
{
var result = int.TryParse(detail.LastPlayTenpoId, out lastPlayTenpoId);
if (!result)
{
lastPlayTenpoId = 1337;
}
pref = authConfig.Machines.FirstOrDefault(m => m.TenpoId == detail.LastPlayTenpoId)?.Pref ?? "nesys";
tenpoName = authConfig.Machines.FirstOrDefault(m => m.TenpoId == detail.LastPlayTenpoId)?.TenpoName ?? "GCLocalServer";
}
var monthlyScoreRank = new MonthlyScoreRank
{
CardId = cardId,
PlayerName = card.PlayerName,
Fcol1 = detail.Fcol1,
Area = "Local",
AreaId = 1,
Pref = pref,
PrefId = 1337,
LastPlayTenpoId = lastPlayTenpoId,
TenpoName = tenpoName,
AvatarId = (int)detail.ScoreI1,
Title = "Title",
TitleId = detail.Fcol2,
TotalScore = score
};
ranks.Add(monthlyScoreRank);
}
ranks.AddRange(GetFakeRanks());
ranks.Sort((rank, other) => -rank.TotalScore.CompareTo(other.TotalScore));
ranks = ranks.Select((rank, i) =>
{
rank.Rank = i + 1;
return rank;
}).ToList();
await cardDbContext.MonthlyScoreRanks.UpsertRange(ranks).RunAsync();
await cardDbContext.SaveChangesAsync(new CancellationToken());
logger.LogInformation("Updating monthly score rank done");
}
private static IEnumerable<MonthlyScoreRank> GetFakeRanks()
{
var fakeList = new List<MonthlyScoreRank>();
for (int i = 0; i < 5; i++)
{
var rank = new MonthlyScoreRank
{
CardId = 1020392010281502 + i,
PlayerName = $"Fake{i}",
Fcol1 = 0,
Area = "Local",
AreaId = 1,
Pref = "nesys",
PrefId = 1337,
LastPlayTenpoId = 1337,
TenpoName = "GCLocalServer",
AvatarId = i + 10,
Title = "Title",
TitleId = i + 100,
TotalScore = (i + 1) * 1000000
};
fakeList.Add(rank);
}
return fakeList;
}
}
+67
View File
@@ -0,0 +1,67 @@
using System.Diagnostics.CodeAnalysis;
using Domain.Entities;
using Microsoft.Extensions.Logging;
using Quartz;
namespace Application.Jobs;
public class UpdatePlayNumRankJob : IJob
{
private readonly ILogger<UpdatePlayNumRankJob> logger;
private readonly ICardDbContext cardDbContext;
private readonly IMusicDbContext musicDbContext;
public static readonly JobKey KEY = new JobKey("UpdatePlayNumRankJob");
public UpdatePlayNumRankJob(ILogger<UpdatePlayNumRankJob> logger, ICardDbContext cardDbContext,
IMusicDbContext musicDbContext)
{
this.logger = logger;
this.cardDbContext = cardDbContext;
this.musicDbContext = musicDbContext;
}
public async Task Execute(IJobExecutionContext context)
{
logger.LogInformation("Start maintaining play num rank");
await UpdatePlayNumRank();
}
[SuppressMessage("ReSharper.DPA", "DPA0007: Large number of DB records",
Justification = "All music will be read")]
private async Task UpdatePlayNumRank()
{
var playRecords = await cardDbContext.CardDetails
.Where(detail => detail.Pcol1 == 20).ToListAsync();
var playNumRanks = new List<PlayNumRank>();
var musics = await musicDbContext.MusicUnlocks.ToListAsync();
foreach (var music in musics)
{
var playCount = playRecords
.Where(detail => detail.Pcol2 == music.MusicId)
.Sum(detail => detail.ScoreUi1);
var playNumRank = new PlayNumRank
{
MusicId = (int)music.MusicId,
Artist = music.Artist ?? string.Empty,
Title = music.Title,
PlayCount = (int)playCount
};
playNumRanks.Add(playNumRank);
}
playNumRanks = playNumRanks.OrderByDescending(rank => rank.PlayCount).ToList();
var result = playNumRanks.Select((rank, i) =>
{
rank.Rank = i+1;
return rank;
}).ToList();
await cardDbContext.PlayNumRanks.UpsertRange(result).RunAsync();
await cardDbContext.SaveChangesAsync(new CancellationToken());
logger.LogInformation("Updating play num rank done");
}
}

Some files were not shown because too many files have changed in this diff Show More