diff --git a/TaikoLocalServer/Common/Constants.cs b/TaikoLocalServer/Common/Constants.cs index ca2e896..13ef74c 100644 --- a/TaikoLocalServer/Common/Constants.cs +++ b/TaikoLocalServer/Common/Constants.cs @@ -27,4 +27,12 @@ public static class Constants public const int MIN_DAN_ID = 1; public const int MAX_DAN_ID = 19; public const int GOT_DAN_BITS = MAX_DAN_ID * 4; + + public const int TONE_UID_MAX = 19; + public const int TITLE_UID_MAX = 814; + public const int COSTUME_FLAG_1_ARRAY_SIZE = 154; + public const int COSTUME_FLAG_2_ARRAY_SIZE = 140; + public const int COSTUME_FLAG_3_ARRAY_SIZE = 156; + public const int COSTUME_FLAG_4_ARRAY_SIZE = 58; + public const int COSTUME_FLAG_5_ARRAY_SIZE = 129; } \ No newline at end of file diff --git a/TaikoLocalServer/Controllers/Game/BaidController.cs b/TaikoLocalServer/Controllers/Game/BaidController.cs index b63d5b9..d7b05c3 100644 --- a/TaikoLocalServer/Controllers/Game/BaidController.cs +++ b/TaikoLocalServer/Controllers/Game/BaidController.cs @@ -1,5 +1,8 @@ -using System.Text.Json; +using System.Collections.Generic; +using System.Collections; +using System.Text.Json; using TaikoLocalServer.Services.Interfaces; +using Throw; namespace TaikoLocalServer.Controllers.Game; @@ -107,8 +110,59 @@ public class BaidController : BaseController costumeData = new List { 0, 0, 0, 0, 0 }; } - var costumeFlag = new byte[10]; - Array.Fill(costumeFlag, byte.MaxValue); + var costumeArrays = Array.Empty(); + try + { + costumeArrays = JsonSerializer.Deserialize(userData.CostumeFlgArray); + } + catch (JsonException e) + { + Logger.LogError(e, "Parsing costume flg json data failed"); + } + + // The only way to get a null is provide string "null" as input, + // which means database content need to be fixed, so better throw + costumeArrays.ThrowIfNull("Costume flg should never be null!"); + + var costumeFlg1 = new byte[Constants.COSTUME_FLAG_1_ARRAY_SIZE]; + var bitSet = new BitArray(Constants.COSTUME_FLAG_1_ARRAY_SIZE); + foreach (var costume in costumeArrays[0]) + { + bitSet.Set((int)costume, true); + } + bitSet.CopyTo(costumeFlg1, 0); + + var costumeFlg2 = new byte[Constants.COSTUME_FLAG_2_ARRAY_SIZE]; + bitSet = new BitArray(Constants.COSTUME_FLAG_2_ARRAY_SIZE); + foreach (var costume in costumeArrays[1]) + { + bitSet.Set((int)costume, true); + } + bitSet.CopyTo(costumeFlg2, 0); + + var costumeFlg3 = new byte[Constants.COSTUME_FLAG_3_ARRAY_SIZE]; + bitSet = new BitArray(Constants.COSTUME_FLAG_3_ARRAY_SIZE); + foreach (var costume in costumeArrays[2]) + { + bitSet.Set((int)costume, true); + } + bitSet.CopyTo(costumeFlg3, 0); + + var costumeFlg4 = new byte[Constants.COSTUME_FLAG_4_ARRAY_SIZE]; + bitSet = new BitArray(Constants.COSTUME_FLAG_4_ARRAY_SIZE); + foreach (var costume in costumeArrays[3]) + { + bitSet.Set((int)costume, true); + } + bitSet.CopyTo(costumeFlg4, 0); + + var costumeFlg5 = new byte[Constants.COSTUME_FLAG_5_ARRAY_SIZE]; + bitSet = new BitArray(Constants.COSTUME_FLAG_5_ARRAY_SIZE); + foreach (var costume in costumeArrays[4]) + { + bitSet.Set((int)costume, true); + } + bitSet.CopyTo(costumeFlg5, 0); var danData = await danScoreDatumService.GetDanScoreDatumByBaid(baid); @@ -145,11 +199,11 @@ public class BaidController : BaseController Costume4 = costumeData[3], Costume5 = costumeData[4] }, - CostumeFlg1 = costumeFlag, - CostumeFlg2 = costumeFlag, - CostumeFlg3 = costumeFlag, - CostumeFlg4 = costumeFlag, - CostumeFlg5 = costumeFlag, + CostumeFlg1 = costumeFlg1, + CostumeFlg2 = costumeFlg2, + CostumeFlg3 = costumeFlg3, + CostumeFlg4 = costumeFlg4, + CostumeFlg5 = costumeFlg5, LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT), IsDispDanOn = userData.DisplayDan, GotDanMax = maxDan, diff --git a/TaikoLocalServer/Controllers/Game/MyDonEntryController.cs b/TaikoLocalServer/Controllers/Game/MyDonEntryController.cs index e4ac874..9fd7e23 100644 --- a/TaikoLocalServer/Controllers/Game/MyDonEntryController.cs +++ b/TaikoLocalServer/Controllers/Game/MyDonEntryController.cs @@ -40,7 +40,10 @@ public class MyDonEntryController : BaseController ColorFace = 0, ColorBody = 1, ColorLimb = 3, - FavoriteSongsArray = "[]" + FavoriteSongsArray = "[]", + ToneFlgArray = "[]", + TitleFlgArray = "[]", + CostumeFlgArray = "[[],[],[],[],[]]" }; await userDatumService.InsertUserDatum(newUser); diff --git a/TaikoLocalServer/Controllers/Game/PlayResultController.cs b/TaikoLocalServer/Controllers/Game/PlayResultController.cs index 73a6e93..173ea16 100644 --- a/TaikoLocalServer/Controllers/Game/PlayResultController.cs +++ b/TaikoLocalServer/Controllers/Game/PlayResultController.cs @@ -1,6 +1,7 @@ using System.Buffers.Binary; using System.Globalization; using System.Text.Json; +using TaikoLocalServer.Entities; using TaikoLocalServer.Services.Interfaces; using Throw; @@ -191,6 +192,21 @@ public class PlayResultController : BaseController userdata.LastPlayDatetime = lastPlayDatetime; userdata.LastPlayMode = playResultData.PlayMode; + + var toneFlgData = JsonSerializer.Deserialize>(userdata.ToneFlgArray); + toneFlgData?.AddRange(playResultData.GetToneNoes ?? new uint[0]); + userdata.ToneFlgArray = JsonSerializer.Serialize(toneFlgData); + var titleFlgData = JsonSerializer.Deserialize>(userdata.TitleFlgArray); + titleFlgData?.AddRange(playResultData.GetTitleNoes ?? new uint[0]); + userdata.TitleFlgArray = JsonSerializer.Serialize(titleFlgData); + var costumeFlgData = JsonSerializer.Deserialize>>(userdata.CostumeFlgArray); + costumeFlgData?[0].AddRange(playResultData.GetCostumeNo1s ?? new uint[0]); + costumeFlgData?[1].AddRange(playResultData.GetCostumeNo2s ?? new uint[0]); + costumeFlgData?[2].AddRange(playResultData.GetCostumeNo3s ?? new uint[0]); + costumeFlgData?[3].AddRange(playResultData.GetCostumeNo4s ?? new uint[0]); + costumeFlgData?[4].AddRange(playResultData.GetCostumeNo5s ?? new uint[0]); + userdata.CostumeFlgArray = JsonSerializer.Serialize(costumeFlgData); + await userDatumService.UpdateUserDatum(userdata); } diff --git a/TaikoLocalServer/Controllers/Game/UserDataController.cs b/TaikoLocalServer/Controllers/Game/UserDataController.cs index e02f074..8db6b30 100644 --- a/TaikoLocalServer/Controllers/Game/UserDataController.cs +++ b/TaikoLocalServer/Controllers/Game/UserDataController.cs @@ -44,8 +44,51 @@ public class UserDataController : BaseController } bitSet.CopyTo(uraSongArray, 0); - var toneArray = new byte[16]; - Array.Fill(toneArray, byte.MaxValue); + var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid); + + var toneFlg = Array.Empty(); + try + { + toneFlg = JsonSerializer.Deserialize(userData.ToneFlgArray); + } + catch (JsonException e) + { + Logger.LogError(e, "Parsing tone flg json data failed"); + } + + // The only way to get a null is provide string "null" as input, + // which means database content need to be fixed, so better throw + toneFlg.ThrowIfNull("Tone flg should never be null!"); + + var toneArray = new byte[Constants.TONE_UID_MAX]; + bitSet = new BitArray(Constants.TONE_UID_MAX); + foreach (var tone in toneFlg) + { + bitSet.Set((int)tone, true); + } + bitSet.CopyTo(toneArray, 0); + + var titleFlg = Array.Empty(); + try + { + titleFlg = JsonSerializer.Deserialize(userData.TitleFlgArray); + } + catch (JsonException e) + { + Logger.LogError(e, "Parsing title flg json data failed"); + } + + // The only way to get a null is provide string "null" as input, + // which means database content need to be fixed, so better throw + titleFlg.ThrowIfNull("Title flg should never be null!"); + + var titleArray = new byte[Constants.TITLE_UID_MAX]; + bitSet = new BitArray(Constants.TITLE_UID_MAX); + foreach (var title in titleFlg) + { + bitSet.Set((int)title, true); + } + bitSet.CopyTo(titleArray, 0); var recentSongs = (await songPlayDatumService.GetSongPlayDatumByBaid(request.Baid)) .AsEnumerable() @@ -67,8 +110,6 @@ public class UserDataController : BaseController recentSongs = recentSet.ToArray(); - var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid); - var favoriteSongs = Array.Empty(); try { @@ -90,7 +131,7 @@ public class UserDataController : BaseController { Result = 1, ToneFlg = toneArray, - // TitleFlg = GZipBytesUtil.GetGZipBytes(new byte[100]), + TitleFlg = titleArray, ReleaseSongFlg = releaseSongArray, UraReleaseSongFlg = uraSongArray, DefaultOptionSetting = defaultOptions, diff --git a/TaikoLocalServer/Entities/UserDatum.cs b/TaikoLocalServer/Entities/UserDatum.cs index c37d2fd..89be015 100644 --- a/TaikoLocalServer/Entities/UserDatum.cs +++ b/TaikoLocalServer/Entities/UserDatum.cs @@ -7,6 +7,9 @@ public string Title { get; set; } = string.Empty; public uint TitlePlateId { get; set; } public string FavoriteSongsArray { get; set; } = string.Empty; + public string ToneFlgArray { get; set; } = string.Empty; + public string TitleFlgArray { get; set; } = string.Empty; + public string CostumeFlgArray { get; set; } = string.Empty; public short OptionSetting { get; set; } public int NotesPosition { get; set; } public bool IsVoiceOn { get; set; } diff --git a/TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.Designer.cs b/TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.Designer.cs new file mode 100644 index 0000000..11a52d9 --- /dev/null +++ b/TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.Designer.cs @@ -0,0 +1,341 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TaikoLocalServer.Context; + +#nullable disable + +namespace TaikoLocalServer.Migrations +{ + [DbContext(typeof(TaikoDbContext))] + [Migration("20220914054039_AddRewardFlgs")] + partial class AddRewardFlgs + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2"); + + modelBuilder.Entity("TaikoLocalServer.Entities.Card", b => + { + b.Property("AccessCode") + .HasColumnType("TEXT"); + + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.HasKey("AccessCode"); + + b.HasIndex(new[] { "Baid" }, "IX_Card_Baid") + .IsUnique(); + + b.ToTable("Card", (string)null); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("DanId") + .HasColumnType("INTEGER"); + + 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"); + + b.ToTable("DanScoreData"); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("DanId") + .HasColumnType("INTEGER"); + + 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", "SongNumber"); + + b.ToTable("DanStageScoreData"); + }); + + modelBuilder.Entity("TaikoLocalServer.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("TaikoLocalServer.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("TaikoLocalServer.Entities.UserDatum", b => + { + b.Property("Baid") + .HasColumnType("INTEGER"); + + b.Property("AchievementDisplayDifficulty") + .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("DisplayAchievement") + .HasColumnType("INTEGER"); + + b.Property("DisplayDan") + .HasColumnType("INTEGER"); + + b.Property("FavoriteSongsArray") + .IsRequired() + .HasColumnType("TEXT"); + + 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("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.HasKey("Baid"); + + b.ToTable("UserData"); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b => + { + b.HasOne("TaikoLocalServer.Entities.Card", "Ba") + .WithMany() + .HasForeignKey("Baid") + .HasPrincipalKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b => + { + b.HasOne("TaikoLocalServer.Entities.DanScoreDatum", "Parent") + .WithMany("DanStageScoreData") + .HasForeignKey("Baid", "DanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.SongBestDatum", b => + { + b.HasOne("TaikoLocalServer.Entities.Card", "Ba") + .WithMany() + .HasForeignKey("Baid") + .HasPrincipalKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.SongPlayDatum", b => + { + b.HasOne("TaikoLocalServer.Entities.Card", "Ba") + .WithMany() + .HasForeignKey("Baid") + .HasPrincipalKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.UserDatum", b => + { + b.HasOne("TaikoLocalServer.Entities.Card", "Ba") + .WithMany() + .HasForeignKey("Baid") + .HasPrincipalKey("Baid") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ba"); + }); + + modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b => + { + b.Navigation("DanStageScoreData"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.cs b/TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.cs new file mode 100644 index 0000000..455a432 --- /dev/null +++ b/TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TaikoLocalServer.Migrations +{ + /// + public partial class AddRewardFlgs : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CostumeFlgArray", + table: "UserData", + type: "TEXT", + nullable: false, + defaultValue: "[[],[],[],[],[]]"); + + migrationBuilder.AddColumn( + name: "TitleFlgArray", + table: "UserData", + type: "TEXT", + nullable: false, + defaultValue: "[]"); + + migrationBuilder.AddColumn( + name: "ToneFlgArray", + table: "UserData", + type: "TEXT", + nullable: false, + defaultValue: "[]"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CostumeFlgArray", + table: "UserData"); + + migrationBuilder.DropColumn( + name: "TitleFlgArray", + table: "UserData"); + + migrationBuilder.DropColumn( + name: "ToneFlgArray", + table: "UserData"); + } + } +} diff --git a/TaikoLocalServer/Migrations/TaikoDbContextModelSnapshot.cs b/TaikoLocalServer/Migrations/TaikoDbContextModelSnapshot.cs index b775c53..2dba260 100644 --- a/TaikoLocalServer/Migrations/TaikoDbContextModelSnapshot.cs +++ b/TaikoLocalServer/Migrations/TaikoDbContextModelSnapshot.cs @@ -57,7 +57,7 @@ namespace TaikoLocalServer.Migrations b.HasKey("Baid", "DanId"); - b.ToTable("DanScoreData", (string)null); + b.ToTable("DanScoreData"); }); modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b => @@ -97,7 +97,7 @@ namespace TaikoLocalServer.Migrations b.HasKey("Baid", "DanId", "SongNumber"); - b.ToTable("DanStageScoreData", (string)null); + b.ToTable("DanStageScoreData"); }); modelBuilder.Entity("TaikoLocalServer.Entities.SongBestDatum", b => @@ -125,7 +125,7 @@ namespace TaikoLocalServer.Migrations b.HasKey("Baid", "SongId", "Difficulty"); - b.ToTable("SongBestData", (string)null); + b.ToTable("SongBestData"); }); modelBuilder.Entity("TaikoLocalServer.Entities.SongPlayDatum", b => @@ -186,7 +186,7 @@ namespace TaikoLocalServer.Migrations b.HasIndex("Baid"); - b.ToTable("SongPlayData", (string)null); + b.ToTable("SongPlayData"); }); modelBuilder.Entity("TaikoLocalServer.Entities.UserDatum", b => @@ -210,6 +210,10 @@ namespace TaikoLocalServer.Migrations .IsRequired() .HasColumnType("TEXT"); + b.Property("CostumeFlgArray") + .IsRequired() + .HasColumnType("TEXT"); + b.Property("DisplayAchievement") .HasColumnType("INTEGER"); @@ -249,12 +253,20 @@ namespace TaikoLocalServer.Migrations .IsRequired() .HasColumnType("TEXT"); + b.Property("TitleFlgArray") + .IsRequired() + .HasColumnType("TEXT"); + b.Property("TitlePlateId") .HasColumnType("INTEGER"); + b.Property("ToneFlgArray") + .IsRequired() + .HasColumnType("TEXT"); + b.HasKey("Baid"); - b.ToTable("UserData", (string)null); + b.ToTable("UserData"); }); modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>