Merge branch 'Refactor' into new-songs-ui
This commit is contained in:
commit
88e6c70e13
@ -4,34 +4,42 @@ namespace GameDatabase.Entities;
|
||||
|
||||
public partial class UserDatum
|
||||
{
|
||||
public uint Baid { get; set; }
|
||||
public string MyDonName { get; set; } = string.Empty;
|
||||
public uint MyDonNameLanguage { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public uint TitlePlateId { get; set; }
|
||||
public string FavoriteSongsArray { get; set; } = "[]";
|
||||
public string ToneFlgArray { get; set; } = "[]";
|
||||
public string TitleFlgArray { get; set; } = "[]";
|
||||
public string CostumeFlgArray { get; set; } = "[[],[],[],[],[]]";
|
||||
public string GenericInfoFlgArray { get; set; } = "[]";
|
||||
public short OptionSetting { get; set; }
|
||||
public int NotesPosition { get; set; }
|
||||
public bool IsVoiceOn { get; set; }
|
||||
public bool IsSkipOn { get; set; }
|
||||
public string DifficultyPlayedArray { get; set; } = "[]";
|
||||
public string DifficultySettingArray { get; set; } = "[]";
|
||||
public uint SelectedToneId { get; set; }
|
||||
public DateTime LastPlayDatetime { get; set; }
|
||||
public uint LastPlayMode { get; set; }
|
||||
public uint ColorBody { get; set; }
|
||||
public uint ColorFace { get; set; }
|
||||
public uint ColorLimb { get; set; }
|
||||
public string CostumeData { get; set; } = "[]";
|
||||
public bool DisplayDan { get; set; }
|
||||
public bool DisplayAchievement { get; set; }
|
||||
public Difficulty AchievementDisplayDifficulty { get; set; }
|
||||
public int AiWinCount { get; set; }
|
||||
public List<Token> Tokens { get; set; } = new();
|
||||
public string UnlockedSongIdList { get; set; } = "[]";
|
||||
public bool IsAdmin { get; set; }
|
||||
public uint Baid { get; set; }
|
||||
public string MyDonName { get; set; } = string.Empty;
|
||||
public uint MyDonNameLanguage { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public uint TitlePlateId { get; set; }
|
||||
public List<uint> FavoriteSongsArray { get; set; } = [];
|
||||
public List<uint> ToneFlgArray { get; set; } = [0];
|
||||
public List<uint> TitleFlgArray { get; set; } = [];
|
||||
public string CostumeFlgArray { get; set; } = "[[],[],[],[],[]]";
|
||||
public List<uint> UnlockedKigurumi { get; set; } = [0];
|
||||
public List<uint> UnlockedHead { get; set; } = [0];
|
||||
public List<uint> UnlockedBody { get; set; }= [0];
|
||||
public List<uint> UnlockedFace { get; set; }= [0];
|
||||
public List<uint> UnlockedPuchi{ get; set; }= [0];
|
||||
public uint[] GenericInfoFlgArray { get; set; } = Array.Empty<uint>();
|
||||
public short OptionSetting { get; set; }
|
||||
public int NotesPosition { get; set; }
|
||||
public bool IsVoiceOn { get; set; }
|
||||
public bool IsSkipOn { get; set; }
|
||||
// TODO: Split into separate fields
|
||||
public string DifficultyPlayedArray { get; set; } = "[]";
|
||||
// TODO: Split into separate fields
|
||||
public string DifficultySettingArray { get; set; } = "[]";
|
||||
public uint SelectedToneId { get; set; }
|
||||
public DateTime LastPlayDatetime { get; set; }
|
||||
public uint LastPlayMode { get; set; }
|
||||
public uint ColorBody { get; set; }
|
||||
public uint ColorFace { get; set; }
|
||||
public uint ColorLimb { get; set; }
|
||||
// TODO: Split into separate fields
|
||||
public string CostumeData { get; set; } = "[]";
|
||||
public bool DisplayDan { get; set; }
|
||||
public bool DisplayAchievement { get; set; }
|
||||
public Difficulty AchievementDisplayDifficulty { get; set; }
|
||||
public int AiWinCount { get; set; }
|
||||
public List<Token> Tokens { get; set; } = new();
|
||||
public List<uint> UnlockedSongIdList { get; set; } = [];
|
||||
public bool IsAdmin { get; set; }
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>11</LangVersion>
|
||||
<LangVersion>12</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
539
GameDatabase/Migrations/20240316064531_SplitCostumeUnlocks.Designer.cs
generated
Normal file
539
GameDatabase/Migrations/20240316064531_SplitCostumeUnlocks.Designer.cs
generated
Normal file
@ -0,0 +1,539 @@
|
||||
// <auto-generated />
|
||||
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("20240316064531_SplitCostumeUnlocks")]
|
||||
partial class SplitCostumeUnlocks
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0-rc.2.23480.1");
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.AiScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty");
|
||||
|
||||
b.ToTable("AiScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.AiSectionScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SectionIndex")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Crown")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DrumrollCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("GoodCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("MissCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("OkCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Score")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty", "SectionIndex");
|
||||
|
||||
b.ToTable("AiSectionScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.Card", b =>
|
||||
{
|
||||
b.Property<string>("AccessCode")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("AccessCode");
|
||||
|
||||
b.HasIndex("Baid");
|
||||
|
||||
b.ToTable("Card", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.Credential", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Baid");
|
||||
|
||||
b.ToTable("Credential", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.DanScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DanId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DanType")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.Property<uint>("ArrivalSongCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ClearState")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0u);
|
||||
|
||||
b.Property<uint>("ComboCountTotal")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SoulGaugeTotal")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "DanId", "DanType");
|
||||
|
||||
b.ToTable("DanScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.DanStageScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DanId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DanType")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(1);
|
||||
|
||||
b.Property<uint>("SongNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("BadCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ComboCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DrumrollCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("GoodCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("HighScore")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("OkCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("PlayScore")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("TotalHitCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "DanId", "DanType", "SongNumber");
|
||||
|
||||
b.ToTable("DanStageScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.SongBestDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("BestCrown")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("BestRate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("BestScore")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("BestScoreRank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty");
|
||||
|
||||
b.ToTable("SongBestData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.SongPlayDatum", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ComboCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Crown")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DrumrollCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("GoodCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("HitCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("MissCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("OkCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("PlayTime")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
b.Property<uint>("Score")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ScoreRank")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ScoreRate")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Skipped")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Baid");
|
||||
|
||||
b.ToTable("SongPlayData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.Token", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "Id");
|
||||
|
||||
b.ToTable("Tokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.UserDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("AchievementDisplayDifficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AiWinCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ColorBody")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ColorFace")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ColorLimb")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CostumeData")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CostumeFlgArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DifficultyPlayedArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DifficultySettingArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DisplayAchievement")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DisplayDan")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FavoriteSongsArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("GenericInfoFlgArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsAdmin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsSkipOn")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsVoiceOn")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastPlayDatetime")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
b.Property<uint>("LastPlayMode")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("MyDonName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("MyDonNameLanguage")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("NotesPosition")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<short>("OptionSetting")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SelectedToneId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TitleFlgArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("TitlePlateId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ToneFlgArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedBody")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedFace")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedHead")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedKigurumi")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedPuchi")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("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.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.DanScoreDatum", b =>
|
||||
{
|
||||
b.Navigation("DanStageScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GameDatabase.Entities.UserDatum", b =>
|
||||
{
|
||||
b.Navigation("Tokens");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
138
GameDatabase/Migrations/20240316064531_SplitCostumeUnlocks.cs
Normal file
138
GameDatabase/Migrations/20240316064531_SplitCostumeUnlocks.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace GameDatabase.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SplitCostumeUnlocks : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "UnlockedBody",
|
||||
table: "UserData",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "UnlockedFace",
|
||||
table: "UserData",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "UnlockedHead",
|
||||
table: "UserData",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "UnlockedKigurumi",
|
||||
table: "UserData",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "UnlockedPuchi",
|
||||
table: "UserData",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
// Split the costumeflgarray into separate fields
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE UserData
|
||||
SET UnlockedKigurumi = json_extract(CostumeFlgArray, '$[0]'),
|
||||
UnlockedHead = json_extract(CostumeFlgArray, '$[1]'),
|
||||
UnlockedBody = json_extract(CostumeFlgArray, '$[2]'),
|
||||
UnlockedFace = json_extract(CostumeFlgArray, '$[3]'),
|
||||
UnlockedPuchi = json_extract(CostumeFlgArray, '$[4]')");
|
||||
// Deduplicate values
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE UserData
|
||||
SET UnlockedKigurumi = (
|
||||
SELECT json_group_array(DISTINCT value)
|
||||
FROM (
|
||||
SELECT value
|
||||
FROM UserData AS ud
|
||||
CROSS JOIN json_each(ud.UnlockedKigurumi)
|
||||
WHERE ud.Baid = UserData.Baid
|
||||
)
|
||||
)");
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE UserData
|
||||
SET UnlockedHead = (
|
||||
SELECT json_group_array(DISTINCT value)
|
||||
FROM (
|
||||
SELECT value
|
||||
FROM UserData AS ud
|
||||
CROSS JOIN json_each(ud.UnlockedHead)
|
||||
WHERE ud.Baid = UserData.Baid
|
||||
)
|
||||
)");
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE UserData
|
||||
SET UnlockedBody = (
|
||||
SELECT json_group_array(DISTINCT value)
|
||||
FROM (
|
||||
SELECT value
|
||||
FROM UserData AS ud
|
||||
CROSS JOIN json_each(ud.UnlockedBody)
|
||||
WHERE ud.Baid = UserData.Baid
|
||||
)
|
||||
)");
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE UserData
|
||||
SET UnlockedFace = (
|
||||
SELECT json_group_array(DISTINCT value)
|
||||
FROM (
|
||||
SELECT value
|
||||
FROM UserData AS ud
|
||||
CROSS JOIN json_each(ud.UnlockedFace)
|
||||
WHERE ud.Baid = UserData.Baid
|
||||
)
|
||||
)");
|
||||
migrationBuilder.Sql(@"
|
||||
UPDATE UserData
|
||||
SET UnlockedPuchi = (
|
||||
SELECT json_group_array(DISTINCT value)
|
||||
FROM (
|
||||
SELECT value
|
||||
FROM UserData AS ud
|
||||
CROSS JOIN json_each(ud.UnlockedPuchi)
|
||||
WHERE ud.Baid = UserData.Baid
|
||||
)
|
||||
)");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UnlockedBody",
|
||||
table: "UserData");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UnlockedFace",
|
||||
table: "UserData");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UnlockedHead",
|
||||
table: "UserData");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UnlockedKigurumi",
|
||||
table: "UserData");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UnlockedPuchi",
|
||||
table: "UserData");
|
||||
}
|
||||
}
|
||||
}
|
@ -388,6 +388,26 @@ namespace TaikoLocalServer.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedBody")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedFace")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedHead")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedKigurumi")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedPuchi")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UnlockedSongIdList")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
10
SharedProject/Enums/NameLanguage.cs
Normal file
10
SharedProject/Enums/NameLanguage.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace SharedProject.Enums;
|
||||
|
||||
public enum NameLanguage : uint
|
||||
{
|
||||
Japanese = 0,
|
||||
English = 1,
|
||||
Korean = 2,
|
||||
ChineseTraditional = 3,
|
||||
ChineseSimplified = 4,
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace SharedProject.Models;
|
||||
|
||||
public class DanData
|
||||
public class DanData : IVerupNo
|
||||
{
|
||||
[JsonPropertyName("danId")]
|
||||
public uint DanId { get; set; }
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace SharedProject.Models;
|
||||
|
||||
public class EventFolderData
|
||||
public class EventFolderData : IVerupNo
|
||||
{
|
||||
[JsonPropertyName("folderId")]
|
||||
public uint FolderId { get; set; }
|
||||
@ -14,7 +14,7 @@ public class EventFolderData
|
||||
public uint Priority { get; set; }
|
||||
|
||||
[JsonPropertyName("songNo")]
|
||||
public uint[]? SongNo { get; set; }
|
||||
public uint[]? SongNoes { get; set; }
|
||||
|
||||
[JsonPropertyName("parentFolderId")]
|
||||
public uint ParentFolderId { get; set; }
|
||||
|
6
SharedProject/Models/IVerupNo.cs
Normal file
6
SharedProject/Models/IVerupNo.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace SharedProject.Models;
|
||||
|
||||
public interface IVerupNo
|
||||
{
|
||||
public uint VerupNo { get; set; }
|
||||
}
|
@ -6,5 +6,6 @@ public class ShopFolderData
|
||||
{
|
||||
[JsonPropertyName("songNo")] public uint SongNo { get; set; }
|
||||
|
||||
public uint Type { get; set; }
|
||||
[JsonPropertyName("price")] public uint Price { get; set; }
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace SharedProject.Models;
|
||||
|
||||
public class SongIntroductionData
|
||||
public class SongIntroductionData : IVerupNo
|
||||
{
|
||||
[JsonPropertyName("setId")]
|
||||
public uint SetId { get; set; }
|
||||
@ -14,5 +14,5 @@ public class SongIntroductionData
|
||||
public uint MainSongNo { get; set; }
|
||||
|
||||
[JsonPropertyName("subSongNo")]
|
||||
public uint[]? SubSongNo { get; set; }
|
||||
public uint[]? SubSongNoes { get; set; }
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Collections.Specialized;
|
||||
using SharedProject.Enums;
|
||||
using SharedProject.Models;
|
||||
using Throw;
|
||||
|
@ -16,4 +16,13 @@ public static class Constants
|
||||
public const string DON_COS_REWARD_BASE_NAME = "don_cos_reward";
|
||||
public const string SHOUGOU_BASE_NAME = "shougou";
|
||||
public const string NEIRO_BASE_NAME = "neiro";
|
||||
|
||||
public const uint DAN_VERUP_MASTER_TYPE = 101;
|
||||
public const uint GAIDEN_VERUP_MASTER_TYPE = 102;
|
||||
public const uint FOLDER_VERUP_MASTER_TYPE = 103;
|
||||
public const uint INTRO_VERUP_MASTER_TYPE = 105;
|
||||
|
||||
public const uint FUNCTION_ID_DANI_FOLDER_AVAILABLE = 1;
|
||||
public const uint FUNCTION_ID_DANI_AVAILABLE = 2;
|
||||
public const uint FUNCTION_ID_AI_BATTLE_AVAILABLE = 3;
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using GameDatabase.Entities;
|
||||
using SharedProject.Utils;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Common.Utils;
|
||||
|
||||
@ -21,4 +22,21 @@ public static class Extensions
|
||||
datum.MissCount = ValueHelpers.Min(sectionData.NgCnt, datum.MissCount);
|
||||
datum.DrumrollCount = ValueHelpers.Max(sectionData.PoundCnt, datum.DrumrollCount);
|
||||
}
|
||||
|
||||
public static void UpdateBest(this AiSectionScoreDatum datum,CommonPlayResultData.AiStageSectionData sectionData)
|
||||
{
|
||||
var crown = (CrownType)sectionData.Crown;
|
||||
if (crown == CrownType.Gold && sectionData.OkCnt == 0)
|
||||
{
|
||||
crown = CrownType.Dondaful;
|
||||
}
|
||||
|
||||
datum.IsWin = sectionData.IsWin ? sectionData.IsWin : datum.IsWin;
|
||||
datum.Crown = ValueHelpers.Max(crown, datum.Crown);
|
||||
datum.Score = ValueHelpers.Max(sectionData.Score, datum.Score);
|
||||
datum.GoodCount = ValueHelpers.Max(sectionData.GoodCnt, datum.GoodCount);
|
||||
datum.OkCount = ValueHelpers.Min(sectionData.OkCnt, datum.OkCount);
|
||||
datum.MissCount = ValueHelpers.Min(sectionData.NgCnt, datum.MissCount);
|
||||
datum.DrumrollCount = ValueHelpers.Max(sectionData.PoundCnt, datum.DrumrollCount);
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using System.Runtime.InteropServices;
|
||||
using GameDatabase.Entities;
|
||||
|
@ -51,27 +51,4 @@ public static class JsonHelper
|
||||
|
||||
return costumeData;
|
||||
}
|
||||
|
||||
public static List<List<uint>> GetCostumeUnlockDataFromUserData(UserDatum userData, ILogger logger)
|
||||
{
|
||||
var costumeUnlockData = new List<List<uint>> { new(), new(), new(), new(), new() };
|
||||
try
|
||||
{
|
||||
costumeUnlockData = JsonSerializer.Deserialize<List<List<uint>>>(userData.CostumeFlgArray);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
logger.LogError(e, "Parsing costume json data failed");
|
||||
}
|
||||
|
||||
if (costumeUnlockData != null && costumeUnlockData.Count >= 5)
|
||||
{
|
||||
return costumeUnlockData;
|
||||
}
|
||||
|
||||
logger.LogWarning("Costume unlock data is null or count less than 5!");
|
||||
costumeUnlockData = new List<List<uint>> { new(), new(), new(), new(), new() };
|
||||
|
||||
return costumeUnlockData;
|
||||
}
|
||||
}
|
@ -16,6 +16,9 @@
|
||||
"GameServer2": {
|
||||
"Url": "https://0.0.0.0:54431"
|
||||
},
|
||||
"GameServerCN": {
|
||||
"Url": "https://0.0.0.0:57402"
|
||||
},
|
||||
"GarmcServer": {
|
||||
"Url": "https://0.0.0.0:443"
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using SharedProject.Models;
|
||||
using SharedProject.Utils;
|
||||
using System.Text.Json;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
@ -31,9 +30,10 @@ public class UserSettingsController : BaseController<UserSettingsController>
|
||||
|
||||
var costumeData = JsonHelper.GetCostumeDataFromUserData(user, Logger);
|
||||
|
||||
var costumeUnlockData = JsonHelper.GetCostumeUnlockDataFromUserData(user, Logger);
|
||||
List<List<uint>> costumeUnlockData =
|
||||
[user.UnlockedKigurumi, user.UnlockedHead, user.UnlockedBody, user.UnlockedFace, user.UnlockedPuchi];
|
||||
|
||||
var unlockedTitle = JsonHelper.GetUIntArrayFromJson(user.TitleFlgArray, 0, Logger, nameof(user.TitleFlgArray))
|
||||
var unlockedTitle = user.TitleFlgArray
|
||||
.ToList();
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
@ -125,19 +125,10 @@ public class UserSettingsController : BaseController<UserSettingsController>
|
||||
user.CostumeData = JsonSerializer.Serialize(costumes);
|
||||
|
||||
// If a locked tone is selected, unlock it
|
||||
uint[] toneFlg = { 0u };
|
||||
try
|
||||
{
|
||||
toneFlg = JsonSerializer.Deserialize<uint[]>(user.ToneFlgArray)!;
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing tone flg json data failed");
|
||||
}
|
||||
toneFlg.ThrowIfNull("Tone flg should never be null!");
|
||||
toneFlg = toneFlg.Append(0u).Append(userSetting.ToneId).Distinct().ToArray();
|
||||
var toneFlg = user.ToneFlgArray;
|
||||
toneFlg = toneFlg.Append(0u).Append(userSetting.ToneId).Distinct().ToList();
|
||||
|
||||
user.ToneFlgArray = JsonSerializer.Serialize(toneFlg);
|
||||
user.ToneFlgArray = toneFlg;
|
||||
|
||||
await userDatumService.UpdateUserDatum(user);
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
using SharedProject.Models.Requests;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
|
@ -3,6 +3,10 @@
|
||||
public abstract class BaseController<T> : ControllerBase where T : BaseController<T>
|
||||
{
|
||||
private ILogger<T>? logger;
|
||||
|
||||
private ISender? mediator;
|
||||
|
||||
protected ISender Mediator => (mediator ??= HttpContext.RequestServices.GetService<ISender>()) ?? throw new InvalidOperationException();
|
||||
|
||||
protected ILogger<T> Logger => (logger ??= HttpContext.RequestServices.GetService<ILogger<T>>()) ?? throw new InvalidOperationException();
|
||||
}
|
@ -1,49 +1,36 @@
|
||||
using System.Text.Json;
|
||||
using GameDatabase.Entities;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using AddTokenCountRequestMapper = TaikoLocalServer.Mappers.AddTokenCountRequestMapper;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/addtokencount_7547j3o4.php")]
|
||||
[ApiController]
|
||||
public class AddTokenCountController : BaseController<AddTokenCountController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
public AddTokenCountController(IUserDatumService userDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/addtokencount_7547j3o4.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> AddTokenCount([FromBody] AddTokenCountRequest request)
|
||||
{
|
||||
Logger.LogInformation("AddTokenCount request : {Request}", request.Stringify());
|
||||
Logger.LogInformation("[3906] AddTokenCount request : {Request}", request.Stringify());
|
||||
|
||||
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
var command = new AddTokenCountCommand(AddTokenCountRequestMapper.Map(request));
|
||||
await Mediator.Send(command);
|
||||
|
||||
foreach (var addTokenCountData in request.AryAddTokenCountDatas)
|
||||
var response = new AddTokenCountResponse
|
||||
{
|
||||
var tokenId = addTokenCountData.TokenId;
|
||||
var addTokenCount = addTokenCountData.AddTokenCount;
|
||||
var token = user.Tokens.FirstOrDefault(t => t.Id == tokenId);
|
||||
if (token != null)
|
||||
{
|
||||
token.Count += addTokenCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Tokens.Add(new Token
|
||||
{
|
||||
Id = (int)tokenId,
|
||||
Count = addTokenCount
|
||||
});
|
||||
}
|
||||
}
|
||||
Result = 1
|
||||
};
|
||||
|
||||
await userDatumService.UpdateUserDatum(user);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/addtokencount.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> AddTokenCount3209([FromBody] Models.v3209.AddTokenCountRequest request)
|
||||
{
|
||||
Logger.LogInformation("[3209] AddTokenCount request : {Request}", request.Stringify());
|
||||
|
||||
var command = new AddTokenCountCommand(AddTokenCountRequestMapper.Map(request));
|
||||
await Mediator.Send(command);
|
||||
|
||||
var response = new AddTokenCountResponse
|
||||
{
|
||||
|
@ -1,200 +1,66 @@
|
||||
using GameDatabase.Entities;
|
||||
using System.Text.Json;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("/v12r08_ww/chassis/baidcheck_dcfxit1u.php")]
|
||||
public class BaidController : BaseController<BaidController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
private readonly ICardService cardService;
|
||||
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
|
||||
private readonly IDanScoreDatumService danScoreDatumService;
|
||||
|
||||
private readonly IAiDatumService aiDatumService;
|
||||
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
public BaidController(IUserDatumService userDatumService, ICardService cardService,
|
||||
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService,
|
||||
IGameDataService gameDataService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.cardService = cardService;
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.danScoreDatumService = danScoreDatumService;
|
||||
this.aiDatumService = aiDatumService;
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/baidcheck_dcfxit1u.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetBaid([FromBody] BAIDRequest request)
|
||||
{
|
||||
Logger.LogInformation("Baid request: {Request}", request.Stringify());
|
||||
var commonResponse = await Mediator.Send(new BaidQuery(request.AccessCode));
|
||||
BAIDResponse response;
|
||||
var card = await cardService.GetCardByAccessCode(request.AccessCode);
|
||||
if (card is null)
|
||||
if (commonResponse.IsNewUser)
|
||||
{
|
||||
Logger.LogInformation("New user with access code {AccessCode}", request.AccessCode);
|
||||
var newId = cardService.GetNextBaid();
|
||||
|
||||
response = new BAIDResponse
|
||||
{
|
||||
Result = 1,
|
||||
PlayerType = 1,
|
||||
Baid = newId,
|
||||
Baid = commonResponse.Baid,
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
var baid = card.Baid;
|
||||
|
||||
var userData = await userDatumService.GetFirstUserDatumOrDefault(baid);
|
||||
|
||||
var songBestData = await songBestDatumService.GetAllSongBestData(baid);
|
||||
|
||||
var achievementDisplayDifficulty = userData.AchievementDisplayDifficulty;
|
||||
if (userData.AchievementDisplayDifficulty == Difficulty.None)
|
||||
{
|
||||
achievementDisplayDifficulty = songBestData.Any(datum => datum.BestCrown >= CrownType.Clear) ?
|
||||
songBestData.Where(datum => datum.BestCrown >= CrownType.Clear).Max(datum => datum.Difficulty) :
|
||||
Difficulty.Easy;
|
||||
}
|
||||
|
||||
var songCountData = songBestData.Where(datum => achievementDisplayDifficulty != Difficulty.UraOni ?
|
||||
datum.Difficulty == achievementDisplayDifficulty :
|
||||
datum.Difficulty is Difficulty.Oni or Difficulty.UraOni).ToList();
|
||||
|
||||
var crownCount = CalculateCrownCount(songCountData);
|
||||
|
||||
var scoreRankCount = CalculateScoreRankCount(songCountData);
|
||||
|
||||
|
||||
var costumeData = JsonHelper.GetCostumeDataFromUserData(userData, Logger);
|
||||
|
||||
var costumeArrays = JsonHelper.GetCostumeUnlockDataFromUserData(userData, Logger);
|
||||
|
||||
var costumeFlagArrays = gameDataService.GetCostumeFlagArraySizes()
|
||||
.Select((size, index) => FlagCalculator.GetBitArrayFromIds(costumeArrays[index], size, Logger))
|
||||
.ToList();
|
||||
|
||||
var danData = await danScoreDatumService.GetDanScoreDataList(baid, DanType.Normal);
|
||||
|
||||
var maxDan = danData.Where(datum => datum.ClearState != DanClearState.NotClear)
|
||||
.Select(datum => datum.DanId)
|
||||
.DefaultIfEmpty()
|
||||
.Max();
|
||||
|
||||
var danDataDictionary = gameDataService.GetDanDataDictionary();
|
||||
var danIdList = danDataDictionary.Keys.ToList();
|
||||
var gotDanFlagArray = FlagCalculator.ComputeGotDanFlags(danData, danIdList);
|
||||
|
||||
var gaidenData = await danScoreDatumService.GetDanScoreDataList(baid, DanType.Gaiden);
|
||||
|
||||
var gaidenDataDictionary = gameDataService.GetGaidenDataDictionary();
|
||||
var gaidenIdList = gaidenDataDictionary.Keys.ToList();
|
||||
var gotGaidenFlagArray = FlagCalculator.ComputeGotDanFlags(gaidenData, gaidenIdList);
|
||||
|
||||
var genericInfoFlg = Array.Empty<uint>();
|
||||
try
|
||||
{
|
||||
genericInfoFlg = JsonSerializer.Deserialize<uint[]>(userData.GenericInfoFlgArray);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing genericinfo 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
|
||||
genericInfoFlg.ThrowIfNull("Genericinfo flg should never be null!");
|
||||
|
||||
var genericInfoFlgLength = genericInfoFlg.Any() ? genericInfoFlg.Max() + 1 : 0;
|
||||
var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, Logger);
|
||||
|
||||
var aiRank = (uint)(userData.AiWinCount / 10);
|
||||
if (aiRank > 11)
|
||||
{
|
||||
aiRank = 11;
|
||||
}
|
||||
response = new BAIDResponse
|
||||
{
|
||||
Result = 1,
|
||||
PlayerType = 0,
|
||||
Baid = baid,
|
||||
MydonName = userData.MyDonName,
|
||||
MydonNameLanguage = userData.MyDonNameLanguage,
|
||||
Title = userData.Title,
|
||||
TitleplateId = userData.TitlePlateId,
|
||||
ColorFace = userData.ColorFace,
|
||||
ColorBody = userData.ColorBody,
|
||||
ColorLimb = userData.ColorLimb,
|
||||
AryCostumedata = new BAIDResponse.CostumeData
|
||||
{
|
||||
Costume1 = costumeData[0],
|
||||
Costume2 = costumeData[1],
|
||||
Costume3 = costumeData[2],
|
||||
Costume4 = costumeData[3],
|
||||
Costume5 = costumeData[4]
|
||||
},
|
||||
CostumeFlg1 = costumeFlagArrays[0],
|
||||
CostumeFlg2 = costumeFlagArrays[1],
|
||||
CostumeFlg3 = costumeFlagArrays[2],
|
||||
CostumeFlg4 = costumeFlagArrays[3],
|
||||
CostumeFlg5 = costumeFlagArrays[4],
|
||||
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT),
|
||||
IsDispDanOn = userData.DisplayDan,
|
||||
GotDanMax = maxDan,
|
||||
GotDanFlg = gotDanFlagArray,
|
||||
GotDanextraFlg = gotGaidenFlagArray,
|
||||
DefaultToneSetting = userData.SelectedToneId,
|
||||
GenericInfoFlg = genericInfoFlgArray,
|
||||
AryCrownCounts = crownCount,
|
||||
AryScoreRankCounts = scoreRankCount,
|
||||
IsDispAchievementOn = userData.DisplayAchievement,
|
||||
DispAchievementType = (uint)achievementDisplayDifficulty,
|
||||
IsDispAchievementTypeSet = true,
|
||||
LastPlayMode = userData.LastPlayMode,
|
||||
IsDispSouuchiOn = true
|
||||
};
|
||||
|
||||
response = Mappers.BaidResponseMapper.Map3906WithPostProcess(commonResponse);
|
||||
response.PlayerType = 0;
|
||||
response.IsDispAchievementTypeSet = true;
|
||||
response.IsDispSouuchiOn = true;
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
private static uint[] CalculateScoreRankCount(IReadOnlyCollection<SongBestDatum> songCountData)
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/baidcheck.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetBaid3209([FromBody] Models.v3209.BAIDRequest request)
|
||||
{
|
||||
var scoreRankCount = new uint[7];
|
||||
foreach (var scoreRankType in Enum.GetValues<ScoreRank>())
|
||||
Logger.LogInformation("Baid request: {Request}", request.Stringify());
|
||||
|
||||
var commonResponse = await Mediator.Send(new BaidQuery(request.WechatQrStr));
|
||||
Models.v3209.BAIDResponse response;
|
||||
if (commonResponse.IsNewUser)
|
||||
{
|
||||
if (scoreRankType != ScoreRank.None)
|
||||
Logger.LogInformation("New user with access code {AccessCode}", request.WechatQrStr);
|
||||
|
||||
response = new Models.v3209.BAIDResponse
|
||||
{
|
||||
scoreRankCount[(int)scoreRankType - 2] =
|
||||
(uint)songCountData.Count(datum => datum.BestScoreRank == scoreRankType);
|
||||
}
|
||||
Result = 1,
|
||||
PlayerType = 1,
|
||||
Baid = commonResponse.Baid,
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
return scoreRankCount;
|
||||
}
|
||||
|
||||
private static uint[] CalculateCrownCount(IReadOnlyCollection<SongBestDatum> songCountData)
|
||||
{
|
||||
var crownCount = new uint[3];
|
||||
foreach (var crownType in Enum.GetValues<CrownType>())
|
||||
{
|
||||
if (crownType != CrownType.None)
|
||||
{
|
||||
crownCount[(int)crownType - 1] = (uint)songCountData.Count(datum => datum.BestCrown == crownType);
|
||||
}
|
||||
}
|
||||
|
||||
return crownCount;
|
||||
response = Mappers.BaidResponseMapper.Map3209WithPostProcess(commonResponse);
|
||||
response.PlayerType = 0;
|
||||
response.IsDispAchievementTypeSet = true;
|
||||
response.IsDispSouuchiOn = true;
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("/v12r08_ww/chassis/bookkeeping_s4esi5un.php")]
|
||||
public class BookkeepingController : BaseController<BookkeepingController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/bookkeeping_s4esi5un.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult StartupAuth([FromBody] BookKeepingRequest request)
|
||||
{
|
||||
Logger.LogInformation("Bookkeeping request: {Request}", request.Stringify());
|
||||
Logger.LogInformation("[3906] Bookkeeping request: {Request}", request.Stringify());
|
||||
var response = new BookKeepingResponse
|
||||
{
|
||||
Result = 1
|
||||
@ -18,4 +17,15 @@ public class BookkeepingController : BaseController<BookkeepingController>
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/bookkeeping.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult StartupAuth3209([FromBody] Models.v3209.BookKeepingRequest request)
|
||||
{
|
||||
Logger.LogInformation("[3209] Bookkeeping request: {Request}", request.Stringify());
|
||||
var response = new BookKeepingResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/challengecompe.php")]
|
||||
[ApiController]
|
||||
public class ChallengeCompetitionController : BaseController<ChallengeCompetitionController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/challengecompe.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult HandleChallenge([FromBody] ChallengeCompeRequest request)
|
||||
{
|
||||
@ -16,6 +15,19 @@ public class ChallengeCompetitionController : BaseController<ChallengeCompetitio
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/challengecompe.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult HandleChallenge3209([FromBody] Models.v3209.ChallengeCompeRequest request)
|
||||
{
|
||||
Logger.LogInformation("ChallengeCompe request : {Request}", request.Stringify());
|
||||
|
||||
var response = new Models.v3209.ChallengeCompeResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ using TaikoLocalServer.Settings;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/crownsdata_oqgqy90s.php")]
|
||||
[ApiController]
|
||||
public class CrownsDataController : BaseController<CrownsDataController>
|
||||
{
|
||||
@ -17,16 +16,50 @@ public class CrownsDataController : BaseController<CrownsDataController>
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/crownsdata_oqgqy90s.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> CrownsData([FromBody] CrownsDataRequest request)
|
||||
{
|
||||
Logger.LogInformation("CrownsData request : {Request}", request.Stringify());
|
||||
|
||||
var songBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
|
||||
var crownData = await Handle(request.Baid);
|
||||
|
||||
var response = new CrownsDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
CrownFlg = crownData.CrownFlg,
|
||||
DondafulCrownFlg = crownData.DondafulCrownFlg
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/crownsdata.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> CrownsData3209([FromBody] Models.v3209.CrownsDataRequest request)
|
||||
{
|
||||
Logger.LogInformation("CrownsData request : {Request}", request.Stringify());
|
||||
|
||||
var crownData = await Handle((uint)request.Baid);
|
||||
|
||||
var response = new Models.v3209.CrownsDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
CrownFlg = crownData.CrownFlg,
|
||||
DondafulCrownFlg = crownData.DondafulCrownFlg
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
public record CrownData(byte[] CrownFlg, byte[] DondafulCrownFlg);
|
||||
|
||||
private async Task<CrownData> Handle(uint baid)
|
||||
{
|
||||
var songBestData = await songBestDatumService.GetAllSongBestData(baid);
|
||||
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var crown = new ushort[songIdMax + 1];
|
||||
var crown = new ushort[songIdMax + 1];
|
||||
var dondafulCrown = new byte[songIdMax + 1];
|
||||
|
||||
for (var songId = 0; songId < songIdMax; songId++)
|
||||
@ -34,7 +67,7 @@ public class CrownsDataController : BaseController<CrownsDataController>
|
||||
var id = songId;
|
||||
dondafulCrown[songId] = songBestData
|
||||
// Select song of this song id with dondaful crown
|
||||
.Where(datum => datum.SongId == id &&
|
||||
.Where(datum => datum.SongId == id &&
|
||||
datum.BestCrown == CrownType.Dondaful)
|
||||
// Calculate flag according to difficulty
|
||||
.Aggregate((byte)0, (flag, datum) => FlagCalculator.ComputeDondafulCrownFlag(flag, datum.Difficulty));
|
||||
@ -46,14 +79,7 @@ public class CrownsDataController : BaseController<CrownsDataController>
|
||||
// Calculate flag according to difficulty
|
||||
.Aggregate((ushort)0, (flag, datum) => FlagCalculator.ComputeCrownFlag(flag, datum.BestCrown, datum.Difficulty));
|
||||
}
|
||||
|
||||
var response = new CrownsDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
CrownFlg = GZipBytesUtil.GetGZipBytes(crown),
|
||||
DondafulCrownFlg = GZipBytesUtil.GetGZipBytes(dondafulCrown)
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
|
||||
return new CrownData(GZipBytesUtil.GetGZipBytes(crown), GZipBytesUtil.GetGZipBytes(dondafulCrown));
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/executeqrcode_rgowsr5m.php")]
|
||||
[ApiController]
|
||||
public class ExecuteQrCodeController : BaseController<ExecuteQrCodeController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/executeqrcode_rgowsr5m.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult ExecuteQrCode([FromBody] ExecuteQrcodeRequest request)
|
||||
{
|
||||
@ -18,4 +17,19 @@ public class ExecuteQrCodeController : BaseController<ExecuteQrCodeController>
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/executeqrcode.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult ExecuteQrCode3209([FromBody] Models.v3209.ExecuteQrcodeRequest request)
|
||||
{
|
||||
Logger.LogInformation("ExecuteQrcode request : {Request}", request.Stringify());
|
||||
|
||||
var response = new Models.v3209.ExecuteQrcodeResponse
|
||||
{
|
||||
QrcodeId = 1,
|
||||
Result = 1
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,34 +1,29 @@
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getaidata_6x30b9nr.php")]
|
||||
[ApiController]
|
||||
public class GetAiDataController : BaseController<GetAiDataController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
public GetAiDataController(IUserDatumService userDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getaidata_6x30b9nr.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetAiData([FromBody] GetAiDataRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetAiData request : {Request}", request.Stringify());
|
||||
|
||||
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
var response = new GetAiDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
TotalWinnings = (uint)user.AiWinCount,
|
||||
InputMedian = "1000",
|
||||
InputVariance = "2000"
|
||||
};
|
||||
var commonResponse = await Mediator.Send(new GetAiDataQuery(request.Baid));
|
||||
var response = Mappers.AiDataResponseMapper.MapTo3906(commonResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getaidata.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetAiData3209([FromBody] Models.v3209.GetAiDataRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetAiData request : {Request}", request.Stringify());
|
||||
|
||||
var commonResponse = await Mediator.Send(new GetAiDataQuery((uint)request.Baid));
|
||||
var response = Mappers.AiDataResponseMapper.MapTo3209(commonResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,105 +1,32 @@
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getaiscore_lp38po4w.php")]
|
||||
[ApiController]
|
||||
public class GetAiScoreController : BaseController<GetAiScoreController>
|
||||
{
|
||||
private readonly IAiDatumService aiDatumService;
|
||||
|
||||
public GetAiScoreController(IAiDatumService aiDatumService)
|
||||
{
|
||||
this.aiDatumService = aiDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getaiscore_lp38po4w.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetAiScore([FromBody] GetAiScoreRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetAiScore request : {Request}", request.Stringify());
|
||||
|
||||
var response = new GetAiScoreResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
var commonResponse = await Mediator.Send(new GetAiScoreQuery(request.Baid, request.SongNo, request.Level));
|
||||
var response = AiScoreMappers.MapTo3906(commonResponse);
|
||||
|
||||
var difficulty = (Difficulty)request.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
var aiData = await aiDatumService.GetSongAiScore(request.Baid, request.SongNo, difficulty);
|
||||
if (aiData is null)
|
||||
{
|
||||
return Ok(response);
|
||||
}
|
||||
[HttpPost("v12r00_cn/chassis/getaiscore.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetAiScore3209([FromBody] Models.v3209.GetAiScoreRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetAiScore request : {Request}", request.Stringify());
|
||||
|
||||
for (var index = 0; index < aiData.AiSectionScoreData.Count; index++)
|
||||
{
|
||||
var sectionScoreDatum = aiData.AiSectionScoreData[index];
|
||||
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData
|
||||
{
|
||||
Crown = (uint)sectionScoreDatum.Crown,
|
||||
GoodCnt = sectionScoreDatum.GoodCount,
|
||||
OkCnt = sectionScoreDatum.OkCount,
|
||||
NgCnt = sectionScoreDatum.MissCount,
|
||||
PoundCnt = sectionScoreDatum.DrumrollCount,
|
||||
Score = sectionScoreDatum.Score,
|
||||
SectionNo = (uint)index
|
||||
});
|
||||
}
|
||||
|
||||
// There's either 3 or 5 total sections
|
||||
// SectionNo doesn't seem to actually affect which section is being assigned to, only the List order matters
|
||||
/*response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
|
||||
{
|
||||
SectionNo = 1,
|
||||
Crown = (uint)CrownType.Clear,
|
||||
Score = 100000,
|
||||
GoodCnt = 100,
|
||||
OkCnt = 50,
|
||||
NgCnt = 25,
|
||||
PoundCnt = 12
|
||||
});
|
||||
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
|
||||
{
|
||||
SectionNo = 2,
|
||||
Crown = (uint)CrownType.Gold,
|
||||
Score = 100001,
|
||||
GoodCnt = 101,
|
||||
OkCnt = 50,
|
||||
NgCnt = 25,
|
||||
PoundCnt = 12
|
||||
});
|
||||
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
|
||||
{
|
||||
SectionNo = 3,
|
||||
Crown = (uint)CrownType.Dondaful,
|
||||
Score = 100002,
|
||||
GoodCnt = 102,
|
||||
OkCnt = 50,
|
||||
NgCnt = 25,
|
||||
PoundCnt = 12
|
||||
});
|
||||
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
|
||||
{
|
||||
SectionNo = 4,
|
||||
Crown = (uint)CrownType.Gold,
|
||||
Score = 100003,
|
||||
GoodCnt = 103,
|
||||
OkCnt = 50,
|
||||
NgCnt = 25,
|
||||
PoundCnt = 12
|
||||
});
|
||||
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
|
||||
{
|
||||
SectionNo = 5,
|
||||
Crown = (uint)CrownType.Clear,
|
||||
Score = 100004,
|
||||
GoodCnt = 104,
|
||||
OkCnt = 50,
|
||||
NgCnt = 25,
|
||||
PoundCnt = 12
|
||||
});*/
|
||||
var commonResponse =
|
||||
await Mediator.Send(new GetAiScoreQuery((uint)request.Baid, request.SongNo, request.Level));
|
||||
var response = AiScoreMappers.MapTo3209(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getapplicationurl.php")]
|
||||
[ApiController]
|
||||
public class GetApplicationUrlController : BaseController<GetApplicationUrlController>
|
||||
{
|
||||
private const string APPLICATION_URL = "vsapi.taiko-p.jp";
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getapplicationurl.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetApplicationUrl([FromBody] GetApplicationUrlRequest request)
|
||||
{
|
||||
@ -15,9 +13,25 @@ public class GetApplicationUrlController : BaseController<GetApplicationUrlContr
|
||||
var response = new GetApplicationUrlResponse
|
||||
{
|
||||
Result = 1,
|
||||
ApplicationUrl = APPLICATION_URL
|
||||
ApplicationUrl = $"{HttpContext.Request.Host.Value}/app"
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getapplicationurl.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetApplicationUrl3209([FromBody] Models.v3209.GetApplicationUrlRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetApplicationUrl request : {Request}", request.Stringify());
|
||||
|
||||
var response = new Models.v3209.GetApplicationUrlResponse
|
||||
{
|
||||
Result = 1,
|
||||
ApplicationUrl = $"{HttpContext.Request.Host.Value}/app"
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +1,14 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getdanodai_ela9zu1a.php")]
|
||||
[ApiController]
|
||||
public class GetDanOdaiController : BaseController<GetDanOdaiController>
|
||||
{
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
public GetDanOdaiController(IGameDataService gameDataService)
|
||||
{
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getdanodai_ela9zu1a.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetDanOdai([FromBody] GetDanOdaiRequest request)
|
||||
public async Task<IActionResult> GetDanOdai([FromBody] GetDanOdaiRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetDanOdai request : {Request}", request.Stringify());
|
||||
|
||||
@ -22,34 +17,25 @@ public class GetDanOdaiController : BaseController<GetDanOdaiController>
|
||||
Result = 1
|
||||
};
|
||||
|
||||
if (request.Type == 1)
|
||||
{
|
||||
foreach (var danId in request.DanIds)
|
||||
{
|
||||
gameDataService.GetDanDataDictionary().TryGetValue(danId, out var odaiData);
|
||||
if (odaiData is null)
|
||||
{
|
||||
Logger.LogWarning("Requested dan id {Id} does not exist!", danId);
|
||||
continue;
|
||||
}
|
||||
var odaiDataList = await Mediator.Send(new GetDanOdaiQuery(request.DanIds, request.Type));
|
||||
response.AryOdaiDatas.AddRange(odaiDataList.Select(DanDataMappers.To3906OdaiData));
|
||||
|
||||
response.AryOdaiDatas.Add(odaiData);
|
||||
}
|
||||
}
|
||||
else if (request.Type == 2)
|
||||
{
|
||||
foreach (var danId in request.DanIds)
|
||||
{
|
||||
gameDataService.GetGaidenDataDictionary().TryGetValue(danId, out var odaiData);
|
||||
if (odaiData is null)
|
||||
{
|
||||
Logger.LogWarning("Requested dan id {Id} does not exist!", danId);
|
||||
continue;
|
||||
}
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getdanodai.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetDanOdai3209([FromBody] Models.v3209.GetDanOdaiRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetDanOdai request : {Request}", request.Stringify());
|
||||
|
||||
response.AryOdaiDatas.Add(odaiData);
|
||||
}
|
||||
}
|
||||
var response = new Models.v3209.GetDanOdaiResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
|
||||
var odaiDataList = await Mediator.Send(new GetDanOdaiQuery(request.DanIds, request.Type));
|
||||
response.AryOdaiDatas.AddRange(odaiDataList.Select(DanDataMappers.To3209OdaiData));
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -1,52 +1,31 @@
|
||||
using GameDatabase.Entities;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getdanscore_frqhg7q6.php")]
|
||||
[ApiController]
|
||||
public class GetDanScoreController : BaseController<GetDanScoreController>
|
||||
{
|
||||
private readonly IDanScoreDatumService danScoreDatumService;
|
||||
|
||||
public GetDanScoreController(IDanScoreDatumService danScoreDatumService)
|
||||
{
|
||||
this.danScoreDatumService = danScoreDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getdanscore_frqhg7q6.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetDanScore([FromBody] GetDanScoreRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetDanScore request : {Request}", request.Stringify());
|
||||
|
||||
var response = new GetDanScoreResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
var commonResponse = await Mediator.Send(new GetDanScoreQuery(request.Baid, request.Type, request.DanIds));
|
||||
var response = DanScoreMappers.MapTo3906(commonResponse);
|
||||
|
||||
var danType = (DanType)request.Type;
|
||||
danType.Throw().IfOutOfRange();
|
||||
var danScoreData = await danScoreDatumService.GetDanScoreDataList(request.Baid, danType);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getdanscore.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetDanScore3209([FromBody] Models.v3209.GetDanScoreRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetDanScore3209 request : {Request}", request.Stringify());
|
||||
|
||||
foreach (var danId in request.DanIds)
|
||||
{
|
||||
var datum = danScoreData.FirstOrDefault(scoreDatum => scoreDatum.DanId == danId, new DanScoreDatum());
|
||||
|
||||
var responseData = new GetDanScoreResponse.DanScoreData
|
||||
{
|
||||
DanId = danId,
|
||||
ArrivalSongCnt = datum.ArrivalSongCount,
|
||||
ComboCntTotal = datum.ComboCountTotal,
|
||||
SoulGaugeTotal = datum.SoulGaugeTotal
|
||||
};
|
||||
foreach (var stageScoreDatum in datum.DanStageScoreData)
|
||||
{
|
||||
responseData.AryDanScoreDataStages.Add(ObjectMappers.DanStageDbToResponseMap.Apply(stageScoreDatum));
|
||||
}
|
||||
|
||||
response.AryDanScoreDatas.Add(responseData);
|
||||
}
|
||||
var commonResponse = await Mediator.Send(new GetDanScoreQuery((uint)request.Baid, request.Type, request.DanIds));
|
||||
var response = DanScoreMappers.MapTo3209(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -1,38 +1,28 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getfolder_rffj346i.php")]
|
||||
[ApiController]
|
||||
public class GetFolderController : BaseController<GetFolderController>
|
||||
{
|
||||
private readonly IGameDataService gameDataService;
|
||||
public GetFolderController(IGameDataService gameDataService)
|
||||
{
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getfolder_rffj346i.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetFolder([FromBody] GetfolderRequest request)
|
||||
public async Task<IActionResult> GetFolder([FromBody] GetfolderRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetFolder request : {Request}", request.Stringify());
|
||||
|
||||
var response = new GetfolderResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
|
||||
foreach (var folderId in request.FolderIds)
|
||||
{
|
||||
gameDataService.GetFolderDictionary().TryGetValue(folderId, out var folderData);
|
||||
if (folderData is null)
|
||||
{
|
||||
Logger.LogWarning("Requested folder id {Id} does not exist!", folderId);
|
||||
continue;
|
||||
}
|
||||
|
||||
response.AryEventfolderDatas.Add(folderData);
|
||||
}
|
||||
|
||||
var commonResponse = await Mediator.Send(new GetFolderQuery(request.FolderIds));
|
||||
var response = FolderDataMappers.MapTo3906(commonResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getfolder.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetFolder([FromBody] Models.v3209.GetfolderRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetFolder3209 request : {Request}", request.Stringify());
|
||||
var commonResponse = await Mediator.Send(new GetFolderQuery(request.FolderIds));
|
||||
var response = FolderDataMappers.MapTo3209(commonResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("/v12r08_ww/chassis/getgenericmaster_ts8om3qd.php")]
|
||||
public class GetGenericMasterController : BaseController<GetGenericMasterController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getgenericmaster_ts8om3qd.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetGenericMaster([FromBody] GetGenericMasterRequest request)
|
||||
{
|
||||
@ -17,6 +16,21 @@ public class GetGenericMasterController : BaseController<GetGenericMasterControl
|
||||
EnableIdBit = FlagCalculator.GetBitArrayTrue(5000)
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getgenericmaster.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetGenericMaster([FromBody] Models.v3209.GetGenericMasterRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetGenericMaster3209Request: {Request}", request.Stringify());
|
||||
|
||||
var response = new Models.v3209.GetGenericMasterResponse
|
||||
{
|
||||
Result = 1,
|
||||
VerupNo = 2,
|
||||
EnableIdBit = FlagCalculator.GetBitArrayTrue(5000)
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -3,36 +3,63 @@ using TaikoLocalServer.Settings;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getscorerank_1c8l7y61.php")]
|
||||
[ApiController]
|
||||
public class GetScoreRankController : BaseController<GetScoreRankController>
|
||||
public class GetScoreRankController(ISongBestDatumService songBestDatumService, IOptions<ServerSettings> settings)
|
||||
: BaseController<GetScoreRankController>
|
||||
{
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
private readonly ServerSettings settings = settings.Value;
|
||||
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public GetScoreRankController(ISongBestDatumService songBestDatumService, IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getscorerank_1c8l7y61.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetScoreRank([FromBody] GetScoreRankRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetScoreRank request : {Request}", request.Stringify());
|
||||
|
||||
var scoreRankData = await Handle(request.Baid);
|
||||
var response = new GetScoreRankResponse
|
||||
{
|
||||
Result = 1,
|
||||
IkiScoreRankFlg = scoreRankData.IkiScoreRankFlg,
|
||||
KiwamiScoreRankFlg = scoreRankData.KiwamiScoreRankFlg,
|
||||
MiyabiScoreRankFlg = scoreRankData.MiyabiScoreRankFlg
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getscorerank.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetScoreRank3209([FromBody] Models.v3209.GetScoreRankRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetScoreRank request : {Request}", request.Stringify());
|
||||
|
||||
var scoreRankData = await Handle((uint)request.Baid);
|
||||
var response = new Models.v3209.GetScoreRankResponse
|
||||
{
|
||||
Result = 1,
|
||||
IkiScoreRankFlg = scoreRankData.IkiScoreRankFlg,
|
||||
KiwamiScoreRankFlg = scoreRankData.KiwamiScoreRankFlg,
|
||||
MiyabiScoreRankFlg = scoreRankData.MiyabiScoreRankFlg
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
public record ScoreRankData(byte[] IkiScoreRankFlg, byte[] KiwamiScoreRankFlg, byte[] MiyabiScoreRankFlg);
|
||||
|
||||
private async Task<ScoreRankData> Handle(uint baid)
|
||||
{
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var kiwamiScores = new byte[songIdMax + 1];
|
||||
var kiwamiScores = new byte[songIdMax + 1];
|
||||
var miyabiScores = new ushort[songIdMax + 1];
|
||||
var ikiScores = new ushort[songIdMax + 1];
|
||||
var songBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
|
||||
var ikiScores = new ushort[songIdMax + 1];
|
||||
var songBestData = await songBestDatumService.GetAllSongBestData(baid);
|
||||
|
||||
for (var songId = 0; songId < songIdMax; songId++)
|
||||
{
|
||||
var id = songId;
|
||||
kiwamiScores[songId] = songBestData
|
||||
.Where(datum => datum.SongId == id &&
|
||||
.Where(datum => datum.SongId == id &&
|
||||
datum.BestScoreRank == ScoreRank.Dondaful)
|
||||
.Aggregate((byte)0, (flag, datum) => FlagCalculator.ComputeKiwamiScoreRankFlag(flag, datum.Difficulty));
|
||||
|
||||
@ -46,14 +73,11 @@ public class GetScoreRankController : BaseController<GetScoreRankController>
|
||||
datum.BestScoreRank is ScoreRank.Gold or ScoreRank.Purple or ScoreRank.Sakura)
|
||||
.Aggregate((ushort)0, (flag, datum) => FlagCalculator.ComputeMiyabiOrIkiScoreRank(flag, datum.BestScoreRank, datum.Difficulty));
|
||||
}
|
||||
var response = new GetScoreRankResponse
|
||||
{
|
||||
Result = 1,
|
||||
IkiScoreRankFlg = GZipBytesUtil.GetGZipBytes(ikiScores),
|
||||
KiwamiScoreRankFlg = GZipBytesUtil.GetGZipBytes(kiwamiScores),
|
||||
MiyabiScoreRankFlg = GZipBytesUtil.GetGZipBytes(miyabiScores)
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
return new ScoreRankData(
|
||||
GZipBytesUtil.GetGZipBytes(ikiScores),
|
||||
GZipBytesUtil.GetGZipBytes(kiwamiScores),
|
||||
GZipBytesUtil.GetGZipBytes(miyabiScores)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,34 +1,31 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getshopfolder_w4xik0uw.php")]
|
||||
[ApiController]
|
||||
public class GetShopFolderController : BaseController<GetShopFolderController>
|
||||
{
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
public GetShopFolderController(IGameDataService gameDataService)
|
||||
{
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getshopfolder_w4xik0uw.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetShopFolder([FromBody] GetShopFolderRequest request)
|
||||
public async Task<IActionResult> GetShopFolder([FromBody] GetShopFolderRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetShopFolder request : {Request}", request.Stringify());
|
||||
|
||||
gameDataService.GetTokenDataDictionary().TryGetValue("shopTokenId", out var shopTokenId);
|
||||
var commonResponse = await Mediator.Send(new GetShopFolderQuery());
|
||||
var response = ShopFolderDataMappers.MapTo3906(commonResponse);
|
||||
|
||||
var shopFolderList = gameDataService.GetShopFolderList();
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getshopfolder.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetShopFolder3209([FromBody] Models.v3209.GetShopFolderRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetShopFolder request : {Request}", request.Stringify());
|
||||
|
||||
var response = new GetShopFolderResponse
|
||||
{
|
||||
Result = 1,
|
||||
TokenId = shopTokenId > 0 ? (uint)shopTokenId : 1,
|
||||
VerupNo = 2
|
||||
};
|
||||
|
||||
response.AryShopFolderDatas.AddRange(shopFolderList);
|
||||
var commonResponse = await Mediator.Send(new GetShopFolderQuery());
|
||||
var response = ShopFolderDataMappers.MapTo3209(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/getsongintroduction_66blw6is.php")]
|
||||
[ApiController]
|
||||
public class GetSongIntroductionController : BaseController<GetSongIntroductionController>
|
||||
{
|
||||
@ -11,29 +13,27 @@ public class GetSongIntroductionController : BaseController<GetSongIntroductionC
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/getsongintroduction_66blw6is.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetSongIntroduction([FromBody] GetSongIntroductionRequest request)
|
||||
public async Task<IActionResult> GetSongIntroduction([FromBody] GetSongIntroductionRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetSongIntroduction request : {Request}", request.Stringify());
|
||||
|
||||
var response = new GetSongIntroductionResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
|
||||
foreach (var setId in request.SetIds)
|
||||
{
|
||||
gameDataService.GetSongIntroDictionary().TryGetValue(setId, out var introData);
|
||||
if (introData is null)
|
||||
{
|
||||
Logger.LogWarning("Requested set id {Id} does not exist!", setId);
|
||||
continue;
|
||||
}
|
||||
|
||||
response.ArySongIntroductionDatas.Add(introData);
|
||||
}
|
||||
var commonResponse = await Mediator.Send(new GetSongIntroductionQuery(request.SetIds));
|
||||
var response = SongIntroductionDataMappers.MapTo3906(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/getsongintroduction.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetSongIntroduction3209([FromBody] Models.v3209.GetSongIntroductionRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetSongIntroduction request : {Request}", request.Stringify());
|
||||
|
||||
var commonResponse = await Mediator.Send(new GetSongIntroductionQuery(request.SetIds));
|
||||
var response = SongIntroductionDataMappers.MapTo3209(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/gettelop_o0cb2z3e.php")]
|
||||
[ApiController]
|
||||
public class GetTelopController : BaseController<GetTelopController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/gettelop_o0cb2z3e.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetTelop([FromBody] GettelopRequest request)
|
||||
{
|
||||
@ -18,7 +17,28 @@ public class GetTelopController : BaseController<GetTelopController>
|
||||
Result = 1,
|
||||
StartDatetime = startDateTime.ToString(Constants.DATE_TIME_FORMAT),
|
||||
EndDatetime = endDateTime.ToString(Constants.DATE_TIME_FORMAT),
|
||||
Telop = "Hello world",
|
||||
Telop = "Hello 3906",
|
||||
VerupNo = 1
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/gettelop.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetTelop3209([FromBody] Models.v3209.GettelopRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetTelop request : {Request}", request.Stringify());
|
||||
|
||||
var startDateTime = DateTime.Now - TimeSpan.FromDays(999.0);
|
||||
var endDateTime = DateTime.Now + TimeSpan.FromDays(999.0);
|
||||
|
||||
var response = new Models.v3209.GettelopResponse
|
||||
{
|
||||
Result = 1,
|
||||
StartDatetime = startDateTime.ToString(Constants.DATE_TIME_FORMAT),
|
||||
EndDatetime = endDateTime.ToString(Constants.DATE_TIME_FORMAT),
|
||||
Telop = "Hello 3209",
|
||||
VerupNo = 1
|
||||
};
|
||||
|
||||
|
@ -1,92 +1,31 @@
|
||||
using System.Text.Json;
|
||||
using GameDatabase.Entities;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/gettokencount_iut9g23g.php")]
|
||||
[ApiController]
|
||||
public class GetTokenCountController : BaseController<GetTokenCountController>
|
||||
{
|
||||
private readonly IGameDataService gameDataService;
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
public GetTokenCountController(IUserDatumService userDatumService, IGameDataService gameDataService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/gettokencount_iut9g23g.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetTokenCount([FromBody] GetTokenCountRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetTokenCount request : {Request}", request.Stringify());
|
||||
|
||||
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
var commonResponse = await Mediator.Send(new GetTokenCountQuery(request.Baid));
|
||||
var response = TokenCountDataMappers.MapTo3906(commonResponse);
|
||||
|
||||
var tokenDataDictionary = gameDataService.GetTokenDataDictionary();
|
||||
tokenDataDictionary.TryGetValue("shopTokenId", out var shopTokenId);
|
||||
tokenDataDictionary.TryGetValue("kaTokenId", out var kaTokenId);
|
||||
tokenDataDictionary.TryGetValue("onePieceTokenId", out var onePieceTokenId);
|
||||
tokenDataDictionary.TryGetValue("soshinaTokenId", out var soshinaTokenId);
|
||||
tokenDataDictionary.TryGetValue("Yatsushika1TokenId", out var yatsushika1TokenId);
|
||||
tokenDataDictionary.TryGetValue("Yatsushika2TokenId", out var yatsushika2TokenId);
|
||||
tokenDataDictionary.TryGetValue("Yatsushika3TokenId", out var yatsushika3TokenId);
|
||||
tokenDataDictionary.TryGetValue("Yatsushika4TokenId", out var yatsushika4TokenId);
|
||||
tokenDataDictionary.TryGetValue("MaskedKid1TokenId", out var maskedKid1TokenId);
|
||||
tokenDataDictionary.TryGetValue("MaskedKid2TokenId", out var maskedKid2TokenId);
|
||||
tokenDataDictionary.TryGetValue("MaskedKid3TokenId", out var maskedKid3TokenId);
|
||||
tokenDataDictionary.TryGetValue("MaskedKid4TokenId", out var maskedKid4TokenId);
|
||||
tokenDataDictionary.TryGetValue("Kiyoshi1TokenId", out var kiyoshi1TokenId);
|
||||
tokenDataDictionary.TryGetValue("Kiyoshi2TokenId", out var kiyoshi2TokenId);
|
||||
tokenDataDictionary.TryGetValue("Kiyoshi3TokenId", out var kiyoshi3TokenId);
|
||||
tokenDataDictionary.TryGetValue("Kiyoshi4TokenId", out var kiyoshi4TokenId);
|
||||
tokenDataDictionary.TryGetValue("Amitie1TokenId", out var amitie1TokenId);
|
||||
tokenDataDictionary.TryGetValue("Amitie2TokenId", out var amitie2TokenId);
|
||||
tokenDataDictionary.TryGetValue("Amitie3TokenId", out var amitie3TokenId);
|
||||
tokenDataDictionary.TryGetValue("Amitie4TokenId", out var amitie4TokenId);
|
||||
tokenDataDictionary.TryGetValue("Machina1TokenId", out var machina1TokenId);
|
||||
tokenDataDictionary.TryGetValue("Machina2TokenId", out var machina2TokenId);
|
||||
tokenDataDictionary.TryGetValue("Machina3TokenId", out var machina3TokenId);
|
||||
tokenDataDictionary.TryGetValue("Machina4TokenId", out var machina4TokenId);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("v12r00_cn/chassis/gettokencount.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetTokenCount3209([FromBody] Models.v3209.GetTokenCountRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetTokenCount request : {Request}", request.Stringify());
|
||||
|
||||
int[] tokenDataIdArray =
|
||||
{
|
||||
shopTokenId, kaTokenId, onePieceTokenId, soshinaTokenId, yatsushika1TokenId, yatsushika2TokenId,
|
||||
yatsushika3TokenId, yatsushika4TokenId, maskedKid1TokenId, maskedKid2TokenId, maskedKid3TokenId,
|
||||
maskedKid4TokenId, kiyoshi1TokenId, kiyoshi2TokenId, kiyoshi3TokenId, kiyoshi4TokenId, amitie1TokenId,
|
||||
amitie2TokenId, amitie3TokenId, amitie4TokenId, machina1TokenId, machina2TokenId, machina3TokenId,
|
||||
machina4TokenId
|
||||
};
|
||||
|
||||
var response = new GetTokenCountResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
|
||||
foreach (var tokenDataId in tokenDataIdArray)
|
||||
{
|
||||
if (tokenDataId <= 0) continue;
|
||||
var castedTokenDataId = (uint)tokenDataId;
|
||||
if (user.Tokens.All(token => token.Id != castedTokenDataId))
|
||||
{
|
||||
user.Tokens.Add(new Token
|
||||
{
|
||||
Id = (int)castedTokenDataId,
|
||||
Count = 0
|
||||
});
|
||||
}
|
||||
var tokenCount = user.Tokens.First(token => token.Id == castedTokenDataId).Count;
|
||||
response.AryTokenCountDatas.Add(new GetTokenCountResponse.TokenCountData
|
||||
{
|
||||
TokenCount = tokenCount,
|
||||
TokenId = castedTokenDataId
|
||||
});
|
||||
}
|
||||
|
||||
await userDatumService.UpdateUserDatum(user);
|
||||
var commonResponse = await Mediator.Send(new GetTokenCountQuery((uint)request.Baid));
|
||||
var response = TokenCountDataMappers.MapTo3209(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
19
TaikoLocalServer/Controllers/Game/HeadClerk2Controller.cs
Normal file
19
TaikoLocalServer/Controllers/Game/HeadClerk2Controller.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using TaikoLocalServer.Models.v3209;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
public class HeadClerk2Controller : BaseController<HeadClerk2Controller>
|
||||
{
|
||||
[HttpPost("/v12r00_cn/chassis/headclerk2.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult UploadHeadClert2([FromBody] HeadClerk2Request request)
|
||||
{
|
||||
Logger.LogInformation("HeadClerk2 request : {Request}", request.Stringify());
|
||||
var response = new HeadClerk2Response
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("/v12r08_ww/chassis/heartbeat_hcv5akgr.php")]
|
||||
public class HeartbeatController : BaseController<HeartbeatController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/heartbeat_hcv5akgr.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult HeartBeat([FromBody] HeartBeatRequest request)
|
||||
{
|
||||
@ -18,4 +17,18 @@ public class HeartbeatController : BaseController<HeartbeatController>
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/heartbeat.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult HeartBeat3209([FromBody] Models.v3209.HeartBeatRequest request)
|
||||
{
|
||||
Logger.LogInformation("Heartbeat request: {Request}", request.Stringify());
|
||||
var response = new Models.v3209.HeartBeatResponse
|
||||
{
|
||||
Result = 1,
|
||||
GameSvrStat = 1
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,139 +1,31 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoLocalServer.Settings;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[ApiController]
|
||||
[Route("/v12r08_ww/chassis/initialdatacheck_vaosv643.php")]
|
||||
public class InitialDataCheckController : BaseController<InitialDataCheckController>
|
||||
{
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public InitialDataCheckController(IGameDataService gameDataService, IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.gameDataService = gameDataService;
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/initialdatacheck_vaosv643.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult InitialDataCheck([FromBody] InitialdatacheckRequest request)
|
||||
public async Task<IActionResult> InitialDataCheck([FromBody] InitialdatacheckRequest request)
|
||||
{
|
||||
Logger.LogInformation("Initial data check request: {Request}", request.Stringify());
|
||||
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var commonResponse = await Mediator.Send(new GetInitialDataQuery());
|
||||
var response = InitialDataMappers.MapTo3906(commonResponse);
|
||||
|
||||
var musicList = gameDataService.GetMusicList();
|
||||
var lockedSongsList = gameDataService.GetLockedSongsList();
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/initialdatacheck.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> InitialDataCheckCN([FromBody] Models.v3209.InitialdatacheckRequest request)
|
||||
{
|
||||
Logger.LogInformation("Initial data check request: {Request}", request.Stringify());
|
||||
|
||||
var enabledArray =
|
||||
FlagCalculator.GetBitArrayFromIds(musicList, songIdMax, Logger);
|
||||
|
||||
var defaultSongList = musicList.Except(lockedSongsList);
|
||||
var defaultSongFlg =
|
||||
FlagCalculator.GetBitArrayFromIds(defaultSongList, songIdMax, Logger);
|
||||
|
||||
var defaultSongWithUraList = gameDataService.GetMusicWithUraList();
|
||||
var uraReleaseBit =
|
||||
FlagCalculator.GetBitArrayFromIds(defaultSongWithUraList, songIdMax, Logger);
|
||||
|
||||
var response = new InitialdatacheckResponse
|
||||
{
|
||||
Result = 1,
|
||||
DefaultSongFlg = defaultSongFlg,
|
||||
AchievementSongBit = enabledArray,
|
||||
UraReleaseBit = uraReleaseBit,
|
||||
SongIntroductionEndDatetime = DateTime.Now.AddYears(10).ToString(Constants.DATE_TIME_FORMAT),
|
||||
};
|
||||
|
||||
var movieDataDictionary = gameDataService.GetMovieDataDictionary();
|
||||
foreach (var movieData in movieDataDictionary) response.AryMovieInfoes.Add(movieData.Value);
|
||||
|
||||
var verupNo1 = new uint[] { 2, 3, 4, 5, 6, 7, 8, 13, 15, 24, 25, 26, 27, 28, 29, 30, 31, 104 };
|
||||
var aryVerUp = verupNo1.Select(i => new InitialdatacheckResponse.VerupNoData1
|
||||
{
|
||||
MasterType = i,
|
||||
VerupNo = 1
|
||||
})
|
||||
.ToList();
|
||||
response.AryVerupNoData1s.AddRange(aryVerUp);
|
||||
|
||||
var danData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
|
||||
var danDataDictionary = gameDataService.GetDanDataDictionary();
|
||||
foreach (var danId in danDataDictionary.Keys)
|
||||
{
|
||||
gameDataService.GetDanDataDictionary().TryGetValue(danId, out var odaiData);
|
||||
danData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
|
||||
{
|
||||
InfoId = danId,
|
||||
VerupNo = odaiData?.VerupNo ?? 1
|
||||
});
|
||||
}
|
||||
var verUp2Type101 = new InitialdatacheckResponse.VerupNoData2
|
||||
{
|
||||
MasterType = 101,
|
||||
};
|
||||
verUp2Type101.AryInformationDatas.AddRange(danData);
|
||||
response.AryVerupNoData2s.Add(verUp2Type101);
|
||||
|
||||
var gaidenData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
|
||||
var gaidenDataDictionary = gameDataService.GetGaidenDataDictionary();
|
||||
foreach (var gaidenId in gaidenDataDictionary.Keys)
|
||||
{
|
||||
gaidenDataDictionary.TryGetValue(gaidenId, out var odaiData);
|
||||
gaidenData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
|
||||
{
|
||||
InfoId = gaidenId,
|
||||
VerupNo = odaiData?.VerupNo ?? 1
|
||||
});
|
||||
}
|
||||
|
||||
var verUp2Type102 = new InitialdatacheckResponse.VerupNoData2
|
||||
{
|
||||
MasterType = 102,
|
||||
};
|
||||
verUp2Type102.AryInformationDatas.AddRange(gaidenData);
|
||||
response.AryVerupNoData2s.Add(verUp2Type102);
|
||||
|
||||
var eventFolderData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
|
||||
var eventFolderDictionary = gameDataService.GetFolderDictionary();
|
||||
foreach (var folderId in eventFolderDictionary.Keys)
|
||||
{
|
||||
eventFolderDictionary.TryGetValue(folderId, out var folderData);
|
||||
eventFolderData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
|
||||
{
|
||||
InfoId = folderId,
|
||||
VerupNo = folderData?.VerupNo ?? 1
|
||||
});
|
||||
}
|
||||
var verUp2Type103 = new InitialdatacheckResponse.VerupNoData2
|
||||
{
|
||||
MasterType = 103,
|
||||
};
|
||||
verUp2Type103.AryInformationDatas.AddRange(eventFolderData);
|
||||
response.AryVerupNoData2s.Add(verUp2Type103);
|
||||
|
||||
var songIntroData = new List<InitialdatacheckResponse.VerupNoData2.InformationData>();
|
||||
var songIntroDictionary = gameDataService.GetSongIntroDictionary();
|
||||
foreach (var setId in songIntroDictionary.Select(item => item.Value.SetId))
|
||||
{
|
||||
songIntroDictionary.TryGetValue(setId, out var introData);
|
||||
songIntroData.Add(new InitialdatacheckResponse.VerupNoData2.InformationData
|
||||
{
|
||||
InfoId = setId,
|
||||
VerupNo = introData?.VerupNo ?? 1
|
||||
});
|
||||
}
|
||||
var verUp2Type105 = new InitialdatacheckResponse.VerupNoData2
|
||||
{
|
||||
MasterType = 105,
|
||||
};
|
||||
verUp2Type105.AryInformationDatas.AddRange(songIntroData);
|
||||
response.AryVerupNoData2s.Add(verUp2Type105);
|
||||
|
||||
response.AryChassisFunctionIds = new uint[] { 1, 2, 3 };
|
||||
var commonResponse = await Mediator.Send(new GetInitialDataQuery());
|
||||
var response = InitialDataMappers.MapTo3209(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -1,73 +1,30 @@
|
||||
using GameDatabase.Entities;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/mydonentry_3nrd7kwk.php")]
|
||||
[ApiController]
|
||||
public class MyDonEntryController : BaseController<MyDonEntryController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
private readonly ICardService cardService;
|
||||
|
||||
private readonly ICredentialService credentialService;
|
||||
|
||||
public MyDonEntryController(IUserDatumService userDatumService, ICardService cardService, ICredentialService credentialService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.cardService = cardService;
|
||||
this.credentialService = credentialService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/mydonentry_3nrd7kwk.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetMyDonEntry([FromBody] MydonEntryRequest request)
|
||||
{
|
||||
Logger.LogInformation("MyDonEntry request : {Request}", request.Stringify());
|
||||
|
||||
var newId = cardService.GetNextBaid();
|
||||
|
||||
var newUser = new UserDatum
|
||||
{
|
||||
Baid = newId,
|
||||
MyDonName = request.MydonName,
|
||||
MyDonNameLanguage = 0,
|
||||
DisplayDan = true,
|
||||
DisplayAchievement = true,
|
||||
AchievementDisplayDifficulty = Difficulty.None,
|
||||
ColorFace = 0,
|
||||
ColorBody = 1,
|
||||
ColorLimb = 3,
|
||||
FavoriteSongsArray = "[]",
|
||||
ToneFlgArray = "[0]",
|
||||
TitleFlgArray = "[]",
|
||||
CostumeFlgArray = "[[0],[0],[0],[0],[0]]",
|
||||
GenericInfoFlgArray = "[]",
|
||||
UnlockedSongIdList = "[]"
|
||||
};
|
||||
await userDatumService.InsertUserDatum(newUser);
|
||||
|
||||
await cardService.AddCard(new Card
|
||||
{
|
||||
AccessCode = request.AccessCode,
|
||||
Baid = newId
|
||||
});
|
||||
|
||||
await credentialService.AddCredential(new Credential
|
||||
{
|
||||
Baid = newId,
|
||||
Password = "",
|
||||
Salt = ""
|
||||
});
|
||||
|
||||
var response = new MydonEntryResponse
|
||||
{
|
||||
Result = 1,
|
||||
Baid = newId,
|
||||
MydonName = request.MydonName,
|
||||
MydonNameLanguage = 0
|
||||
};
|
||||
var commonResponse = await Mediator.Send(new AddMyDonEntryCommand(request.AccessCode, request.MydonName, request.MydonNameLanguage));
|
||||
var response = MyDonEntryMappers.MapTo3906(commonResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/mydonentry.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetMyDonEntry3209([FromBody] Models.v3209.MydonEntryRequest request)
|
||||
{
|
||||
Logger.LogInformation("MyDonEntry request : {Request}", request.Stringify());
|
||||
|
||||
var commonResponse = await Mediator.Send(new AddMyDonEntryCommand(request.WechatQrStr, request.MydonName, request.MydonNameLanguage));
|
||||
var response = MyDonEntryMappers.MapTo3209(commonResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,410 +1,47 @@
|
||||
using GameDatabase.Entities;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
using StageData = PlayResultDataRequest.StageData;
|
||||
|
||||
[Route("/v12r08_ww/chassis/playresult_r3ky4a4z.php")]
|
||||
[ApiController]
|
||||
public class PlayResultController : BaseController<PlayResultController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
private readonly ISongPlayDatumService songPlayDatumService;
|
||||
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
|
||||
private readonly IDanScoreDatumService danScoreDatumService;
|
||||
|
||||
private readonly IAiDatumService aiDatumService;
|
||||
|
||||
public PlayResultController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService,
|
||||
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.songPlayDatumService = songPlayDatumService;
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.danScoreDatumService = danScoreDatumService;
|
||||
this.aiDatumService = aiDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/playresult_r3ky4a4z.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> UploadPlayResult([FromBody] PlayResultRequest request)
|
||||
{
|
||||
Logger.LogInformation("PlayResult request : {Request}", request.Stringify());
|
||||
|
||||
var truncated = request.PlayresultData.Skip(32).ToArray();
|
||||
|
||||
var decompressed = GZipBytesUtil.DecompressGZipBytes(truncated);
|
||||
|
||||
var playResultData = Serializer.Deserialize<PlayResultDataRequest>(new ReadOnlySpan<byte>(decompressed));
|
||||
|
||||
Logger.LogInformation("Play result data {Data}", playResultData.Stringify());
|
||||
|
||||
var commonRequest = PlayResultMappers.Map(playResultData);
|
||||
var commonResponse = await Mediator.Send(new UpdatePlayResultCommand(request.BaidConf, commonRequest));
|
||||
var response = new PlayResultResponse
|
||||
{
|
||||
Result = 1
|
||||
Result = commonResponse
|
||||
};
|
||||
|
||||
// Fix issue caused by guest play, god knows why they send guest play data
|
||||
if (request.BaidConf == 0)
|
||||
{
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
if (await userDatumService.GetFirstUserDatumOrNull(request.BaidConf) is null)
|
||||
{
|
||||
Logger.LogWarning("Game uploading a non exisiting user with baid {Baid}", request.BaidConf);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
var lastPlayDatetime = DateTime.ParseExact(playResultData.PlayDatetime, Constants.DATE_TIME_FORMAT,
|
||||
CultureInfo.InvariantCulture);
|
||||
|
||||
await UpdateUserData(request, playResultData, lastPlayDatetime);
|
||||
|
||||
var playMode = (PlayMode)playResultData.PlayMode;
|
||||
|
||||
if (playMode is PlayMode.DanMode or PlayMode.GaidenMode)
|
||||
{
|
||||
var danType = playMode == PlayMode.DanMode ? DanType.Normal : DanType.Gaiden;
|
||||
await UpdateDanPlayData(request, playResultData, danType);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
for (var songNumber = 0; songNumber < playResultData.AryStageInfoes.Count; songNumber++)
|
||||
{
|
||||
var stageData = playResultData.AryStageInfoes[songNumber];
|
||||
|
||||
if (stageData.IsSkipUse)
|
||||
{
|
||||
await UpdatePlayData(request, songNumber, stageData, lastPlayDatetime);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (playMode == PlayMode.AiBattle)
|
||||
{
|
||||
await UpdateAiBattleData(request, stageData);
|
||||
}
|
||||
|
||||
await UpdateBestData(request, stageData);
|
||||
|
||||
await UpdatePlayData(request, songNumber, stageData, lastPlayDatetime);
|
||||
}
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
private async Task UpdateDanPlayData(PlayResultRequest request, PlayResultDataRequest playResultDataRequest, DanType danType)
|
||||
[HttpPost("/v12r00_cn/chassis/playresult.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> UploadPlayResult3209([FromBody] Models.v3209.PlayResultRequest request)
|
||||
{
|
||||
if (playResultDataRequest.IsNotRecordedDan)
|
||||
Logger.LogInformation("PlayResult3209 request : {Request}", request.Stringify());
|
||||
var decompressed = GZipBytesUtil.DecompressGZipBytes(request.PlayresultData);
|
||||
var playResultData =
|
||||
Serializer.Deserialize<Models.v3209.PlayResultDataRequest>(new ReadOnlySpan<byte>(decompressed));
|
||||
Logger.LogInformation("Play result data 3209 {Data}", playResultData.Stringify());
|
||||
|
||||
var commonRequest = PlayResultMappers.Map(playResultData);
|
||||
var commonResponse = await Mediator.Send(new UpdatePlayResultCommand((uint) request.BaidConf, commonRequest));
|
||||
var response = new Models.v3209.PlayResultResponse
|
||||
{
|
||||
Logger.LogInformation("Dan score will not be saved!");
|
||||
return;
|
||||
}
|
||||
|
||||
var danScoreData =
|
||||
await danScoreDatumService.GetSingleDanScoreDatum(request.BaidConf, playResultDataRequest.DanId, danType) ??
|
||||
new DanScoreDatum
|
||||
{
|
||||
Baid = request.BaidConf,
|
||||
DanId = playResultDataRequest.DanId,
|
||||
DanType = danType
|
||||
};
|
||||
danScoreData.ClearState =
|
||||
(DanClearState)Math.Max(playResultDataRequest.DanResult, (uint)danScoreData.ClearState);
|
||||
danScoreData.ArrivalSongCount =
|
||||
Math.Max((uint)playResultDataRequest.AryStageInfoes.Count, danScoreData.ArrivalSongCount);
|
||||
danScoreData.ComboCountTotal = Math.Max(playResultDataRequest.ComboCntTotal, danScoreData.ComboCountTotal);
|
||||
danScoreData.SoulGaugeTotal = Math.Max(playResultDataRequest.SoulGaugeTotal, danScoreData.SoulGaugeTotal);
|
||||
|
||||
UpdateDanStageData(playResultDataRequest, danScoreData);
|
||||
|
||||
await danScoreDatumService.InsertOrUpdateDanScoreDatum(danScoreData);
|
||||
}
|
||||
|
||||
private void UpdateDanStageData(PlayResultDataRequest playResultDataRequest, DanScoreDatum danScoreData)
|
||||
{
|
||||
for (var i = 0; i < playResultDataRequest.AryStageInfoes.Count; i++)
|
||||
{
|
||||
var stageData = playResultDataRequest.AryStageInfoes[i];
|
||||
|
||||
var songNumber = i;
|
||||
var danStageData = danScoreData.DanStageScoreData.FirstOrDefault(datum => datum.SongNumber == songNumber,
|
||||
new DanStageScoreDatum
|
||||
{
|
||||
Baid = danScoreData.Baid,
|
||||
DanId = danScoreData.DanId,
|
||||
DanType = danScoreData.DanType,
|
||||
SongNumber = (uint)songNumber,
|
||||
OkCount = stageData.OkCnt,
|
||||
BadCount = stageData.NgCnt
|
||||
});
|
||||
|
||||
danStageData.HighScore = Math.Max(danStageData.HighScore, stageData.PlayScore);
|
||||
danStageData.ComboCount = Math.Max(danStageData.ComboCount, stageData.ComboCnt);
|
||||
danStageData.DrumrollCount = Math.Max(danStageData.DrumrollCount, stageData.PoundCnt);
|
||||
danStageData.GoodCount = Math.Max(danStageData.GoodCount, stageData.GoodCnt);
|
||||
danStageData.TotalHitCount = Math.Max(danStageData.TotalHitCount, stageData.HitCnt);
|
||||
danStageData.OkCount = Math.Min(danStageData.OkCount, stageData.OkCnt);
|
||||
danStageData.BadCount = Math.Min(danStageData.BadCount, stageData.NgCnt);
|
||||
|
||||
var index = danScoreData.DanStageScoreData.IndexOf(danStageData);
|
||||
if (index == -1)
|
||||
{
|
||||
danScoreDatumService.TrackDanStageData(danStageData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdatePlayData(PlayResultRequest request, int songNumber, StageData stageData,
|
||||
DateTime lastPlayDatetime)
|
||||
{
|
||||
var songPlayDatum = new SongPlayDatum
|
||||
{
|
||||
Baid = request.BaidConf,
|
||||
SongNumber = (uint)songNumber,
|
||||
GoodCount = stageData.GoodCnt,
|
||||
OkCount = stageData.OkCnt,
|
||||
MissCount = stageData.NgCnt,
|
||||
ComboCount = stageData.ComboCnt,
|
||||
HitCount = stageData.HitCnt,
|
||||
DrumrollCount = stageData.PoundCnt,
|
||||
Crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt),
|
||||
Score = stageData.PlayScore,
|
||||
ScoreRate = stageData.ScoreRate,
|
||||
ScoreRank = (ScoreRank)stageData.ScoreRank,
|
||||
Skipped = stageData.IsSkipUse,
|
||||
SongId = stageData.SongNo,
|
||||
PlayTime = lastPlayDatetime,
|
||||
Difficulty = (Difficulty)stageData.Level
|
||||
Result = commonResponse
|
||||
};
|
||||
await songPlayDatumService.AddSongPlayDatum(songPlayDatum);
|
||||
}
|
||||
|
||||
private async Task UpdateUserData(PlayResultRequest request, PlayResultDataRequest playResultData,
|
||||
DateTime lastPlayDatetime)
|
||||
{
|
||||
var userdata = await userDatumService.GetFirstUserDatumOrNull(request.BaidConf);
|
||||
|
||||
userdata.ThrowIfNull($"User data is null! Baid: {request.BaidConf}");
|
||||
|
||||
var playMode = (PlayMode)playResultData.PlayMode;
|
||||
|
||||
userdata.Title = playResultData.Title;
|
||||
userdata.TitlePlateId = playResultData.TitleplateId;
|
||||
var costumeData = new List<uint>
|
||||
{
|
||||
playResultData.AryCurrentCostume.Costume1,
|
||||
playResultData.AryCurrentCostume.Costume2,
|
||||
playResultData.AryCurrentCostume.Costume3,
|
||||
playResultData.AryCurrentCostume.Costume4,
|
||||
playResultData.AryCurrentCostume.Costume5
|
||||
};
|
||||
userdata.CostumeData = JsonSerializer.Serialize(costumeData);
|
||||
|
||||
// Skip user setting altogether following official logic
|
||||
// Skip user setting saving when in dan mode as dan mode uses its own default setting
|
||||
// if (playMode != PlayMode.DanMode)
|
||||
// {
|
||||
// var lastStage = playResultData.AryStageInfoes.Last();
|
||||
// var option = BinaryPrimitives.ReadInt16LittleEndian(lastStage.OptionFlg);
|
||||
// userdata.OptionSetting = option;
|
||||
// userdata.IsSkipOn = lastStage.IsSkipOn;
|
||||
// userdata.IsVoiceOn = !lastStage.IsVoiceOn;
|
||||
// userdata.NotesPosition = lastStage.NotesPosition;
|
||||
// }
|
||||
|
||||
userdata.LastPlayDatetime = lastPlayDatetime;
|
||||
userdata.LastPlayMode = playResultData.PlayMode;
|
||||
|
||||
userdata.ToneFlgArray =
|
||||
UpdateJsonUintFlagArray(userdata.ToneFlgArray, playResultData.GetToneNoes, nameof(userdata.ToneFlgArray));
|
||||
|
||||
userdata.TitleFlgArray =
|
||||
UpdateJsonUintFlagArray(userdata.TitleFlgArray, playResultData.GetTitleNoes, nameof(userdata.TitleFlgArray));
|
||||
|
||||
userdata.CostumeFlgArray = UpdateJsonCostumeFlagArray(userdata.CostumeFlgArray,
|
||||
new[]
|
||||
{
|
||||
playResultData.GetCostumeNo1s,
|
||||
playResultData.GetCostumeNo2s,
|
||||
playResultData.GetCostumeNo3s,
|
||||
playResultData.GetCostumeNo4s,
|
||||
playResultData.GetCostumeNo5s
|
||||
});
|
||||
|
||||
userdata.GenericInfoFlgArray =
|
||||
UpdateJsonUintFlagArray(userdata.GenericInfoFlgArray, playResultData.GetGenericInfoNoes, nameof(userdata.GenericInfoFlgArray));
|
||||
|
||||
var difficultyPlayedArray = new List<uint>
|
||||
{
|
||||
playResultData.DifficultyPlayedCourse,
|
||||
playResultData.DifficultyPlayedStar,
|
||||
playResultData.DifficultyPlayedSort
|
||||
};
|
||||
userdata.DifficultyPlayedArray = JsonSerializer.Serialize(difficultyPlayedArray);
|
||||
|
||||
userdata.AiWinCount += playResultData.AryStageInfoes.Count(data => data.IsWin);
|
||||
await userDatumService.UpdateUserDatum(userdata);
|
||||
}
|
||||
|
||||
private string UpdateJsonUintFlagArray(string originalValue, IReadOnlyCollection<uint>? newValue, string fieldName)
|
||||
{
|
||||
var flgData = new List<uint>();
|
||||
try
|
||||
{
|
||||
flgData = JsonSerializer.Deserialize<List<uint>>(originalValue);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing {FieldName} json data failed", fieldName);
|
||||
}
|
||||
|
||||
flgData?.AddRange(newValue ?? Array.Empty<uint>());
|
||||
var flgArray = flgData ?? new List<uint>();
|
||||
return JsonSerializer.Serialize(flgArray);
|
||||
}
|
||||
|
||||
private string UpdateJsonCostumeFlagArray(string originalValue, IReadOnlyList<IReadOnlyCollection<uint>?>? newValue)
|
||||
{
|
||||
var flgData = new List<List<uint>>();
|
||||
try
|
||||
{
|
||||
flgData = JsonSerializer.Deserialize<List<List<uint>>>(originalValue);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing Costume flag json data failed");
|
||||
}
|
||||
|
||||
if (flgData is null)
|
||||
{
|
||||
flgData = new List<List<uint>>();
|
||||
}
|
||||
|
||||
for (var index = 0; index < flgData.Count; index++)
|
||||
{
|
||||
var subFlgData = flgData[index];
|
||||
subFlgData.AddRange(newValue?[index] ?? Array.Empty<uint>());
|
||||
}
|
||||
|
||||
if (flgData.Count >= 5)
|
||||
{
|
||||
return JsonSerializer.Serialize(flgData);
|
||||
}
|
||||
|
||||
Logger.LogWarning("Costume flag array count less than 5!");
|
||||
flgData = new List<List<uint>>
|
||||
{
|
||||
new(), new(), new(), new(), new()
|
||||
};
|
||||
|
||||
return JsonSerializer.Serialize(flgData);
|
||||
}
|
||||
|
||||
private async Task UpdateBestData(PlayResultRequest request, StageData stageData)
|
||||
{
|
||||
var difficulty = (Difficulty)stageData.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
var existing = await songBestDatumService.GetSongBestData(request.BaidConf, stageData.SongNo, difficulty);
|
||||
|
||||
// Determine whether it is dondaful crown as this is not reflected by play result
|
||||
var crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt);
|
||||
|
||||
if (existing is null)
|
||||
{
|
||||
var songBestDatum = new SongBestDatum
|
||||
{
|
||||
Baid = request.BaidConf,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
BestScore = stageData.PlayScore,
|
||||
BestRate = stageData.ScoreRate,
|
||||
BestCrown = crown,
|
||||
BestScoreRank = (ScoreRank)stageData.ScoreRank
|
||||
};
|
||||
|
||||
await songBestDatumService.InsertSongBestData(songBestDatum);
|
||||
return;
|
||||
}
|
||||
|
||||
existing.UpdateBestData(crown, stageData.ScoreRank, stageData.PlayScore, stageData.ScoreRate);
|
||||
|
||||
await songBestDatumService.UpdateSongBestData(existing);
|
||||
}
|
||||
|
||||
private async Task UpdateAiBattleData(PlayResultRequest request, StageData stageData)
|
||||
{
|
||||
var difficulty = (Difficulty)stageData.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
var existing = await aiDatumService.GetSongAiScore(request.BaidConf,
|
||||
stageData.SongNo, difficulty);
|
||||
|
||||
if (existing is null)
|
||||
{
|
||||
var aiScoreDatum = new AiScoreDatum
|
||||
{
|
||||
Baid = request.BaidConf,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
IsWin = stageData.IsWin
|
||||
};
|
||||
for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
|
||||
{
|
||||
AddNewAiSectionScore(request, stageData, index, difficulty, aiScoreDatum);
|
||||
}
|
||||
|
||||
await aiDatumService.InsertSongAiScore(aiScoreDatum);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
|
||||
{
|
||||
var sectionData = stageData.ArySectionDatas[index];
|
||||
if (index < existing.AiSectionScoreData.Count)
|
||||
{
|
||||
existing.AiSectionScoreData[index].UpdateBest(sectionData);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddNewAiSectionScore(request, stageData, index, difficulty, existing);
|
||||
}
|
||||
}
|
||||
|
||||
await aiDatumService.UpdateSongAiScore(existing);
|
||||
}
|
||||
|
||||
private static void AddNewAiSectionScore(PlayResultRequest request, StageData stageData, int index, Difficulty difficulty,
|
||||
AiScoreDatum aiScoreDatum)
|
||||
{
|
||||
var sectionData = stageData.ArySectionDatas[index];
|
||||
var aiSectionScoreDatum = new AiSectionScoreDatum
|
||||
{
|
||||
Baid = request.BaidConf,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
SectionIndex = index,
|
||||
OkCount = sectionData.OkCnt,
|
||||
MissCount = sectionData.NgCnt
|
||||
};
|
||||
aiSectionScoreDatum.UpdateBest(sectionData);
|
||||
aiScoreDatum.AiSectionScoreData.Add(aiSectionScoreDatum);
|
||||
}
|
||||
|
||||
|
||||
private static CrownType PlayResultToCrown(uint playResult, uint okCount)
|
||||
{
|
||||
var crown = (CrownType)playResult;
|
||||
if (crown == CrownType.Gold && okCount == 0)
|
||||
{
|
||||
crown = CrownType.Dondaful;
|
||||
}
|
||||
|
||||
return crown;
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/rewarditem.php")]
|
||||
[ApiController]
|
||||
public class RewardItemController : BaseController<RewardItemController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/rewarditem.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult RewardItem([FromBody] RewardItemRequest request)
|
||||
{
|
||||
@ -17,4 +16,18 @@ public class RewardItemController : BaseController<RewardItemController>
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/rewarditem.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult RewardItem3209([FromBody] Models.v3209.RewardItemRequest request)
|
||||
{
|
||||
Logger.LogInformation("RewardItem request : {Request}", request.Stringify());
|
||||
|
||||
var response = new Models.v3209.RewardItemResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,81 +1,34 @@
|
||||
using GameDatabase.Entities;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/selfbest_5nz47auu.php")]
|
||||
[ApiController]
|
||||
public class SelfBestController : BaseController<SelfBestController>
|
||||
{
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
public SelfBestController(ISongBestDatumService songBestDatumService, IGameDataService gameDataService)
|
||||
{
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/selfbest_5nz47auu.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> SelfBest([FromBody] SelfBestRequest request)
|
||||
{
|
||||
Logger.LogInformation("SelfBest request : {Request}", request.Stringify());
|
||||
|
||||
var response = new SelfBestResponse
|
||||
{
|
||||
Result = 1,
|
||||
Level = request.Level
|
||||
};
|
||||
|
||||
var requestDifficulty = (Difficulty)request.Level;
|
||||
requestDifficulty.Throw().IfOutOfRange();
|
||||
|
||||
var playerBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
|
||||
playerBestData = playerBestData
|
||||
.Where(datum => datum.Difficulty == requestDifficulty ||
|
||||
(datum.Difficulty == Difficulty.UraOni && requestDifficulty == Difficulty.Oni))
|
||||
.ToList();
|
||||
foreach (var songNo in request.ArySongNoes)
|
||||
{
|
||||
if (!gameDataService.GetMusicList().Contains(songNo))
|
||||
{
|
||||
Logger.LogWarning("Music no {No} is missing!", songNo);
|
||||
continue;
|
||||
}
|
||||
|
||||
var selfBestData = GetSongSelfBestData(playerBestData, songNo);
|
||||
|
||||
response.ArySelfbestScores.Add(selfBestData);
|
||||
}
|
||||
response.ArySelfbestScores.Sort((data, otherData) => data.SongNo.CompareTo(otherData.SongNo));
|
||||
var commonResponse =
|
||||
await Mediator.Send(new GetSelfBestQuery(request.Baid, request.Level, request.ArySongNoes));
|
||||
var response = SelfBestMappers.MapTo3906(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
private static SelfBestResponse.SelfBestData GetSongSelfBestData(IEnumerable<SongBestDatum> playerBestData, uint songNo)
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/selfbest.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> SelfBest3209([FromBody] Models.v3209.SelfBestRequest request)
|
||||
{
|
||||
var songBestDatum = playerBestData.Where(datum => datum.SongId == songNo);
|
||||
Logger.LogInformation("SelfBest3209 request : {Request}", request.Stringify());
|
||||
|
||||
var selfBestData = new SelfBestResponse.SelfBestData
|
||||
{
|
||||
SongNo = songNo,
|
||||
};
|
||||
var commonResponse =
|
||||
await Mediator.Send(new GetSelfBestQuery((uint)request.Baid, request.Level, request.ArySongNoes));
|
||||
var response = SelfBestMappers.MapTo3209(commonResponse);
|
||||
|
||||
foreach (var datum in songBestDatum)
|
||||
{
|
||||
if (datum.Difficulty == Difficulty.UraOni)
|
||||
{
|
||||
selfBestData.UraBestScore = datum.BestScore;
|
||||
selfBestData.UraBestScoreRate = datum.BestRate;
|
||||
continue;
|
||||
}
|
||||
|
||||
selfBestData.SelfBestScore = datum.BestScore;
|
||||
selfBestData.SelfBestScoreRate = datum.BestRate;
|
||||
}
|
||||
|
||||
return selfBestData;
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/setanystring_mssxf3bo.php")]
|
||||
[ApiController]
|
||||
public class SetAnyStringController : BaseController<SetAnyStringController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/setanystring_mssxf3bo.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult SetAnyString([FromBody] SetAnyStringRequest request)
|
||||
{
|
||||
@ -17,4 +16,18 @@ public class SetAnyStringController : BaseController<SetAnyStringController>
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/setanystring.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult SetAnyString3209([FromBody] Models.v3209.SetAnyStringRequest request)
|
||||
{
|
||||
Logger.LogInformation("SetAnyString request : {Request}", request.Stringify());
|
||||
|
||||
var response = new Models.v3209.SetAnyStringResponse
|
||||
{
|
||||
Result = 1,
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,85 +1,30 @@
|
||||
using System.Text.Json;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/songpurchase_wm2fh5bl.php")]
|
||||
[ApiController]
|
||||
public class SongPurchaseController : BaseController<SongPurchaseController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
public SongPurchaseController(IUserDatumService userDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/songpurchase_wm2fh5bl.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> SongPurchase([FromBody] SongPurchaseRequest request)
|
||||
{
|
||||
Logger.LogInformation("SongPurchase request : {Request}", request.Stringify());
|
||||
|
||||
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
|
||||
/*var tokenCountDict = new Dictionary<uint, int>();
|
||||
try
|
||||
{
|
||||
tokenCountDict = !string.IsNullOrEmpty(user.TokenCountDict)
|
||||
? JsonSerializer.Deserialize<Dictionary<uint, int>>(user.TokenCountDict)
|
||||
: new Dictionary<uint, int>();
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing TokenCountDict data for user with baid {Request} failed!", request.Baid);
|
||||
}
|
||||
|
||||
tokenCountDict.ThrowIfNull("TokenCountDict should never be null");*/
|
||||
|
||||
Logger.LogInformation("Original UnlockedSongIdList: {UnlockedSongIdList}", user.UnlockedSongIdList);
|
||||
|
||||
var unlockedSongIdList = new List<uint>();
|
||||
try
|
||||
{
|
||||
unlockedSongIdList = !string.IsNullOrEmpty(user.UnlockedSongIdList)
|
||||
? JsonSerializer.Deserialize<List<uint>>(user.UnlockedSongIdList)
|
||||
: new List<uint>();
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing UnlockedSongIdList data for user with baid {Request} failed!", request.Baid);
|
||||
}
|
||||
|
||||
unlockedSongIdList.ThrowIfNull("UnlockedSongIdList should never be null");
|
||||
|
||||
//if (tokenCountDict.ContainsKey(request.TokenId)) tokenCountDict[request.TokenId] -= (int)request.Price;
|
||||
|
||||
var token = user.Tokens.FirstOrDefault(t => t.Id == request.TokenId);
|
||||
if (token is not null && token.Count >= request.Price)
|
||||
{
|
||||
token.Count -= (int)request.Price;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError("User with baid {Baid} does not have enough tokens to purchase song with id {SongNo}!", request.Baid, request.SongNo);
|
||||
return Ok(new SongPurchaseResponse { Result = 0 });
|
||||
}
|
||||
|
||||
if (!unlockedSongIdList.Contains(request.SongNo)) unlockedSongIdList.Add(request.SongNo);
|
||||
|
||||
user.UnlockedSongIdList = JsonSerializer.Serialize(unlockedSongIdList);
|
||||
|
||||
Logger.LogInformation("Updated UnlockedSongIdList: {UnlockedSongIdList}", user.UnlockedSongIdList);
|
||||
|
||||
await userDatumService.UpdateUserDatum(user);
|
||||
|
||||
var response = new SongPurchaseResponse
|
||||
{
|
||||
Result = 1,
|
||||
TokenCount = token.Count
|
||||
};
|
||||
var commonResponse = await Mediator.Send(SongPurchaseMappers.MapToCommand(request));
|
||||
var response = SongPurchaseMappers.MapTo3906(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/songpurchase.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> SongPurchase3209([FromBody] Models.v3209.SongPurchaseRequest request)
|
||||
{
|
||||
Logger.LogInformation("SongPurchase request : {Request}", request.Stringify());
|
||||
|
||||
var commonResponse = await Mediator.Send(SongPurchaseMappers.MapToCommand(request));
|
||||
var response = SongPurchaseMappers.MapTo3209(commonResponse);
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/tournamentcheck.php")]
|
||||
[ApiController]
|
||||
public class TournamentCheckController : BaseController<TournamentCheckController>
|
||||
{
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/tournamentcheck.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult TournamentCheck([FromBody] TournamentcheckRequest request)
|
||||
{
|
||||
@ -17,4 +16,18 @@ public class TournamentCheckController : BaseController<TournamentCheckControlle
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/tournamentcheck.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult TournamentCheck3209([FromBody] Models.v3209.TournamentcheckRequest request)
|
||||
{
|
||||
Logger.LogInformation("TournamentCheck request : {Request}", request.Stringify());
|
||||
|
||||
var response = new Models.v3209.TournamentcheckResponse
|
||||
{
|
||||
Result = 1,
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
}
|
@ -1,178 +1,31 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text.Json;
|
||||
using TaikoLocalServer.Settings;
|
||||
using Throw;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Mappers;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/userdata_gc6x17o8.php")]
|
||||
[ApiController]
|
||||
public class UserDataController : BaseController<UserDataController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
private readonly ISongPlayDatumService songPlayDatumService;
|
||||
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public UserDataController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService,
|
||||
IGameDataService gameDataService, IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.songPlayDatumService = songPlayDatumService;
|
||||
this.gameDataService = gameDataService;
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/userdata_gc6x17o8.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetUserData([FromBody] UserDataRequest request)
|
||||
{
|
||||
Logger.LogInformation("UserData request : {Request}", request.Stringify());
|
||||
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var commonResponse = await Mediator.Send(new UserDataQuery(request.Baid));
|
||||
var response = UserDataMappers.MapTo3906(commonResponse);
|
||||
|
||||
var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid);
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/userdata.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public async Task<IActionResult> GetUserData3209([FromBody] Models.v3209.UserDataRequest request)
|
||||
{
|
||||
Logger.LogInformation("UserData request : {Request}", request.Stringify());
|
||||
|
||||
var unlockedSongIdList = new List<uint>();
|
||||
try
|
||||
{
|
||||
unlockedSongIdList = !string.IsNullOrEmpty(userData.UnlockedSongIdList)
|
||||
? JsonSerializer.Deserialize<List<uint>>(userData.UnlockedSongIdList)
|
||||
: new List<uint>();
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing UnlockedSongIdList data for user with baid {Request} failed!", request.Baid);
|
||||
}
|
||||
|
||||
unlockedSongIdList.ThrowIfNull("UnlockedSongIdList should never be null");
|
||||
|
||||
var musicList = gameDataService.GetMusicList();
|
||||
var lockedSongsList = gameDataService.GetLockedSongsList();
|
||||
lockedSongsList = lockedSongsList.Except(unlockedSongIdList).ToList();
|
||||
var enabledMusicList = musicList.Except(lockedSongsList);
|
||||
var releaseSongArray =
|
||||
FlagCalculator.GetBitArrayFromIds(enabledMusicList, songIdMax, Logger);
|
||||
|
||||
var defaultSongWithUraList = gameDataService.GetMusicWithUraList();
|
||||
var enabledUraMusicList = defaultSongWithUraList.Except(lockedSongsList);
|
||||
var uraSongArray =
|
||||
FlagCalculator.GetBitArrayFromIds(enabledUraMusicList, songIdMax, Logger);
|
||||
|
||||
var toneFlg = Array.Empty<uint>();
|
||||
try
|
||||
{
|
||||
toneFlg = JsonSerializer.Deserialize<uint[]>(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!");
|
||||
|
||||
// If toneFlg is empty, add 0 to it
|
||||
if (toneFlg.Length == 0)
|
||||
{
|
||||
toneFlg = new uint[] { 0 };
|
||||
userData.ToneFlgArray = JsonSerializer.Serialize(toneFlg);
|
||||
await userDatumService.UpdateUserDatum(userData);
|
||||
}
|
||||
|
||||
var toneArray = FlagCalculator.GetBitArrayFromIds(toneFlg, gameDataService.GetToneFlagArraySize(), Logger);
|
||||
|
||||
var titleFlg = Array.Empty<uint>();
|
||||
try
|
||||
{
|
||||
titleFlg = JsonSerializer.Deserialize<uint[]>(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 = FlagCalculator.GetBitArrayFromIds(titleFlg, gameDataService.GetTitleFlagArraySize(), Logger);
|
||||
|
||||
var recentSongs = (await songPlayDatumService.GetSongPlayDatumByBaid(request.Baid))
|
||||
.AsEnumerable()
|
||||
.OrderByDescending(datum => datum.PlayTime)
|
||||
.ThenByDescending(datum => datum.SongNumber)
|
||||
.Select(datum => datum.SongId)
|
||||
.ToArray();
|
||||
|
||||
// Use custom implementation as distinctby cannot guarantee preserved element
|
||||
var recentSet = new OrderedSet<uint>();
|
||||
foreach (var id in recentSongs)
|
||||
{
|
||||
recentSet.Add(id);
|
||||
if (recentSet.Count == 10)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
recentSongs = recentSet.ToArray();
|
||||
|
||||
var favoriteSongs = Array.Empty<uint>();
|
||||
try
|
||||
{
|
||||
favoriteSongs = JsonSerializer.Deserialize<uint[]>(userData.FavoriteSongsArray);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
Logger.LogError(e, "Parsing favorite songs 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
|
||||
favoriteSongs.ThrowIfNull("Favorite song should never be null!");
|
||||
|
||||
var defaultOptions = new byte[2];
|
||||
BinaryPrimitives.WriteInt16LittleEndian(defaultOptions, userData.OptionSetting);
|
||||
|
||||
var difficultySettingArray = JsonHelper.GetUIntArrayFromJson(userData.DifficultySettingArray, 3, Logger, nameof(userData.DifficultySettingArray));
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (difficultySettingArray[i] >= 2)
|
||||
{
|
||||
difficultySettingArray[i] -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
var difficultyPlayedArray = JsonHelper.GetUIntArrayFromJson(userData.DifficultyPlayedArray, 3, Logger, nameof(userData.DifficultyPlayedArray));
|
||||
|
||||
var response = new UserDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
ToneFlg = toneArray,
|
||||
TitleFlg = titleArray,
|
||||
ReleaseSongFlg = releaseSongArray,
|
||||
UraReleaseSongFlg = uraSongArray,
|
||||
AryFavoriteSongNoes = favoriteSongs,
|
||||
AryRecentSongNoes = recentSongs,
|
||||
DefaultOptionSetting = defaultOptions,
|
||||
NotesPosition = userData.NotesPosition,
|
||||
IsVoiceOn = userData.IsVoiceOn,
|
||||
IsSkipOn = userData.IsSkipOn,
|
||||
DifficultySettingCourse = difficultySettingArray[0],
|
||||
DifficultySettingStar = difficultySettingArray[1],
|
||||
DifficultySettingSort = difficultySettingArray[2],
|
||||
DifficultyPlayedCourse = difficultyPlayedArray[0],
|
||||
DifficultyPlayedStar = difficultyPlayedArray[1],
|
||||
DifficultyPlayedSort = difficultyPlayedArray[2],
|
||||
IsChallengecompe = false,
|
||||
SongRecentCnt = (uint)recentSongs.Length
|
||||
};
|
||||
var commonResponse = await Mediator.Send(new UserDataQuery((uint)request.Baid));
|
||||
var response = UserDataMappers.MapTo3209(commonResponse);
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r08_ww/chassis/verifyqrcode_ku5ra5q7.php")]
|
||||
[ApiController]
|
||||
public class VerifyQrCodeController : BaseController<VerifyQrCodeController>
|
||||
{
|
||||
@ -11,27 +10,60 @@ public class VerifyQrCodeController : BaseController<VerifyQrCodeController>
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[HttpPost("/v12r08_ww/chassis/verifyqrcode_ku5ra5q7.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult VerifyQrCode([FromBody] VerifyQrcodeRequest request)
|
||||
{
|
||||
Logger.LogInformation("VerifyQrCode request : {Request}", request.Stringify());
|
||||
|
||||
var qrCodeDataDictionary = gameDataService.GetQRCodeDataDictionary();
|
||||
|
||||
qrCodeDataDictionary.TryGetValue(request.QrcodeSerial, out var qrCodeId);
|
||||
|
||||
if (qrCodeId == 0)
|
||||
{
|
||||
Logger.LogWarning("Requested QR code serial {Serial} does not exist!", request.QrcodeSerial);
|
||||
}
|
||||
|
||||
var qrCodeId = VerifyQr(request.QrcodeSerial);
|
||||
var response = new VerifyQrcodeResponse
|
||||
{
|
||||
Result = 1,
|
||||
QrcodeId = qrCodeId
|
||||
QrcodeId = (uint)qrCodeId
|
||||
};
|
||||
|
||||
if (qrCodeId == -1)
|
||||
{
|
||||
response.Result = 51;
|
||||
}
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
[HttpPost("/v12r00_cn/chassis/verifyqrcode.php")]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult VerifyQrCode3209([FromBody] Models.v3209.VerifyQrcodeRequest request)
|
||||
{
|
||||
Logger.LogInformation("VerifyQrCode request : {Request}", request.Stringify());
|
||||
|
||||
var qrCodeId = VerifyQr(request.QrcodeSerial);
|
||||
var response = new Models.v3209.VerifyQrcodeResponse
|
||||
{
|
||||
Result = 1,
|
||||
QrcodeId = (uint)qrCodeId
|
||||
};
|
||||
|
||||
if (qrCodeId == -1)
|
||||
{
|
||||
response.Result = 51;
|
||||
}
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
private int VerifyQr(string serial)
|
||||
{
|
||||
var qrCodeDataDictionary = gameDataService.GetQRCodeDataDictionary();
|
||||
|
||||
qrCodeDataDictionary.TryGetValue(serial, out var qrCodeId);
|
||||
|
||||
if (qrCodeId == 0)
|
||||
{
|
||||
Logger.LogWarning("Requested QR code serial {Serial} does not exist!", serial);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)qrCodeId;
|
||||
}
|
||||
}
|
@ -1,8 +1,4 @@
|
||||
using System.Buffers;
|
||||
using System.Security.Cryptography;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoLocalServer.Settings;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
@ -1,7 +1,4 @@
|
||||
using System.Buffers;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoLocalServer.Settings;
|
||||
using System.Text.Json;
|
||||
using System.Text;
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
@ -2,9 +2,10 @@
|
||||
global using Microsoft.AspNetCore.Mvc;
|
||||
global using Microsoft.EntityFrameworkCore;
|
||||
global using ProtoBuf;
|
||||
global using MediatR;
|
||||
global using Swan.Formatters;
|
||||
global using SharedProject.Enums;
|
||||
global using taiko.game;
|
||||
global using TaikoLocalServer.Models.v3906;
|
||||
global using TaikoLocalServer.Common;
|
||||
global using TaikoLocalServer.Common.Utils;
|
||||
global using TaikoLocalServer.Models;
|
||||
|
68
TaikoLocalServer/Handlers/AddMyDonEntryCommand.cs
Normal file
68
TaikoLocalServer/Handlers/AddMyDonEntryCommand.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using GameDatabase.Context;
|
||||
using GameDatabase.Entities;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
public record AddMyDonEntryCommand(string AccessCode, string Name, uint Language) : IRequest<CommonMyDonEntryResponse>;
|
||||
|
||||
public class AddMyDonEntryCommandHandler(TaikoDbContext context, ILogger<AddMyDonEntryCommandHandler> logger)
|
||||
: IRequestHandler<AddMyDonEntryCommand, CommonMyDonEntryResponse>
|
||||
{
|
||||
public async Task<CommonMyDonEntryResponse> Handle(AddMyDonEntryCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var nextBaid = await context.Cards.Select(card => card.Baid)
|
||||
.DefaultIfEmpty()
|
||||
.MaxAsync(cancellationToken) + 1;
|
||||
var newUser = new UserDatum
|
||||
{
|
||||
Baid = nextBaid,
|
||||
MyDonName = request.Name,
|
||||
MyDonNameLanguage = request.Language,
|
||||
DisplayDan = true,
|
||||
DisplayAchievement = true,
|
||||
AchievementDisplayDifficulty = Difficulty.None,
|
||||
ColorFace = 0,
|
||||
ColorBody = 1,
|
||||
ColorLimb = 3,
|
||||
FavoriteSongsArray = [],
|
||||
ToneFlgArray = [0],
|
||||
TitleFlgArray = [],
|
||||
UnlockedKigurumi = [0],
|
||||
UnlockedBody = [0],
|
||||
UnlockedFace = [0],
|
||||
UnlockedHead = [0],
|
||||
UnlockedPuchi = [0],
|
||||
GenericInfoFlgArray = [],
|
||||
UnlockedSongIdList = []
|
||||
};
|
||||
|
||||
context.UserData.Add(newUser);
|
||||
|
||||
var newCard = new Card
|
||||
{
|
||||
AccessCode = request.AccessCode,
|
||||
Baid = nextBaid
|
||||
};
|
||||
context.Cards.Add(newCard);
|
||||
|
||||
var newCredential = new Credential
|
||||
{
|
||||
Baid = nextBaid,
|
||||
Password = "",
|
||||
Salt = ""
|
||||
};
|
||||
context.Credentials.Add(newCredential);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
var response = new CommonMyDonEntryResponse
|
||||
{
|
||||
Result = 1,
|
||||
Baid = nextBaid,
|
||||
MydonName = request.Name,
|
||||
MydonNameLanguage = request.Language,
|
||||
ComSvrResult = 1,
|
||||
AccessCode = request.AccessCode
|
||||
};
|
||||
return response;
|
||||
}
|
||||
}
|
52
TaikoLocalServer/Handlers/AddTokenCountCommand.cs
Normal file
52
TaikoLocalServer/Handlers/AddTokenCountCommand.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using GameDatabase.Context;
|
||||
using GameDatabase.Entities;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record AddTokenCountCommand(CommonAddTokenCountRequest Request) : IRequest;
|
||||
|
||||
public class AddTokenCountCommandHandler : IRequestHandler<AddTokenCountCommand>
|
||||
{
|
||||
private readonly TaikoDbContext context;
|
||||
|
||||
private readonly ILogger<AddTokenCountCommandHandler> logger;
|
||||
|
||||
public AddTokenCountCommandHandler(TaikoDbContext context, ILogger<AddTokenCountCommandHandler> logger)
|
||||
{
|
||||
this.context = context;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async Task Handle(AddTokenCountCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
var request = command.Request;
|
||||
var user = await context.UserData
|
||||
.Include(userDatum => userDatum.Tokens)
|
||||
.FirstOrDefaultAsync(datum => datum.Baid == request.Baid, cancellationToken);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
|
||||
foreach (var addTokenCountData in request.AryAddTokenCountDatas)
|
||||
{
|
||||
var tokenId = addTokenCountData.TokenId;
|
||||
var addTokenCount = addTokenCountData.AddTokenCount;
|
||||
var token = user.Tokens.FirstOrDefault(t => t.Id == tokenId);
|
||||
if (token is not null)
|
||||
{
|
||||
token.Count += addTokenCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.Tokens.Add(new Token
|
||||
{
|
||||
Id = (int)tokenId,
|
||||
Count = addTokenCount
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
context.Update(user);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
138
TaikoLocalServer/Handlers/BaidQuery.cs
Normal file
138
TaikoLocalServer/Handlers/BaidQuery.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using GameDatabase.Context;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record BaidQuery(string AccessCode) : IRequest<CommonBaidResponse>;
|
||||
|
||||
public class BaidQueryHandler(
|
||||
TaikoDbContext context,
|
||||
ILogger<BaidQueryHandler> logger,
|
||||
IGameDataService gameDataService)
|
||||
: IRequestHandler<BaidQuery, CommonBaidResponse>
|
||||
{
|
||||
public async Task<CommonBaidResponse> Handle(BaidQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var card = await context.Cards.FindAsync(request.AccessCode);
|
||||
if (card is null)
|
||||
{
|
||||
logger.LogInformation("New user with access code {AccessCode}", request.AccessCode);
|
||||
return new CommonBaidResponse
|
||||
{
|
||||
Result = 1,
|
||||
IsNewUser = true,
|
||||
Baid = context.Cards.Any() ? context.Cards.AsEnumerable().Max(c => c.Baid) + 1 : 1
|
||||
};
|
||||
}
|
||||
|
||||
var baid = card.Baid;
|
||||
var userData = await context.UserData.FindAsync(baid, cancellationToken);
|
||||
userData.ThrowIfNull($"User not found for card with Baid {baid}!");
|
||||
|
||||
var songBestData = context.SongBestData.Where(datum => datum.Baid == baid).ToList();
|
||||
var achievementDisplayDifficulty = userData.AchievementDisplayDifficulty;
|
||||
if (achievementDisplayDifficulty == Difficulty.None)
|
||||
{
|
||||
achievementDisplayDifficulty = songBestData
|
||||
.Where(datum => datum.BestCrown >= CrownType.Clear)
|
||||
.Select(datum => datum.Difficulty)
|
||||
.DefaultIfEmpty(Difficulty.Easy)
|
||||
.Max();
|
||||
}
|
||||
// For each crown type, calculate how many songs have that crown type
|
||||
var crownCountData = songBestData
|
||||
.Where(datum => datum.BestCrown >= CrownType.Clear)
|
||||
.GroupBy(datum => datum.BestCrown)
|
||||
.ToDictionary(datums => datums.Key, datums => (uint)datums.Count());
|
||||
var crownCount = new uint[3];
|
||||
foreach (var crownType in Enum.GetValues<CrownType>())
|
||||
{
|
||||
if (crownType != CrownType.None)
|
||||
{
|
||||
crownCount[(int)crownType - 1] = crownCountData.GetValueOrDefault(crownType, (uint)0);
|
||||
}
|
||||
}
|
||||
|
||||
var scoreRankData = songBestData
|
||||
.Where(datum => datum.BestCrown >= CrownType.Clear)
|
||||
.GroupBy(datum => datum.BestScoreRank)
|
||||
.ToDictionary(datums => datums.Key, datums => (uint)datums.Count());
|
||||
var scoreRankCount = new uint[7];
|
||||
foreach (var scoreRank in Enum.GetValues<ScoreRank>())
|
||||
{
|
||||
if (scoreRank != ScoreRank.None)
|
||||
{
|
||||
scoreRankCount[(int)scoreRank - 2] = scoreRankData.GetValueOrDefault(scoreRank, (uint)0);
|
||||
}
|
||||
}
|
||||
|
||||
var costumeData = JsonHelper.GetCostumeDataFromUserData(userData, logger);
|
||||
|
||||
List<List<uint>> costumeArrays =
|
||||
[userData.UnlockedKigurumi, userData.UnlockedHead, userData.UnlockedBody, userData.UnlockedFace, userData.UnlockedPuchi];
|
||||
|
||||
var costumeFlagArrays = gameDataService.GetCostumeFlagArraySizes()
|
||||
.Select((size, index) => FlagCalculator.GetBitArrayFromIds(costumeArrays[index], size, logger))
|
||||
.ToList();
|
||||
|
||||
var danData = await context.DanScoreData
|
||||
.Where(datum => datum.Baid == baid && datum.DanType == DanType.Normal)
|
||||
.Include(datum => datum.DanStageScoreData).ToListAsync(cancellationToken);
|
||||
var gaidenData = await context.DanScoreData
|
||||
.Where(datum => datum.Baid == baid && datum.DanType == DanType.Gaiden)
|
||||
.Include(datum => datum.DanStageScoreData).ToListAsync(cancellationToken);
|
||||
|
||||
var maxDan = danData.Where(datum => datum.ClearState != DanClearState.NotClear)
|
||||
.Select(datum => datum.DanId)
|
||||
.DefaultIfEmpty()
|
||||
.Max();
|
||||
|
||||
var danDataDictionary = gameDataService.GetCommonDanDataDictionary();
|
||||
var danIdList = danDataDictionary.Keys.ToList();
|
||||
var gotDanFlagArray = FlagCalculator.ComputeGotDanFlags(danData, danIdList);
|
||||
|
||||
var gaidenDataDictionary = gameDataService.GetCommonGaidenDataDictionary();
|
||||
var gaidenIdList = gaidenDataDictionary.Keys.ToList();
|
||||
var gotGaidenFlagArray = FlagCalculator.ComputeGotDanFlags(gaidenData, gaidenIdList);
|
||||
|
||||
var genericInfoFlg = userData.GenericInfoFlgArray;
|
||||
|
||||
var genericInfoFlgLength = genericInfoFlg.Any() ? genericInfoFlg.Max() + 1 : 0;
|
||||
var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, logger);
|
||||
|
||||
var aiRank = (uint)(userData.AiWinCount / 10);
|
||||
if (aiRank > 11)
|
||||
{
|
||||
aiRank = 11;
|
||||
}
|
||||
|
||||
return new CommonBaidResponse
|
||||
{
|
||||
Result = 1,
|
||||
IsNewUser = false,
|
||||
Baid = baid,
|
||||
MyDonName = userData.MyDonName,
|
||||
MyDonNameLanguage = userData.MyDonNameLanguage,
|
||||
AryCrownCounts = crownCount,
|
||||
AryScoreRankCounts = scoreRankCount,
|
||||
ColorBody = userData.ColorBody,
|
||||
ColorFace = userData.ColorFace,
|
||||
ColorLimb = userData.ColorLimb,
|
||||
CostumeData = costumeData,
|
||||
CostumeFlagArrays = costumeFlagArrays,
|
||||
DisplayDan = userData.DisplayDan,
|
||||
DispAchievementType = (uint)achievementDisplayDifficulty,
|
||||
GenericInfoFlg = genericInfoFlgArray,
|
||||
GotDanFlg = gotDanFlagArray,
|
||||
GotDanMax = maxDan,
|
||||
GotGaidenFlg = gotGaidenFlagArray,
|
||||
IsDispAchievementOn = userData.DisplayAchievement,
|
||||
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT),
|
||||
LastPlayMode = userData.LastPlayMode,
|
||||
SelectedToneId = userData.SelectedToneId,
|
||||
Title = userData.Title,
|
||||
TitlePlateId = userData.TitlePlateId
|
||||
};
|
||||
}
|
||||
}
|
35
TaikoLocalServer/Handlers/GetAiDataQuery.cs
Normal file
35
TaikoLocalServer/Handlers/GetAiDataQuery.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using GameDatabase.Context;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetAiDataQuery(uint Baid) : IRequest<CommonAiDataResponse>;
|
||||
|
||||
public class GetAiDataQueryHandler : IRequestHandler<GetAiDataQuery, CommonAiDataResponse>
|
||||
{
|
||||
private readonly TaikoDbContext context;
|
||||
|
||||
private readonly ILogger<GetAiDataQueryHandler> logger;
|
||||
|
||||
|
||||
public GetAiDataQueryHandler(TaikoDbContext context, ILogger<GetAiDataQueryHandler> logger)
|
||||
{
|
||||
this.context = context;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async Task<CommonAiDataResponse> Handle(GetAiDataQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await context.UserData.FirstOrDefaultAsync(datum => datum.Baid == request.Baid);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
var response = new CommonAiDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
TotalWinnings = (uint)user.AiWinCount,
|
||||
InputMedian = "1",
|
||||
InputVariance = "0"
|
||||
};
|
||||
return response;
|
||||
}
|
||||
}
|
33
TaikoLocalServer/Handlers/GetAiScoreQuery.cs
Normal file
33
TaikoLocalServer/Handlers/GetAiScoreQuery.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using GameDatabase.Context;
|
||||
using TaikoLocalServer.Mappers;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetAiScoreQuery(uint Baid, uint SongId, uint Level) : IRequest<CommonAiScoreResponse>;
|
||||
|
||||
public class GetAiScoreQueryHandler(TaikoDbContext context, ILogger<GetAiScoreQueryHandler> logger)
|
||||
: IRequestHandler<GetAiScoreQuery, CommonAiScoreResponse>
|
||||
{
|
||||
public async Task<CommonAiScoreResponse> Handle(GetAiScoreQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var difficulty = (Difficulty)request.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
|
||||
var aiData = await context.AiScoreData.Where(datum => datum.Baid == request.Baid &&
|
||||
datum.SongId == request.SongId &&
|
||||
datum.Difficulty == difficulty)
|
||||
.Include(datum => datum.AiSectionScoreData)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
if (aiData is null)
|
||||
{
|
||||
return new CommonAiScoreResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
}
|
||||
aiData.AiSectionScoreData.Sort((a, b) => a.SectionIndex.CompareTo(b.SectionIndex));
|
||||
return AiScoreMappers.MapAsSuccess(aiData);
|
||||
}
|
||||
}
|
52
TaikoLocalServer/Handlers/GetDanOdaiQuery.cs
Normal file
52
TaikoLocalServer/Handlers/GetDanOdaiQuery.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using SharedProject.Models;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetDanOdaiQuery(uint[] DanIds, uint Type) : IRequest<List<DanData>>;
|
||||
|
||||
public class GetDanOdaiQueryHandler : IRequestHandler<GetDanOdaiQuery, List<DanData>>
|
||||
{
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
public GetDanOdaiQueryHandler(IGameDataService gameDataService)
|
||||
{
|
||||
this.gameDataService = gameDataService;
|
||||
}
|
||||
|
||||
public Task<List<DanData>> Handle(GetDanOdaiQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var type = (DanType)request.Type;
|
||||
type.Throw().IfOutOfRange();
|
||||
var danDataList = new List<DanData>();
|
||||
switch (type)
|
||||
{
|
||||
case DanType.Normal:
|
||||
var danDataDictionary = gameDataService.GetCommonDanDataDictionary();
|
||||
|
||||
foreach (var danId in request.DanIds)
|
||||
{
|
||||
if (danDataDictionary.TryGetValue(danId, out var danData))
|
||||
{
|
||||
danDataList.Add(danData);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DanType.Gaiden:
|
||||
var gaidenDataDictionary = gameDataService.GetCommonGaidenDataDictionary();
|
||||
|
||||
foreach (var danId in request.DanIds)
|
||||
{
|
||||
if (gaidenDataDictionary.TryGetValue(danId, out var danData))
|
||||
{
|
||||
danDataList.Add(danData);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ApplicationException("Impossible");
|
||||
}
|
||||
|
||||
return Task.FromResult(danDataList);
|
||||
}
|
||||
}
|
73
TaikoLocalServer/Handlers/GetDanScoreQuery.cs
Normal file
73
TaikoLocalServer/Handlers/GetDanScoreQuery.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using GameDatabase.Context;
|
||||
using GameDatabase.Entities;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetDanScoreQuery(uint Baid, uint Type, uint[] DanIds) : IRequest<CommonDanScoreDataResponse>;
|
||||
|
||||
public class GetDanScoreQueryHandler : IRequestHandler<GetDanScoreQuery, CommonDanScoreDataResponse>
|
||||
{
|
||||
private readonly ILogger<GetDanScoreQueryHandler> logger;
|
||||
private readonly TaikoDbContext context;
|
||||
|
||||
|
||||
public GetDanScoreQueryHandler(ILogger<GetDanScoreQueryHandler> logger, TaikoDbContext context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public async Task<CommonDanScoreDataResponse> Handle(GetDanScoreQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var danType = (DanType)request.Type;
|
||||
danType.Throw().IfOutOfRange();
|
||||
|
||||
var idList = request.DanIds.ToList();
|
||||
// Select the dan score data from the database where baid and type matches and danid is in the list of danids
|
||||
var danScoreData = await context.DanScoreData
|
||||
.Where(d => d.Baid == request.Baid && d.DanType == danType &&idList.Contains(d.DanId))
|
||||
.Include(d => d.DanStageScoreData)
|
||||
.ToListAsync(cancellationToken);
|
||||
var response = new CommonDanScoreDataResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
foreach (var danScoreDatum in danScoreData)
|
||||
{
|
||||
var responseData = new CommonDanScoreDataResponse.DanScoreData
|
||||
{
|
||||
DanId = danScoreDatum.DanId,
|
||||
ArrivalSongCnt = danScoreDatum.ArrivalSongCount,
|
||||
ComboCntTotal = danScoreDatum.ComboCountTotal,
|
||||
SoulGaugeTotal = danScoreDatum.SoulGaugeTotal
|
||||
};
|
||||
for (int i = 0; i < danScoreDatum.ArrivalSongCount; i++)
|
||||
{
|
||||
var songNumber = i;
|
||||
var stageScoreDatum = danScoreDatum.DanStageScoreData.FirstOrDefault(d => d.SongNumber == songNumber);
|
||||
if (stageScoreDatum is null)
|
||||
{
|
||||
logger.LogWarning("Stage score data for dan {DanId} song number {SongNumber} not found", danScoreDatum.DanId, songNumber);
|
||||
stageScoreDatum = new DanStageScoreDatum();
|
||||
}
|
||||
responseData.AryDanScoreDataStages.Add(new CommonDanScoreDataResponse.DanScoreDataStage
|
||||
{
|
||||
PlayScore = stageScoreDatum.PlayScore,
|
||||
GoodCnt = stageScoreDatum.GoodCount,
|
||||
OkCnt = stageScoreDatum.OkCount,
|
||||
NgCnt = stageScoreDatum.BadCount,
|
||||
PoundCnt = stageScoreDatum.DrumrollCount,
|
||||
HitCnt = stageScoreDatum.TotalHitCount,
|
||||
ComboCnt = stageScoreDatum.ComboCount,
|
||||
HighScore = stageScoreDatum.HighScore
|
||||
});
|
||||
}
|
||||
response.AryDanScoreDatas.Add(responseData);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
|
29
TaikoLocalServer/Handlers/GetFolderQuery.cs
Normal file
29
TaikoLocalServer/Handlers/GetFolderQuery.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetFolderQuery(uint[] FolderIds) : IRequest<CommonGetFolderResponse>;
|
||||
|
||||
public class GetFolderQueryHandler(ILogger<GetFolderQueryHandler> logger, IGameDataService gameDataService)
|
||||
: IRequestHandler<GetFolderQuery, CommonGetFolderResponse>
|
||||
{
|
||||
public Task<CommonGetFolderResponse> Handle(GetFolderQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new CommonGetFolderResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
var eventFolders = gameDataService.GetEventFolderDictionary();
|
||||
foreach (var folderId in request.FolderIds)
|
||||
{
|
||||
eventFolders.TryGetValue(folderId, out var folderData);
|
||||
if (folderData is null)
|
||||
{
|
||||
logger.LogWarning("Folder data for folder {FolderId} not found", folderId);
|
||||
continue;
|
||||
}
|
||||
response.AryEventfolderDatas.Add(folderData);
|
||||
}
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
102
TaikoLocalServer/Handlers/GetInitialDataQuery.cs
Normal file
102
TaikoLocalServer/Handlers/GetInitialDataQuery.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SharedProject.Models;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using TaikoLocalServer.Settings;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetInitialDataQuery : IRequest<CommonInitialDataCheckResponse>;
|
||||
|
||||
public class GetInitialDataQueryHandler(IGameDataService gameDataService,
|
||||
ILogger<GetInitialDataQueryHandler> logger,
|
||||
IOptions<ServerSettings> settings)
|
||||
: IRequestHandler<GetInitialDataQuery, CommonInitialDataCheckResponse>
|
||||
{
|
||||
|
||||
private readonly ServerSettings settings = settings.Value;
|
||||
|
||||
public Task<CommonInitialDataCheckResponse> Handle(GetInitialDataQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
|
||||
var musicList = gameDataService.GetMusicList();
|
||||
var lockedSongsList = gameDataService.GetLockedSongsList();
|
||||
|
||||
var enabledArray =
|
||||
FlagCalculator.GetBitArrayFromIds(musicList, songIdMax, logger);
|
||||
|
||||
var defaultSongList = musicList.Except(lockedSongsList);
|
||||
var defaultSongFlg =
|
||||
FlagCalculator.GetBitArrayFromIds(defaultSongList, songIdMax, logger);
|
||||
|
||||
var defaultSongWithUraList = gameDataService.GetMusicWithUraList();
|
||||
var uraReleaseBit =
|
||||
FlagCalculator.GetBitArrayFromIds(defaultSongWithUraList, songIdMax, logger);
|
||||
|
||||
var response = new CommonInitialDataCheckResponse
|
||||
{
|
||||
Result = 1,
|
||||
DefaultSongFlg = defaultSongFlg,
|
||||
AchievementSongBit = enabledArray,
|
||||
UraReleaseBit = uraReleaseBit,
|
||||
SongIntroductionEndDatetime = DateTime.Now.AddYears(10).ToString(Constants.DATE_TIME_FORMAT),
|
||||
ServerCurrentDatetime = (ulong)DateTimeOffset.Now.ToUnixTimeSeconds()
|
||||
};
|
||||
|
||||
var movieDataDictionary = gameDataService.GetMovieDataDictionary();
|
||||
foreach (var movieData in movieDataDictionary)
|
||||
{
|
||||
response.AryMovieInfoes.Add(movieData.Value);
|
||||
}
|
||||
|
||||
// TODO: Figure out what they are individually
|
||||
var verupNo1 = new uint[] { 2, 3, 4, 5, 6, 7, 8, 13, 15, 24, 25, 26, 27, 28, 29, 30, 31, 104 };
|
||||
var aryVerUp = verupNo1.Select(i => new CommonInitialDataCheckResponse.VerupNoData1
|
||||
{
|
||||
MasterType = i,
|
||||
VerupNo = 1
|
||||
}).ToList();
|
||||
response.AryVerupNoData1s.AddRange(aryVerUp);
|
||||
|
||||
var commonDanDataDictionary = gameDataService.GetCommonDanDataDictionary();
|
||||
var commonGaidenDataDictionary = gameDataService.GetCommonGaidenDataDictionary();
|
||||
var eventFolderDictionary = gameDataService.GetEventFolderDictionary();
|
||||
var songIntroDictionary = gameDataService.GetSongIntroductionDictionary();
|
||||
|
||||
CommonInitialDataCheckResponse.VerupNoData2[] verupNo2List =
|
||||
[
|
||||
GetVerupNoData2(Constants.DAN_VERUP_MASTER_TYPE, commonDanDataDictionary),
|
||||
GetVerupNoData2(Constants.GAIDEN_VERUP_MASTER_TYPE, commonGaidenDataDictionary),
|
||||
GetVerupNoData2(Constants.FOLDER_VERUP_MASTER_TYPE, eventFolderDictionary),
|
||||
GetVerupNoData2(Constants.INTRO_VERUP_MASTER_TYPE, songIntroDictionary)
|
||||
];
|
||||
response.AryVerupNoData2s.AddRange(verupNo2List);
|
||||
|
||||
response.AryChassisFunctionIds =
|
||||
[
|
||||
Constants.FUNCTION_ID_DANI_AVAILABLE,
|
||||
Constants.FUNCTION_ID_DANI_FOLDER_AVAILABLE,
|
||||
Constants.FUNCTION_ID_AI_BATTLE_AVAILABLE
|
||||
];
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
private CommonInitialDataCheckResponse.VerupNoData2 GetVerupNoData2<T>(uint masterType, ImmutableDictionary<uint, T> dictionary)
|
||||
where T:IVerupNo
|
||||
{
|
||||
var infoData = dictionary.Select(pair => new CommonInitialDataCheckResponse.VerupNoData2.InformationData
|
||||
{
|
||||
InfoId = pair.Key,
|
||||
VerupNo = pair.Value.VerupNo
|
||||
}).ToList();
|
||||
return new CommonInitialDataCheckResponse.VerupNoData2
|
||||
{
|
||||
MasterType = masterType,
|
||||
AryInformationDatas = infoData
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
57
TaikoLocalServer/Handlers/GetSelfBestQuery.cs
Normal file
57
TaikoLocalServer/Handlers/GetSelfBestQuery.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using GameDatabase.Context;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetSelfBestQuery(uint Baid, uint Difficulty, uint[] SongIdList) : IRequest<CommonSelfBestResponse>;
|
||||
|
||||
public class GetSelfBestQueryHandler(IGameDataService gameDataService, TaikoDbContext context, ILogger<GetSelfBestQueryHandler> logger)
|
||||
: IRequestHandler<GetSelfBestQuery, CommonSelfBestResponse>
|
||||
{
|
||||
public async Task<CommonSelfBestResponse> Handle(GetSelfBestQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var requestDifficulty = (Difficulty)request.Difficulty;
|
||||
requestDifficulty.Throw().IfOutOfRange();
|
||||
|
||||
var allSongSet = gameDataService.GetMusicList().ToHashSet();
|
||||
var requestSet = request.SongIdList.ToHashSet();
|
||||
if (!requestSet.IsSubsetOf(allSongSet))
|
||||
{
|
||||
var invalidSongIds = requestSet.Except(allSongSet);
|
||||
logger.LogWarning("Invalid song IDs: {InvalidSongIds}", invalidSongIds.Stringify());
|
||||
requestSet.ExceptWith(invalidSongIds);
|
||||
}
|
||||
|
||||
var selfbestScores = await context.SongBestData
|
||||
.Where(datum => datum.Baid == request.Baid &&
|
||||
requestSet.Contains(datum.SongId) &&
|
||||
(datum.Difficulty == requestDifficulty ||
|
||||
(datum.Difficulty == Difficulty.UraOni && requestDifficulty == Difficulty.Oni)))
|
||||
.OrderBy(datum => datum.SongId)
|
||||
.ToListAsync(cancellationToken);
|
||||
var selfBestList = selfbestScores.ConvertAll(datum => new CommonSelfBestResponse.SelfBestData
|
||||
{
|
||||
SongNo = datum.SongId,
|
||||
SelfBestScore = datum.BestScore,
|
||||
UraBestScore = datum.Difficulty == Difficulty.UraOni ? datum.BestScore : 0,
|
||||
SelfBestScoreRate = datum.BestRate,
|
||||
UraBestScoreRate = datum.Difficulty == Difficulty.UraOni ? datum.BestRate : 0
|
||||
});
|
||||
// For songs that don't have a score, add them to the response with 0s
|
||||
var missingSongs = requestSet.Except(selfBestList.Select(datum => datum.SongNo));
|
||||
selfBestList.AddRange(missingSongs.Select(songNo => new CommonSelfBestResponse.SelfBestData
|
||||
{
|
||||
SongNo = songNo
|
||||
}));
|
||||
|
||||
var response = new CommonSelfBestResponse
|
||||
{
|
||||
Result = 1,
|
||||
Level = request.Difficulty,
|
||||
ArySelfbestScores = selfBestList
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
26
TaikoLocalServer/Handlers/GetShopFolderQuery.cs
Normal file
26
TaikoLocalServer/Handlers/GetShopFolderQuery.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetShopFolderQuery : IRequest<CommonGetShopFolderResponse>;
|
||||
|
||||
public class GetShopFolderHandler(IGameDataService gameDataService)
|
||||
: IRequestHandler<GetShopFolderQuery, CommonGetShopFolderResponse>
|
||||
{
|
||||
public Task<CommonGetShopFolderResponse> Handle(GetShopFolderQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
gameDataService.GetTokenDataDictionary().TryGetValue("shopTokenId", out var shopTokenId);
|
||||
|
||||
var shopFolderList = gameDataService.GetShopFolderList();
|
||||
|
||||
var response = new CommonGetShopFolderResponse
|
||||
{
|
||||
Result = 1,
|
||||
TokenId = shopTokenId > 0 ? (uint)shopTokenId : 1,
|
||||
VerupNo = 2,
|
||||
AryShopFolderDatas = shopFolderList
|
||||
};
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
32
TaikoLocalServer/Handlers/GetSongIntroductionQuery.cs
Normal file
32
TaikoLocalServer/Handlers/GetSongIntroductionQuery.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetSongIntroductionQuery(uint[] SetIds) : IRequest<CommonGetSongIntroductionResponse>;
|
||||
|
||||
public class GetSongIntroductionQueryHandler(IGameDataService gameDataService, ILogger<GetSongIntroductionQueryHandler> logger)
|
||||
: IRequestHandler<GetSongIntroductionQuery, CommonGetSongIntroductionResponse>
|
||||
{
|
||||
|
||||
public Task<CommonGetSongIntroductionResponse> Handle(GetSongIntroductionQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new CommonGetSongIntroductionResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
foreach (var setId in request.SetIds)
|
||||
{
|
||||
gameDataService.GetSongIntroductionDictionary().TryGetValue(setId, out var introData);
|
||||
if (introData is null)
|
||||
{
|
||||
logger.LogWarning("Requested set id {Id} does not exist!", setId);
|
||||
continue;
|
||||
}
|
||||
|
||||
response.ArySongIntroductionDatas.Add(introData);
|
||||
}
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
|
55
TaikoLocalServer/Handlers/GetTokenCountQuery.cs
Normal file
55
TaikoLocalServer/Handlers/GetTokenCountQuery.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using GameDatabase.Context;
|
||||
using GameDatabase.Entities;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record GetTokenCountQuery(uint Baid) : IRequest<CommonGetTokenCountResponse>;
|
||||
|
||||
public class GetTokenCountQueryHandler(IGameDataService gameDataService,
|
||||
TaikoDbContext context,
|
||||
ILogger<GetTokenCountQueryHandler> logger)
|
||||
: IRequestHandler<GetTokenCountQuery, CommonGetTokenCountResponse>
|
||||
{
|
||||
public async Task<CommonGetTokenCountResponse> Handle(GetTokenCountQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new CommonGetTokenCountResponse
|
||||
{
|
||||
Result = 1
|
||||
};
|
||||
|
||||
string[] tokenNames = ["shopTokenId", "kaTokenId", "onePieceTokenId", "soshinaTokenId", "Yatsushika1TokenId", "Yatsushika2TokenId",
|
||||
"Yatsushika3TokenId", "Yatsushika4TokenId", "MaskedKid1TokenId", "MaskedKid2TokenId", "MaskedKid3TokenId", "MaskedKid4TokenId",
|
||||
"Kiyoshi1TokenId", "Kiyoshi2TokenId", "Kiyoshi3TokenId", "Kiyoshi4TokenId", "Amitie1TokenId", "Amitie2TokenId", "Amitie3TokenId",
|
||||
"Amitie4TokenId", "Machina1TokenId", "Machina2TokenId", "Machina3TokenId", "Machina4TokenId"];
|
||||
|
||||
var tokenDataDictionary = gameDataService.GetTokenDataDictionary();
|
||||
foreach (var tokenName in tokenNames)
|
||||
{
|
||||
tokenDataDictionary.TryGetValue(tokenName, out var tokenId);
|
||||
if (tokenId <= 0) continue;
|
||||
var token = await context.Tokens.FirstOrDefaultAsync(t => t.Baid == request.Baid &&
|
||||
t.Id == tokenId,
|
||||
cancellationToken);
|
||||
if (token is null)
|
||||
{
|
||||
token = new Token
|
||||
{
|
||||
Baid = request.Baid,
|
||||
Id = tokenId,
|
||||
Count = 0,
|
||||
};
|
||||
context.Tokens.Add(token);
|
||||
}
|
||||
|
||||
response.AryTokenCountDatas.Add(new CommonTokenCountData
|
||||
{
|
||||
TokenId = (uint)token.Id,
|
||||
TokenCount = token.Count
|
||||
});
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
return response;
|
||||
}
|
||||
}
|
41
TaikoLocalServer/Handlers/PurchaseSongCommand.cs
Normal file
41
TaikoLocalServer/Handlers/PurchaseSongCommand.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using GameDatabase.Context;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record PurchaseSongCommand(uint Baid, uint SongNo, uint TokenId, uint Price) : IRequest<CommonSongPurchaseResponse>;
|
||||
|
||||
public class PurchaseSongCommandHandler(TaikoDbContext context, ILogger<PurchaseSongCommandHandler> logger)
|
||||
: IRequestHandler<PurchaseSongCommand, CommonSongPurchaseResponse>
|
||||
{
|
||||
|
||||
public async Task<CommonSongPurchaseResponse> Handle(PurchaseSongCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await context.UserData
|
||||
.Include(u => u.Tokens)
|
||||
.FirstOrDefaultAsync(u => u.Baid == request.Baid, cancellationToken);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
if (user.UnlockedSongIdList.Contains(request.SongNo))
|
||||
{
|
||||
logger.LogWarning("User with baid {Baid} already has song with id {SongNo} unlocked!", request.Baid, request.SongNo);
|
||||
return new CommonSongPurchaseResponse { Result = 0 };
|
||||
}
|
||||
|
||||
var token = user.Tokens.FirstOrDefault(t => t.Id == request.TokenId);
|
||||
if (token is not null && token.Count >= request.Price)
|
||||
{
|
||||
token.Count -= (int)request.Price;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogError("User with baid {Baid} does not have enough tokens to purchase song with id {SongNo}!", request.Baid, request.SongNo);
|
||||
return new CommonSongPurchaseResponse { Result = 0 };
|
||||
}
|
||||
|
||||
user.UnlockedSongIdList.Add(request.SongNo);
|
||||
context.UserData.Update(user);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
return new CommonSongPurchaseResponse { Result = 1, TokenCount = token.Count };
|
||||
}
|
||||
}
|
285
TaikoLocalServer/Handlers/UpdatePlayResultCommand.cs
Normal file
285
TaikoLocalServer/Handlers/UpdatePlayResultCommand.cs
Normal file
@ -0,0 +1,285 @@
|
||||
using System.Text.Json;
|
||||
using GameDatabase.Context;
|
||||
using GameDatabase.Entities;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record UpdatePlayResultCommand(uint Baid, CommonPlayResultData PlayResultData) : IRequest<uint>;
|
||||
|
||||
public class UpdatePlayResultCommandHandler(TaikoDbContext context, ILogger<UpdatePlayResultCommandHandler> logger)
|
||||
: IRequestHandler<UpdatePlayResultCommand, uint>
|
||||
{
|
||||
public async Task<uint> Handle(UpdatePlayResultCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.Baid == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var user = await context.UserData.FindAsync(request.Baid, cancellationToken);
|
||||
if (user is null)
|
||||
{
|
||||
logger.LogWarning("Game uploading a non exisiting user with baid {Baid}", request.Baid);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var lastPlayDateTime = DateTime.Now;
|
||||
var playResultData = request.PlayResultData;
|
||||
UpdateUserData(user, playResultData, lastPlayDateTime);
|
||||
|
||||
var playMode = (PlayMode)playResultData.PlayMode;
|
||||
if (playMode is PlayMode.DanMode or PlayMode.GaidenMode)
|
||||
{
|
||||
var danType = playMode == PlayMode.DanMode ? DanType.Normal : DanType.Gaiden;
|
||||
var danPlayData = await context.DanScoreData
|
||||
.Include(datum => datum.DanStageScoreData)
|
||||
.FirstOrDefaultAsync(datum => datum.Baid == request.Baid &&
|
||||
datum.DanId == playResultData.DanId &&
|
||||
datum.DanType == danType, cancellationToken);
|
||||
if (danPlayData is null)
|
||||
{
|
||||
danPlayData = new DanScoreDatum
|
||||
{
|
||||
Baid = request.Baid,
|
||||
DanId = playResultData.DanId,
|
||||
DanType = danType
|
||||
};
|
||||
UpdateDanPlayData(danPlayData, playResultData);
|
||||
context.DanScoreData.Add(danPlayData);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateDanPlayData(danPlayData, playResultData);
|
||||
context.DanScoreData.Update(danPlayData);
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (var songNumber = 0; songNumber < playResultData.AryStageInfoes.Count; songNumber++)
|
||||
{
|
||||
var stageData = playResultData.AryStageInfoes[songNumber];
|
||||
|
||||
if (playMode == PlayMode.AiBattle)
|
||||
{
|
||||
await UpdateAiBattleData(playResultData, stageData);
|
||||
}
|
||||
|
||||
await UpdateBestData(playResultData, stageData);
|
||||
|
||||
var songPlayDatum = new SongPlayDatum
|
||||
{
|
||||
Baid = request.Baid,
|
||||
SongNumber = (uint)songNumber,
|
||||
GoodCount = stageData.GoodCnt,
|
||||
OkCount = stageData.OkCnt,
|
||||
MissCount = stageData.NgCnt,
|
||||
ComboCount = stageData.ComboCnt,
|
||||
HitCount = stageData.HitCnt,
|
||||
DrumrollCount = stageData.PoundCnt,
|
||||
Crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt),
|
||||
Score = stageData.PlayScore,
|
||||
ScoreRate = stageData.ScoreRate,
|
||||
ScoreRank = (ScoreRank)stageData.ScoreRank,
|
||||
Skipped = stageData.IsSkipUse,
|
||||
SongId = stageData.SongNo,
|
||||
PlayTime = lastPlayDateTime,
|
||||
Difficulty = (Difficulty)stageData.Level
|
||||
};
|
||||
context.SongPlayData.Add(songPlayDatum);
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private async Task UpdateBestData(CommonPlayResultData playResultData, CommonPlayResultData.StageData stageData)
|
||||
{
|
||||
var difficulty = (Difficulty)stageData.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
var existing = await context.SongBestData.FindAsync(playResultData.Baid, stageData.SongNo, difficulty);
|
||||
|
||||
|
||||
// Determine whether it is dondaful crown as this is not reflected by play result
|
||||
var crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt);
|
||||
|
||||
if (existing is null)
|
||||
{
|
||||
existing = new SongBestDatum
|
||||
{
|
||||
Baid = playResultData.Baid,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
BestScore = stageData.PlayScore,
|
||||
BestRate = stageData.ScoreRate,
|
||||
BestCrown = crown,
|
||||
BestScoreRank = (ScoreRank)stageData.ScoreRank
|
||||
};
|
||||
|
||||
context.SongBestData.Add(existing);
|
||||
return;
|
||||
}
|
||||
|
||||
existing.UpdateBestData(crown, stageData.ScoreRank, stageData.PlayScore, stageData.ScoreRate);
|
||||
context.SongBestData.Update(existing);
|
||||
}
|
||||
|
||||
private async Task UpdateAiBattleData(CommonPlayResultData playResultData, CommonPlayResultData.StageData stageData)
|
||||
{
|
||||
var difficulty = (Difficulty)stageData.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
var existing = await context.AiScoreData
|
||||
.Include(datum => datum.AiSectionScoreData)
|
||||
.FirstOrDefaultAsync(datum => datum.Baid == playResultData.Baid &&
|
||||
datum.SongId == stageData.SongNo &&
|
||||
datum.Difficulty == difficulty);
|
||||
if (existing is null)
|
||||
{
|
||||
existing = new AiScoreDatum
|
||||
{
|
||||
Baid = playResultData.Baid,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
IsWin = stageData.IsWin
|
||||
};
|
||||
var aiSections = stageData.ArySectionDatas.Select((data, i) =>
|
||||
{
|
||||
var section = new AiSectionScoreDatum
|
||||
{
|
||||
Baid = playResultData.Baid,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
SectionIndex = i,
|
||||
OkCount = data.OkCnt,
|
||||
MissCount = data.NgCnt
|
||||
};
|
||||
section.UpdateBest(data);
|
||||
return section;
|
||||
}
|
||||
);
|
||||
existing.AiSectionScoreData.AddRange(aiSections);
|
||||
context.AiScoreData.Add(existing);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
|
||||
{
|
||||
var sectionData = stageData.ArySectionDatas[index];
|
||||
if (index < existing.AiSectionScoreData.Count)
|
||||
{
|
||||
existing.AiSectionScoreData[index].UpdateBest(sectionData);
|
||||
}
|
||||
else
|
||||
{
|
||||
var aiSectionScoreDatum = new AiSectionScoreDatum
|
||||
{
|
||||
Baid = playResultData.Baid,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
SectionIndex = index,
|
||||
OkCount = sectionData.OkCnt,
|
||||
MissCount = sectionData.NgCnt
|
||||
};
|
||||
aiSectionScoreDatum.UpdateBest(sectionData);
|
||||
existing.AiSectionScoreData.Add(aiSectionScoreDatum);
|
||||
context.AiScoreData.Update(existing);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdateDanPlayData(DanScoreDatum danPlayData, CommonPlayResultData playResultData)
|
||||
{
|
||||
danPlayData.ClearState =
|
||||
(DanClearState)Math.Max(playResultData.DanResult, (uint)danPlayData.ClearState);
|
||||
danPlayData.ArrivalSongCount =
|
||||
Math.Max((uint)playResultData.AryStageInfoes.Count, danPlayData.ArrivalSongCount);
|
||||
danPlayData.ComboCountTotal = Math.Max(playResultData.ComboCntTotal, danPlayData.ComboCountTotal);
|
||||
danPlayData.SoulGaugeTotal = Math.Max(playResultData.SoulGaugeTotal, danPlayData.SoulGaugeTotal);
|
||||
|
||||
for (var i = 0; i < playResultData.AryStageInfoes.Count; i++)
|
||||
{
|
||||
var stageData = playResultData.AryStageInfoes[i];
|
||||
|
||||
var songNumber = i;
|
||||
var danStageData = danPlayData.DanStageScoreData.FirstOrDefault(datum => datum.SongNumber == songNumber,
|
||||
new DanStageScoreDatum
|
||||
{
|
||||
Baid = danPlayData.Baid,
|
||||
DanId = danPlayData.DanId,
|
||||
DanType = danPlayData.DanType,
|
||||
SongNumber = (uint)songNumber,
|
||||
OkCount = stageData.OkCnt,
|
||||
BadCount = stageData.NgCnt
|
||||
});
|
||||
|
||||
danStageData.HighScore = Math.Max(danStageData.HighScore, stageData.PlayScore);
|
||||
danStageData.ComboCount = Math.Max(danStageData.ComboCount, stageData.ComboCnt);
|
||||
danStageData.DrumrollCount = Math.Max(danStageData.DrumrollCount, stageData.PoundCnt);
|
||||
danStageData.GoodCount = Math.Max(danStageData.GoodCount, stageData.GoodCnt);
|
||||
danStageData.TotalHitCount = Math.Max(danStageData.TotalHitCount, stageData.HitCnt);
|
||||
danStageData.OkCount = Math.Min(danStageData.OkCount, stageData.OkCnt);
|
||||
danStageData.BadCount = Math.Min(danStageData.BadCount, stageData.NgCnt);
|
||||
|
||||
var index = danPlayData.DanStageScoreData.IndexOf(danStageData);
|
||||
if (index == -1)
|
||||
{
|
||||
context.DanStageScoreData.Add(danStageData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUserData(UserDatum user, CommonPlayResultData playResultData, DateTime lastPlayDateTime)
|
||||
{
|
||||
user.Title = playResultData.Title;
|
||||
user.TitlePlateId = playResultData.TitleplateId;
|
||||
var costumeData = new List<uint>
|
||||
{
|
||||
playResultData.AryCurrentCostume.Costume1,
|
||||
playResultData.AryCurrentCostume.Costume2,
|
||||
playResultData.AryCurrentCostume.Costume3,
|
||||
playResultData.AryCurrentCostume.Costume4,
|
||||
playResultData.AryCurrentCostume.Costume5
|
||||
};
|
||||
user.CostumeData = JsonSerializer.Serialize(costumeData);
|
||||
user.LastPlayDatetime = lastPlayDateTime;
|
||||
user.LastPlayMode = playResultData.PlayMode;
|
||||
|
||||
user.ToneFlgArray.AddRange(playResultData.GetToneNoes);
|
||||
user.TitleFlgArray.AddRange(playResultData.GetTitleNoes);
|
||||
|
||||
user.UnlockedKigurumi.AddRange(playResultData.GetCostumeNo1s);
|
||||
user.UnlockedHead.AddRange(playResultData.GetCostumeNo2s);
|
||||
user.UnlockedBody.AddRange(playResultData.GetCostumeNo3s);
|
||||
user.UnlockedFace.AddRange(playResultData.GetCostumeNo4s);
|
||||
user.UnlockedPuchi.AddRange(playResultData.GetCostumeNo5s);
|
||||
var genericInfo = user.GenericInfoFlgArray.ToList();
|
||||
genericInfo.AddRange(playResultData.GetGenericInfoNoes);
|
||||
user.GenericInfoFlgArray = genericInfo.ToArray();
|
||||
|
||||
var difficultyPlayedArray = new List<uint>
|
||||
{
|
||||
playResultData.DifficultyPlayedCourse,
|
||||
playResultData.DifficultyPlayedStar,
|
||||
playResultData.DifficultyPlayedSort
|
||||
};
|
||||
user.DifficultyPlayedArray = JsonSerializer.Serialize(difficultyPlayedArray);
|
||||
|
||||
user.AiWinCount += playResultData.AryStageInfoes.Count(data => data.IsWin);
|
||||
context.UserData.Update(user);
|
||||
}
|
||||
|
||||
private static CrownType PlayResultToCrown(uint playResult, uint okCount)
|
||||
{
|
||||
var crown = (CrownType)playResult;
|
||||
if (crown == CrownType.Gold && okCount == 0)
|
||||
{
|
||||
crown = CrownType.Dondaful;
|
||||
}
|
||||
|
||||
return crown;
|
||||
}
|
||||
}
|
104
TaikoLocalServer/Handlers/UserDataQuery.cs
Normal file
104
TaikoLocalServer/Handlers/UserDataQuery.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System.Buffers.Binary;
|
||||
using GameDatabase.Context;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Handlers;
|
||||
|
||||
public record UserDataQuery(uint Baid) : IRequest<CommonUserDataResponse>;
|
||||
|
||||
public class UserDataQueryHandler(TaikoDbContext context, IGameDataService gameDataService, ILogger<UserDataQueryHandler> logger)
|
||||
: IRequestHandler<UserDataQuery, CommonUserDataResponse>
|
||||
{
|
||||
|
||||
public async Task<CommonUserDataResponse> Handle(UserDataQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var userData = await context.UserData.FindAsync(request.Baid, cancellationToken);
|
||||
userData.ThrowIfNull($"User not found for Baid {request.Baid}!");
|
||||
|
||||
var unlockedSongIdList = userData.UnlockedSongIdList;
|
||||
|
||||
var musicList = gameDataService.GetMusicList();
|
||||
var lockedSongsList = gameDataService.GetLockedSongsList();
|
||||
lockedSongsList = lockedSongsList.Except(unlockedSongIdList).ToList();
|
||||
var enabledMusicList = musicList.Except(lockedSongsList);
|
||||
var releaseSongArray =
|
||||
FlagCalculator.GetBitArrayFromIds(enabledMusicList, Constants.MUSIC_ID_MAX, logger);
|
||||
|
||||
var defaultSongWithUraList = gameDataService.GetMusicWithUraList();
|
||||
var enabledUraMusicList = defaultSongWithUraList.Except(lockedSongsList);
|
||||
var uraSongArray =
|
||||
FlagCalculator.GetBitArrayFromIds(enabledUraMusicList, Constants.MUSIC_ID_MAX, logger);
|
||||
|
||||
if (userData.ToneFlgArray.Count == 0)
|
||||
{
|
||||
userData.ToneFlgArray = [0];
|
||||
context.UserData.Update(userData);
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
var toneArray = FlagCalculator.GetBitArrayFromIds(userData.ToneFlgArray, gameDataService.GetToneFlagArraySize(), logger);
|
||||
|
||||
var titleArray = FlagCalculator.GetBitArrayFromIds(userData.TitleFlgArray, gameDataService.GetTitleFlagArraySize(), logger);
|
||||
|
||||
var recentSongs = await context.SongPlayData
|
||||
.Where(datum => datum.Baid == request.Baid)
|
||||
.OrderByDescending(datum => datum.PlayTime)
|
||||
.ThenByDescending(datum => datum.SongNumber)
|
||||
.Select(datum => datum.SongId)
|
||||
.ToArrayAsync(cancellationToken);
|
||||
|
||||
// Use custom implementation as distinctby cannot guarantee preserved element
|
||||
var recentSet = new OrderedSet<uint>();
|
||||
foreach (var id in recentSongs)
|
||||
{
|
||||
recentSet.Add(id);
|
||||
if (recentSet.Count == 10)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
recentSongs = recentSet.ToArray();
|
||||
|
||||
var defaultOptions = new byte[2];
|
||||
BinaryPrimitives.WriteInt16LittleEndian(defaultOptions, userData.OptionSetting);
|
||||
|
||||
var difficultySettingArray = JsonHelper.GetUIntArrayFromJson(userData.DifficultySettingArray, 3, logger, nameof(userData.DifficultySettingArray));
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (difficultySettingArray[i] >= 2)
|
||||
{
|
||||
difficultySettingArray[i] -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
var difficultyPlayedArray = JsonHelper.GetUIntArrayFromJson(userData.DifficultyPlayedArray, 3, logger, nameof(userData.DifficultyPlayedArray));
|
||||
|
||||
var response = new CommonUserDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
ToneFlg = toneArray,
|
||||
TitleFlg = titleArray,
|
||||
ReleaseSongFlg = releaseSongArray,
|
||||
UraReleaseSongFlg = uraSongArray,
|
||||
AryFavoriteSongNoes = userData.FavoriteSongsArray.ToArray(),
|
||||
AryRecentSongNoes = recentSongs,
|
||||
DefaultOptionSetting = defaultOptions,
|
||||
NotesPosition = userData.NotesPosition,
|
||||
IsVoiceOn = userData.IsVoiceOn,
|
||||
IsSkipOn = userData.IsSkipOn,
|
||||
DifficultySettingCourse = difficultySettingArray[0],
|
||||
DifficultySettingStar = difficultySettingArray[1],
|
||||
DifficultySettingSort = difficultySettingArray[2],
|
||||
DifficultyPlayedCourse = difficultyPlayedArray[0],
|
||||
DifficultyPlayedStar = difficultyPlayedArray[1],
|
||||
DifficultyPlayedSort = difficultyPlayedArray[2],
|
||||
SongRecentCnt = (uint)recentSongs.Length,
|
||||
IsChallengecompe = false,
|
||||
// TODO: Other fields
|
||||
};
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
12
TaikoLocalServer/Mappers/AddTokenCountRequestMapper.cs
Normal file
12
TaikoLocalServer/Mappers/AddTokenCountRequestMapper.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class AddTokenCountRequestMapper
|
||||
{
|
||||
public static partial CommonAddTokenCountRequest Map(AddTokenCountRequest request);
|
||||
|
||||
public static partial CommonAddTokenCountRequest Map(Models.v3209.AddTokenCountRequest request);
|
||||
}
|
12
TaikoLocalServer/Mappers/AiDataResponseMapper.cs
Normal file
12
TaikoLocalServer/Mappers/AiDataResponseMapper.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class AiDataResponseMapper
|
||||
{
|
||||
public static partial GetAiDataResponse MapTo3906(CommonAiDataResponse response);
|
||||
|
||||
public static partial Models.v3209.GetAiDataResponse MapTo3209(CommonAiDataResponse response);
|
||||
}
|
23
TaikoLocalServer/Mappers/AiScoreMappers.cs
Normal file
23
TaikoLocalServer/Mappers/AiScoreMappers.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using GameDatabase.Entities;
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class AiScoreMappers
|
||||
{
|
||||
[MapProperty(nameof(AiScoreDatum.AiSectionScoreData), nameof(CommonAiScoreResponse.AryBestSectionDatas))]
|
||||
public static partial CommonAiScoreResponse MapToCommonAiScoreResponse(AiScoreDatum datum);
|
||||
|
||||
public static CommonAiScoreResponse MapAsSuccess(AiScoreDatum datum)
|
||||
{
|
||||
var response= MapToCommonAiScoreResponse(datum);
|
||||
response.Result = 1;
|
||||
return response;
|
||||
}
|
||||
|
||||
public static partial GetAiScoreResponse MapTo3906(CommonAiScoreResponse response);
|
||||
|
||||
public static partial Models.v3209.GetAiScoreResponse MapTo3209(CommonAiScoreResponse response);
|
||||
}
|
50
TaikoLocalServer/Mappers/BaidResponseMapper.cs
Normal file
50
TaikoLocalServer/Mappers/BaidResponseMapper.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class BaidResponseMapper
|
||||
{
|
||||
public static partial BAIDResponse MapTo3906(CommonBaidResponse commonBaidResponse);
|
||||
|
||||
public static BAIDResponse Map3906WithPostProcess(CommonBaidResponse commonBaidResponse)
|
||||
{
|
||||
var response = MapTo3906(commonBaidResponse);
|
||||
response.AryCostumedata = new BAIDResponse.CostumeData
|
||||
{
|
||||
Costume1 = commonBaidResponse.CostumeData[0],
|
||||
Costume2 = commonBaidResponse.CostumeData[1],
|
||||
Costume3 = commonBaidResponse.CostumeData[2],
|
||||
Costume4 = commonBaidResponse.CostumeData[3],
|
||||
Costume5 = commonBaidResponse.CostumeData[4]
|
||||
};
|
||||
response.CostumeFlg1 = commonBaidResponse.CostumeFlagArrays[0];
|
||||
response.CostumeFlg2 = commonBaidResponse.CostumeFlagArrays[1];
|
||||
response.CostumeFlg3 = commonBaidResponse.CostumeFlagArrays[2];
|
||||
response.CostumeFlg4 = commonBaidResponse.CostumeFlagArrays[3];
|
||||
response.CostumeFlg5 = commonBaidResponse.CostumeFlagArrays[4];
|
||||
return response;
|
||||
}
|
||||
|
||||
public static partial Models.v3209.BAIDResponse MapTo3209(CommonBaidResponse commonBaidResponse);
|
||||
|
||||
public static Models.v3209.BAIDResponse Map3209WithPostProcess(CommonBaidResponse commonBaidResponse)
|
||||
{
|
||||
var response = MapTo3209(commonBaidResponse);
|
||||
response.AryCostumedata = new Models.v3209.BAIDResponse.CostumeData
|
||||
{
|
||||
Costume1 = commonBaidResponse.CostumeData[0],
|
||||
Costume2 = commonBaidResponse.CostumeData[1],
|
||||
Costume3 = commonBaidResponse.CostumeData[2],
|
||||
Costume4 = commonBaidResponse.CostumeData[3],
|
||||
Costume5 = commonBaidResponse.CostumeData[4]
|
||||
};
|
||||
response.CostumeFlg1 = commonBaidResponse.CostumeFlagArrays[0];
|
||||
response.CostumeFlg2 = commonBaidResponse.CostumeFlagArrays[1];
|
||||
response.CostumeFlg3 = commonBaidResponse.CostumeFlagArrays[2];
|
||||
response.CostumeFlg4 = commonBaidResponse.CostumeFlagArrays[3];
|
||||
response.CostumeFlg5 = commonBaidResponse.CostumeFlagArrays[4];
|
||||
return response;
|
||||
}
|
||||
}
|
12
TaikoLocalServer/Mappers/DanDataMappers.cs
Normal file
12
TaikoLocalServer/Mappers/DanDataMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using SharedProject.Models;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class DanDataMappers
|
||||
{
|
||||
public static partial GetDanOdaiResponse.OdaiData To3906OdaiData(DanData data);
|
||||
|
||||
public static partial Models.v3209.GetDanOdaiResponse.OdaiData To3209OdaiData(DanData data);
|
||||
}
|
11
TaikoLocalServer/Mappers/DanScoreMappers.cs
Normal file
11
TaikoLocalServer/Mappers/DanScoreMappers.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class DanScoreMappers
|
||||
{
|
||||
public static partial GetDanScoreResponse MapTo3906(CommonDanScoreDataResponse response);
|
||||
public static partial Models.v3209.GetDanScoreResponse MapTo3209(CommonDanScoreDataResponse response);
|
||||
}
|
12
TaikoLocalServer/Mappers/FolderDataMappers.cs
Normal file
12
TaikoLocalServer/Mappers/FolderDataMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class FolderDataMappers
|
||||
{
|
||||
public static partial GetfolderResponse MapTo3906(CommonGetFolderResponse response);
|
||||
|
||||
public static partial Models.v3209.GetfolderResponse MapTo3209(CommonGetFolderResponse response);
|
||||
}
|
12
TaikoLocalServer/Mappers/InitialDataMappers.cs
Normal file
12
TaikoLocalServer/Mappers/InitialDataMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class InitialDataMappers
|
||||
{
|
||||
public static partial InitialdatacheckResponse MapTo3906(CommonInitialDataCheckResponse response);
|
||||
|
||||
public static partial Models.v3209.InitialdatacheckResponse MapTo3209(CommonInitialDataCheckResponse response);
|
||||
}
|
12
TaikoLocalServer/Mappers/MyDonEntryMappers.cs
Normal file
12
TaikoLocalServer/Mappers/MyDonEntryMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class MyDonEntryMappers
|
||||
{
|
||||
public static partial MydonEntryResponse MapTo3906(CommonMyDonEntryResponse response);
|
||||
|
||||
public static partial Models.v3209.MydonEntryResponse MapTo3209(CommonMyDonEntryResponse response);
|
||||
}
|
12
TaikoLocalServer/Mappers/PlayResultMappers.cs
Normal file
12
TaikoLocalServer/Mappers/PlayResultMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class PlayResultMappers
|
||||
{
|
||||
public static partial CommonPlayResultData Map(PlayResultDataRequest request);
|
||||
|
||||
public static partial CommonPlayResultData Map(Models.v3209.PlayResultDataRequest request);
|
||||
}
|
12
TaikoLocalServer/Mappers/SelfBestMappers.cs
Normal file
12
TaikoLocalServer/Mappers/SelfBestMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class SelfBestMappers
|
||||
{
|
||||
public static partial SelfBestResponse MapTo3906(CommonSelfBestResponse response);
|
||||
|
||||
public static partial Models.v3209.SelfBestResponse MapTo3209(CommonSelfBestResponse response);
|
||||
}
|
12
TaikoLocalServer/Mappers/ShopFolderDataMappers.cs
Normal file
12
TaikoLocalServer/Mappers/ShopFolderDataMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class ShopFolderDataMappers
|
||||
{
|
||||
public static partial GetShopFolderResponse MapTo3906(CommonGetShopFolderResponse response);
|
||||
|
||||
public static partial Models.v3209.GetShopFolderResponse MapTo3209(CommonGetShopFolderResponse response);
|
||||
}
|
13
TaikoLocalServer/Mappers/SongIntroductionDataMappers.cs
Normal file
13
TaikoLocalServer/Mappers/SongIntroductionDataMappers.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class SongIntroductionDataMappers
|
||||
{
|
||||
public static partial GetSongIntroductionResponse MapTo3906(CommonGetSongIntroductionResponse response);
|
||||
|
||||
public static partial Models.v3209.GetSongIntroductionResponse
|
||||
MapTo3209(CommonGetSongIntroductionResponse response);
|
||||
}
|
17
TaikoLocalServer/Mappers/SongPurchaseMappers.cs
Normal file
17
TaikoLocalServer/Mappers/SongPurchaseMappers.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Handlers;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class SongPurchaseMappers
|
||||
{
|
||||
public static partial SongPurchaseResponse MapTo3906(CommonSongPurchaseResponse response);
|
||||
|
||||
public static partial Models.v3209.SongPurchaseResponse MapTo3209(CommonSongPurchaseResponse response);
|
||||
|
||||
public static partial PurchaseSongCommand MapToCommand(SongPurchaseRequest request);
|
||||
|
||||
public static partial PurchaseSongCommand MapToCommand(Models.v3209.SongPurchaseRequest request);
|
||||
}
|
12
TaikoLocalServer/Mappers/TokenCountDataMappers.cs
Normal file
12
TaikoLocalServer/Mappers/TokenCountDataMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class TokenCountDataMappers
|
||||
{
|
||||
public static partial GetTokenCountResponse MapTo3906(CommonGetTokenCountResponse response);
|
||||
|
||||
public static partial Models.v3209.GetTokenCountResponse MapTo3209(CommonGetTokenCountResponse response);
|
||||
}
|
12
TaikoLocalServer/Mappers/UserDataMappers.cs
Normal file
12
TaikoLocalServer/Mappers/UserDataMappers.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
using TaikoLocalServer.Models.Application;
|
||||
|
||||
namespace TaikoLocalServer.Mappers;
|
||||
|
||||
[Mapper]
|
||||
public static partial class UserDataMappers
|
||||
{
|
||||
public static partial UserDataResponse MapTo3906(CommonUserDataResponse response);
|
||||
|
||||
public static partial Models.v3209.UserDataResponse MapTo3209(CommonUserDataResponse response);
|
||||
}
|
3225
TaikoLocalServer/Models/3209/Game.cs
Normal file
3225
TaikoLocalServer/Models/3209/Game.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@
|
||||
|
||||
#region Designer generated code
|
||||
#pragma warning disable CS0612, CS0618, CS1591, CS3021, IDE0079, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
|
||||
namespace taiko.game
|
||||
namespace TaikoLocalServer.Models.v3906
|
||||
{
|
||||
[global::ProtoBuf.ProtoContract()]
|
||||
public partial class HeartBeatRequest : global::ProtoBuf.IExtensible
|
||||
@ -657,10 +657,10 @@ namespace taiko.game
|
||||
public string Title { get; set; }
|
||||
|
||||
[global::ProtoBuf.ProtoMember(4, Name = @"ary_odai_song")]
|
||||
public global::System.Collections.Generic.List<OdaiSong> AryOdaiSongs { get; } = new global::System.Collections.Generic.List<OdaiSong>();
|
||||
public global::System.Collections.Generic.List<OdaiSong> OdaiSongList { get; } = new global::System.Collections.Generic.List<OdaiSong>();
|
||||
|
||||
[global::ProtoBuf.ProtoMember(5, Name = @"ary_odai_border")]
|
||||
public global::System.Collections.Generic.List<OdaiBorder> AryOdaiBorders { get; } = new global::System.Collections.Generic.List<OdaiBorder>();
|
||||
public global::System.Collections.Generic.List<OdaiBorder> OdaiBorderList { get; } = new global::System.Collections.Generic.List<OdaiBorder>();
|
||||
|
||||
[global::ProtoBuf.ProtoContract()]
|
||||
public partial class OdaiSong : global::ProtoBuf.IExtensible
|
||||
@ -1260,7 +1260,7 @@ namespace taiko.game
|
||||
|
||||
[global::ProtoBuf.ProtoMember(12, Name = @"mydon_name")]
|
||||
[global::System.ComponentModel.DefaultValue("")]
|
||||
public string MydonName
|
||||
public string MyDonName
|
||||
{
|
||||
get => __pbn__MydonName ?? "";
|
||||
set => __pbn__MydonName = value;
|
||||
@ -1270,7 +1270,7 @@ namespace taiko.game
|
||||
private string __pbn__MydonName;
|
||||
|
||||
[global::ProtoBuf.ProtoMember(13, Name = @"mydon_name_language")]
|
||||
public uint MydonNameLanguage
|
||||
public uint MyDonNameLanguage
|
||||
{
|
||||
get => __pbn__MydonNameLanguage.GetValueOrDefault();
|
||||
set => __pbn__MydonNameLanguage = value;
|
||||
@ -1291,7 +1291,7 @@ namespace taiko.game
|
||||
private string __pbn__Title;
|
||||
|
||||
[global::ProtoBuf.ProtoMember(15, Name = @"titleplate_id")]
|
||||
public uint TitleplateId
|
||||
public uint TitlePlateId
|
||||
{
|
||||
get => __pbn__TitleplateId.GetValueOrDefault();
|
||||
set => __pbn__TitleplateId = value;
|
||||
@ -1395,7 +1395,7 @@ namespace taiko.game
|
||||
private string __pbn__LastPlayDatetime;
|
||||
|
||||
[global::ProtoBuf.ProtoMember(26, Name = @"is_disp_dan_on")]
|
||||
public bool IsDispDanOn
|
||||
public bool DisplayDan
|
||||
{
|
||||
get => __pbn__IsDispDanOn.GetValueOrDefault();
|
||||
set => __pbn__IsDispDanOn = value;
|
||||
@ -1425,17 +1425,17 @@ namespace taiko.game
|
||||
private byte[] __pbn__GotDanFlg;
|
||||
|
||||
[global::ProtoBuf.ProtoMember(29, Name = @"got_danextra_flg")]
|
||||
public byte[] GotDanextraFlg
|
||||
public byte[] GotGaidenFlg
|
||||
{
|
||||
get => __pbn__GotDanextraFlg;
|
||||
set => __pbn__GotDanextraFlg = value;
|
||||
get => pbnGotGaidenFlg;
|
||||
set => pbnGotGaidenFlg = value;
|
||||
}
|
||||
public bool ShouldSerializeGotDanextraFlg() => __pbn__GotDanextraFlg != null;
|
||||
public void ResetGotDanextraFlg() => __pbn__GotDanextraFlg = null;
|
||||
private byte[] __pbn__GotDanextraFlg;
|
||||
public bool ShouldSerializeGotDanextraFlg() => pbnGotGaidenFlg != null;
|
||||
public void ResetGotDanextraFlg() => pbnGotGaidenFlg = null;
|
||||
private byte[] pbnGotGaidenFlg;
|
||||
|
||||
[global::ProtoBuf.ProtoMember(30, Name = @"default_tone_setting")]
|
||||
public uint DefaultToneSetting
|
||||
public uint SelectedToneId
|
||||
{
|
||||
get => __pbn__DefaultToneSetting.GetValueOrDefault();
|
||||
set => __pbn__DefaultToneSetting = value;
|
||||
@ -2867,7 +2867,7 @@ namespace taiko.game
|
||||
=> global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
|
||||
|
||||
[global::ProtoBuf.ProtoMember(1, Name = @"section_no", IsRequired = true)]
|
||||
public uint SectionNo { get; set; }
|
||||
public uint SectionIndex { get; set; }
|
||||
|
||||
[global::ProtoBuf.ProtoMember(2, Name = @"crown")]
|
||||
public uint Crown
|
||||
@ -2890,16 +2890,16 @@ namespace taiko.game
|
||||
private uint? __pbn__Score;
|
||||
|
||||
[global::ProtoBuf.ProtoMember(4, Name = @"good_cnt", IsRequired = true)]
|
||||
public uint GoodCnt { get; set; }
|
||||
public uint GoodCount { get; set; }
|
||||
|
||||
[global::ProtoBuf.ProtoMember(5, Name = @"ok_cnt", IsRequired = true)]
|
||||
public uint OkCnt { get; set; }
|
||||
public uint OkCount { get; set; }
|
||||
|
||||
[global::ProtoBuf.ProtoMember(6, Name = @"ng_cnt", IsRequired = true)]
|
||||
public uint NgCnt { get; set; }
|
||||
public uint MissCount { get; set; }
|
||||
|
||||
[global::ProtoBuf.ProtoMember(7, Name = @"pound_cnt", IsRequired = true)]
|
||||
public uint PoundCnt { get; set; }
|
||||
public uint DrumrollCount { get; set; }
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
namespace TaikoLocalServer.Models.Application;
|
||||
|
||||
public class CommonAddTokenCountRequest
|
||||
{
|
||||
public uint Baid { get; set; }
|
||||
public List<AddTokenCountData> AryAddTokenCountDatas { get; set; } = new();
|
||||
}
|
||||
|
||||
public class AddTokenCountData
|
||||
{
|
||||
public uint TokenId { get; set; }
|
||||
public int AddTokenCount { get; set; }
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace TaikoLocalServer.Models.Application;
|
||||
|
||||
public class CommonAiDataResponse
|
||||
{
|
||||
public uint Result { get; set; }
|
||||
public uint TotalWinnings { get; set; }
|
||||
public string InputMedian { get; set; } = "1";
|
||||
public string InputVariance { get; set; } = "0";
|
||||
}
|
18
TaikoLocalServer/Models/Application/CommonAiScoreResponse.cs
Normal file
18
TaikoLocalServer/Models/Application/CommonAiScoreResponse.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace TaikoLocalServer.Models.Application;
|
||||
|
||||
public class CommonAiScoreResponse
|
||||
{
|
||||
public uint Result { get; set; }
|
||||
public List<CommonAiBestSectionData> AryBestSectionDatas { get; set; } = new();
|
||||
}
|
||||
|
||||
public class CommonAiBestSectionData
|
||||
{
|
||||
public uint SectionIndex { get; set; }
|
||||
public uint Crown { get; set; }
|
||||
public uint Score { get; set; }
|
||||
public uint GoodCount { get; set; }
|
||||
public uint OkCount { get; set; }
|
||||
public uint MissCount { get; set; }
|
||||
public uint DrumrollCount { get; set; }
|
||||
}
|
31
TaikoLocalServer/Models/Application/CommonBaidResponse.cs
Normal file
31
TaikoLocalServer/Models/Application/CommonBaidResponse.cs
Normal file
@ -0,0 +1,31 @@
|
||||
namespace TaikoLocalServer.Models.Application;
|
||||
|
||||
public class CommonBaidResponse
|
||||
{
|
||||
public uint Result { get; set; }
|
||||
public bool IsNewUser { get; set; }
|
||||
public uint Baid { get; set; }
|
||||
public string MyDonName { get; set; } = string.Empty;
|
||||
public uint MyDonNameLanguage { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public uint TitlePlateId { get; set; }
|
||||
public uint ColorFace { get; set; }
|
||||
public uint ColorBody { get; set; }
|
||||
public uint ColorLimb { get; set; }
|
||||
public List<uint> CostumeData { get; set; } = new() { 0, 0, 0, 0, 0 };
|
||||
public List<byte[]> CostumeFlagArrays { get; set; }
|
||||
= new() { Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>(), Array.Empty<byte>() };
|
||||
|
||||
public string LastPlayDatetime { get; set; } = DateTime.Now.ToString(Constants.DATE_TIME_FORMAT);
|
||||
public bool DisplayDan { get; set; }
|
||||
public uint GotDanMax { get; set; }
|
||||
public byte[] GotDanFlg { get; set; } = Array.Empty<byte>();
|
||||
public byte[] GotGaidenFlg { get; set; } = Array.Empty<byte>();
|
||||
public uint SelectedToneId { get; set; }
|
||||
public byte[] GenericInfoFlg { get; set; } = Array.Empty<byte>();
|
||||
public uint[] AryCrownCounts { get; set; } = Array.Empty<uint>();
|
||||
public uint[] AryScoreRankCounts { get; set; } = Array.Empty<uint>();
|
||||
public bool IsDispAchievementOn { get; set; }
|
||||
public uint DispAchievementType { get; set; }
|
||||
public uint LastPlayMode { get; set; }
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user