1
0
mirror of synced 2025-03-02 08:21:20 +01:00

Change Db Structure & Add OnlyPlayOnce & Add CompeteState & Compete Option can be null

This commit is contained in:
ptmaster 2024-09-11 22:17:17 +08:00
parent e545598c1c
commit d755d6edd5
11 changed files with 185 additions and 22 deletions

View File

@ -6,6 +6,7 @@ public partial class ChallengeCompeteDatum
{
public uint CompId { get; set; }
public CompeteModeType CompeteMode { get; set; } = CompeteModeType.None;
public CompeteState State { get; set; } = CompeteState.Normal;
public uint Baid { get; set; }
public string CompeteName { get; set; } = String.Empty;
public string CompeteDescribe { get; set; } = String.Empty;
@ -13,6 +14,7 @@ public partial class ChallengeCompeteDatum
public DateTime CreateTime { get; set; }
public DateTime ExpireTime { get; set; }
public uint RequireTitle { get; set; } = 0;
public bool OnlyPlayOnce { get; set; } = false;
public ShareType Share { get; set; } = ShareType.EveryOne;
public CompeteTargetType CompeteTarget { get; set; } = CompeteTargetType.EveryOne;
public List<ChallengeCompeteSongDatum> Songs { get; set; } = new();

View File

@ -7,7 +7,10 @@ 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 uint? Speed { get; set; } = null;
public bool? IsVanishOn { get; set; } = null;
public bool? IsInverseOn { get; set; } = null;
public RandomType? RandomType { get; set; } = null;
public List<ChallengeCompeteBestDatum> BestScores { get; set; } = new();
public virtual ChallengeCompeteDatum? ChallengeCompeteData { get; set; }
}

View File

