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 0000000..ef909aa Binary files /dev/null and b/MainServer/Database/card.db3-shm differ diff --git a/MainServer/Database/card.db3-wal b/MainServer/Database/card.db3-wal new file mode 100644 index 0000000..1f6b768 Binary files /dev/null and b/MainServer/Database/card.db3-wal differ 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 +