From 46d9a63a4525d0f505c5ef40e29074fce025b22b Mon Sep 17 00:00:00 2001 From: asesidaa <1061472754@qq.com> Date: Mon, 27 Feb 2023 02:43:13 +0800 Subject: [PATCH] Migrate online matching --- Application/Dto/Game/OnlineMatchEntryDto.cs | 67 +++ .../StartOnlineMatchingCommand.cs | 85 +++- .../UpdateOnlineMatchingCommand.cs | 71 ++- .../UploadOnlineMatchingResultCommand.cs | 11 +- Application/Game/Server/GetDataQuery.cs | 2 +- Application/Interfaces/ICardDbContext.cs | 4 + Application/Jobs/MaintainNullValuesJob.cs | 8 + Application/Mappers/OnlineMatchMapper.cs | 17 + Domain/Entities/OnlineMatch.cs | 4 + GC-local-server-rewrite.sln | 6 - ...226141744_AddOnlineMatchTables.Designer.cs | 480 ++++++++++++++++++ .../20230226141744_AddOnlineMatchTables.cs | 93 ++++ .../Migrations/CardDbContextModelSnapshot.cs | 105 +++- Infrastructure/Persistence/CardDbContext.cs | 21 + MainServer/Configurations/events.json | 2 +- MainServer/Database/card.db3-shm | Bin 0 -> 32768 bytes MainServer/Database/card.db3-wal | Bin 0 -> 2043552 bytes MainServer/MainServer.csproj | 5 + 18 files changed, 963 insertions(+), 18 deletions(-) create mode 100644 Application/Dto/Game/OnlineMatchEntryDto.cs create mode 100644 Application/Mappers/OnlineMatchMapper.cs create mode 100644 Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.Designer.cs create mode 100644 Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.cs create mode 100644 MainServer/Database/card.db3-shm create mode 100644 MainServer/Database/card.db3-wal diff --git a/Application/Dto/Game/OnlineMatchEntryDto.cs b/Application/Dto/Game/OnlineMatchEntryDto.cs new file mode 100644 index 0000000..e38adf2 --- /dev/null +++ b/Application/Dto/Game/OnlineMatchEntryDto.cs @@ -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; +} \ No newline at end of file diff --git a/Application/Game/Card/OnlineMatching/StartOnlineMatchingCommand.cs b/Application/Game/Card/OnlineMatching/StartOnlineMatchingCommand.cs index 5ab8ef5..11b0a57 100644 --- a/Application/Game/Card/OnlineMatching/StartOnlineMatchingCommand.cs +++ b/Application/Game/Card/OnlineMatching/StartOnlineMatchingCommand.cs @@ -1,15 +1,92 @@ -namespace Application.Game.Card.OnlineMatching; +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; public class StartOnlineMatchingCommandHandler : RequestHandlerBase { - public StartOnlineMatchingCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) + 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 logger; + + public StartOnlineMatchingCommandHandler(ICardDependencyAggregate aggregate, ILogger logger) : base(aggregate) { + this.logger = logger; } - public override Task> Handle(StartOnlineMatchingCommand request, CancellationToken cancellationToken) + public override async Task> Handle(StartOnlineMatchingCommand request, CancellationToken cancellationToken) { - throw new NotImplementedException(); + var dto = request.Data.DeserializeCardData(); + 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(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(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(ServiceError.DatabaseSaveFailed); } } \ No newline at end of file diff --git a/Application/Game/Card/OnlineMatching/UpdateOnlineMatchingCommand.cs b/Application/Game/Card/OnlineMatching/UpdateOnlineMatchingCommand.cs index aa749c7..d3dc61f 100644 --- a/Application/Game/Card/OnlineMatching/UpdateOnlineMatchingCommand.cs +++ b/Application/Game/Card/OnlineMatching/UpdateOnlineMatchingCommand.cs @@ -1,15 +1,78 @@ -namespace Application.Game.Card.OnlineMatching; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; + +namespace Application.Game.Card.OnlineMatching; public record UpdateOnlineMatchingCommand(long CardId, string Data) : IRequestWrapper; public class UpdateOnlineMatchingCommandHandler : RequestHandlerBase { - public UpdateOnlineMatchingCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) + private readonly ILogger 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 logger) : base(aggregate) { + this.logger = logger; } - public override Task> Handle(UpdateOnlineMatchingCommand request, CancellationToken cancellationToken) + [SuppressMessage("ReSharper.DPA", "DPA0006: Large number of DB commands")] + public override async Task> Handle(UpdateOnlineMatchingCommand request, + CancellationToken cancellationToken) { - throw new NotImplementedException(); + var data = request.Data.DeserializeCardData(); + 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(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(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(ServiceError.DatabaseSaveFailed); } } \ No newline at end of file diff --git a/Application/Game/Card/OnlineMatching/UploadOnlineMatchingResultCommand.cs b/Application/Game/Card/OnlineMatching/UploadOnlineMatchingResultCommand.cs index 4aaf06c..7b0ff05 100644 --- a/Application/Game/Card/OnlineMatching/UploadOnlineMatchingResultCommand.cs +++ b/Application/Game/Card/OnlineMatching/UploadOnlineMatchingResultCommand.cs @@ -4,12 +4,21 @@ public record UploadOnlineMatchingResultCommand(long CardId, string Data) : IReq public class UploadOnlineMatchingResultCommandHandler : RequestHandlerBase { + private const string XPATH = "/root/online_battle_result"; + public UploadOnlineMatchingResultCommandHandler(ICardDependencyAggregate aggregate) : base(aggregate) { } public override Task> Handle(UploadOnlineMatchingResultCommand request, CancellationToken cancellationToken) { - throw new NotImplementedException(); + var result = new OnlineMatchingResult { Status = 1 }.SerializeCardData(XPATH); + return Task.FromResult(new ServiceResult(result)); } +} + +public class OnlineMatchingResult +{ + [XmlElement(ElementName = "status")] + public int Status { get; set; } } \ No newline at end of file diff --git a/Application/Game/Server/GetDataQuery.cs b/Application/Game/Server/GetDataQuery.cs index 20984ad..48a9a66 100644 --- a/Application/Game/Server/GetDataQuery.cs +++ b/Application/Game/Server/GetDataQuery.cs @@ -38,7 +38,7 @@ public class GetDataQueryHandler : IRequestHandler } response = $"count={count}\n" + - "nexttime=180\n" + + "nexttime=1\n" + $"{dataString}"; return Task.FromResult(response); diff --git a/Application/Interfaces/ICardDbContext.cs b/Application/Interfaces/ICardDbContext.cs index 44ac24e..46bb223 100644 --- a/Application/Interfaces/ICardDbContext.cs +++ b/Application/Interfaces/ICardDbContext.cs @@ -19,6 +19,10 @@ public interface ICardDbContext public DbSet MonthlyScoreRanks { get; set; } public DbSet ShopScoreRanks { get; set; } + + public DbSet OnlineMatches { get; set; } + + public DbSet OnlineMatchEntries { get; set; } public Task SaveChangesAsync(CancellationToken cancellationToken); diff --git a/Application/Jobs/MaintainNullValuesJob.cs b/Application/Jobs/MaintainNullValuesJob.cs index ceb7b6e..aa33865 100644 --- a/Application/Jobs/MaintainNullValuesJob.cs +++ b/Application/Jobs/MaintainNullValuesJob.cs @@ -38,5 +38,13 @@ public class MaintainNullValuesJob : IJob 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); } } \ No newline at end of file diff --git a/Application/Mappers/OnlineMatchMapper.cs b/Application/Mappers/OnlineMatchMapper.cs new file mode 100644 index 0000000..c01992e --- /dev/null +++ b/Application/Mappers/OnlineMatchMapper.cs @@ -0,0 +1,17 @@ +using Domain.Entities; +using Riok.Mapperly.Abstractions; + +namespace Application.Mappers; + +[Mapper] +public static partial class OnlineMatchMapper +{ + public static partial OnlineMatchEntryDto OnlineMatchEntryToDto(this OnlineMatchEntry entry); + + public static partial OnlineMatchEntry DtoToOnlineMatchEntry(this OnlineMatchEntryDto entryDto); + + private static string DateTimeToString(DateTime dateTime) + { + return dateTime.ToString("yyyy/MM/dd hh:mm:ss"); + } +} \ No newline at end of file diff --git a/Domain/Entities/OnlineMatch.cs b/Domain/Entities/OnlineMatch.cs index a2b407d..a8264ce 100644 --- a/Domain/Entities/OnlineMatch.cs +++ b/Domain/Entities/OnlineMatch.cs @@ -5,4 +5,8 @@ public class OnlineMatch public long MatchId { get; set; } public List Entries { get; set; } = new(); + + public bool IsOpen { get; set; } + + public Guid Guid { get; set; } } \ No newline at end of file diff --git a/GC-local-server-rewrite.sln b/GC-local-server-rewrite.sln index 887445e..0fb73be 100644 --- a/GC-local-server-rewrite.sln +++ b/GC-local-server-rewrite.sln @@ -9,8 +9,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedProject", "SharedProj EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GCRelayServer", "GCRelayServer\GCRelayServer.csproj", "{268178DF-6345-4D9E-A389-9E809CBF39C9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MatchServer", "MatchServer\MatchServer.csproj", "{CB91C3D3-ED69-4DC6-A205-B262A08BEE49}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MainServer", "MainServer\MainServer.csproj", "{B8C6CA7E-5E58-43BC-8E03-84306916DE39}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application", "Application\Application.csproj", "{B0691233-0D7E-4694-8923-646E7A3BDBF4}" @@ -41,10 +39,6 @@ Global {268178DF-6345-4D9E-A389-9E809CBF39C9}.Debug|Any CPU.Build.0 = Debug|Any CPU {268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {268178DF-6345-4D9E-A389-9E809CBF39C9}.Release|Any CPU.Build.0 = Release|Any CPU - {CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CB91C3D3-ED69-4DC6-A205-B262A08BEE49}.Release|Any CPU.Build.0 = Release|Any CPU {B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8C6CA7E-5E58-43BC-8E03-84306916DE39}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.Designer.cs b/Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.Designer.cs new file mode 100644 index 0000000..e1cd3ab --- /dev/null +++ b/Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.Designer.cs @@ -0,0 +1,480 @@ +// +using System; +using Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(CardDbContext))] + [Migration("20230226141744_AddOnlineMatchTables")] + partial class AddOnlineMatchTables + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.2"); + + modelBuilder.Entity("Domain.Entities.CardBdatum", b => + { + b.Property("CardId") + .HasColumnType("INTEGER") + .HasColumnName("card_id"); + + b.Property("Bdata") + .HasColumnType("TEXT") + .HasColumnName("bdata"); + + b.Property("BdataSize") + .HasColumnType("INTEGER") + .HasColumnName("bdata_size"); + + b.HasKey("CardId"); + + b.ToTable("card_bdata", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.CardDetail", b => + { + b.Property("CardId") + .HasColumnType("INTEGER") + .HasColumnName("card_id"); + + b.Property("Pcol1") + .HasColumnType("INTEGER") + .HasColumnName("pcol1"); + + b.Property("Pcol2") + .HasColumnType("INTEGER") + .HasColumnName("pcol2"); + + b.Property("Pcol3") + .HasColumnType("INTEGER") + .HasColumnName("pcol3"); + + b.Property("Fcol1") + .HasColumnType("INTEGER") + .HasColumnName("fcol1"); + + b.Property("Fcol2") + .HasColumnType("INTEGER") + .HasColumnName("fcol2"); + + b.Property("Fcol3") + .HasColumnType("INTEGER") + .HasColumnName("fcol3"); + + b.Property("LastPlayTenpoId") + .HasColumnType("TEXT") + .HasColumnName("last_play_tenpo_id"); + + b.Property("LastPlayTime") + .HasColumnType("INTEGER") + .HasColumnName("last_play_time"); + + b.Property("ScoreBi1") + .HasColumnType("INTEGER") + .HasColumnName("score_bi1"); + + b.Property("ScoreI1") + .HasColumnType("INTEGER") + .HasColumnName("score_i1"); + + b.Property("ScoreUi1") + .HasColumnType("INTEGER") + .HasColumnName("score_ui1"); + + b.Property("ScoreUi2") + .HasColumnType("INTEGER") + .HasColumnName("score_ui2"); + + b.Property("ScoreUi3") + .HasColumnType("INTEGER") + .HasColumnName("score_ui3"); + + b.Property("ScoreUi4") + .HasColumnType("INTEGER") + .HasColumnName("score_ui4"); + + b.Property("ScoreUi5") + .HasColumnType("INTEGER") + .HasColumnName("score_ui5"); + + b.Property("ScoreUi6") + .HasColumnType("INTEGER") + .HasColumnName("score_ui6"); + + b.HasKey("CardId", "Pcol1", "Pcol2", "Pcol3"); + + b.ToTable("card_detail", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.CardMain", b => + { + b.Property("CardId") + .HasColumnType("INTEGER") + .HasColumnName("card_id"); + + b.Property("AchieveStatus") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("achieve_status"); + + b.Property("Created") + .HasColumnType("TEXT") + .HasColumnName("created"); + + b.Property("Fcol1") + .HasColumnType("INTEGER") + .HasColumnName("fcol1"); + + b.Property("Fcol2") + .HasColumnType("INTEGER") + .HasColumnName("fcol2"); + + b.Property("Fcol3") + .HasColumnType("INTEGER") + .HasColumnName("fcol3"); + + b.Property("Modified") + .HasColumnType("TEXT") + .HasColumnName("modified"); + + b.Property("PlayerName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("player_name"); + + b.Property("ScoreI1") + .HasColumnType("INTEGER") + .HasColumnName("score_i1"); + + b.HasKey("CardId"); + + b.ToTable("card_main", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.CardPlayCount", b => + { + b.Property("CardId") + .HasColumnType("INTEGER") + .HasColumnName("card_id"); + + b.Property("LastPlayedTime") + .HasColumnType("INTEGER") + .HasColumnName("last_played_time"); + + b.Property("PlayCount") + .HasColumnType("INTEGER") + .HasColumnName("play_count"); + + b.HasKey("CardId"); + + b.ToTable("CardPlayCount", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.GlobalScoreRank", b => + { + b.Property("CardId") + .HasColumnType("INTEGER"); + + b.Property("Area") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AreaId") + .HasColumnType("INTEGER"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("Fcol1") + .HasColumnType("INTEGER"); + + b.Property("LastPlayTenpoId") + .HasColumnType("INTEGER"); + + b.Property("PlayerName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Pref") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PrefId") + .HasColumnType("INTEGER"); + + b.Property("Rank") + .HasColumnType("INTEGER"); + + b.Property("TenpoName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TitleId") + .HasColumnType("INTEGER"); + + b.Property("TotalScore") + .HasColumnType("INTEGER"); + + b.HasKey("CardId"); + + b.ToTable("GlobalScoreRank", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.MonthlyScoreRank", b => + { + b.Property("CardId") + .HasColumnType("INTEGER"); + + b.Property("Area") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AreaId") + .HasColumnType("INTEGER"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("Fcol1") + .HasColumnType("INTEGER"); + + b.Property("LastPlayTenpoId") + .HasColumnType("INTEGER"); + + b.Property("PlayerName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Pref") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PrefId") + .HasColumnType("INTEGER"); + + b.Property("Rank") + .HasColumnType("INTEGER"); + + b.Property("TenpoName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TitleId") + .HasColumnType("INTEGER"); + + b.Property("TotalScore") + .HasColumnType("INTEGER"); + + b.HasKey("CardId"); + + b.ToTable("MonthlyScoreRank", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.OnlineMatch", b => + { + b.Property("MatchId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Guid") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("IsOpen") + .HasColumnType("INTEGER"); + + b.HasKey("MatchId"); + + b.ToTable("OnlineMatches"); + }); + + modelBuilder.Entity("Domain.Entities.OnlineMatchEntry", b => + { + b.Property("MatchId") + .HasColumnType("INTEGER"); + + b.Property("EntryId") + .HasColumnType("INTEGER"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CardId") + .HasColumnType("INTEGER"); + + b.Property("ClassId") + .HasColumnType("INTEGER"); + + b.Property("EventId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("MachineId") + .HasColumnType("INTEGER"); + + b.Property("MatchRemainingTime") + .HasColumnType("INTEGER"); + + b.Property("MatchTimeout") + .HasColumnType("INTEGER"); + + b.Property("MatchWaitTime") + .HasColumnType("INTEGER"); + + b.Property("MessageId") + .HasColumnType("INTEGER"); + + b.Property("PlayerName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Pref") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PrefId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TenpoId") + .HasColumnType("INTEGER"); + + b.Property("TenpoName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TitleId") + .HasColumnType("INTEGER"); + + b.HasKey("MatchId", "EntryId"); + + b.ToTable("OnlineMatchEntries"); + }); + + modelBuilder.Entity("Domain.Entities.PlayNumRank", b => + { + b.Property("MusicId") + .HasColumnType("INTEGER"); + + b.Property("Artist") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PlayCount") + .HasColumnType("INTEGER"); + + b.Property("PrevRank") + .HasColumnType("INTEGER"); + + b.Property("PrevRank2") + .HasColumnType("INTEGER"); + + b.Property("Rank") + .HasColumnType("INTEGER"); + + b.Property("Rank2") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("MusicId"); + + b.ToTable("PlayNumRank", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.ShopScoreRank", b => + { + b.Property("CardId") + .HasColumnType("INTEGER"); + + b.Property("Area") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("AreaId") + .HasColumnType("INTEGER"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("Fcol1") + .HasColumnType("INTEGER"); + + b.Property("LastPlayTenpoId") + .HasColumnType("INTEGER"); + + b.Property("PlayerName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Pref") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PrefId") + .HasColumnType("INTEGER"); + + b.Property("Rank") + .HasColumnType("INTEGER"); + + b.Property("TenpoName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TitleId") + .HasColumnType("INTEGER"); + + b.Property("TotalScore") + .HasColumnType("INTEGER"); + + b.HasKey("CardId"); + + b.ToTable("ShopScoreRank", (string)null); + }); + + modelBuilder.Entity("Domain.Entities.OnlineMatchEntry", b => + { + b.HasOne("Domain.Entities.OnlineMatch", null) + .WithMany("Entries") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.OnlineMatch", b => + { + b.Navigation("Entries"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.cs b/Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.cs new file mode 100644 index 0000000..a619447 --- /dev/null +++ b/Infrastructure/Migrations/20230226141744_AddOnlineMatchTables.cs @@ -0,0 +1,93 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + /// + public partial class AddOnlineMatchTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "last_play_time", + table: "card_detail", + type: "INTEGER", + nullable: true, + oldClrType: typeof(long), + oldType: "INTEGER"); + + migrationBuilder.CreateTable( + name: "OnlineMatches", + columns: table => new + { + MatchId = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + IsOpen = table.Column(type: "INTEGER", nullable: false), + Guid = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OnlineMatches", x => x.MatchId); + }); + + migrationBuilder.CreateTable( + name: "OnlineMatchEntries", + columns: table => new + { + MatchId = table.Column(type: "INTEGER", nullable: false), + EntryId = table.Column(type: "INTEGER", nullable: false), + MachineId = table.Column(type: "INTEGER", nullable: false), + EventId = table.Column(type: "INTEGER", nullable: false), + StartTime = table.Column(type: "TEXT", nullable: false), + Status = table.Column(type: "INTEGER", nullable: false), + CardId = table.Column(type: "INTEGER", nullable: false), + PlayerName = table.Column(type: "TEXT", nullable: false), + AvatarId = table.Column(type: "INTEGER", nullable: false), + TitleId = table.Column(type: "INTEGER", nullable: false), + ClassId = table.Column(type: "INTEGER", nullable: false), + GroupId = table.Column(type: "INTEGER", nullable: false), + TenpoId = table.Column(type: "INTEGER", nullable: false), + TenpoName = table.Column(type: "TEXT", nullable: false), + PrefId = table.Column(type: "INTEGER", nullable: false), + Pref = table.Column(type: "TEXT", nullable: false), + MessageId = table.Column(type: "INTEGER", nullable: false), + MatchTimeout = table.Column(type: "INTEGER", nullable: false), + MatchWaitTime = table.Column(type: "INTEGER", nullable: false), + MatchRemainingTime = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OnlineMatchEntries", x => new { x.MatchId, x.EntryId }); + table.ForeignKey( + name: "FK_OnlineMatchEntries_OnlineMatches_MatchId", + column: x => x.MatchId, + principalTable: "OnlineMatches", + principalColumn: "MatchId", + onDelete: ReferentialAction.Cascade); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OnlineMatchEntries"); + + migrationBuilder.DropTable( + name: "OnlineMatches"); + + migrationBuilder.AlterColumn( + name: "last_play_time", + table: "card_detail", + type: "INTEGER", + nullable: false, + defaultValue: 0L, + oldClrType: typeof(long), + oldType: "INTEGER", + oldNullable: true); + } + } +} diff --git a/Infrastructure/Migrations/CardDbContextModelSnapshot.cs b/Infrastructure/Migrations/CardDbContextModelSnapshot.cs index b1302cb..7512bfc 100644 --- a/Infrastructure/Migrations/CardDbContextModelSnapshot.cs +++ b/Infrastructure/Migrations/CardDbContextModelSnapshot.cs @@ -1,4 +1,5 @@ // +using System; using Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -69,7 +70,7 @@ namespace Infrastructure.Migrations .HasColumnType("TEXT") .HasColumnName("last_play_tenpo_id"); - b.Property("LastPlayTime") + b.Property("LastPlayTime") .HasColumnType("INTEGER") .HasColumnName("last_play_time"); @@ -282,6 +283,94 @@ namespace Infrastructure.Migrations b.ToTable("MonthlyScoreRank", (string)null); }); + modelBuilder.Entity("Domain.Entities.OnlineMatch", b => + { + b.Property("MatchId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Guid") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("IsOpen") + .HasColumnType("INTEGER"); + + b.HasKey("MatchId"); + + b.ToTable("OnlineMatches"); + }); + + modelBuilder.Entity("Domain.Entities.OnlineMatchEntry", b => + { + b.Property("MatchId") + .HasColumnType("INTEGER"); + + b.Property("EntryId") + .HasColumnType("INTEGER"); + + b.Property("AvatarId") + .HasColumnType("INTEGER"); + + b.Property("CardId") + .HasColumnType("INTEGER"); + + b.Property("ClassId") + .HasColumnType("INTEGER"); + + b.Property("EventId") + .HasColumnType("INTEGER"); + + b.Property("GroupId") + .HasColumnType("INTEGER"); + + b.Property("MachineId") + .HasColumnType("INTEGER"); + + b.Property("MatchRemainingTime") + .HasColumnType("INTEGER"); + + b.Property("MatchTimeout") + .HasColumnType("INTEGER"); + + b.Property("MatchWaitTime") + .HasColumnType("INTEGER"); + + b.Property("MessageId") + .HasColumnType("INTEGER"); + + b.Property("PlayerName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Pref") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PrefId") + .HasColumnType("INTEGER"); + + b.Property("StartTime") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TenpoId") + .HasColumnType("INTEGER"); + + b.Property("TenpoName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TitleId") + .HasColumnType("INTEGER"); + + b.HasKey("MatchId", "EntryId"); + + b.ToTable("OnlineMatchEntries"); + }); + modelBuilder.Entity("Domain.Entities.PlayNumRank", b => { b.Property("MusicId") @@ -368,6 +457,20 @@ namespace Infrastructure.Migrations b.ToTable("ShopScoreRank", (string)null); }); + + modelBuilder.Entity("Domain.Entities.OnlineMatchEntry", b => + { + b.HasOne("Domain.Entities.OnlineMatch", null) + .WithMany("Entries") + .HasForeignKey("MatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Domain.Entities.OnlineMatch", b => + { + b.Navigation("Entries"); + }); #pragma warning restore 612, 618 } } diff --git a/Infrastructure/Persistence/CardDbContext.cs b/Infrastructure/Persistence/CardDbContext.cs index bcf63c4..31042b6 100644 --- a/Infrastructure/Persistence/CardDbContext.cs +++ b/Infrastructure/Persistence/CardDbContext.cs @@ -33,6 +33,10 @@ public partial class CardDbContext : DbContext, ICardDbContext public virtual DbSet ShopScoreRanks { get; set; } = null!; + public virtual DbSet OnlineMatches { get; set; } = null!; + + public virtual DbSet OnlineMatchEntries { get; set; } = null!; + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -202,6 +206,23 @@ public partial class CardDbContext : DbContext, ICardDbContext entity.Property(e => e.PlayerName); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.MatchId); + + entity.Property(e => e.MatchId).ValueGeneratedNever(); + entity.Property(e => e.Guid).IsConcurrencyToken(); + + entity.HasMany(e => e.Entries) + .WithOne() + .HasForeignKey(p => p.MatchId); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.MatchId, e.EntryId }); + }); + OnModelCreatingPartial(modelBuilder); } diff --git a/MainServer/Configurations/events.json b/MainServer/Configurations/events.json index 7ebe1ba..7f5133b 100644 --- a/MainServer/Configurations/events.json +++ b/MainServer/Configurations/events.json @@ -1,6 +1,6 @@ { "Events": { - "UseEvents": false, + "UseEvents": true, "EventFiles": [ { "FileName": "event_103_20201125.evt", diff --git a/MainServer/Database/card.db3-shm b/MainServer/Database/card.db3-shm new file mode 100644 index 0000000000000000000000000000000000000000..ef909aaf63c1687894d1946cb84fa16a0a40116d GIT binary patch literal 32768 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 literal 0 HcmV?d00001 diff --git a/MainServer/MainServer.csproj b/MainServer/MainServer.csproj index 81a7396..be4d375 100644 --- a/MainServer/MainServer.csproj +++ b/MainServer/MainServer.csproj @@ -108,6 +108,11 @@ PreserveNewest true + + + PreserveNewest + true +