@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace GameDatabase.Migrations
{
[DbContext(typeof(TaikoDbContext))]
[Migration("20240910100030_AddChallengeCompete")]
partial class AddChallengeCompete
[Migration("20240911133119_AddChallengeCompetion")]
partial class AddChallengeCompetion
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@ -182,12 +182,18 @@ namespace GameDatabase.Migrations
b.Property<uint>("MaxParticipant")
.HasColumnType("INTEGER");
b.Property<bool>("OnlyPlayOnce")
.HasColumnType("INTEGER");
b.Property<uint>("RequireTitle")
.HasColumnType("INTEGER");
b.Property<uint>("Share")
.HasColumnType("INTEGER");
b.Property<uint>("State")
.HasColumnType("INTEGER");
b.HasKey("CompId");
b.ToTable("ChallengeCompeteData");
@ -222,7 +228,16 @@ namespace GameDatabase.Migrations
b.Property<uint>("Difficulty")
.HasColumnType("INTEGER");
b.Property<short>("SongOpt")
b.Property<bool?>("IsInverseOn")
.HasColumnType("INTEGER");
b.Property<bool?>("IsVanishOn")
.HasColumnType("INTEGER");
b.Property<int?>("RandomType")
.HasColumnType("INTEGER");
b.Property<uint?>("Speed")
.HasColumnType("INTEGER");
b.HasKey("CompId", "SongId");

View File

@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace GameDatabase.Migrations
{
/// <inheritdoc />
public partial class AddChallengeCompete : Migration
public partial class AddChallengeCompetion : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
@ -18,6 +18,7 @@ namespace GameDatabase.Migrations
CompId = table.Column<uint>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
CompeteMode = table.Column<uint>(type: "INTEGER", nullable: false),
State = table.Column<uint>(type: "INTEGER", nullable: false),
Baid = table.Column<uint>(type: "INTEGER", nullable: false),
CompeteName = table.Column<string>(type: "TEXT", nullable: false),
CompeteDescribe = table.Column<string>(type: "TEXT", nullable: false),
@ -25,6 +26,7 @@ namespace GameDatabase.Migrations
CreateTime = table.Column<DateTime>(type: "datetime", nullable: false),
ExpireTime = table.Column<DateTime>(type: "datetime", nullable: false),
RequireTitle = table.Column<uint>(type: "INTEGER", nullable: false),
OnlyPlayOnce = table.Column<bool>(type: "INTEGER", nullable: false),
Share = table.Column<uint>(type: "INTEGER", nullable: false),
CompeteTarget = table.Column<uint>(type: "INTEGER", nullable: false)
},
@ -65,7 +67,10 @@ namespace GameDatabase.Migrations
CompId = table.Column<uint>(type: "INTEGER", nullable: false),
SongId = table.Column<uint>(type: "INTEGER", nullable: false),
Difficulty = table.Column<uint>(type: "INTEGER", nullable: false),
SongOpt = table.Column<short>(type: "INTEGER", nullable: false)
Speed = table.Column<uint>(type: "INTEGER", nullable: true),
IsVanishOn = table.Column<bool>(type: "INTEGER", nullable: true),
IsInverseOn = table.Column<bool>(type: "INTEGER", nullable: true),
RandomType = table.Column<int>(type: "INTEGER", nullable: true)
},
constraints: table =>
{

View File

@ -179,12 +179,18 @@ namespace TaikoLocalServer.Migrations
b.Property<uint>("MaxParticipant")
.HasColumnType("INTEGER");
b.Property<bool>("OnlyPlayOnce")
.HasColumnType("INTEGER");
b.Property<uint>("RequireTitle")
.HasColumnType("INTEGER");
b.Property<uint>("Share")
.HasColumnType("INTEGER");
b.Property<uint>("State")
.HasColumnType("INTEGER");
b.HasKey("CompId");
b.ToTable("ChallengeCompeteData");
@ -219,7 +225,16 @@ namespace TaikoLocalServer.Migrations
b.Property<uint>("Difficulty")
.HasColumnType("INTEGER");
b.Property<short>("SongOpt")
b.Property<bool?>("IsInverseOn")
.HasColumnType("INTEGER");
b.Property<bool?>("IsVanishOn")
.HasColumnType("INTEGER");
b.Property<int?>("RandomType")
.HasColumnType("INTEGER");
b.Property<uint?>("Speed")
.HasColumnType("INTEGER");
b.HasKey("CompId", "SongId");

View File

@ -0,0 +1,10 @@
namespace SharedProject.Enums;
public enum CompeteState : uint
{
Disabled = 0,
Normal = 1, // In Progress
Waiting = 2, // Waiting for Answer
Finished = 3, // Finished Once Compete
Rejected = 4 // Rejected Challenge
}

View File

@ -11,10 +11,60 @@ public class ChallengeCompeteManageController(IChallengeCompeteService challenge
public ActionResult<List<ChallengeCompeteDatum>> GetAllChallengeCompete()
{
List<ChallengeCompeteDatum> datum = challengeCompeteService.GetAllChallengeCompete();
foreach (var data in datum)
{
foreach (var participant in data.Participants)
{
participant.ChallengeCompeteData = null;
}
foreach (var song in data.Songs)
{
song.ChallengeCompeteData = null;
foreach (var bestScore in song.BestScores)
{
bestScore.ChallengeCompeteSongData = null;
}
}
}
return Ok(datum);
}
[HttpGet("test/{mode}")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public ActionResult<List<ChallengeCompeteDatum>> testCreateCompete(uint mode)
{
ChallengeCompeteInfo info = new()
{
Name = "测试数据",
Desc = "测试数据描述",
CompeteMode = (CompeteModeType)mode,
MaxParticipant = 100,
LastFor = 365,
RequiredTitle = 0,
ShareType = ShareType.EveryOne,
CompeteTargetType = CompeteTargetType.EveryOne,
challengeCompeteSongs = [
new() {
SongId = 1,
Difficulty = Difficulty.Oni,
RandomType = RandomType.Messy
},
new() {
SongId = 2,
Difficulty = Difficulty.Oni,
},
new() {
SongId = 3,
Difficulty = Difficulty.Oni,
},
]
};
challengeCompeteService.CreateCompete(1, info);
return NoContent();
}
[HttpPost("{baid}/createCompete")]
[ServiceFilter(typeof(AuthorizeIfRequiredAttribute))]
public async Task<IActionResult> CreateCompete(uint baid, ChallengeCompeteInfo challengeCompeteInfo)

View File

@ -1,4 +1,5 @@
using GameDatabase.Context;
using SharedProject.Utils;
using System.Buffers.Binary;
namespace TaikoLocalServer.Handlers;
@ -26,7 +27,14 @@ public class ChallengeCompeteQueryHandler(TaikoDbContext context, IChallengeComp
foreach (var song in compete.Songs)
{
var songOptions = new byte[2];
BinaryPrimitives.WriteInt16LittleEndian(songOptions, song.SongOpt);
short songOpt = PlaySettingConverter.PlaySettingToShort(new()
{
Speed = song.Speed != null ? (uint)song.Speed : 0,
IsVanishOn = song.IsVanishOn != null ? (bool)song.IsVanishOn : false,
IsInverseOn = song.IsInverseOn != null ? (bool)song.IsInverseOn : false,
RandomType = song.RandomType != null ? (RandomType)song.RandomType : RandomType.None,
});
BinaryPrimitives.WriteInt16LittleEndian(songOptions, songOpt);
uint myHighScore = 0;
foreach (var bestScore in song.BestScores)

View File

@ -16,6 +16,9 @@ public class ChallengeCompeteInfo
[JsonPropertyName("maxParticipant")]
public uint MaxParticipant { get; set; }
[JsonPropertyName("onlyPlayOnce")]
public bool OnlyPlayOnce { get; set; }
[JsonPropertyName("lastFor")]
public uint LastFor { get; set; }

View File

@ -11,6 +11,15 @@ public class ChallengeCompeteSongInfo
[JsonPropertyName("difficulty")]
public Difficulty Difficulty { get; set; }
[JsonPropertyName("playSetting")]
public PlaySetting PlaySetting { get; set; } = new();
[JsonPropertyName("speed")]
public uint? Speed { get; set; } = null;
[JsonPropertyName("isVanishOn")]
public bool? IsVanishOn { get; set; } = null;
[JsonPropertyName("isInverseOn")]
public bool? IsInverseOn { get; set; } = null;
[JsonPropertyName("randomType")]
public RandomType? RandomType { get; set; } = null;
}

View File

@ -1,5 +1,6 @@

using GameDatabase.Context;
using SharedProject.Models;
using SharedProject.Utils;
using Throw;
@ -17,10 +18,17 @@ public class ChallengeCompeteService : IChallengeCompeteService
{
return context.ChallengeCompeteData
.Include(c => c.Participants)
.Include(c => c.Songs)
.ThenInclude(s => s.BestScores)
.Any(data =>
data.State == CompeteState.Normal &&
data.CreateTime < DateTime.Now &&
data.ExpireTime > DateTime.Now &&
data.Participants.Any(participant => participant.Baid == baid && participant.IsActive)
data.Participants.Any(participant => participant.Baid == baid && participant.IsActive) &&
(
// Only Play Once need there is no Score for current Compete
!data.OnlyPlayOnce || data.Songs.Any(song => !song.BestScores.Any(s => s.Baid == baid))
)
);
}
@ -28,16 +36,27 @@ public class ChallengeCompeteService : IChallengeCompeteService
{
return context.ChallengeCompeteData
.Include(c => c.Participants)
.Include(c => c.Songs)
.ThenInclude(s => s.BestScores)
.Where(data =>
data.State == CompeteState.Normal &&
data.CreateTime < DateTime.Now &&
data.ExpireTime > DateTime.Now &&
data.Participants.Any(participant => participant.Baid == baid)
data.Participants.Any(participant => participant.Baid == baid && participant.IsActive) &&
(
// Only Play Once need there is no Score for current Compete
!data.OnlyPlayOnce || data.Songs.Any(song => !song.BestScores.Any(s => s.Baid == baid))
)
).ToList();
}
public List<ChallengeCompeteDatum> GetAllChallengeCompete()
{
return context.ChallengeCompeteData.Where(data => true).ToList();
return context.ChallengeCompeteData
.Include(c => c.Participants)
.Include(c => c.Songs)
.ThenInclude(s => s.BestScores)
.Where(data => true).ToList();
}
public async Task CreateCompete(uint baid, ChallengeCompeteInfo challengeCompeteInfo)
@ -46,10 +65,12 @@ public class ChallengeCompeteService : IChallengeCompeteService
{
CompId = context.ChallengeCompeteData.Any() ? context.ChallengeCompeteData.AsEnumerable().Max(c => c.CompId) + 1 : 1,
CompeteMode = challengeCompeteInfo.CompeteMode,
State = CompeteState.Normal,
Baid = baid,
CompeteName = challengeCompeteInfo.Name,
CompeteDescribe = challengeCompeteInfo.Desc,
MaxParticipant = challengeCompeteInfo.MaxParticipant,
OnlyPlayOnce = challengeCompeteInfo.OnlyPlayOnce,
CreateTime = DateTime.Now,
ExpireTime = DateTime.Now.AddDays(challengeCompeteInfo.LastFor),
RequireTitle = challengeCompeteInfo.RequiredTitle,
@ -64,7 +85,10 @@ public class ChallengeCompeteService : IChallengeCompeteService
CompId = challengeCompeteData.CompId,
SongId = song.SongId,
Difficulty = song.Difficulty,
SongOpt = PlaySettingConverter.PlaySettingToShort(song.PlaySetting)
Speed = song.Speed,
IsInverseOn = song.IsInverseOn,
IsVanishOn = song.IsVanishOn,
RandomType = song.RandomType
};
await context.AddAsync(challengeCompeteSongData);
}
@ -75,6 +99,7 @@ public class ChallengeCompeteService : IChallengeCompeteService
IsActive = true
};
await context.AddAsync(participantDatum);
await context.SaveChangesAsync();
}
public async Task<bool> ParticipateCompete(uint compId, uint baid)
@ -95,6 +120,7 @@ public class ChallengeCompeteService : IChallengeCompeteService
IsActive = true,
};
await context.AddAsync(participantDatum);
await context.SaveChangesAsync();
return true;
}
@ -104,11 +130,13 @@ public class ChallengeCompeteService : IChallengeCompeteService
ChallengeCompeteDatum challengeCompeteData = new()
{
CompId = context.ChallengeCompeteData.Any() ? context.ChallengeCompeteData.AsEnumerable().Max(c => c.CompId) + 1 : 1,
CompeteMode = challengeCompeteInfo.CompeteMode,
CompeteMode = CompeteModeType.Chanllenge,
State = CompeteState.Waiting,
Baid = baid,
CompeteName = challengeCompeteInfo.Name,
CompeteDescribe = challengeCompeteInfo.Desc,
MaxParticipant = challengeCompeteInfo.MaxParticipant,
MaxParticipant = 2,
OnlyPlayOnce = challengeCompeteInfo.OnlyPlayOnce,
CreateTime = DateTime.Now,
ExpireTime = DateTime.Now.AddDays(challengeCompeteInfo.LastFor),
RequireTitle = challengeCompeteInfo.RequiredTitle,
@ -123,7 +151,10 @@ public class ChallengeCompeteService : IChallengeCompeteService
CompId = challengeCompeteData.CompId,
SongId = song.SongId,
Difficulty = song.Difficulty,
SongOpt = PlaySettingConverter.PlaySettingToShort(song.PlaySetting)
Speed = song.Speed,
IsInverseOn = song.IsInverseOn,
IsVanishOn = song.IsVanishOn,
RandomType = song.RandomType
};
await context.AddAsync(challengeCompeteSongData);
}
@ -141,6 +172,7 @@ public class ChallengeCompeteService : IChallengeCompeteService
IsActive = false
};
await context.AddAsync(targetDatum);
await context.SaveChangesAsync();
}
public async Task<bool> AnswerChallenge(uint compId, uint baid, bool accept)
@ -158,6 +190,7 @@ public class ChallengeCompeteService : IChallengeCompeteService
if (accept)
{
challengeCompete.State = CompeteState.Normal;
foreach (var participant in challengeCompete.Participants)
{
participant.IsActive = true;
@ -166,8 +199,10 @@ public class ChallengeCompeteService : IChallengeCompeteService
}
else
{
context.Remove(challengeCompete);
challengeCompete.State = CompeteState.Rejected;
}
context.Update(challengeCompete);
await context.SaveChangesAsync();
return true;
}
@ -176,15 +211,22 @@ public class ChallengeCompeteService : IChallengeCompeteService
{
List<ChallengeCompeteDatum> challengeCompetes = context.ChallengeCompeteData
.Include(e => e.Songs)
.ThenInclude(s => s.BestScores)
.Include(e => e.Participants)
.Where(e => e.CreateTime < DateTime.Now && DateTime.Now < e.ExpireTime)
.Where(e => e.Participants.Any(d => d.Baid == baid && d.IsActive))
.Where(e => e.Songs.Any(d => d.SongId == playData.SongId && d.SongOpt == option))
.Where(e => e.Songs.Any(d => d.SongId == playData.SongId && d.Difficulty == playData.Difficulty))
.Where(e => !e.OnlyPlayOnce || e.Songs.Any(song => !song.BestScores.Any(s => s.Baid == baid)))
.ToList();
PlaySetting setting = PlaySettingConverter.ShortToPlaySetting(option);
foreach (var challengeCompete in challengeCompetes)
{
ChallengeCompeteSongDatum? song = challengeCompete.Songs.Find(e => e.SongId == playData.SongId);
if (song == null || song.Difficulty != playData.Difficulty) continue;
ChallengeCompeteSongDatum? song = challengeCompete.Songs.Find(e => e.SongId == playData.SongId && e.Difficulty == playData.Difficulty);
if (song == null) continue;
if (song.Speed != null && song.Speed != setting.Speed) continue;
if (song.IsVanishOn != null && song.IsVanishOn != setting.IsVanishOn) continue;
if (song.IsInverseOn != null && song.IsInverseOn != setting.IsInverseOn) continue;
if (song.RandomType != null && song.RandomType != setting.RandomType) continue;
ChallengeCompeteBestDatum? bestScore = song.BestScores.Find(e => e.Baid == baid);
if (bestScore == null)
@ -208,7 +250,7 @@ public class ChallengeCompeteService : IChallengeCompeteService
Skipped = playData.Skipped
});
}
else if (bestScore.Score < playData.Score)
else if (!challengeCompete.OnlyPlayOnce && bestScore.Score < playData.Score)
{
bestScore.Crown = playData.Crown;
bestScore.Score = playData.Score;
@ -224,5 +266,6 @@ public class ChallengeCompeteService : IChallengeCompeteService
context.Update(bestScore);
}
}
await context.AddRangeAsync();
}
}