diff --git a/GameDatabase/Context/TaikoDbContext.cs b/GameDatabase/Context/TaikoDbContext.cs index 7e95b79..0e0458c 100644 --- a/GameDatabase/Context/TaikoDbContext.cs +++ b/GameDatabase/Context/TaikoDbContext.cs @@ -127,8 +127,10 @@ namespace GameDatabase.Context }); OnModelCreatingPartial(modelBuilder); + OnModelCreatingChallengeCompete(modelBuilder); } partial void OnModelCreatingPartial(ModelBuilder modelBuilder); + partial void OnModelCreatingChallengeCompete(ModelBuilder modelBuilder); } } \ No newline at end of file diff --git a/GameDatabase/Context/TaikoDbContextChallengeCompete.cs b/GameDatabase/Context/TaikoDbContextChallengeCompete.cs new file mode 100644 index 0000000..992c3ae --- /dev/null +++ b/GameDatabase/Context/TaikoDbContextChallengeCompete.cs @@ -0,0 +1,78 @@ +using GameDatabase.Entities; +using Microsoft.EntityFrameworkCore; + +namespace GameDatabase.Context; + +public partial class TaikoDbContext +{ + public virtual DbSet ChallengeCompeteData { get; set; } = null; + public virtual DbSet ChallengeCompeteParticipantData { get; set; } = null; + public virtual DbSet ChallengeCompeteSongData { get; set; } = null; + public virtual DbSet ChallengeCompeteBestData { get; set; } = null; + + partial void OnModelCreatingChallengeCompete(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.CompId }); + + entity.Property(e => e.CompeteMode).HasConversion(); + entity.Property(e => e.Share).HasConversion(); + entity.Property(e => e.CompeteTarget).HasConversion(); + + entity.Property(e => e.CreateTime).HasColumnType("datetime"); + entity.Property(e => e.ExpireTime).HasColumnType("datetime"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.CompId, e.Baid }); + + entity.HasOne(e => e.UserData) + .WithMany() + .HasPrincipalKey(p => p.Baid) + .HasForeignKey(d => d.Baid) + .OnDelete(DeleteBehavior.Cascade); + + entity.HasOne(e => e.ChallengeCompeteData) + .WithMany(p => p.Participants) + .HasPrincipalKey(p => p.CompId) + .HasForeignKey(d => d.CompId) + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.CompId, e.SongId }); + + entity.HasOne(e => e.ChallengeCompeteData) + .WithMany(p => p.Songs) + .HasPrincipalKey(p => p.CompId) + .HasForeignKey(d => d.CompId) + .OnDelete(DeleteBehavior.Cascade); + + entity.Property(e => e.Difficulty).HasConversion(); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.CompId, e.Baid, e.SongId }); + + entity.HasOne(e => e.UserData) + .WithMany() + .HasPrincipalKey(p => p.Baid) + .HasForeignKey(d => d.Baid) + .OnDelete(DeleteBehavior.Cascade); + + entity.HasOne(e => e.ChallengeCompeteSongData) + .WithMany(p => p.BestScores) + .HasPrincipalKey(p => new { p.CompId, p.SongId }) + .HasForeignKey(d => new { d.CompId, d.SongId }) + .OnDelete(DeleteBehavior.Cascade); + + entity.Property(e => e.Difficulty).HasConversion(); + entity.Property(e => e.Crown).HasConversion(); + entity.Property(e => e.ScoreRank).HasConversion(); + }); + } +} diff --git a/GameDatabase/Entities/ChallengeCompeteBestDatum.cs b/GameDatabase/Entities/ChallengeCompeteBestDatum.cs new file mode 100644 index 0000000..13130b7 --- /dev/null +++ b/GameDatabase/Entities/ChallengeCompeteBestDatum.cs @@ -0,0 +1,24 @@ +using SharedProject.Enums; + +namespace GameDatabase.Entities; + +public partial class ChallengeCompeteBestDatum +{ + public uint CompId { get; set; } + public uint Baid { get; set; } + public uint SongId { get; set; } + public Difficulty Difficulty { get; set; } + public CrownType Crown { get; set; } + public uint Score { get; set; } + public uint ScoreRate { get; set; } + public ScoreRank ScoreRank { get; set; } + public uint GoodCount { get; set; } + public uint OkCount { get; set; } + public uint MissCount { get; set; } + public uint ComboCount { get; set; } + public uint HitCount { get; set; } + public uint DrumrollCount { get; set; } + public bool Skipped { get; set; } + public virtual ChallengeCompeteSongDatum? ChallengeCompeteSongData { get; set; } + public virtual UserDatum? UserData { get; set; } +} diff --git a/GameDatabase/Entities/ChallengeCompeteDatum.cs b/GameDatabase/Entities/ChallengeCompeteDatum.cs new file mode 100644 index 0000000..a8cea9c --- /dev/null +++ b/GameDatabase/Entities/ChallengeCompeteDatum.cs @@ -0,0 +1,21 @@ +using SharedProject.Enums; + +namespace GameDatabase.Entities; + +public partial class ChallengeCompeteDatum +{ + public uint CompId { get; set; } + public CompeteModeType CompeteMode { get; set; } = CompeteModeType.None; + public uint Baid { get; set; } + public string CompeteName { get; set; } = String.Empty; + public string CompeteDescribe { get; set; } = String.Empty; + public uint MaxParticipant { get; set; } = 2; + public DateTime CreateTime { get; set; } + public DateTime ExpireTime { get; set; } + public uint RequireTitle { get; set; } = 0; + public ShareType Share { get; set; } = ShareType.EveryOne; + public CompeteTargetType CompeteTarget { get; set; } = CompeteTargetType.EveryOne; + public List Songs { get; set; } = new(); + public List Participants { get; set; } = new(); +} + diff --git a/GameDatabase/Entities/ChallengeCompeteParticipantDatum.cs b/GameDatabase/Entities/ChallengeCompeteParticipantDatum.cs new file mode 100644 index 0000000..08fc057 --- /dev/null +++ b/GameDatabase/Entities/ChallengeCompeteParticipantDatum.cs @@ -0,0 +1,10 @@ +namespace GameDatabase.Entities; + +public partial class ChallengeCompeteParticipantDatum +{ + public uint CompId { get; set; } + public uint Baid { get; set; } + public bool IsActive { get; set; } + public virtual ChallengeCompeteDatum? ChallengeCompeteData { get; set; } + public virtual UserDatum? UserData { get; set; } +} diff --git a/GameDatabase/Entities/ChallengeCompeteSongDatum.cs b/GameDatabase/Entities/ChallengeCompeteSongDatum.cs new file mode 100644 index 0000000..846c909 --- /dev/null +++ b/GameDatabase/Entities/ChallengeCompeteSongDatum.cs @@ -0,0 +1,13 @@ +using SharedProject.Enums; + +namespace GameDatabase.Entities; + +public partial class ChallengeCompeteSongDatum +{ + public uint CompId { get; set; } + public uint SongId { get; set; } + public Difficulty Difficulty { get; set; } + public short SongOpt { get; set; } + public List BestScores { get; set; } = new(); + public virtual ChallengeCompeteDatum? ChallengeCompeteData { get; set; } +} diff --git a/GameDatabase/Migrations/20240910100030_AddChallengeCompete.Designer.cs b/GameDatabase/Migrations/20240910100030_AddChallengeCompete.Designer.cs new file mode 100644 index 0000000..7d504f4 --- /dev/null +++ b/GameDatabase/Migrations/20240910100030_AddChallengeCompete.Designer.cs @@ -0,0 +1,769 @@ +// +using System; +using GameDatabase.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace GameDatabase.Migrations +{ + [DbContext(typeof(TaikoDbContext))] + [Migration("20240910100030_AddChallengeCompete")] + partial class AddChallengeCompete + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.3"); + + modelBuilder.Entity("GameDatabase.Entities.AiScoreDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("IsWin") + .HasColumnType("INTEGER"); + + b.HasKey("Baid", "SongId", "Difficulty"); + + b.ToTable("AiScoreData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.AiSectionScoreDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("SectionIndex") + .HasColumnType("INTEGER"); + + b.Property("Crown") + .HasColumnType("INTEGER"); + + b.Property("DrumrollCount") + .HasColumnType("INTEGER"); + + b.Property("GoodCount") + .HasColumnType("INTEGER"); + + b.Property("IsWin") + .HasColumnType("INTEGER"); + + b.Property("MissCount") + .HasColumnType("INTEGER"); + + b.Property("OkCount") + .HasColumnType("INTEGER"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.HasKey("Baid", "SongId", "Difficulty", "SectionIndex"); + + b.ToTable("AiSectionScoreData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.Card", b => + { + b.Property("AccessCode") + .HasColumnType("TEXT"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.HasKey("AccessCode"); + + b.HasIndex("Baid"); + + b.ToTable("Card", (string)null); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteBestDatum", b => + { + b.Property("CompId") + .HasColumnType("INTEGER"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("ComboCount") + .HasColumnType("INTEGER"); + + b.Property("Crown") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("DrumrollCount") + .HasColumnType("INTEGER"); + + b.Property("GoodCount") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("MissCount") + .HasColumnType("INTEGER"); + + b.Property("OkCount") + .HasColumnType("INTEGER"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.Property("ScoreRank") + .HasColumnType("INTEGER"); + + b.Property("ScoreRate") + .HasColumnType("INTEGER"); + + b.Property("Skipped") + .HasColumnType("INTEGER"); + + b.HasKey("CompId", "Baid", "SongId"); + + b.HasIndex("Baid"); + + b.HasIndex("CompId", "SongId"); + + b.ToTable("ChallengeCompeteBestData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteDatum", b => + { + b.Property("CompId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("CompeteDescribe") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CompeteMode") + .HasColumnType("INTEGER"); + + b.Property("CompeteName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CompeteTarget") + .HasColumnType("INTEGER"); + + b.Property("CreateTime") + .HasColumnType("datetime"); + + b.Property("ExpireTime") + .HasColumnType("datetime"); + + b.Property("MaxParticipant") + .HasColumnType("INTEGER"); + + b.Property("RequireTitle") + .HasColumnType("INTEGER"); + + b.Property("Share") + .HasColumnType("INTEGER"); + + b.HasKey("CompId"); + + b.ToTable("ChallengeCompeteData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteParticipantDatum", b => + { + b.Property("CompId") + .HasColumnType("INTEGER"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.HasKey("CompId", "Baid"); + + b.HasIndex("Baid"); + + b.ToTable("ChallengeCompeteParticipantData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteSongDatum", b => + { + b.Property("CompId") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("SongOpt") + .HasColumnType("INTEGER"); + + b.HasKey("CompId", "SongId"); + + b.ToTable("ChallengeCompeteSongData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.Credential", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Salt") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Baid"); + + b.ToTable("Credential", (string)null); + }); + + modelBuilder.Entity("GameDatabase.Entities.DanScoreDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("DanId") + .HasColumnType("INTEGER"); + + b.Property("DanType") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(1); + + b.Property("ArrivalSongCount") + .HasColumnType("INTEGER"); + + b.Property("ClearState") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0u); + + b.Property("ComboCountTotal") + .HasColumnType("INTEGER"); + + b.Property("SoulGaugeTotal") + .HasColumnType("INTEGER"); + + b.HasKey("Baid", "DanId", "DanType"); + + b.ToTable("DanScoreData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.DanStageScoreDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("DanId") + .HasColumnType("INTEGER"); + + b.Property("DanType") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(1); + + b.Property("SongNumber") + .HasColumnType("INTEGER"); + + b.Property("BadCount") + .HasColumnType("INTEGER"); + + b.Property("ComboCount") + .HasColumnType("INTEGER"); + + b.Property("DrumrollCount") + .HasColumnType("INTEGER"); + + b.Property("GoodCount") + .HasColumnType("INTEGER"); + + b.Property("HighScore") + .HasColumnType("INTEGER"); + + b.Property("OkCount") + .HasColumnType("INTEGER"); + + b.Property("PlayScore") + .HasColumnType("INTEGER"); + + b.Property("TotalHitCount") + .HasColumnType("INTEGER"); + + b.HasKey("Baid", "DanId", "DanType", "SongNumber"); + + b.ToTable("DanStageScoreData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.SongBestDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("BestCrown") + .HasColumnType("INTEGER"); + + b.Property("BestRate") + .HasColumnType("INTEGER"); + + b.Property("BestScore") + .HasColumnType("INTEGER"); + + b.Property("BestScoreRank") + .HasColumnType("INTEGER"); + + b.HasKey("Baid", "SongId", "Difficulty"); + + b.ToTable("SongBestData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.SongPlayDatum", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("ComboCount") + .HasColumnType("INTEGER"); + + b.Property("Crown") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("DrumrollCount") + .HasColumnType("INTEGER"); + + b.Property("GoodCount") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("MissCount") + .HasColumnType("INTEGER"); + + b.Property("OkCount") + .HasColumnType("INTEGER"); + + b.Property("PlayTime") + .HasColumnType("datetime"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.Property("ScoreRank") + .HasColumnType("INTEGER"); + + b.Property("ScoreRate") + .HasColumnType("INTEGER"); + + b.Property("Skipped") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("SongNumber") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Baid"); + + b.ToTable("SongPlayData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.Token", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Count") + .HasColumnType("INTEGER"); + + b.HasKey("Baid", "Id"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("GameDatabase.Entities.UserDatum", b => + { + b.Property("Baid") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AchievementDisplayDifficulty") + .HasColumnType("INTEGER"); + + b.Property("AiWinCount") + .HasColumnType("INTEGER"); + + b.Property("ColorBody") + .HasColumnType("INTEGER"); + + b.Property("ColorFace") + .HasColumnType("INTEGER"); + + b.Property("ColorLimb") + .HasColumnType("INTEGER"); + + b.Property("CostumeData") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CostumeFlgArray") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CurrentBody") + .HasColumnType("INTEGER"); + + b.Property("CurrentFace") + .HasColumnType("INTEGER"); + + b.Property("CurrentHead") + .HasColumnType("INTEGER"); + + b.Property("CurrentKigurumi") + .HasColumnType("INTEGER"); + + b.Property("CurrentPuchi") + .HasColumnType("INTEGER"); + + b.Property("DifficultyPlayedArray") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DifficultyPlayedCourse") + .HasColumnType("INTEGER"); + + b.Property("DifficultyPlayedSort") + .HasColumnType("INTEGER"); + + b.Property("DifficultyPlayedStar") + .HasColumnType("INTEGER"); + + b.Property("DifficultySettingArray") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DifficultySettingCourse") + .HasColumnType("INTEGER"); + + b.Property("DifficultySettingSort") + .HasColumnType("INTEGER"); + + b.Property("DifficultySettingStar") + .HasColumnType("INTEGER"); + + b.Property("DisplayAchievement") + .HasColumnType("INTEGER"); + + b.Property("DisplayDan") + .HasColumnType("INTEGER"); + + b.Property("FavoriteSongsArray") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("GenericInfoFlgArray") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsAdmin") + .HasColumnType("INTEGER"); + + b.Property("IsSkipOn") + .HasColumnType("INTEGER"); + + b.Property("IsVoiceOn") + .HasColumnType("INTEGER"); + + b.Property("LastPlayDatetime") + .HasColumnType("datetime"); + + b.Property("LastPlayMode") + .HasColumnType("INTEGER"); + + b.Property("MyDonName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MyDonNameLanguage") + .HasColumnType("INTEGER"); + + b.Property("NotesPosition") + .HasColumnType("INTEGER"); + + b.Property("OptionSetting") + .HasColumnType("INTEGER"); + + b.Property("SelectedToneId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TitleFlgArray") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TitlePlateId") + .HasColumnType("INTEGER"); + + b.Property("ToneFlgArray") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UnlockedBody") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UnlockedFace") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UnlockedHead") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UnlockedKigurumi") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UnlockedPuchi") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UnlockedSongIdList") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Baid"); + + b.ToTable("UserData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.AiScoreDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "Ba") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("GameDatabase.Entities.AiSectionScoreDatum", b => + { + b.HasOne("GameDatabase.Entities.AiScoreDatum", "Parent") + .WithMany("AiSectionScoreData") + .HasForeignKey("Baid", "SongId", "Difficulty") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("GameDatabase.Entities.Card", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "Ba") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteBestDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "UserData") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameDatabase.Entities.ChallengeCompeteSongDatum", "ChallengeCompeteSongData") + .WithMany("BestScores") + .HasForeignKey("CompId", "SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeCompeteSongData"); + + b.Navigation("UserData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteParticipantDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "UserData") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameDatabase.Entities.ChallengeCompeteDatum", "ChallengeCompeteData") + .WithMany("Participants") + .HasForeignKey("CompId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeCompeteData"); + + b.Navigation("UserData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteSongDatum", b => + { + b.HasOne("GameDatabase.Entities.ChallengeCompeteDatum", "ChallengeCompeteData") + .WithMany("Songs") + .HasForeignKey("CompId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeCompeteData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.Credential", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "Ba") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("GameDatabase.Entities.DanScoreDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "Ba") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("GameDatabase.Entities.DanStageScoreDatum", b => + { + b.HasOne("GameDatabase.Entities.DanScoreDatum", "Parent") + .WithMany("DanStageScoreData") + .HasForeignKey("Baid", "DanId", "DanType") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("GameDatabase.Entities.SongBestDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "Ba") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("GameDatabase.Entities.SongPlayDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "Ba") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("GameDatabase.Entities.Token", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "Datum") + .WithMany("Tokens") + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Datum"); + }); + + modelBuilder.Entity("GameDatabase.Entities.AiScoreDatum", b => + { + b.Navigation("AiSectionScoreData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteDatum", b => + { + b.Navigation("Participants"); + + b.Navigation("Songs"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteSongDatum", b => + { + b.Navigation("BestScores"); + }); + + modelBuilder.Entity("GameDatabase.Entities.DanScoreDatum", b => + { + b.Navigation("DanStageScoreData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.UserDatum", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/GameDatabase/Migrations/20240910100030_AddChallengeCompete.cs b/GameDatabase/Migrations/20240910100030_AddChallengeCompete.cs new file mode 100644 index 0000000..0993106 --- /dev/null +++ b/GameDatabase/Migrations/20240910100030_AddChallengeCompete.cs @@ -0,0 +1,150 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace GameDatabase.Migrations +{ + /// + public partial class AddChallengeCompete : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ChallengeCompeteData", + columns: table => new + { + CompId = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CompeteMode = table.Column(type: "INTEGER", nullable: false), + Baid = table.Column(type: "INTEGER", nullable: false), + CompeteName = table.Column(type: "TEXT", nullable: false), + CompeteDescribe = table.Column(type: "TEXT", nullable: false), + MaxParticipant = table.Column(type: "INTEGER", nullable: false), + CreateTime = table.Column(type: "datetime", nullable: false), + ExpireTime = table.Column(type: "datetime", nullable: false), + RequireTitle = table.Column(type: "INTEGER", nullable: false), + Share = table.Column(type: "INTEGER", nullable: false), + CompeteTarget = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeCompeteData", x => x.CompId); + }); + + migrationBuilder.CreateTable( + name: "ChallengeCompeteParticipantData", + columns: table => new + { + CompId = table.Column(type: "INTEGER", nullable: false), + Baid = table.Column(type: "INTEGER", nullable: false), + IsActive = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeCompeteParticipantData", x => new { x.CompId, x.Baid }); + table.ForeignKey( + name: "FK_ChallengeCompeteParticipantData_ChallengeCompeteData_CompId", + column: x => x.CompId, + principalTable: "ChallengeCompeteData", + principalColumn: "CompId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ChallengeCompeteParticipantData_UserData_Baid", + column: x => x.Baid, + principalTable: "UserData", + principalColumn: "Baid", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ChallengeCompeteSongData", + columns: table => new + { + CompId = table.Column(type: "INTEGER", nullable: false), + SongId = table.Column(type: "INTEGER", nullable: false), + Difficulty = table.Column(type: "INTEGER", nullable: false), + SongOpt = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeCompeteSongData", x => new { x.CompId, x.SongId }); + table.ForeignKey( + name: "FK_ChallengeCompeteSongData_ChallengeCompeteData_CompId", + column: x => x.CompId, + principalTable: "ChallengeCompeteData", + principalColumn: "CompId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ChallengeCompeteBestData", + columns: table => new + { + CompId = table.Column(type: "INTEGER", nullable: false), + Baid = table.Column(type: "INTEGER", nullable: false), + SongId = table.Column(type: "INTEGER", nullable: false), + Difficulty = table.Column(type: "INTEGER", nullable: false), + Crown = table.Column(type: "INTEGER", nullable: false), + Score = table.Column(type: "INTEGER", nullable: false), + ScoreRate = table.Column(type: "INTEGER", nullable: false), + ScoreRank = table.Column(type: "INTEGER", nullable: false), + GoodCount = table.Column(type: "INTEGER", nullable: false), + OkCount = table.Column(type: "INTEGER", nullable: false), + MissCount = table.Column(type: "INTEGER", nullable: false), + ComboCount = table.Column(type: "INTEGER", nullable: false), + HitCount = table.Column(type: "INTEGER", nullable: false), + DrumrollCount = table.Column(type: "INTEGER", nullable: false), + Skipped = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ChallengeCompeteBestData", x => new { x.CompId, x.Baid, x.SongId }); + table.ForeignKey( + name: "FK_ChallengeCompeteBestData_ChallengeCompeteSongData_CompId_SongId", + columns: x => new { x.CompId, x.SongId }, + principalTable: "ChallengeCompeteSongData", + principalColumns: new[] { "CompId", "SongId" }, + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ChallengeCompeteBestData_UserData_Baid", + column: x => x.Baid, + principalTable: "UserData", + principalColumn: "Baid", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeCompeteBestData_Baid", + table: "ChallengeCompeteBestData", + column: "Baid"); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeCompeteBestData_CompId_SongId", + table: "ChallengeCompeteBestData", + columns: new[] { "CompId", "SongId" }); + + migrationBuilder.CreateIndex( + name: "IX_ChallengeCompeteParticipantData_Baid", + table: "ChallengeCompeteParticipantData", + column: "Baid"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ChallengeCompeteBestData"); + + migrationBuilder.DropTable( + name: "ChallengeCompeteParticipantData"); + + migrationBuilder.DropTable( + name: "ChallengeCompeteSongData"); + + migrationBuilder.DropTable( + name: "ChallengeCompeteData"); + } + } +} diff --git a/GameDatabase/Migrations/TaikoDbContextModelSnapshot.cs b/GameDatabase/Migrations/TaikoDbContextModelSnapshot.cs index 8fa0e9f..2e6165f 100644 --- a/GameDatabase/Migrations/TaikoDbContextModelSnapshot.cs +++ b/GameDatabase/Migrations/TaikoDbContextModelSnapshot.cs @@ -91,6 +91,142 @@ namespace TaikoLocalServer.Migrations b.ToTable("Card", (string)null); }); + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteBestDatum", b => + { + b.Property("CompId") + .HasColumnType("INTEGER"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("ComboCount") + .HasColumnType("INTEGER"); + + b.Property("Crown") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("DrumrollCount") + .HasColumnType("INTEGER"); + + b.Property("GoodCount") + .HasColumnType("INTEGER"); + + b.Property("HitCount") + .HasColumnType("INTEGER"); + + b.Property("MissCount") + .HasColumnType("INTEGER"); + + b.Property("OkCount") + .HasColumnType("INTEGER"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.Property("ScoreRank") + .HasColumnType("INTEGER"); + + b.Property("ScoreRate") + .HasColumnType("INTEGER"); + + b.Property("Skipped") + .HasColumnType("INTEGER"); + + b.HasKey("CompId", "Baid", "SongId"); + + b.HasIndex("Baid"); + + b.HasIndex("CompId", "SongId"); + + b.ToTable("ChallengeCompeteBestData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteDatum", b => + { + b.Property("CompId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("CompeteDescribe") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CompeteMode") + .HasColumnType("INTEGER"); + + b.Property("CompeteName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CompeteTarget") + .HasColumnType("INTEGER"); + + b.Property("CreateTime") + .HasColumnType("datetime"); + + b.Property("ExpireTime") + .HasColumnType("datetime"); + + b.Property("MaxParticipant") + .HasColumnType("INTEGER"); + + b.Property("RequireTitle") + .HasColumnType("INTEGER"); + + b.Property("Share") + .HasColumnType("INTEGER"); + + b.HasKey("CompId"); + + b.ToTable("ChallengeCompeteData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteParticipantDatum", b => + { + b.Property("CompId") + .HasColumnType("INTEGER"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.HasKey("CompId", "Baid"); + + b.HasIndex("Baid"); + + b.ToTable("ChallengeCompeteParticipantData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteSongDatum", b => + { + b.Property("CompId") + .HasColumnType("INTEGER"); + + b.Property("SongId") + .HasColumnType("INTEGER"); + + b.Property("Difficulty") + .HasColumnType("INTEGER"); + + b.Property("SongOpt") + .HasColumnType("INTEGER"); + + b.HasKey("CompId", "SongId"); + + b.ToTable("ChallengeCompeteSongData"); + }); + modelBuilder.Entity("GameDatabase.Entities.Credential", b => { b.Property("Baid") @@ -483,6 +619,55 @@ namespace TaikoLocalServer.Migrations b.Navigation("Ba"); }); + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteBestDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "UserData") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameDatabase.Entities.ChallengeCompeteSongDatum", "ChallengeCompeteSongData") + .WithMany("BestScores") + .HasForeignKey("CompId", "SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeCompeteSongData"); + + b.Navigation("UserData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteParticipantDatum", b => + { + b.HasOne("GameDatabase.Entities.UserDatum", "UserData") + .WithMany() + .HasForeignKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameDatabase.Entities.ChallengeCompeteDatum", "ChallengeCompeteData") + .WithMany("Participants") + .HasForeignKey("CompId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeCompeteData"); + + b.Navigation("UserData"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteSongDatum", b => + { + b.HasOne("GameDatabase.Entities.ChallengeCompeteDatum", "ChallengeCompeteData") + .WithMany("Songs") + .HasForeignKey("CompId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChallengeCompeteData"); + }); + modelBuilder.Entity("GameDatabase.Entities.Credential", b => { b.HasOne("GameDatabase.Entities.UserDatum", "Ba") @@ -554,6 +739,18 @@ namespace TaikoLocalServer.Migrations b.Navigation("AiSectionScoreData"); }); + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteDatum", b => + { + b.Navigation("Participants"); + + b.Navigation("Songs"); + }); + + modelBuilder.Entity("GameDatabase.Entities.ChallengeCompeteSongDatum", b => + { + b.Navigation("BestScores"); + }); + modelBuilder.Entity("GameDatabase.Entities.DanScoreDatum", b => { b.Navigation("DanStageScoreData"); diff --git a/SharedProject/Enums/CompeteModeType.cs b/SharedProject/Enums/CompeteModeType.cs new file mode 100644 index 0000000..fd286cb --- /dev/null +++ b/SharedProject/Enums/CompeteModeType.cs @@ -0,0 +1,9 @@ +namespace SharedProject.Enums; +public enum CompeteModeType : uint +{ + None = 0, + Chanllenge = 1, + Compete = 2, + OfficialCompete = 3 +} + diff --git a/SharedProject/Enums/CompeteTargetType.cs b/SharedProject/Enums/CompeteTargetType.cs new file mode 100644 index 0000000..1f9dc95 --- /dev/null +++ b/SharedProject/Enums/CompeteTargetType.cs @@ -0,0 +1,9 @@ +namespace SharedProject.Enums; + +public enum CompeteTargetType : uint +{ + EveryOne = 0, + Beginner = 1, + Superior = 2 +} + diff --git a/SharedProject/Enums/ShareType.cs b/SharedProject/Enums/ShareType.cs new file mode 100644 index 0000000..281a176 --- /dev/null +++ b/SharedProject/Enums/ShareType.cs @@ -0,0 +1,7 @@ +namespace SharedProject.Enums; +public enum ShareType : uint +{ + EveryOne = 0, + FriendOnly = 1, + FriendAndFollower = 2 +} diff --git a/TaikoLocalServer/Controllers/Api/ChallengeCompeteManageController.cs b/TaikoLocalServer/Controllers/Api/ChallengeCompeteManageController.cs new file mode 100644 index 0000000..4f282e2 --- /dev/null +++ b/TaikoLocalServer/Controllers/Api/ChallengeCompeteManageController.cs @@ -0,0 +1,63 @@ +using TaikoLocalServer.Filters; + +namespace TaikoLocalServer.Controllers.Api; + +[ApiController] +[Route("api/[controller]")] +public class ChallengeCompeteManageController(IChallengeCompeteService challengeCompeteService) : BaseController +{ + [HttpGet] + [ServiceFilter(typeof(AuthorizeIfRequiredAttribute))] + public ActionResult> GetAllChallengeCompete() + { + List datum = challengeCompeteService.GetAllChallengeCompete(); + + return Ok(datum); + } + + [HttpPost("{baid}/createCompete")] + [ServiceFilter(typeof(AuthorizeIfRequiredAttribute))] + public async Task CreateCompete(uint baid, ChallengeCompeteInfo challengeCompeteInfo) + { + await challengeCompeteService.CreateCompete(baid, challengeCompeteInfo); + + return NoContent(); + } + + [HttpPost("{baid}/createChallenge/{targetBaid}")] + [ServiceFilter(typeof(AuthorizeIfRequiredAttribute))] + public async Task CreateChallenge(uint baid, uint targetBaid, ChallengeCompeteInfo challengeCompeteInfo) + { + await challengeCompeteService.CreateChallenge(baid, targetBaid, challengeCompeteInfo); + + return NoContent(); + } + + [HttpGet("{baid}/joinCompete/{compId}")] + [ServiceFilter(typeof(AuthorizeIfRequiredAttribute))] + public async Task> JoinCompete(uint baid, uint compId) + { + bool result = await challengeCompeteService.ParticipateCompete(compId, baid); + + return Ok(result); + } + + [HttpGet("{baid}/acceptChallenge/{compId}")] + [ServiceFilter(typeof(AuthorizeIfRequiredAttribute))] + public async Task> AcceptChallenge(uint baid, uint compId) + { + bool result = await challengeCompeteService.AnswerChallenge(compId, baid, true); + + return Ok(result); + } + + [HttpGet("{baid}/rejectChallenge/{compId}")] + [ServiceFilter(typeof(AuthorizeIfRequiredAttribute))] + public async Task> RejectChallenge(uint baid, uint compId) + { + bool result = await challengeCompeteService.AnswerChallenge(compId, baid, false); + + return Ok(result); + } + +} \ No newline at end of file diff --git a/TaikoLocalServer/Controllers/Game/ChallengeCompetitionController.cs b/TaikoLocalServer/Controllers/Game/ChallengeCompetitionController.cs index 62a8f84..7324c93 100644 --- a/TaikoLocalServer/Controllers/Game/ChallengeCompetitionController.cs +++ b/TaikoLocalServer/Controllers/Game/ChallengeCompetitionController.cs @@ -3,16 +3,14 @@ [ApiController] public class ChallengeCompetitionController : BaseController { - [HttpPost("/v12r08_ww/chassis/challengecompe.php")] + + [HttpPost("/v12r08_ww/chassis/challengecompe_mn4g8uq1.php")] [Produces("application/protobuf")] - public IActionResult HandleChallenge([FromBody] ChallengeCompeRequest request) + public async Task HandleChallenge([FromBody] ChallengeCompeRequest request) { Logger.LogInformation("ChallengeCompe request : {Request}", request.Stringify()); - var response = new ChallengeCompeResponse - { - Result = 1 - }; + var response = await Mediator.Send(new ChallengeCompeteQuery(request.Baid)); return Ok(response); } diff --git a/TaikoLocalServer/Handlers/ChallengeCompeteQuery.cs b/TaikoLocalServer/Handlers/ChallengeCompeteQuery.cs new file mode 100644 index 0000000..c0571c9 --- /dev/null +++ b/TaikoLocalServer/Handlers/ChallengeCompeteQuery.cs @@ -0,0 +1,65 @@ +using GameDatabase.Context; +using System.Buffers.Binary; + +namespace TaikoLocalServer.Handlers; + +public record ChallengeCompeteQuery(uint Baid) : IRequest; + +public class ChallengeCompeteQueryHandler(TaikoDbContext context, IChallengeCompeteService challengeCompeteService, ILogger logger) + : IRequestHandler +{ + + public async Task Handle(ChallengeCompeteQuery request, CancellationToken cancellationToken) + { + List competes = challengeCompeteService.GetInProgressChallengeCompete(request.Baid); + ChallengeCompeResponse response = new() + { + Result = 1 + }; + + foreach (var compete in competes) + { + ChallengeCompeResponse.CompeData compeData = new() + { + CompeId = compete.CompId + }; + foreach (var song in compete.Songs) + { + var songOptions = new byte[2]; + BinaryPrimitives.WriteInt16LittleEndian(songOptions, song.SongOpt); + + uint myHighScore = 0; + foreach (var bestScore in song.BestScores) + { + if (bestScore.Baid == request.Baid) + { + myHighScore = bestScore.Score; + } + } + + ChallengeCompeResponse.CompeData.TracksData tracksData = new() + { + SongNo = song.SongId, + Level = (uint) song.Difficulty, + OptionFlg = songOptions, + HighScore = myHighScore + }; + compeData.AryTrackStats.Add(tracksData); + } + switch (compete.CompeteMode) + { + case CompeteModeType.Chanllenge: + response.AryChallengeStats.Add(compeData); + break; + case CompeteModeType.Compete: + response.AryUserCompeStats.Add(compeData); + break; + case CompeteModeType.OfficialCompete: + response.AryBngCompeStats.Add(compeData); + break; + } + } + + return response; + } +} diff --git a/TaikoLocalServer/Handlers/UserDataQuery.cs b/TaikoLocalServer/Handlers/UserDataQuery.cs index 79723f6..2cd688e 100644 --- a/TaikoLocalServer/Handlers/UserDataQuery.cs +++ b/TaikoLocalServer/Handlers/UserDataQuery.cs @@ -1,12 +1,13 @@ using System.Buffers.Binary; using GameDatabase.Context; +using TaikoLocalServer.Services; using Throw; namespace TaikoLocalServer.Handlers; public record UserDataQuery(uint Baid) : IRequest; -public class UserDataQueryHandler(TaikoDbContext context, IGameDataService gameDataService, ILogger logger) +public class UserDataQueryHandler(TaikoDbContext context, IGameDataService gameDataService, IChallengeCompeteService challengeCompeteService, ILogger logger) : IRequestHandler { @@ -71,7 +72,9 @@ public class UserDataQueryHandler(TaikoDbContext context, IGameDataService gameD difficultySettingArray[i] -= 1; } } - + + bool hasChallengeCompe = challengeCompeteService.HasChallengeCompete(request.Baid); + var response = new CommonUserDataResponse { Result = 1, @@ -92,7 +95,7 @@ public class UserDataQueryHandler(TaikoDbContext context, IGameDataService gameD DifficultyPlayedStar = userData.DifficultyPlayedStar, DifficultyPlayedSort = userData.DifficultyPlayedSort, SongRecentCnt = (uint)recentSongs.Length, - IsChallengecompe = false, + IsChallengecompe = hasChallengeCompe, // TODO: Other fields }; diff --git a/TaikoLocalServer/Models/ChallengeCompeteInfo.cs b/TaikoLocalServer/Models/ChallengeCompeteInfo.cs new file mode 100644 index 0000000..d46391c --- /dev/null +++ b/TaikoLocalServer/Models/ChallengeCompeteInfo.cs @@ -0,0 +1,33 @@ +using System.Text.Json.Serialization; + +namespace TaikoLocalServer.Models; + +public class ChallengeCompeteInfo +{ + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("desc")] + public string Desc { get; set; } = string.Empty; + + [JsonPropertyName("competeMode")] + public CompeteModeType CompeteMode { get; set; } + + [JsonPropertyName("maxParticipant")] + public uint MaxParticipant { get; set; } + + [JsonPropertyName("lastFor")] + public uint LastFor { get; set; } + + [JsonPropertyName("requiredTitle")] + public uint RequiredTitle { get; set; } + + [JsonPropertyName("shareType")] + public ShareType ShareType { get; set; } + + [JsonPropertyName("competeTargetType")] + public CompeteTargetType CompeteTargetType { get; set; } + + [JsonPropertyName("competeTargetType")] + public List challengeCompeteSongs { get; set; } = new(); +} diff --git a/TaikoLocalServer/Models/ChallengeCompeteSongInfo.cs b/TaikoLocalServer/Models/ChallengeCompeteSongInfo.cs new file mode 100644 index 0000000..f995316 --- /dev/null +++ b/TaikoLocalServer/Models/ChallengeCompeteSongInfo.cs @@ -0,0 +1,16 @@ +using SharedProject.Models; +using System.Text.Json.Serialization; + +namespace TaikoLocalServer.Models; + +public class ChallengeCompeteSongInfo +{ + [JsonPropertyName("songId")] + public uint SongId { get; set; } + + [JsonPropertyName("difficulty")] + public Difficulty Difficulty { get; set; } + + [JsonPropertyName("playSetting")] + public PlaySetting PlaySetting { get; set; } = new(); +} diff --git a/TaikoLocalServer/Services/ChallengeCompeteService.cs b/TaikoLocalServer/Services/ChallengeCompeteService.cs new file mode 100644 index 0000000..3d9bb14 --- /dev/null +++ b/TaikoLocalServer/Services/ChallengeCompeteService.cs @@ -0,0 +1,174 @@ + +using GameDatabase.Context; +using SharedProject.Utils; +using Throw; + +namespace TaikoLocalServer.Services; + +public class ChallengeCompeteService : IChallengeCompeteService +{ + private readonly TaikoDbContext context; + public ChallengeCompeteService(TaikoDbContext context) + { + this.context = context; + } + + public bool HasChallengeCompete(uint baid) + { + return context.ChallengeCompeteData + .Include(c => c.Participants) + .Any(data => + data.CreateTime < DateTime.Now && + data.ExpireTime > DateTime.Now && + data.Participants.Any(participant => participant.Baid == baid && participant.IsActive) + ); + } + + public List GetInProgressChallengeCompete(uint baid) + { + return context.ChallengeCompeteData + .Include(c => c.Participants) + .Where(data => + data.CreateTime < DateTime.Now && + data.ExpireTime > DateTime.Now && + data.Participants.Any(participant => participant.Baid == baid) + ).ToList(); + } + + public List GetAllChallengeCompete() + { + return context.ChallengeCompeteData.Where(data => true).ToList(); + } + + public async Task CreateCompete(uint baid, ChallengeCompeteInfo challengeCompeteInfo) + { + ChallengeCompeteDatum challengeCompeteData = new() + { + CompId = context.ChallengeCompeteData.Any() ? context.ChallengeCompeteData.AsEnumerable().Max(c => c.CompId) + 1 : 1, + CompeteMode = challengeCompeteInfo.CompeteMode, + Baid = baid, + CompeteName = challengeCompeteInfo.Name, + CompeteDescribe = challengeCompeteInfo.Desc, + MaxParticipant = challengeCompeteInfo.MaxParticipant, + CreateTime = DateTime.Now, + ExpireTime = DateTime.Now.AddDays(challengeCompeteInfo.LastFor), + RequireTitle = challengeCompeteInfo.RequiredTitle, + Share = challengeCompeteInfo.ShareType, + CompeteTarget = challengeCompeteInfo.CompeteTargetType + }; + await context.AddAsync(challengeCompeteData); + foreach (var song in challengeCompeteInfo.challengeCompeteSongs) + { + ChallengeCompeteSongDatum challengeCompeteSongData = new() + { + CompId = challengeCompeteData.CompId, + SongId = song.SongId, + Difficulty = song.Difficulty, + SongOpt = PlaySettingConverter.PlaySettingToShort(song.PlaySetting) + }; + await context.AddAsync(challengeCompeteSongData); + } + ChallengeCompeteParticipantDatum participantDatum = new() + { + CompId = challengeCompeteData.CompId, + Baid = baid, + IsActive = true + }; + await context.AddAsync(participantDatum); + } + + public async Task ParticipateCompete(uint compId, uint baid) + { + var challengeCompete = await context.ChallengeCompeteData.FindAsync(compId); + challengeCompete.ThrowIfNull($"Challenge not found for CompId {compId}!"); + + if (challengeCompete.MaxParticipant <= challengeCompete.Participants.Count()) return false; + foreach (var participant in challengeCompete.Participants) + { + if (participant.Baid == baid) return false; + } + + ChallengeCompeteParticipantDatum participantDatum = new() + { + CompId = challengeCompete.CompId, + Baid = baid, + IsActive = true, + }; + await context.AddAsync(participantDatum); + + return true; + } + + public async Task CreateChallenge(uint baid, uint targetBaid, ChallengeCompeteInfo challengeCompeteInfo) + { + ChallengeCompeteDatum challengeCompeteData = new() + { + CompId = context.ChallengeCompeteData.Any() ? context.ChallengeCompeteData.AsEnumerable().Max(c => c.CompId) + 1 : 1, + CompeteMode = challengeCompeteInfo.CompeteMode, + Baid = baid, + CompeteName = challengeCompeteInfo.Name, + CompeteDescribe = challengeCompeteInfo.Desc, + MaxParticipant = challengeCompeteInfo.MaxParticipant, + CreateTime = DateTime.Now, + ExpireTime = DateTime.Now.AddDays(challengeCompeteInfo.LastFor), + RequireTitle = challengeCompeteInfo.RequiredTitle, + Share = challengeCompeteInfo.ShareType, + CompeteTarget = challengeCompeteInfo.CompeteTargetType + }; + await context.AddAsync(challengeCompeteData); + foreach (var song in challengeCompeteInfo.challengeCompeteSongs) + { + ChallengeCompeteSongDatum challengeCompeteSongData = new() + { + CompId = challengeCompeteData.CompId, + SongId = song.SongId, + Difficulty = song.Difficulty, + SongOpt = PlaySettingConverter.PlaySettingToShort(song.PlaySetting) + }; + await context.AddAsync(challengeCompeteSongData); + } + ChallengeCompeteParticipantDatum participantDatum = new() + { + CompId = challengeCompeteData.CompId, + Baid = baid, + IsActive = false + }; + await context.AddAsync(participantDatum); + ChallengeCompeteParticipantDatum targetDatum = new() + { + CompId = challengeCompeteData.CompId, + Baid = targetBaid, + IsActive = false + }; + await context.AddAsync(targetDatum); + } + + public async Task AnswerChallenge(uint compId, uint baid, bool accept) + { + var challengeCompete = await context.ChallengeCompeteData.FindAsync(compId); + challengeCompete.ThrowIfNull($"Challenge not found for CompId {compId}!"); + + if (challengeCompete.Baid == baid) return false; + bool hasTarget = false; + foreach (var participant in challengeCompete.Participants) + { + if (participant.Baid == baid) hasTarget = true; + } + if (!hasTarget) return false; + + if (accept) + { + foreach (var participant in challengeCompete.Participants) + { + participant.IsActive = true; + context.Update(participant); + } + } + else + { + context.Remove(challengeCompete); + } + + return true; + } +} diff --git a/TaikoLocalServer/Services/Interfaces/IChallengeCompeteService.cs b/TaikoLocalServer/Services/Interfaces/IChallengeCompeteService.cs new file mode 100644 index 0000000..7e7d866 --- /dev/null +++ b/TaikoLocalServer/Services/Interfaces/IChallengeCompeteService.cs @@ -0,0 +1,18 @@ +namespace TaikoLocalServer.Services.Interfaces; + +public interface IChallengeCompeteService +{ + public bool HasChallengeCompete(uint baid); + + public List GetInProgressChallengeCompete(uint baid); + + public List GetAllChallengeCompete(); + + public Task CreateCompete(uint baid, ChallengeCompeteInfo challengeCompeteInfo); + + public Task ParticipateCompete(uint compId, uint baid); + + public Task CreateChallenge(uint baid, uint targetBaid, ChallengeCompeteInfo challengeCompeteInfo); + + public Task AnswerChallenge(uint compId, uint baid, bool accept); +}