Merge remote-tracking branch 'origin/master' into AIBattle
This commit is contained in:
commit
2dffe001e0
42
README.md
42
README.md
@ -12,7 +12,10 @@ This is a server for Taiko no Tatsujin Nijiiro ver 08.18
|
|||||||
|
|
||||||
1. Download the server from release page, extract anywhere
|
1. Download the server from release page, extract anywhere
|
||||||
|
|
||||||
2. Modify hosts, add the following entries:
|
2. From game's `Data\x64\datatable` folder, find `music_attribute.bin`, `musicinfo.bin`, `music_order.bin` and `wordlist.bin`, decompress them, add `.json` prefix to them.
|
||||||
|
The result is `music_attribute.json`, `musicinfo.json`, `music_order.json` and `wordlist.json`. Put the json files under` wwwroot/data` folder in server.
|
||||||
|
|
||||||
|
3. Modify hosts, add the following entries:
|
||||||
|
|
||||||
```
|
```
|
||||||
server.ip tenporouter.loc
|
server.ip tenporouter.loc
|
||||||
@ -23,23 +26,40 @@ This is a server for Taiko no Tatsujin Nijiiro ver 08.18
|
|||||||
|
|
||||||
where `server.ip` is your computers ip (or the server's ip)
|
where `server.ip` is your computers ip (or the server's ip)
|
||||||
|
|
||||||
3. Setup Apache as reverse proxy. Notice the following assumes a windows install, the server also works on Linux, but the guide only covers windows.
|
4. Setup Apache as reverse proxy. Notice the following assumes a windows install, the server also works on Linux, but the guide only covers windows.
|
||||||
|
|
||||||
1. Download [Apache](https://www.apachelounge.com/download/), extract anywhere
|
1. Download [Apache](https://www.apachelounge.com/download/), extract anywhere
|
||||||
|
|
||||||
2. Copy the content in Apache folder to Apache root folder (and replace)
|
2. **Copy the content in release rar's Apache folder to installed Apache root folder (and replace, which includes httpd.conf and httpd-vhosts.conf, if no prompt to replace files, you are extracting to wrong folder)**
|
||||||
|
|
||||||
|
3. Open `conf/httpd.conf` (under installed Apache folder), find this line (line 37 by default), modify it to your Apache install (extracted) full path
|
||||||
|
|
||||||
|
```htaccess
|
||||||
|
# For example, if your Apache is extracted to C:\users\username\Apache24, then this should be "c:/users/username/Apache24"
|
||||||
|
Define SRVROOT "d:/Projects/Apache24"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
4. Open the certs folder Apache root folder, then click on the localhost.crt file and import it to trusted root store.
|
||||||
|
|
||||||
5. Open the certs folder Apache root folder, then click on the localhost.crt file and import it to trusted root store.
|
|
||||||
|
|
||||||
If everything is correct, run bin/httpd.exe, a command prompt will open (and stay open, if it shut down, probably something is not setup correctly)
|
If everything is correct, run bin/httpd.exe, a command prompt will open (and stay open, if it shut down, probably something is not setup correctly)
|
||||||
|
|
||||||
4. Now run the server, if everything is setup correctly, visit http://localhost:5000, you should be able to see the web ui up and running without errors.
|
|
||||||
|
|
||||||
5. Go to game folder, copy the config files (AMConfig.ini and WritableConfig.ini) in the config folder from server release to AMCUS folder and replace the original ones.
|
5. Now run the server, if everything is setup correctly, visit http://localhost:5000, you should be able to see the web ui up and running without errors. (If you encounter errors in web ui for the first time, try visit https://naominet.jp:10122/)
|
||||||
|
|
||||||
6. Open command prompt as admin, navigate to game root folder (where init.ps1 is). Run `regsvr32 .\AMCUS\iauthdll.dll`. It should prompt about success.
|
6. Go to game folder, copy the config files (AMConfig.ini and WritableConfig.ini) in the AMCUS folder from server release to AMCUS folder and replace the original ones.
|
||||||
|
|
||||||
7. Run AMCUS/AMAuthd.exe, then run AMCUS/AMUpdater.exe. If the updater run and exits without issue, you are ready to run the game and connect to server.
|
7. Open command prompt as admin, navigate to game root folder (where init.ps1 is). Run `regsvr32 .\AMCUS\iauthdll.dll`. It should prompt about success.
|
||||||
|
|
||||||
8. Run the game, it should now connect to the server.
|
8. Run AMCUS/AMAuthd.exe, then run AMCUS/AMUpdater.exe. If the updater run and exits without issue, you are ready to run the game and connect to server.
|
||||||
|
|
||||||
|
9. Run the game, it should now connect to the server.
|
||||||
|
|
||||||
|
### Run the server on another computer
|
||||||
|
|
||||||
|
If you want to run the server on another computer, the procedure is almost identical.
|
||||||
|
|
||||||
|
Before you open browser, in `wwwroot/appsettings.json`, change `BaseUrl` to `https://naominet.jp:10122` then instead of visit localhost, visit the server using domain name to test.
|
||||||
|
|
||||||
|
Also note that now the cetificate also need to be imported on client computer, or web ui may not work. If you don't need https, change `BaseUrl` to `http://server.ip:80`, and visit on client. The game does not care about certificate.
|
||||||
|
|
||||||
|
18
SharedProject/Models/SongIntroductionData.cs
Normal file
18
SharedProject/Models/SongIntroductionData.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SharedProject.Models;
|
||||||
|
|
||||||
|
public class SongIntroductionData
|
||||||
|
{
|
||||||
|
[JsonPropertyName("setId")]
|
||||||
|
public uint SetId { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("verupNo")]
|
||||||
|
public uint VerupNo { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("mainSongNo")]
|
||||||
|
public uint MainSongNo { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("subSongNo")]
|
||||||
|
public uint[]? SubSongNo { get; set; }
|
||||||
|
}
|
@ -20,9 +20,36 @@ public class UserSetting
|
|||||||
|
|
||||||
public int NotesPosition { get; set; }
|
public int NotesPosition { get; set; }
|
||||||
|
|
||||||
public string MyDonName { get; set; } = String.Empty;
|
public string MyDonName { get; set; } = string.Empty;
|
||||||
|
|
||||||
public string Title { get; set; } = String.Empty;
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
public uint TitlePlateId { get; set; }
|
public uint TitlePlateId { get; set; }
|
||||||
|
|
||||||
|
public uint Kigurumi { get; set; }
|
||||||
|
|
||||||
|
public uint Head { get; set; }
|
||||||
|
|
||||||
|
public uint Body { get; set; }
|
||||||
|
|
||||||
|
public uint Face { get; set; }
|
||||||
|
|
||||||
|
public uint Puchi { get; set; }
|
||||||
|
|
||||||
|
public List<uint> UnlockedKigurumi { get; set; } = new();
|
||||||
|
|
||||||
|
public List<uint> UnlockedHead { get; set; } = new();
|
||||||
|
|
||||||
|
public List<uint> UnlockedBody { get; set; } = new();
|
||||||
|
|
||||||
|
public List<uint> UnlockedFace { get; set; } = new();
|
||||||
|
|
||||||
|
public List<uint> UnlockedPuchi { get; set; } = new();
|
||||||
|
|
||||||
|
public uint FaceColor { get; set; }
|
||||||
|
|
||||||
|
public uint BodyColor { get; set; }
|
||||||
|
|
||||||
|
public uint LimbColor { get; set; }
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,6 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kigurumi/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=musicinfo/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=musicinfo/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Namco/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Namco/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Puchi/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vocaloid/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vocaloid/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
1
TaikoLocalServer/.gitignore
vendored
Normal file
1
TaikoLocalServer/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
wwwroot/data/music_attribute.json
|
@ -4,9 +4,7 @@ public static class Constants
|
|||||||
{
|
{
|
||||||
public const string DATE_TIME_FORMAT = "yyyyMMddHHmmss";
|
public const string DATE_TIME_FORMAT = "yyyyMMddHHmmss";
|
||||||
|
|
||||||
public const int MUSIC_ID_MAX = 1600;
|
public const int MUSIC_ID_MAX = 9000;
|
||||||
|
|
||||||
public const int MUSIC_FLAG_ARRAY_SIZE = MUSIC_ID_MAX / 8 + 1;
|
|
||||||
|
|
||||||
public const int CROWN_FLAG_ARRAY_SIZE = MUSIC_ID_MAX + 1;
|
public const int CROWN_FLAG_ARRAY_SIZE = MUSIC_ID_MAX + 1;
|
||||||
|
|
||||||
@ -24,7 +22,27 @@ public static class Constants
|
|||||||
|
|
||||||
public const string DAN_DATA_FILE_NAME = "dan_data.json";
|
public const string DAN_DATA_FILE_NAME = "dan_data.json";
|
||||||
|
|
||||||
|
public const string INTRO_DATA_FILE_NAME = "intro_data.json";
|
||||||
|
|
||||||
public const int MIN_DAN_ID = 1;
|
public const int MIN_DAN_ID = 1;
|
||||||
public const int MAX_DAN_ID = 19;
|
public const int MAX_DAN_ID = 19;
|
||||||
public const int GOT_DAN_BITS = MAX_DAN_ID * 4;
|
|
||||||
|
public const int TONE_UID_MAX = 19;
|
||||||
|
|
||||||
|
public const int TITLE_UID_MAX = 814;
|
||||||
|
|
||||||
|
private const int COSTUME_FLAG_1_ARRAY_SIZE = 154;
|
||||||
|
private const int COSTUME_FLAG_2_ARRAY_SIZE = 140;
|
||||||
|
private const int COSTUME_FLAG_3_ARRAY_SIZE = 156;
|
||||||
|
private const int COSTUME_FLAG_4_ARRAY_SIZE = 58;
|
||||||
|
private const int COSTUME_FLAG_5_ARRAY_SIZE = 129;
|
||||||
|
public static readonly int[] CostumeFlagArraySizes =
|
||||||
|
{
|
||||||
|
COSTUME_FLAG_1_ARRAY_SIZE,
|
||||||
|
COSTUME_FLAG_2_ARRAY_SIZE,
|
||||||
|
COSTUME_FLAG_3_ARRAY_SIZE,
|
||||||
|
COSTUME_FLAG_4_ARRAY_SIZE,
|
||||||
|
COSTUME_FLAG_5_ARRAY_SIZE
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
@ -1,48 +0,0 @@
|
|||||||
using System.Collections.Immutable;
|
|
||||||
using System.Text.Json;
|
|
||||||
using SharedProject.Models;
|
|
||||||
using Swan.Mapping;
|
|
||||||
|
|
||||||
namespace TaikoLocalServer.Common.Utils;
|
|
||||||
|
|
||||||
public class DanOdaiDataManager
|
|
||||||
{
|
|
||||||
public ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> OdaiDataList { get; }
|
|
||||||
|
|
||||||
static DanOdaiDataManager() {}
|
|
||||||
|
|
||||||
private DanOdaiDataManager()
|
|
||||||
{
|
|
||||||
var dataPath = PathHelper.GetDataPath();
|
|
||||||
var filePath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME);
|
|
||||||
var jsonString = File.ReadAllText(filePath);
|
|
||||||
|
|
||||||
var result = JsonSerializer.Deserialize<List<DanData>>(jsonString);
|
|
||||||
|
|
||||||
if (result is null)
|
|
||||||
{
|
|
||||||
throw new ApplicationException("Cannot parse dan data json!");
|
|
||||||
}
|
|
||||||
|
|
||||||
OdaiDataList = result.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData);
|
|
||||||
}
|
|
||||||
private GetDanOdaiResponse.OdaiData ToResponseOdaiData(DanData data)
|
|
||||||
{
|
|
||||||
var responseOdaiData = new GetDanOdaiResponse.OdaiData
|
|
||||||
{
|
|
||||||
DanId = data.DanId,
|
|
||||||
Title = data.Title,
|
|
||||||
VerupNo = data.VerupNo
|
|
||||||
};
|
|
||||||
|
|
||||||
var odaiSongs = data.OdaiSongList.Select(song => song.CopyPropertiesToNew<GetDanOdaiResponse.OdaiData.OdaiSong>());
|
|
||||||
responseOdaiData.AryOdaiSongs.AddRange(odaiSongs);
|
|
||||||
|
|
||||||
var odaiBorders = data.OdaiBorderList.Select(border => border.CopyPropertiesToNew<GetDanOdaiResponse.OdaiData.OdaiBorder>());
|
|
||||||
responseOdaiData.AryOdaiBorders.AddRange(odaiBorders);
|
|
||||||
|
|
||||||
return responseOdaiData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DanOdaiDataManager Instance { get; } = new();
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Specialized;
|
using System.Collections;
|
||||||
|
using System.Collections.Specialized;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace TaikoLocalServer.Common.Utils;
|
namespace TaikoLocalServer.Common.Utils;
|
||||||
@ -106,4 +107,22 @@ public static class FlagCalculator
|
|||||||
gotDanFlagList.Add(gotDanFlag.Data);
|
gotDanFlagList.Add(gotDanFlag.Data);
|
||||||
return MemoryMarshal.AsBytes(new ReadOnlySpan<int>(gotDanFlagList.ToArray())).ToArray();
|
return MemoryMarshal.AsBytes(new ReadOnlySpan<int>(gotDanFlagList.ToArray())).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] GetBitArrayFromIds(IEnumerable<uint> idArray, int bitArraySize, ILogger logger)
|
||||||
|
{
|
||||||
|
var result = new byte[bitArraySize / 8 + 1];
|
||||||
|
var bitSet = new BitArray(bitArraySize + 1);
|
||||||
|
foreach (var id in idArray)
|
||||||
|
{
|
||||||
|
if (id >= bitArraySize)
|
||||||
|
{
|
||||||
|
logger.LogWarning("Id out of range!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bitSet.Set((int)id, true);
|
||||||
|
}
|
||||||
|
bitSet.CopyTo(result, 0);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
52
TaikoLocalServer/Common/Utils/JsonHelper.cs
Normal file
52
TaikoLocalServer/Common/Utils/JsonHelper.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Common.Utils;
|
||||||
|
|
||||||
|
public static class JsonHelper
|
||||||
|
{
|
||||||
|
public static List<uint> GetCostumeDataFromUserData(UserDatum userData, ILogger logger)
|
||||||
|
{
|
||||||
|
var costumeData = new List<uint> { 0, 0, 0, 0, 0 };
|
||||||
|
try
|
||||||
|
{
|
||||||
|
costumeData = JsonSerializer.Deserialize<List<uint>>(userData.CostumeData);
|
||||||
|
}
|
||||||
|
catch (JsonException e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, "Parsing costume json data failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (costumeData != null && costumeData.Count >= 5)
|
||||||
|
{
|
||||||
|
return costumeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogWarning("Costume data is null or count less than 5!");
|
||||||
|
costumeData = new List<uint> { 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,42 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace TaikoLocalServer.Common.Utils;
|
|
||||||
|
|
||||||
public class MusicAttributeManager
|
|
||||||
{
|
|
||||||
public readonly Dictionary<uint,MusicAttribute> MusicAttributes;
|
|
||||||
|
|
||||||
static MusicAttributeManager()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private MusicAttributeManager()
|
|
||||||
{
|
|
||||||
var dataPath = PathHelper.GetDataPath();
|
|
||||||
var filePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_FILE_NAME);
|
|
||||||
var jsonString = File.ReadAllText(filePath);
|
|
||||||
|
|
||||||
var result = JsonSerializer.Deserialize<List<MusicAttribute>>(jsonString);
|
|
||||||
if (result is null)
|
|
||||||
{
|
|
||||||
throw new ApplicationException("Cannot parse music attribute json!");
|
|
||||||
}
|
|
||||||
|
|
||||||
MusicAttributes = result.ToDictionary(attribute => attribute.MusicId);
|
|
||||||
|
|
||||||
Musics = MusicAttributes.Select(pair => pair.Key)
|
|
||||||
.ToList();
|
|
||||||
Musics.Sort();
|
|
||||||
|
|
||||||
MusicsWithUra = MusicAttributes.Where(attribute => attribute.Value.HasUra)
|
|
||||||
.Select(pair => pair.Key)
|
|
||||||
.ToList();
|
|
||||||
MusicsWithUra.Sort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MusicAttributeManager Instance { get; } = new();
|
|
||||||
|
|
||||||
public readonly List<uint> Musics;
|
|
||||||
|
|
||||||
public readonly List<uint> MusicsWithUra;
|
|
||||||
}
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
public static class PathHelper
|
public static class PathHelper
|
||||||
{
|
{
|
||||||
public static string GetDataPath()
|
public static string GetRootPath()
|
||||||
{
|
{
|
||||||
var path = Environment.ProcessPath;
|
var path = Environment.ProcessPath;
|
||||||
if (path is null)
|
if (path is null)
|
||||||
@ -16,4 +16,9 @@ public static class PathHelper
|
|||||||
}
|
}
|
||||||
return Path.Combine(parentPath.ToString(), "wwwroot");
|
return Path.Combine(parentPath.ToString(), "wwwroot");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetDataPath()
|
||||||
|
{
|
||||||
|
return Path.Combine(GetRootPath(), "data");
|
||||||
|
}
|
||||||
}
|
}
|
@ -22,7 +22,7 @@
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var path = Path.Combine(PathHelper.GetDataPath(), Constants.DEFAULT_DB_NAME);
|
var path = Path.Combine(PathHelper.GetRootPath(), Constants.DEFAULT_DB_NAME);
|
||||||
optionsBuilder.UseSqlite($"Data Source={path}");
|
optionsBuilder.UseSqlite($"Data Source={path}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
|
using System.Text.Json;
|
||||||
using SharedProject.Models;
|
using SharedProject.Models;
|
||||||
using SharedProject.Models.Responses;
|
using SharedProject.Models.Responses;
|
||||||
using SharedProject.Utils;
|
using SharedProject.Utils;
|
||||||
using TaikoLocalServer.Services;
|
using TaikoLocalServer.Services;
|
||||||
using TaikoLocalServer.Services.Interfaces;
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
namespace TaikoLocalServer.Controllers.Api;
|
namespace TaikoLocalServer.Controllers.Api;
|
||||||
|
|
||||||
@ -28,6 +30,10 @@ public class UserSettingsController : BaseController<UserSettingsController>
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var costumeData = JsonHelper.GetCostumeDataFromUserData(user, Logger);
|
||||||
|
|
||||||
|
var costumeUnlockData = JsonHelper.GetCostumeUnlockDataFromUserData(user, Logger);
|
||||||
|
|
||||||
var response = new UserSetting
|
var response = new UserSetting
|
||||||
{
|
{
|
||||||
AchievementDisplayDifficulty = user.AchievementDisplayDifficulty,
|
AchievementDisplayDifficulty = user.AchievementDisplayDifficulty,
|
||||||
@ -40,7 +46,20 @@ public class UserSettingsController : BaseController<UserSettingsController>
|
|||||||
ToneId = user.SelectedToneId,
|
ToneId = user.SelectedToneId,
|
||||||
MyDonName = user.MyDonName,
|
MyDonName = user.MyDonName,
|
||||||
Title = user.Title,
|
Title = user.Title,
|
||||||
TitlePlateId = user.TitlePlateId
|
TitlePlateId = user.TitlePlateId,
|
||||||
|
Kigurumi = costumeData[0],
|
||||||
|
Head = costumeData[1],
|
||||||
|
Body = costumeData[2],
|
||||||
|
Face = costumeData[3],
|
||||||
|
Puchi = costumeData[4],
|
||||||
|
UnlockedKigurumi = costumeUnlockData[0],
|
||||||
|
UnlockedHead = costumeUnlockData[1],
|
||||||
|
UnlockedBody = costumeUnlockData[2],
|
||||||
|
UnlockedFace = costumeUnlockData[3],
|
||||||
|
UnlockedPuchi = costumeUnlockData[4],
|
||||||
|
BodyColor = user.ColorBody,
|
||||||
|
FaceColor = user.ColorFace,
|
||||||
|
LimbColor = user.ColorLimb
|
||||||
};
|
};
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
@ -55,6 +74,15 @@ public class UserSettingsController : BaseController<UserSettingsController>
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var costumes = new List<uint>
|
||||||
|
{
|
||||||
|
userSetting.Kigurumi,
|
||||||
|
userSetting.Head,
|
||||||
|
userSetting.Body,
|
||||||
|
userSetting.Face,
|
||||||
|
userSetting.Puchi,
|
||||||
|
};
|
||||||
|
|
||||||
user.IsSkipOn = userSetting.IsSkipOn;
|
user.IsSkipOn = userSetting.IsSkipOn;
|
||||||
user.IsVoiceOn = userSetting.IsVoiceOn;
|
user.IsVoiceOn = userSetting.IsVoiceOn;
|
||||||
user.DisplayAchievement = userSetting.IsDisplayAchievement;
|
user.DisplayAchievement = userSetting.IsDisplayAchievement;
|
||||||
@ -66,6 +94,11 @@ public class UserSettingsController : BaseController<UserSettingsController>
|
|||||||
user.MyDonName = userSetting.MyDonName;
|
user.MyDonName = userSetting.MyDonName;
|
||||||
user.Title = userSetting.Title;
|
user.Title = userSetting.Title;
|
||||||
user.TitlePlateId = userSetting.TitlePlateId;
|
user.TitlePlateId = userSetting.TitlePlateId;
|
||||||
|
user.ColorBody = userSetting.BodyColor;
|
||||||
|
user.ColorFace = userSetting.FaceColor;
|
||||||
|
user.ColorLimb = userSetting.LimbColor;
|
||||||
|
user.CostumeData = JsonSerializer.Serialize(costumes);
|
||||||
|
|
||||||
|
|
||||||
await userDatumService.UpdateUserDatum(user);
|
await userDatumService.UpdateUserDatum(user);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using TaikoLocalServer.Services.Interfaces;
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
namespace TaikoLocalServer.Controllers.Game;
|
namespace TaikoLocalServer.Controllers.Game;
|
||||||
|
|
||||||
@ -73,42 +74,18 @@ public class BaidController : BaseController<BaidController>
|
|||||||
datum.Difficulty == achievementDisplayDifficulty :
|
datum.Difficulty == achievementDisplayDifficulty :
|
||||||
datum.Difficulty is Difficulty.Oni or Difficulty.UraOni).ToList();
|
datum.Difficulty is Difficulty.Oni or Difficulty.UraOni).ToList();
|
||||||
|
|
||||||
var crownCount = new uint[3];
|
var crownCount = CalculateCrownCount(songCountData);
|
||||||
foreach (var crownType in Enum.GetValues<CrownType>())
|
|
||||||
{
|
|
||||||
if (crownType != CrownType.None)
|
|
||||||
{
|
|
||||||
crownCount[(int)crownType - 1] = (uint)songCountData.Count(datum => datum.BestCrown == crownType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var scoreRankCount = new uint[7];
|
var scoreRankCount = CalculateScoreRankCount(songCountData);
|
||||||
foreach (var scoreRankType in Enum.GetValues<ScoreRank>())
|
|
||||||
{
|
|
||||||
if (scoreRankType != ScoreRank.None)
|
|
||||||
{
|
|
||||||
scoreRankCount[(int)scoreRankType - 2] = (uint)songCountData.Count(datum => datum.BestScoreRank == scoreRankType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var costumeData = new List<uint>{ 0, 0, 0, 0, 0 };
|
var costumeData = JsonHelper.GetCostumeDataFromUserData(userData, Logger);
|
||||||
try
|
|
||||||
{
|
|
||||||
costumeData = JsonSerializer.Deserialize<List<uint>>(userData.CostumeData);
|
|
||||||
}
|
|
||||||
catch (JsonException e)
|
|
||||||
{
|
|
||||||
Logger.LogError(e, "Parsing costume json data failed");
|
|
||||||
}
|
|
||||||
if (costumeData == null || costumeData.Count < 5)
|
|
||||||
{
|
|
||||||
Logger.LogWarning("Costume data is null or count less than 5!");
|
|
||||||
costumeData = new List<uint> { 0, 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
var costumeFlag = new byte[10];
|
var costumeArrays = JsonHelper.GetCostumeUnlockDataFromUserData(userData, Logger);
|
||||||
Array.Fill(costumeFlag, byte.MaxValue);
|
|
||||||
|
var costumeFlagArrays = Constants.CostumeFlagArraySizes
|
||||||
|
.Select((size, index) => FlagCalculator.GetBitArrayFromIds(costumeArrays[index], size, Logger))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var danData = await danScoreDatumService.GetDanScoreDatumByBaid(baid);
|
var danData = await danScoreDatumService.GetDanScoreDatumByBaid(baid);
|
||||||
|
|
||||||
@ -118,6 +95,23 @@ public class BaidController : BaseController<BaidController>
|
|||||||
.Max();
|
.Max();
|
||||||
var gotDanFlagArray = FlagCalculator.ComputeGotDanFlags(danData);
|
var gotDanFlagArray = FlagCalculator.ComputeGotDanFlags(danData);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
response = new BAIDResponse
|
response = new BAIDResponse
|
||||||
{
|
{
|
||||||
Result = 1,
|
Result = 1,
|
||||||
@ -145,18 +139,18 @@ public class BaidController : BaseController<BaidController>
|
|||||||
Costume4 = costumeData[3],
|
Costume4 = costumeData[3],
|
||||||
Costume5 = costumeData[4]
|
Costume5 = costumeData[4]
|
||||||
},
|
},
|
||||||
CostumeFlg1 = costumeFlag,
|
CostumeFlg1 = costumeFlagArrays[0],
|
||||||
CostumeFlg2 = costumeFlag,
|
CostumeFlg2 = costumeFlagArrays[1],
|
||||||
CostumeFlg3 = costumeFlag,
|
CostumeFlg3 = costumeFlagArrays[2],
|
||||||
CostumeFlg4 = costumeFlag,
|
CostumeFlg4 = costumeFlagArrays[3],
|
||||||
CostumeFlg5 = costumeFlag,
|
CostumeFlg5 = costumeFlagArrays[4],
|
||||||
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT),
|
LastPlayDatetime = userData.LastPlayDatetime.ToString(Constants.DATE_TIME_FORMAT),
|
||||||
IsDispDanOn = userData.DisplayDan,
|
IsDispDanOn = userData.DisplayDan,
|
||||||
GotDanMax = maxDan,
|
GotDanMax = maxDan,
|
||||||
GotDanFlg = gotDanFlagArray,
|
GotDanFlg = gotDanFlagArray,
|
||||||
GotDanextraFlg = new byte[20],
|
GotDanextraFlg = new byte[20],
|
||||||
DefaultToneSetting = userData.SelectedToneId,
|
DefaultToneSetting = userData.SelectedToneId,
|
||||||
GenericInfoFlg = new byte[10],
|
GenericInfoFlg = genericInfoFlgArray,
|
||||||
AryCrownCounts = crownCount,
|
AryCrownCounts = crownCount,
|
||||||
AryScoreRankCounts = scoreRankCount,
|
AryScoreRankCounts = scoreRankCount,
|
||||||
IsDispAchievementOn = userData.DisplayAchievement,
|
IsDispAchievementOn = userData.DisplayAchievement,
|
||||||
@ -173,4 +167,32 @@ public class BaidController : BaseController<BaidController>
|
|||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static uint[] CalculateScoreRankCount(IReadOnlyCollection<SongBestDatum> songCountData)
|
||||||
|
{
|
||||||
|
var scoreRankCount = new uint[7];
|
||||||
|
foreach (var scoreRankType in Enum.GetValues<ScoreRank>())
|
||||||
|
{
|
||||||
|
if (scoreRankType != ScoreRank.None)
|
||||||
|
{
|
||||||
|
scoreRankCount[(int)scoreRankType - 2] =
|
||||||
|
(uint)songCountData.Count(datum => datum.BestScoreRank == scoreRankType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,9 +1,18 @@
|
|||||||
namespace TaikoLocalServer.Controllers.Game;
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Controllers.Game;
|
||||||
|
|
||||||
[Route("/v12r03/chassis/getdanodai.php")]
|
[Route("/v12r03/chassis/getdanodai.php")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class GetDanOdaiController : BaseController<GetDanOdaiController>
|
public class GetDanOdaiController : BaseController<GetDanOdaiController>
|
||||||
{
|
{
|
||||||
|
private readonly IGameDataService gameDataService;
|
||||||
|
|
||||||
|
public GetDanOdaiController(IGameDataService gameDataService)
|
||||||
|
{
|
||||||
|
this.gameDataService = gameDataService;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Produces("application/protobuf")]
|
[Produces("application/protobuf")]
|
||||||
public IActionResult GetDanOdai([FromBody] GetDanOdaiRequest request)
|
public IActionResult GetDanOdai([FromBody] GetDanOdaiRequest request)
|
||||||
@ -19,11 +28,10 @@ public class GetDanOdaiController : BaseController<GetDanOdaiController>
|
|||||||
{
|
{
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
var manager = DanOdaiDataManager.Instance;
|
|
||||||
foreach (var danId in request.DanIds)
|
foreach (var danId in request.DanIds)
|
||||||
{
|
{
|
||||||
manager.OdaiDataList.TryGetValue(danId, out var odaiData);
|
gameDataService.GetDanDataDictionary().TryGetValue(danId, out var odaiData);
|
||||||
if (odaiData is null)
|
if (odaiData is null)
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Requested dan id {Id} does not exist!", danId);
|
Logger.LogWarning("Requested dan id {Id} does not exist!", danId);
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
namespace TaikoLocalServer.Controllers.Game;
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Controllers.Game;
|
||||||
|
|
||||||
[Route("/v12r03/chassis/getsongintroduction.php")]
|
[Route("/v12r03/chassis/getsongintroduction.php")]
|
||||||
[ApiController]
|
[ApiController]
|
||||||
public class GetSongIntroductionController : BaseController<GetSongIntroductionController>
|
public class GetSongIntroductionController : BaseController<GetSongIntroductionController>
|
||||||
{
|
{
|
||||||
|
private readonly IGameDataService gameDataService;
|
||||||
|
|
||||||
|
public GetSongIntroductionController(IGameDataService gameDataService)
|
||||||
|
{
|
||||||
|
this.gameDataService = gameDataService;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Produces("application/protobuf")]
|
[Produces("application/protobuf")]
|
||||||
public IActionResult GetSongIntroduction([FromBody] GetSongIntroductionRequest request)
|
public IActionResult GetSongIntroduction([FromBody] GetSongIntroductionRequest request)
|
||||||
@ -14,16 +23,17 @@ public class GetSongIntroductionController : BaseController<GetSongIntroductionC
|
|||||||
{
|
{
|
||||||
Result = 1
|
Result = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var setId in request.SetIds)
|
foreach (var setId in request.SetIds)
|
||||||
{
|
{
|
||||||
response.ArySongIntroductionDatas.Add(new GetSongIntroductionResponse.SongIntroductionData
|
gameDataService.GetSongIntroDictionary().TryGetValue(setId, out var introData);
|
||||||
|
if (introData is null)
|
||||||
{
|
{
|
||||||
MainSongNo = 2,
|
Logger.LogWarning("Requested set id {Id} does not exist!", setId);
|
||||||
SubSongNoes = new uint[] {177,193,3,4},
|
continue;
|
||||||
SetId = setId,
|
}
|
||||||
VerupNo = 1
|
|
||||||
});
|
response.ArySongIntroductionDatas.Add(introData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using System.Collections;
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
|
|
||||||
namespace TaikoLocalServer.Controllers.Game;
|
namespace TaikoLocalServer.Controllers.Game;
|
||||||
|
|
||||||
@ -6,21 +6,21 @@ namespace TaikoLocalServer.Controllers.Game;
|
|||||||
[Route("/v12r03/chassis/initialdatacheck.php")]
|
[Route("/v12r03/chassis/initialdatacheck.php")]
|
||||||
public class InitialDataCheckController : BaseController<InitialDataCheckController>
|
public class InitialDataCheckController : BaseController<InitialDataCheckController>
|
||||||
{
|
{
|
||||||
|
private readonly IGameDataService gameDataService;
|
||||||
|
|
||||||
|
public InitialDataCheckController(IGameDataService gameDataService)
|
||||||
|
{
|
||||||
|
this.gameDataService = gameDataService;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Produces("application/protobuf")]
|
[Produces("application/protobuf")]
|
||||||
public IActionResult InitialDataCheck([FromBody] InitialdatacheckRequest request)
|
public IActionResult InitialDataCheck([FromBody] InitialdatacheckRequest request)
|
||||||
{
|
{
|
||||||
Logger.LogInformation("Initial data check request: {Request}", request.Stringify());
|
Logger.LogInformation("Initial data check request: {Request}", request.Stringify());
|
||||||
|
|
||||||
var musicAttributeManager = MusicAttributeManager.Instance;
|
var enabledArray =
|
||||||
|
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicList(), Constants.MUSIC_ID_MAX, Logger);
|
||||||
var enabledArray = new byte[Constants.MUSIC_FLAG_ARRAY_SIZE];
|
|
||||||
var bitSet = new BitArray(Constants.MUSIC_ID_MAX);
|
|
||||||
foreach (var music in musicAttributeManager.Musics)
|
|
||||||
{
|
|
||||||
bitSet.Set((int)music, true);
|
|
||||||
}
|
|
||||||
bitSet.CopyTo(enabledArray, 0);
|
|
||||||
|
|
||||||
var danData = new List<InitialdatacheckResponse.InformationData>();
|
var danData = new List<InitialdatacheckResponse.InformationData>();
|
||||||
for (var danId = Constants.MIN_DAN_ID; danId <= Constants.MAX_DAN_ID; danId++)
|
for (var danId = Constants.MIN_DAN_ID; danId <= Constants.MAX_DAN_ID; danId++)
|
||||||
@ -32,6 +32,16 @@ public class InitialDataCheckController : BaseController<InitialDataCheckControl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var introData = new List<InitialdatacheckResponse.InformationData>();
|
||||||
|
for (var setId = 1; setId <= gameDataService.GetSongIntroDictionary().Count; setId++)
|
||||||
|
{
|
||||||
|
introData.Add(new InitialdatacheckResponse.InformationData
|
||||||
|
{
|
||||||
|
InfoId = (uint)setId,
|
||||||
|
VerupNo = 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var response = new InitialdatacheckResponse
|
var response = new InitialdatacheckResponse
|
||||||
{
|
{
|
||||||
Result = 1,
|
Result = 1,
|
||||||
@ -100,6 +110,7 @@ public class InitialDataCheckController : BaseController<InitialDataCheckControl
|
|||||||
});*/
|
});*/
|
||||||
};
|
};
|
||||||
response.AryDanOdaiDatas.AddRange(danData);
|
response.AryDanOdaiDatas.AddRange(danData);
|
||||||
|
response.ArySongIntroductionDatas.AddRange(introData);
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,11 @@ public class MyDonEntryController : BaseController<MyDonEntryController>
|
|||||||
ColorFace = 0,
|
ColorFace = 0,
|
||||||
ColorBody = 1,
|
ColorBody = 1,
|
||||||
ColorLimb = 3,
|
ColorLimb = 3,
|
||||||
FavoriteSongsArray = "[]"
|
FavoriteSongsArray = "[]",
|
||||||
|
ToneFlgArray = "[]",
|
||||||
|
TitleFlgArray = "[]",
|
||||||
|
CostumeFlgArray = "[[],[],[],[],[]]",
|
||||||
|
GenericInfoFlgArray = "[]"
|
||||||
};
|
};
|
||||||
|
|
||||||
await userDatumService.InsertUserDatum(newUser);
|
await userDatumService.InsertUserDatum(newUser);
|
||||||
|
@ -65,7 +65,7 @@ public class PlayResultController : BaseController<PlayResultController>
|
|||||||
|
|
||||||
if (playMode == PlayMode.AiBattle)
|
if (playMode == PlayMode.AiBattle)
|
||||||
{
|
{
|
||||||
await UpdateAiBattleData(request, stageData);
|
// await UpdateAiBattleData(request, stageData);
|
||||||
// Update AI win count here somewhere, or in UpdatePlayData?
|
// Update AI win count here somewhere, or in UpdatePlayData?
|
||||||
// I have no clue how to update input median or variance
|
// I have no clue how to update input median or variance
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ public class PlayResultController : BaseController<PlayResultController>
|
|||||||
|
|
||||||
await UpdatePlayData(request, songNumber, stageData, lastPlayDatetime);
|
await UpdatePlayData(request, songNumber, stageData, lastPlayDatetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,9 +191,83 @@ public class PlayResultController : BaseController<PlayResultController>
|
|||||||
|
|
||||||
userdata.LastPlayDatetime = lastPlayDatetime;
|
userdata.LastPlayDatetime = lastPlayDatetime;
|
||||||
userdata.LastPlayMode = playResultData.PlayMode;
|
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));
|
||||||
|
|
||||||
await userDatumService.UpdateUserDatum(userdata);
|
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,
|
private async Task UpdateBestData(PlayResultRequest request, StageData stageData,
|
||||||
IEnumerable<SongBestDatum> bestData)
|
IEnumerable<SongBestDatum> bestData)
|
||||||
{
|
{
|
||||||
@ -215,7 +289,8 @@ public class PlayResultController : BaseController<PlayResultController>
|
|||||||
await songBestDatumService.UpdateOrInsertSongBestDatum(bestDatum);
|
await songBestDatumService.UpdateOrInsertSongBestDatum(bestDatum);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateAiBattleData(PlayResultRequest request, StageData stageData)
|
// TODO: AI battle
|
||||||
|
/*private async Task UpdateAiBattleData(PlayResultRequest request, StageData stageData)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < stageData.ArySectionDatas.Count; i++)
|
for (int i = 0; i < stageData.ArySectionDatas.Count; i++)
|
||||||
{
|
{
|
||||||
@ -226,7 +301,7 @@ public class PlayResultController : BaseController<PlayResultController>
|
|||||||
// if any aspect of the section is higher than the previous best, update it
|
// if any aspect of the section is higher than the previous best, update it
|
||||||
// Similar to Dan best play updates
|
// Similar to Dan best play updates
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
private static CrownType PlayResultToCrown(StageData stageData)
|
private static CrownType PlayResultToCrown(StageData stageData)
|
||||||
{
|
{
|
||||||
|
@ -8,10 +8,13 @@ namespace TaikoLocalServer.Controllers.Game;
|
|||||||
public class SelfBestController : BaseController<SelfBestController>
|
public class SelfBestController : BaseController<SelfBestController>
|
||||||
{
|
{
|
||||||
private readonly ISongBestDatumService songBestDatumService;
|
private readonly ISongBestDatumService songBestDatumService;
|
||||||
|
|
||||||
|
private readonly IGameDataService gameDataService;
|
||||||
|
|
||||||
public SelfBestController(ISongBestDatumService songBestDatumService)
|
public SelfBestController(ISongBestDatumService songBestDatumService, IGameDataService gameDataService)
|
||||||
{
|
{
|
||||||
this.songBestDatumService = songBestDatumService;
|
this.songBestDatumService = songBestDatumService;
|
||||||
|
this.gameDataService = gameDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@ -25,8 +28,6 @@ public class SelfBestController : BaseController<SelfBestController>
|
|||||||
Result = 1,
|
Result = 1,
|
||||||
Level = request.Level
|
Level = request.Level
|
||||||
};
|
};
|
||||||
|
|
||||||
var manager = MusicAttributeManager.Instance;
|
|
||||||
|
|
||||||
var requestDifficulty = (Difficulty)request.Level;
|
var requestDifficulty = (Difficulty)request.Level;
|
||||||
requestDifficulty.Throw().IfOutOfRange();
|
requestDifficulty.Throw().IfOutOfRange();
|
||||||
@ -38,7 +39,7 @@ public class SelfBestController : BaseController<SelfBestController>
|
|||||||
.ToList();
|
.ToList();
|
||||||
foreach (var songNo in request.ArySongNoes)
|
foreach (var songNo in request.ArySongNoes)
|
||||||
{
|
{
|
||||||
if (!manager.MusicAttributes.ContainsKey(songNo))
|
if (!gameDataService.GetMusicAttributes().ContainsKey(songNo))
|
||||||
{
|
{
|
||||||
Logger.LogWarning("Music no {No} is missing!", songNo);
|
Logger.LogWarning("Music no {No} is missing!", songNo);
|
||||||
continue;
|
continue;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using TaikoLocalServer.Services.Interfaces;
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
using Throw;
|
using Throw;
|
||||||
@ -13,11 +12,14 @@ public class UserDataController : BaseController<UserDataController>
|
|||||||
private readonly IUserDatumService userDatumService;
|
private readonly IUserDatumService userDatumService;
|
||||||
|
|
||||||
private readonly ISongPlayDatumService songPlayDatumService;
|
private readonly ISongPlayDatumService songPlayDatumService;
|
||||||
|
|
||||||
|
private readonly IGameDataService gameDataService;
|
||||||
|
|
||||||
public UserDataController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService)
|
public UserDataController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService, IGameDataService gameDataService)
|
||||||
{
|
{
|
||||||
this.userDatumService = userDatumService;
|
this.userDatumService = userDatumService;
|
||||||
this.songPlayDatumService = songPlayDatumService;
|
this.songPlayDatumService = songPlayDatumService;
|
||||||
|
this.gameDataService = gameDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@ -26,26 +28,45 @@ public class UserDataController : BaseController<UserDataController>
|
|||||||
{
|
{
|
||||||
Logger.LogInformation("UserData request : {Request}", request.Stringify());
|
Logger.LogInformation("UserData request : {Request}", request.Stringify());
|
||||||
|
|
||||||
var musicAttributeManager = MusicAttributeManager.Instance;
|
var releaseSongArray =
|
||||||
|
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicList(), Constants.MUSIC_ID_MAX, Logger);
|
||||||
|
|
||||||
var releaseSongArray = new byte[Constants.MUSIC_FLAG_ARRAY_SIZE];
|
var uraSongArray =
|
||||||
var bitSet = new BitArray(Constants.MUSIC_ID_MAX);
|
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicWithUraList(), Constants.MUSIC_ID_MAX, Logger);
|
||||||
foreach (var music in musicAttributeManager.Musics)
|
|
||||||
{
|
|
||||||
bitSet.Set((int)music, true);
|
|
||||||
}
|
|
||||||
bitSet.CopyTo(releaseSongArray, 0);
|
|
||||||
|
|
||||||
var uraSongArray = new byte[Constants.MUSIC_FLAG_ARRAY_SIZE];
|
|
||||||
bitSet.SetAll(false);
|
|
||||||
foreach (var music in musicAttributeManager.MusicsWithUra)
|
|
||||||
{
|
|
||||||
bitSet.Set((int)music, true);
|
|
||||||
}
|
|
||||||
bitSet.CopyTo(uraSongArray, 0);
|
|
||||||
|
|
||||||
var toneArray = new byte[16];
|
var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid);
|
||||||
Array.Fill(toneArray, byte.MaxValue);
|
|
||||||
|
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!");
|
||||||
|
|
||||||
|
var toneArray = FlagCalculator.GetBitArrayFromIds(toneFlg, Constants.TONE_UID_MAX, 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, Constants.TITLE_UID_MAX, Logger);
|
||||||
|
|
||||||
var recentSongs = (await songPlayDatumService.GetSongPlayDatumByBaid(request.Baid))
|
var recentSongs = (await songPlayDatumService.GetSongPlayDatumByBaid(request.Baid))
|
||||||
.AsEnumerable()
|
.AsEnumerable()
|
||||||
@ -67,8 +88,6 @@ public class UserDataController : BaseController<UserDataController>
|
|||||||
|
|
||||||
recentSongs = recentSet.ToArray();
|
recentSongs = recentSet.ToArray();
|
||||||
|
|
||||||
var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid);
|
|
||||||
|
|
||||||
var favoriteSongs = Array.Empty<uint>();
|
var favoriteSongs = Array.Empty<uint>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -90,7 +109,7 @@ public class UserDataController : BaseController<UserDataController>
|
|||||||
{
|
{
|
||||||
Result = 1,
|
Result = 1,
|
||||||
ToneFlg = toneArray,
|
ToneFlg = toneArray,
|
||||||
// TitleFlg = GZipBytesUtil.GetGZipBytes(new byte[100]),
|
TitleFlg = titleArray,
|
||||||
ReleaseSongFlg = releaseSongArray,
|
ReleaseSongFlg = releaseSongArray,
|
||||||
UraReleaseSongFlg = uraSongArray,
|
UraReleaseSongFlg = uraSongArray,
|
||||||
DefaultOptionSetting = defaultOptions,
|
DefaultOptionSetting = defaultOptions,
|
||||||
|
@ -6,7 +6,11 @@
|
|||||||
public string MyDonName { get; set; } = string.Empty;
|
public string MyDonName { get; set; } = string.Empty;
|
||||||
public string Title { get; set; } = string.Empty;
|
public string Title { get; set; } = string.Empty;
|
||||||
public uint TitlePlateId { get; set; }
|
public uint TitlePlateId { get; set; }
|
||||||
public string FavoriteSongsArray { get; set; } = string.Empty;
|
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 short OptionSetting { get; set; }
|
||||||
public int NotesPosition { get; set; }
|
public int NotesPosition { get; set; }
|
||||||
public bool IsVoiceOn { get; set; }
|
public bool IsVoiceOn { get; set; }
|
||||||
@ -17,7 +21,7 @@
|
|||||||
public uint ColorBody { get; set; }
|
public uint ColorBody { get; set; }
|
||||||
public uint ColorFace { get; set; }
|
public uint ColorFace { get; set; }
|
||||||
public uint ColorLimb { get; set; }
|
public uint ColorLimb { get; set; }
|
||||||
public string CostumeData { get; set; } = string.Empty;
|
public string CostumeData { get; set; } = "[[],[],[],[],[]]";
|
||||||
public bool DisplayDan { get; set; }
|
public bool DisplayDan { get; set; }
|
||||||
public bool DisplayAchievement { get; set; }
|
public bool DisplayAchievement { get; set; }
|
||||||
public Difficulty AchievementDisplayDifficulty { get; set; }
|
public Difficulty AchievementDisplayDifficulty { get; set; }
|
||||||
|
341
TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.Designer.cs
generated
Normal file
341
TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.Designer.cs
generated
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using TaikoLocalServer.Context;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(TaikoDbContext))]
|
||||||
|
[Migration("20220914054039_AddRewardFlgs")]
|
||||||
|
partial class AddRewardFlgs
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.Card", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("AccessCode")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("AccessCode");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "Baid" }, "IX_Card_Baid")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Card", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("DanId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
b.ToTable("DanScoreData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("DanId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
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", "SongNumber");
|
||||||
|
|
||||||
|
b.ToTable("DanStageScoreData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.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("TaikoLocalServer.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("TaikoLocalServer.Entities.UserDatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("AchievementDisplayDifficulty")
|
||||||
|
.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<bool>("DisplayAchievement")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("DisplayDan")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("FavoriteSongsArray")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
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<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.HasKey("Baid");
|
||||||
|
|
||||||
|
b.ToTable("UserData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.DanScoreDatum", "Parent")
|
||||||
|
.WithMany("DanStageScoreData")
|
||||||
|
.HasForeignKey("Baid", "DanId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Parent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.SongBestDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.SongPlayDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.UserDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("DanStageScoreData");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.cs
Normal file
51
TaikoLocalServer/Migrations/20220914054039_AddRewardFlgs.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddRewardFlgs : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "CostumeFlgArray",
|
||||||
|
table: "UserData",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "[[],[],[],[],[]]");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "TitleFlgArray",
|
||||||
|
table: "UserData",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "[]");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ToneFlgArray",
|
||||||
|
table: "UserData",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CostumeFlgArray",
|
||||||
|
table: "UserData");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TitleFlgArray",
|
||||||
|
table: "UserData");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ToneFlgArray",
|
||||||
|
table: "UserData");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
345
TaikoLocalServer/Migrations/20220916121143_AddGenericInfoFlg.Designer.cs
generated
Normal file
345
TaikoLocalServer/Migrations/20220916121143_AddGenericInfoFlg.Designer.cs
generated
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using TaikoLocalServer.Context;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(TaikoDbContext))]
|
||||||
|
[Migration("20220916121143_AddGenericInfoFlg")]
|
||||||
|
partial class AddGenericInfoFlg
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2");
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.Card", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("AccessCode")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("AccessCode");
|
||||||
|
|
||||||
|
b.HasIndex(new[] { "Baid" }, "IX_Card_Baid")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Card", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("DanId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
b.ToTable("DanScoreData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("DanId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
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", "SongNumber");
|
||||||
|
|
||||||
|
b.ToTable("DanStageScoreData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.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("TaikoLocalServer.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("TaikoLocalServer.Entities.UserDatum", b =>
|
||||||
|
{
|
||||||
|
b.Property<uint>("Baid")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<uint>("AchievementDisplayDifficulty")
|
||||||
|
.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<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>("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<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.HasKey("Baid");
|
||||||
|
|
||||||
|
b.ToTable("UserData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.DanScoreDatum", "Parent")
|
||||||
|
.WithMany("DanStageScoreData")
|
||||||
|
.HasForeignKey("Baid", "DanId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Parent");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.SongBestDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.SongPlayDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.UserDatum", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("Baid")
|
||||||
|
.HasPrincipalKey("Baid")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Ba");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("DanStageScoreData");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddGenericInfoFlg : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "GenericInfoFlgArray",
|
||||||
|
table: "UserData",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "GenericInfoFlgArray",
|
||||||
|
table: "UserData");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -57,7 +57,7 @@ namespace TaikoLocalServer.Migrations
|
|||||||
|
|
||||||
b.HasKey("Baid", "DanId");
|
b.HasKey("Baid", "DanId");
|
||||||
|
|
||||||
b.ToTable("DanScoreData", (string)null);
|
b.ToTable("DanScoreData");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b =>
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanStageScoreDatum", b =>
|
||||||
@ -97,7 +97,7 @@ namespace TaikoLocalServer.Migrations
|
|||||||
|
|
||||||
b.HasKey("Baid", "DanId", "SongNumber");
|
b.HasKey("Baid", "DanId", "SongNumber");
|
||||||
|
|
||||||
b.ToTable("DanStageScoreData", (string)null);
|
b.ToTable("DanStageScoreData");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TaikoLocalServer.Entities.SongBestDatum", b =>
|
modelBuilder.Entity("TaikoLocalServer.Entities.SongBestDatum", b =>
|
||||||
@ -125,7 +125,7 @@ namespace TaikoLocalServer.Migrations
|
|||||||
|
|
||||||
b.HasKey("Baid", "SongId", "Difficulty");
|
b.HasKey("Baid", "SongId", "Difficulty");
|
||||||
|
|
||||||
b.ToTable("SongBestData", (string)null);
|
b.ToTable("SongBestData");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TaikoLocalServer.Entities.SongPlayDatum", b =>
|
modelBuilder.Entity("TaikoLocalServer.Entities.SongPlayDatum", b =>
|
||||||
@ -186,7 +186,7 @@ namespace TaikoLocalServer.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Baid");
|
b.HasIndex("Baid");
|
||||||
|
|
||||||
b.ToTable("SongPlayData", (string)null);
|
b.ToTable("SongPlayData");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TaikoLocalServer.Entities.UserDatum", b =>
|
modelBuilder.Entity("TaikoLocalServer.Entities.UserDatum", b =>
|
||||||
@ -210,6 +210,10 @@ namespace TaikoLocalServer.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CostumeFlgArray")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<bool>("DisplayAchievement")
|
b.Property<bool>("DisplayAchievement")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@ -220,6 +224,10 @@ namespace TaikoLocalServer.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("GenericInfoFlgArray")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<bool>("IsSkipOn")
|
b.Property<bool>("IsSkipOn")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@ -249,12 +257,20 @@ namespace TaikoLocalServer.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TitleFlgArray")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<uint>("TitlePlateId")
|
b.Property<uint>("TitlePlateId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ToneFlgArray")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.HasKey("Baid");
|
b.HasKey("Baid");
|
||||||
|
|
||||||
b.ToTable("UserData", (string)null);
|
b.ToTable("UserData");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace TaikoLocalServer.Models;
|
namespace TaikoLocalServer.Models;
|
||||||
|
|
||||||
public class MusicAttribute
|
public class MusicAttributeEntry
|
||||||
{
|
{
|
||||||
[JsonPropertyName("uniqueId")]
|
[JsonPropertyName("uniqueId")]
|
||||||
public uint MusicId { get; set; }
|
public uint MusicId { get; set; }
|
9
TaikoLocalServer/Models/MusicAttributes.cs
Normal file
9
TaikoLocalServer/Models/MusicAttributes.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Models;
|
||||||
|
|
||||||
|
public class MusicAttributes
|
||||||
|
{
|
||||||
|
[JsonPropertyName("items")]
|
||||||
|
public List<MusicAttributeEntry> MusicAttributeEntries { get; set; } = new();
|
||||||
|
}
|
@ -6,6 +6,7 @@ using TaikoLocalServer.Services;
|
|||||||
using TaikoLocalServer.Services.Extentions;
|
using TaikoLocalServer.Services.Extentions;
|
||||||
using TaikoLocalServer.Services.Interfaces;
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
using TaikoLocalServer.Settings;
|
using TaikoLocalServer.Settings;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
// Manually enable tls 1.0
|
// Manually enable tls 1.0
|
||||||
@ -19,6 +20,7 @@ builder.WebHost.UseKestrel(kestrelOptions =>
|
|||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddOptions();
|
builder.Services.AddOptions();
|
||||||
|
builder.Services.AddSingleton<IGameDataService, GameDataService>();
|
||||||
builder.Services.Configure<UrlSettings>(builder.Configuration.GetSection(nameof(UrlSettings)));
|
builder.Services.Configure<UrlSettings>(builder.Configuration.GetSection(nameof(UrlSettings)));
|
||||||
builder.Services.AddControllers().AddProtoBufNet();
|
builder.Services.AddControllers().AddProtoBufNet();
|
||||||
builder.Services.AddDbContext<TaikoDbContext>(option =>
|
builder.Services.AddDbContext<TaikoDbContext>(option =>
|
||||||
@ -28,7 +30,7 @@ builder.Services.AddDbContext<TaikoDbContext>(option =>
|
|||||||
{
|
{
|
||||||
dbName = Constants.DEFAULT_DB_NAME;
|
dbName = Constants.DEFAULT_DB_NAME;
|
||||||
}
|
}
|
||||||
var path = Path.Combine(PathHelper.GetDataPath(), dbName);
|
var path = Path.Combine(PathHelper.GetRootPath(), dbName);
|
||||||
option.UseSqlite($"Data Source={path}");
|
option.UseSqlite($"Data Source={path}");
|
||||||
});
|
});
|
||||||
builder.Services.AddHttpLogging(options =>
|
builder.Services.AddHttpLogging(options =>
|
||||||
@ -58,6 +60,10 @@ using (var scope = app.Services.CreateScope())
|
|||||||
db.Database.Migrate();
|
db.Database.Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gameDataService = app.Services.GetService<IGameDataService>();
|
||||||
|
gameDataService.ThrowIfNull();
|
||||||
|
await gameDataService.InitializeAsync();
|
||||||
|
|
||||||
// For reverse proxy
|
// For reverse proxy
|
||||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||||
{
|
{
|
||||||
|
130
TaikoLocalServer/Services/GameDataService.cs
Normal file
130
TaikoLocalServer/Services/GameDataService.cs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Text.Json;
|
||||||
|
using SharedProject.Models;
|
||||||
|
using Swan.Mapping;
|
||||||
|
using TaikoLocalServer.Services.Interfaces;
|
||||||
|
using Throw;
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Services;
|
||||||
|
|
||||||
|
public class GameDataService : IGameDataService
|
||||||
|
{
|
||||||
|
private ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> danDataDictionary =
|
||||||
|
ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData>.Empty;
|
||||||
|
|
||||||
|
private ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData> introDataDictionary =
|
||||||
|
ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData>.Empty;
|
||||||
|
|
||||||
|
private ImmutableDictionary<uint, MusicAttributeEntry> musicAttributes =
|
||||||
|
ImmutableDictionary<uint, MusicAttributeEntry>.Empty;
|
||||||
|
|
||||||
|
private List<uint> musics = new();
|
||||||
|
|
||||||
|
private List<uint> musicsWithUra = new();
|
||||||
|
|
||||||
|
public List<uint> GetMusicList()
|
||||||
|
{
|
||||||
|
return musics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<uint> GetMusicWithUraList()
|
||||||
|
{
|
||||||
|
return musicsWithUra;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableDictionary<uint, MusicAttributeEntry> GetMusicAttributes()
|
||||||
|
{
|
||||||
|
return musicAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> GetDanDataDictionary()
|
||||||
|
{
|
||||||
|
return danDataDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData> GetSongIntroDictionary()
|
||||||
|
{
|
||||||
|
return introDataDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
var dataPath = PathHelper.GetDataPath();
|
||||||
|
var musicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_FILE_NAME);
|
||||||
|
var danDataPath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME);
|
||||||
|
var songIntroDataPath = Path.Combine(dataPath, Constants.INTRO_DATA_FILE_NAME);
|
||||||
|
|
||||||
|
await using var musicAttributeFile = File.OpenRead(musicAttributePath);
|
||||||
|
await using var danDataFile = File.OpenRead(danDataPath);
|
||||||
|
await using var songIntroDataFile = File.OpenRead(songIntroDataPath);
|
||||||
|
|
||||||
|
var attributesData = await JsonSerializer.DeserializeAsync<MusicAttributes>(musicAttributeFile);
|
||||||
|
var danData = await JsonSerializer.DeserializeAsync<List<DanData>>(danDataFile);
|
||||||
|
var introData = await JsonSerializer.DeserializeAsync<List<SongIntroductionData>>(songIntroDataFile);
|
||||||
|
|
||||||
|
InitializeMusicAttributes(attributesData);
|
||||||
|
|
||||||
|
InitializeDanData(danData);
|
||||||
|
|
||||||
|
InitializeIntroData(introData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeIntroData(List<SongIntroductionData>? introData)
|
||||||
|
{
|
||||||
|
introData.ThrowIfNull("Shouldn't happen!");
|
||||||
|
introDataDictionary = introData.ToImmutableDictionary(data => data.SetId, ToResponseIntroData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeDanData(List<DanData>? danData)
|
||||||
|
{
|
||||||
|
danData.ThrowIfNull("Shouldn't happen!");
|
||||||
|
danDataDictionary = danData.ToImmutableDictionary(data => data.DanId, ToResponseOdaiData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeMusicAttributes(MusicAttributes? attributesData)
|
||||||
|
{
|
||||||
|
attributesData.ThrowIfNull("Shouldn't happen!");
|
||||||
|
|
||||||
|
musicAttributes = attributesData.MusicAttributeEntries.ToImmutableDictionary(attribute => attribute.MusicId);
|
||||||
|
|
||||||
|
musics = musicAttributes.Select(pair => pair.Key)
|
||||||
|
.ToList();
|
||||||
|
musics.Sort();
|
||||||
|
|
||||||
|
musicsWithUra = musicAttributes.Where(attribute => attribute.Value.HasUra)
|
||||||
|
.Select(pair => pair.Key)
|
||||||
|
.ToList();
|
||||||
|
musicsWithUra.Sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GetDanOdaiResponse.OdaiData ToResponseOdaiData(DanData data)
|
||||||
|
{
|
||||||
|
var responseOdaiData = new GetDanOdaiResponse.OdaiData
|
||||||
|
{
|
||||||
|
DanId = data.DanId,
|
||||||
|
Title = data.Title,
|
||||||
|
VerupNo = data.VerupNo
|
||||||
|
};
|
||||||
|
|
||||||
|
var odaiSongs = data.OdaiSongList.Select(song => song.CopyPropertiesToNew<GetDanOdaiResponse.OdaiData.OdaiSong>());
|
||||||
|
responseOdaiData.AryOdaiSongs.AddRange(odaiSongs);
|
||||||
|
|
||||||
|
var odaiBorders = data.OdaiBorderList.Select(border => border.CopyPropertiesToNew<GetDanOdaiResponse.OdaiData.OdaiBorder>());
|
||||||
|
responseOdaiData.AryOdaiBorders.AddRange(odaiBorders);
|
||||||
|
|
||||||
|
return responseOdaiData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GetSongIntroductionResponse.SongIntroductionData ToResponseIntroData(SongIntroductionData data)
|
||||||
|
{
|
||||||
|
var responseOdaiData = new GetSongIntroductionResponse.SongIntroductionData
|
||||||
|
{
|
||||||
|
SetId = data.SetId,
|
||||||
|
VerupNo = data.VerupNo,
|
||||||
|
MainSongNo = data.MainSongNo,
|
||||||
|
SubSongNoes = data.SubSongNo
|
||||||
|
};
|
||||||
|
|
||||||
|
return responseOdaiData;
|
||||||
|
}
|
||||||
|
}
|
18
TaikoLocalServer/Services/Interfaces/IGameDataService.cs
Normal file
18
TaikoLocalServer/Services/Interfaces/IGameDataService.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
|
||||||
|
namespace TaikoLocalServer.Services.Interfaces;
|
||||||
|
|
||||||
|
public interface IGameDataService
|
||||||
|
{
|
||||||
|
public Task InitializeAsync();
|
||||||
|
|
||||||
|
public List<uint> GetMusicList();
|
||||||
|
|
||||||
|
public List<uint> GetMusicWithUraList();
|
||||||
|
|
||||||
|
public ImmutableDictionary<uint, MusicAttributeEntry> GetMusicAttributes();
|
||||||
|
|
||||||
|
public ImmutableDictionary<uint, GetDanOdaiResponse.OdaiData> GetDanDataDictionary();
|
||||||
|
|
||||||
|
public ImmutableDictionary<uint, GetSongIntroductionResponse.SongIntroductionData> GetSongIntroDictionary();
|
||||||
|
}
|
@ -17,4 +17,6 @@ public interface IUserDatumService
|
|||||||
public Task<List<uint>> GetFavoriteSongIds(uint baid);
|
public Task<List<uint>> GetFavoriteSongIds(uint baid);
|
||||||
|
|
||||||
public Task UpdateFavoriteSong(uint baid, uint songId, bool isFavorite);
|
public Task UpdateFavoriteSong(uint baid, uint songId, bool isFavorite);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -35,10 +35,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Update="wwwroot\music_attribute.json">
|
<Content Update="wwwroot\data\music_attribute.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Update="wwwroot\dan_data.json">
|
<Content Update="wwwroot\data\dan_data.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\data\intro_data.json">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
74
TaikoLocalServer/wwwroot/data/intro_data.json
Normal file
74
TaikoLocalServer/wwwroot/data/intro_data.json
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"setId":1,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":895,
|
||||||
|
"subSongNo":[894,732,44,921]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":2,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":912,
|
||||||
|
"subSongNo":[827,871,36,227]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":3,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":913,
|
||||||
|
"subSongNo":[460,916,430,872]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":4,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":842,
|
||||||
|
"subSongNo":[7,233,256,831]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":5,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":947,
|
||||||
|
"subSongNo":[926,882,730,695]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":6,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":937,
|
||||||
|
"subSongNo":[828,925,474,924]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":7,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":956,
|
||||||
|
"subSongNo":[839,255,285,187]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":8,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":923,
|
||||||
|
"subSongNo":[729,873,789,893]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":9,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":915,
|
||||||
|
"subSongNo":[726,811,711,303]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":10,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":885,
|
||||||
|
"subSongNo":[837,464,801,18]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":11,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":898,
|
||||||
|
"subSongNo":[47,135,374,792]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"setId":12,
|
||||||
|
"verupNo":1,
|
||||||
|
"mainSongNo":948,
|
||||||
|
"subSongNo":[412,538,411,413]
|
||||||
|
}
|
||||||
|
]
|
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@ global using Microsoft.AspNetCore.Components.Web;
|
|||||||
global using MudBlazor;
|
global using MudBlazor;
|
||||||
global using TaikoWebUI;
|
global using TaikoWebUI;
|
||||||
global using TaikoWebUI.Services;
|
global using TaikoWebUI.Services;
|
||||||
|
global using TaikoWebUI.Shared;
|
||||||
global using SharedProject.Models;
|
global using SharedProject.Models;
|
||||||
global using SharedProject.Models.Requests;
|
global using SharedProject.Models.Requests;
|
||||||
global using SharedProject.Models.Responses;
|
global using SharedProject.Models.Responses;
|
||||||
|
105
TaikoWebUI/Pages/Dialogs/ChooseTitleDialog.razor
Normal file
105
TaikoWebUI/Pages/Dialogs/ChooseTitleDialog.razor
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
@using TaikoWebUI.Shared.Models
|
||||||
|
@using System.Collections.Immutable
|
||||||
|
@inject IGameDataService GameDataService
|
||||||
|
|
||||||
|
<MudDialog>
|
||||||
|
<DialogContent>
|
||||||
|
<MudTable Items="@titles" Filter="@Filter" @bind-SelectedItem="@selectedTitle" Height="40vh" Hover="true">
|
||||||
|
<ColGroup>
|
||||||
|
<col style="width: 50px;" />
|
||||||
|
<col />
|
||||||
|
</ColGroup>
|
||||||
|
<ToolBarContent>
|
||||||
|
<MudTextField @bind-Value="searchString" Placeholder="Search" Adornment="Adornment.Start" Immediate="true"
|
||||||
|
AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium" Class="mt-0">
|
||||||
|
</MudTextField>
|
||||||
|
</ToolBarContent>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>
|
||||||
|
<MudTableSortLabel SortBy="@(new Func<Title, object>(x => x.TitleId))">
|
||||||
|
ID
|
||||||
|
</MudTableSortLabel>
|
||||||
|
</MudTh>
|
||||||
|
<MudTh>
|
||||||
|
<MudTableSortLabel SortBy="@(new Func<Title, object>(x => x.TitleName))">
|
||||||
|
Title
|
||||||
|
</MudTableSortLabel>
|
||||||
|
</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
@{
|
||||||
|
// Disable nullable warnings, which seems to be a false positive
|
||||||
|
# pragma warning disable CS8602
|
||||||
|
}
|
||||||
|
<MudTd DataLabel="Id" Class="cursor-pointer">@context.TitleId</MudTd>
|
||||||
|
<MudTd DataLabel="Title" Class="cursor-pointer">@context.TitleName</MudTd>
|
||||||
|
@{
|
||||||
|
#pragma warning restore CS8602
|
||||||
|
}
|
||||||
|
</RowTemplate>
|
||||||
|
<PagerContent>
|
||||||
|
<MudTablePager/>
|
||||||
|
</PagerContent>
|
||||||
|
</MudTable>
|
||||||
|
<MudText Class="mt-4 d-block" Typo="Typo.caption"><b>Selected Title:</b> @selectedTitle?.TitleName</MudText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<MudButton OnClick="Cancel">Cancel</MudButton>
|
||||||
|
<MudButton Color="Color.Primary" OnClick="Submit">Ok</MudButton>
|
||||||
|
</DialogActions>
|
||||||
|
</MudDialog>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
MudDialogInstance MudDialog { get; set; } = null!;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public UserSetting UserSetting { get; set; } = new();
|
||||||
|
|
||||||
|
private IEnumerable<Title> titles = new List<Title>();
|
||||||
|
|
||||||
|
private Title? selectedTitle;
|
||||||
|
|
||||||
|
private string searchString = string.Empty;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
base.OnInitialized();
|
||||||
|
var titleSet = GameDataService.GetTitles();
|
||||||
|
titles = titleSet.ToImmutableList().Sort((title, title1) => title.TitleId.CompareTo(title1.TitleId));
|
||||||
|
var currentTitle = new Title
|
||||||
|
{
|
||||||
|
TitleName = UserSetting.Title
|
||||||
|
};
|
||||||
|
if (titleSet.Contains(currentTitle))
|
||||||
|
{
|
||||||
|
titleSet.TryGetValue(new Title
|
||||||
|
{
|
||||||
|
TitleName = UserSetting.Title
|
||||||
|
}, out selectedTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Filter(Title? title)
|
||||||
|
{
|
||||||
|
if (title is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return string.IsNullOrEmpty(searchString) ||
|
||||||
|
title.TitleName.Contains(searchString, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Submit()
|
||||||
|
{
|
||||||
|
if (selectedTitle is not null)
|
||||||
|
{
|
||||||
|
UserSetting.Title = selectedTitle.TitleName;
|
||||||
|
}
|
||||||
|
MudDialog.Close(DialogResult.Ok(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel() => MudDialog.Cancel();
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
@page "/Cards/{baid:int}/Profile"
|
@page "/Cards/{baid:int}/Profile"
|
||||||
@inject HttpClient Client
|
@inject HttpClient Client
|
||||||
|
@inject IGameDataService GameDataService
|
||||||
|
@inject IDialogService DialogService
|
||||||
|
|
||||||
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
|
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
|
||||||
|
|
||||||
@ -8,107 +10,205 @@
|
|||||||
|
|
||||||
@if (response is not null)
|
@if (response is not null)
|
||||||
{
|
{
|
||||||
<MudGrid>
|
<MudGrid Class="my-4 pb-10">
|
||||||
<MudItem xs="12" md="8">
|
<MudItem xs="12" md="8">
|
||||||
<MudPaper Class="py-8 px-8 my-8" Outlined="true">
|
<MudPaper Elevation="0" Outlined="true">
|
||||||
<MudStack Spacing="4">
|
<MudTabs Rounded="true" Border="true" PanelClass="pa-8">
|
||||||
<h2>Profile Options</h2>
|
<MudTabPanel Text="Profile">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<h2>Profile Options</h2>
|
||||||
|
|
||||||
<MudTextField @bind-Value="@response.MyDonName" Label="Name"></MudTextField>
|
<MudTextField @bind-Value="@response.MyDonName" Label="Name"></MudTextField>
|
||||||
|
|
||||||
<MudGrid>
|
<MudGrid>
|
||||||
<MudItem xs="12" md="8">
|
<MudItem xs="12" md="8">
|
||||||
<MudTextField @bind-Value="@response.Title" Label="Title"></MudTextField>
|
<MudTextField @bind-Value="@response.Title" Label="Title"/>
|
||||||
</MudItem>
|
<MudButton Color="Color.Primary" Class="mt-1" Size="Size.Small" OnClick="@((e)=>OpenChooseTitleDialog())">
|
||||||
<MudItem xs="12" md="4">
|
Select a Title
|
||||||
<MudSelect @bind-Value="@response.TitlePlateId" Label="Title Plate">
|
</MudButton>
|
||||||
@for (uint i = 0; i < 8; i++)
|
</MudItem>
|
||||||
{
|
<MudItem xs="12" md="4">
|
||||||
var index = i;
|
<MudSelect @bind-Value="@response.TitlePlateId" Label="Title Plate">
|
||||||
<MudSelectItem Value="@i">@titlePlateStrings[index]</MudSelectItem>
|
@for (uint i = 0; i < 8; i++)
|
||||||
}
|
|
||||||
</MudSelect>
|
|
||||||
</MudItem>
|
|
||||||
</MudGrid>
|
|
||||||
|
|
||||||
<MudSelect @bind-Value="@response.AchievementDisplayDifficulty"
|
|
||||||
Label="Achievement Panel Difficulty">
|
|
||||||
@foreach (var item in Enum.GetValues<Difficulty>())
|
|
||||||
{
|
|
||||||
<MudSelectItem Value="@item"/>
|
|
||||||
}
|
|
||||||
</MudSelect>
|
|
||||||
|
|
||||||
<MudSwitch @bind-Checked="@response.IsDisplayAchievement" Label="Display Achievement Panel" Color="Color.Primary"/>
|
|
||||||
<MudSwitch @bind-Checked="@response.IsDisplayDanOnNamePlate" Label="Display Dan Rank on Name Plate" Color="Color.Primary"/>
|
|
||||||
</MudStack>
|
|
||||||
</MudPaper>
|
|
||||||
|
|
||||||
<MudPaper Class="py-8 px-8 my-8" Outlined="true">
|
|
||||||
<MudStack Spacing="4">
|
|
||||||
<h2>Song Options</h2>
|
|
||||||
<MudGrid>
|
|
||||||
<MudItem xs="12" md="4">
|
|
||||||
<MudStack Spacing="4">
|
|
||||||
<MudSwitch @bind-Checked="@response.PlaySetting.IsVanishOn" Label="Vanish" Color="Color.Primary"/>
|
|
||||||
<MudSwitch @bind-Checked="@response.PlaySetting.IsInverseOn" Label="Inverse" Color="Color.Primary"/>
|
|
||||||
<MudSwitch @bind-Checked="@response.IsSkipOn" Label="Give Up" Color="Color.Primary"/>
|
|
||||||
<MudSwitch @bind-Checked="@response.IsVoiceOn" Label="Voice" Color="Color.Primary"/>
|
|
||||||
</MudStack>
|
|
||||||
</MudItem>
|
|
||||||
<MudItem xs="12" md="8">
|
|
||||||
<MudStack Spacing="4">
|
|
||||||
<MudSelect @bind-Value="@response.PlaySetting.Speed" Label="Speed">
|
|
||||||
@for (uint i = 0; i < 15; i++)
|
|
||||||
{
|
{
|
||||||
var index = i;
|
var index = i;
|
||||||
<MudSelectItem Value="@i">@speedStrings[index]</MudSelectItem>
|
<MudSelectItem Value="@i">@TitlePlateStrings[index]</MudSelectItem>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
|
||||||
<MudSelect @bind-Value="@response.PlaySetting.RandomType"
|
<MudSelect @bind-Value="@response.AchievementDisplayDifficulty"
|
||||||
Label="Random">
|
Label="Achievement Panel Difficulty">
|
||||||
@foreach (var item in Enum.GetValues<RandomType>())
|
@foreach (var item in Enum.GetValues<Difficulty>())
|
||||||
{
|
{
|
||||||
<MudSelectItem Value="@item"/>
|
<MudSelectItem Value="@item"/>
|
||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
|
||||||
<MudSelect @bind-Value="@response.ToneId" Label="Tone">
|
<MudSwitch @bind-Checked="@response.IsDisplayAchievement" Label="Display Achievement Panel" Color="Color.Primary"/>
|
||||||
@for (uint i = 0; i < 19; i++)
|
<MudSwitch @bind-Checked="@response.IsDisplayDanOnNamePlate" Label="Display Dan Rank on Name Plate" Color="Color.Primary"/>
|
||||||
{
|
</MudStack>
|
||||||
var index = i;
|
</MudTabPanel>
|
||||||
<MudSelectItem Value="@i">@toneStrings[index]</MudSelectItem>
|
|
||||||
}
|
|
||||||
</MudSelect>
|
|
||||||
|
|
||||||
<MudSlider Class="mb-8" @bind-Value="@response.NotesPosition" Size="Size.Medium" Min="-5" Max="5" Step="1" TickMarks="true" TickMarkLabels="@notePositionStrings">
|
<MudTabPanel Text="Costume">
|
||||||
<MudText Typo="Typo.caption">Notes Position</MudText>
|
<MudStack Spacing="4">
|
||||||
</MudSlider>
|
<h2>Costume Options</h2>
|
||||||
</MudStack>
|
<MudGrid>
|
||||||
</MudItem>
|
<MudItem xs="12">
|
||||||
</MudGrid>
|
<MudStack Spacing="4" Class="mb-8">
|
||||||
</MudStack>
|
<MudSelect @bind-Value="@response.Head" Label="Head">
|
||||||
</MudPaper>
|
@for (var i = 0; i < Constants.COSTUME_HEAD_MAX; i++)
|
||||||
</MudItem>
|
{
|
||||||
<MudItem md="4" xs="12" Class="py-8 px-8 my-4 pt-8">
|
var index = (uint)i;
|
||||||
<MudStack Spacing="4" Style="top:100px" Class="sticky">
|
var costumeTitle = GameDataService.GetHeadTitle(index);
|
||||||
<MudButton Disabled="@isSavingOptions"
|
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
|
||||||
OnClick="SaveOptions"
|
}
|
||||||
Variant="Variant.Filled"
|
</MudSelect>
|
||||||
Color="Color.Primary">
|
|
||||||
@if (isSavingOptions)
|
<MudSelect @bind-Value="@response.Body" Label="Body">
|
||||||
{
|
@for (var i = 0; i < Constants.COSTUME_BODY_MAX; i++)
|
||||||
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
|
{
|
||||||
<MudText Class="ms-2">Saving...</MudText>
|
var index = (uint)i;
|
||||||
}
|
var costumeTitle = GameDataService.GetBodyTitle(index);
|
||||||
else
|
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
|
||||||
{
|
}
|
||||||
<MudIcon Icon="@Icons.Filled.Save" Class="mx-2"></MudIcon>
|
</MudSelect>
|
||||||
<MudText>Save</MudText>
|
|
||||||
}
|
<MudSelect @bind-Value="@response.Face" Label="Face">
|
||||||
</MudButton>
|
@for (var i = 0; i < Constants.COSTUME_FACE_MAX; i++)
|
||||||
</MudStack>
|
{
|
||||||
</MudItem>
|
var index = (uint)i;
|
||||||
|
var costumeTitle = GameDataService.GetFaceTitle(index);
|
||||||
|
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudSelect @bind-Value="@response.Kigurumi" Label="Kigurumi">
|
||||||
|
@for (var i = 0; i < Constants.COSTUME_KIGURUMI_MAX; i++)
|
||||||
|
{
|
||||||
|
var index = (uint)i;
|
||||||
|
var costumeTitle = GameDataService.GetKigurumiTitle(index);
|
||||||
|
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudSelect @bind-Value="@response.Puchi" Label="Puchi">
|
||||||
|
@for (var i = 0; i < Constants.COSTUME_PUCHI_MAX; i++)
|
||||||
|
{
|
||||||
|
var index = (uint)i;
|
||||||
|
var costumeTitle = GameDataService.GetPuchiTitle(index);
|
||||||
|
<MudSelectItem Value="@index">@index - @costumeTitle</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
<MudStack Row="true">
|
||||||
|
<MudSelect @bind-Value="@response.BodyColor" Label="Body Color">
|
||||||
|
@for (uint i = 0; i < Constants.COSTUME_COLOR_MAX; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
<MudSelectItem Value="@index">
|
||||||
|
<div class="color-box" style=@($"background: {CostumeColors[index]}")></div>
|
||||||
|
@index
|
||||||
|
</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
<MudSelect @bind-Value="@response.FaceColor" Label="Face Color">
|
||||||
|
@for (uint i = 0; i < Constants.COSTUME_COLOR_MAX; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
<MudSelectItem Value="@index">
|
||||||
|
<div class="color-box" style=@($"background: {CostumeColors[index]}")></div>
|
||||||
|
@index
|
||||||
|
</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
<MudSelect @bind-Value="@response.LimbColor" Label="Limb Color">
|
||||||
|
@for (uint i = 0; i < Constants.COSTUME_COLOR_MAX; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
<MudSelectItem Value="@index">
|
||||||
|
<div class="color-box" style=@($"background: {CostumeColors[index]}")></div>
|
||||||
|
@index
|
||||||
|
</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudStack>
|
||||||
|
</MudTabPanel>
|
||||||
|
|
||||||
|
<MudTabPanel Text="Song Options">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<h2>Song Options</h2>
|
||||||
|
<MudGrid>
|
||||||
|
<MudItem xs="12" md="4">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<MudSwitch @bind-Checked="@response.PlaySetting.IsVanishOn" Label="Vanish" Color="Color.Primary"/>
|
||||||
|
<MudSwitch @bind-Checked="@response.PlaySetting.IsInverseOn" Label="Inverse" Color="Color.Primary"/>
|
||||||
|
<MudSwitch @bind-Checked="@response.IsSkipOn" Label="Give Up" Color="Color.Primary"/>
|
||||||
|
<MudSwitch @bind-Checked="@response.IsVoiceOn" Label="Voice" Color="Color.Primary"/>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
<MudItem xs="12" md="8">
|
||||||
|
<MudStack Spacing="4">
|
||||||
|
<MudSelect @bind-Value="@response.PlaySetting.Speed" Label="Speed">
|
||||||
|
@for (uint i = 0; i < 15; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
<MudSelectItem Value="@i">@SpeedStrings[index]</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudSelect @bind-Value="@response.PlaySetting.RandomType"
|
||||||
|
Label="Random">
|
||||||
|
@foreach (var item in Enum.GetValues<RandomType>())
|
||||||
|
{
|
||||||
|
<MudSelectItem Value="@item"/>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudSelect @bind-Value="@response.ToneId" Label="Tone">
|
||||||
|
@for (uint i = 0; i < 19; i++)
|
||||||
|
{
|
||||||
|
var index = i;
|
||||||
|
<MudSelectItem Value="@i">@ToneStrings[index]</MudSelectItem>
|
||||||
|
}
|
||||||
|
</MudSelect>
|
||||||
|
|
||||||
|
<MudSlider Class="mb-8" @bind-Value="@response.NotesPosition" Size="Size.Medium" Min="-5" Max="5" Step="1" TickMarks="true" TickMarkLabels="@NotePositionStrings">
|
||||||
|
<MudText Typo="Typo.caption">Notes Position</MudText>
|
||||||
|
</MudSlider>
|
||||||
|
</MudStack>
|
||||||
|
</MudItem>
|
||||||
|
</MudGrid>
|
||||||
|
</MudStack>
|
||||||
|
</MudTabPanel>
|
||||||
|
</MudTabs>
|
||||||
|
</MudPaper>
|
||||||
|
</MudItem>
|
||||||
|
|
||||||
|
<MudItem md="4" xs="12" Class="py-4 px-8">
|
||||||
|
<MudStack Style="top:100px" Class="sticky">
|
||||||
|
<MudButton Disabled="@isSavingOptions"
|
||||||
|
OnClick="SaveOptions"
|
||||||
|
Variant="Variant.Filled"
|
||||||
|
Color="Color.Primary">
|
||||||
|
@if (isSavingOptions)
|
||||||
|
{
|
||||||
|
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
|
||||||
|
<MudText Class="ms-2">Saving...</MudText>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<MudIcon Icon="@Icons.Filled.Save" Class="mx-2"></MudIcon>
|
||||||
|
<MudText>Save</MudText>
|
||||||
|
}
|
||||||
|
</MudButton>
|
||||||
|
</MudStack>
|
||||||
|
|
||||||
|
</MudItem>
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
}
|
}
|
@ -1,4 +1,6 @@
|
|||||||
namespace TaikoWebUI.Pages;
|
using TaikoWebUI.Pages.Dialogs;
|
||||||
|
|
||||||
|
namespace TaikoWebUI.Pages;
|
||||||
|
|
||||||
public partial class Profile
|
public partial class Profile
|
||||||
{
|
{
|
||||||
@ -9,16 +11,31 @@ public partial class Profile
|
|||||||
|
|
||||||
private bool isSavingOptions;
|
private bool isSavingOptions;
|
||||||
|
|
||||||
private readonly string[] speedStrings =
|
private static readonly string[] CostumeColors =
|
||||||
|
{
|
||||||
|
"#F84828", "#68C0C0", "#DC1500", "#F8F0E0", "#009687", "#00BF87",
|
||||||
|
"#00FF9A", "#66FFC2", "#FFFFFF", "#690000", "#FF0000", "#FF6666",
|
||||||
|
"#FFB3B3", "#00BCC2", "#00F7FF", "#66FAFF", "#B3FDFF", "#E4E4E4",
|
||||||
|
"#993800", "#FF5E00", "#FF9E78", "#FFCFB3", "#005199", "#0088FF",
|
||||||
|
"#66B8FF", "#B3DBFF", "#B9B9B9", "#B37700", "#FFAA00", "#FFCC66",
|
||||||
|
"#FFE2B3", "#000C80", "#0019FF", "#6675FF", "#B3BAFF", "#858585",
|
||||||
|
"#B39B00", "#FFDD00", "#FFFF00", "#FFFF71", "#2B0080", "#5500FF",
|
||||||
|
"#9966FF", "#CCB3FF", "#505050", "#38A100", "#78C900", "#B3FF00",
|
||||||
|
"#DCFF8A", "#610080", "#C400FF", "#DC66FF", "#EDB3FF", "#232323",
|
||||||
|
"#006600", "#00B800", "#00FF00", "#8AFF9E", "#990059", "#FF0095",
|
||||||
|
"#FF66BF", "#FFB3DF", "#000000"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly string[] SpeedStrings =
|
||||||
{
|
{
|
||||||
"1.0", "1.1", "1.2", "1.3", "1.4",
|
"1.0", "1.1", "1.2", "1.3", "1.4",
|
||||||
"1.5", "1.6", "1.7", "1.8", "1.9",
|
"1.5", "1.6", "1.7", "1.8", "1.9",
|
||||||
"2.0", "2.5", "3.0", "3.5", "4.0"
|
"2.0", "2.5", "3.0", "3.5", "4.0"
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly string[] notePositionStrings = { "-5", "-4", "-3", "-2", "-1", "0", "+1", "+2", "+3", "+4", "+5" };
|
private static readonly string[] NotePositionStrings = { "-5", "-4", "-3", "-2", "-1", "0", "+1", "+2", "+3", "+4", "+5" };
|
||||||
|
|
||||||
private readonly string[] toneStrings =
|
private static readonly string[] ToneStrings =
|
||||||
{
|
{
|
||||||
"Taiko", "Festival", "Dogs & Cats", "Deluxe",
|
"Taiko", "Festival", "Dogs & Cats", "Deluxe",
|
||||||
"Drumset", "Tambourine", "Don Wada", "Clapping",
|
"Drumset", "Tambourine", "Don Wada", "Clapping",
|
||||||
@ -27,13 +44,13 @@ public partial class Profile
|
|||||||
"Synth Drum", "Shuriken", "Bubble Pop", "Electric Guitar"
|
"Synth Drum", "Shuriken", "Bubble Pop", "Electric Guitar"
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly string[] titlePlateStrings =
|
private static readonly string[] TitlePlateStrings =
|
||||||
{
|
{
|
||||||
"Wood", "Rainbow", "Gold", "Purple",
|
"Wood", "Rainbow", "Gold", "Purple",
|
||||||
"AI 1", "AI 2", "AI 3", "AI 4"
|
"AI 1", "AI 2", "AI 3", "AI 4"
|
||||||
};
|
};
|
||||||
|
|
||||||
private List<BreadcrumbItem> breadcrumbs = new()
|
private readonly List<BreadcrumbItem> breadcrumbs = new()
|
||||||
{
|
{
|
||||||
new BreadcrumbItem("Cards", href: "/Cards"),
|
new BreadcrumbItem("Cards", href: "/Cards"),
|
||||||
};
|
};
|
||||||
@ -55,4 +72,25 @@ public partial class Profile
|
|||||||
isSavingOptions = false;
|
isSavingOptions = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OpenChooseTitleDialog()
|
||||||
|
{
|
||||||
|
var options = new DialogOptions
|
||||||
|
{
|
||||||
|
//CloseButton = false,
|
||||||
|
CloseOnEscapeKey = false,
|
||||||
|
DisableBackdropClick = true,
|
||||||
|
MaxWidth = MaxWidth.Medium,
|
||||||
|
FullWidth = true
|
||||||
|
};
|
||||||
|
var parameters = new DialogParameters
|
||||||
|
{
|
||||||
|
["UserSetting"] = response
|
||||||
|
};
|
||||||
|
var dialog = DialogService.Show<ChooseTitleDialog>("Player Titles", parameters, options);
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (!result.Cancelled)
|
||||||
|
{
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -30,30 +30,25 @@
|
|||||||
@if (songBestDataMap.ContainsKey(difficulty))
|
@if (songBestDataMap.ContainsKey(difficulty))
|
||||||
{
|
{
|
||||||
<MudDataGrid Items="@songBestDataMap[difficulty]"
|
<MudDataGrid Items="@songBestDataMap[difficulty]"
|
||||||
ColumnResizeMode="ResizeMode.Container" RowsPerPage="25" Elevation="0">
|
ColumnResizeMode="ResizeMode.None" RowsPerPage="25" Elevation="0">
|
||||||
<Columns>
|
<Columns>
|
||||||
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Song" StickyLeft="true" Class="clm-row-large">
|
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Song" StickyLeft="true">
|
||||||
<CellTemplate>
|
<CellTemplate>
|
||||||
<MudGrid Justify="Justify.Center">
|
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
||||||
<MudItem xs="10">
|
<div style="width:300px">
|
||||||
<MudStack Spacing="0">
|
<MudText Typo="Typo.body2" Style="font-weight:bold">@context.Item.MusicName</MudText>
|
||||||
<MudText Typo="Typo.body2" Style="font-weight:bold">@context.Item.MusicName</MudText>
|
<MudText Typo="Typo.caption">@context.Item.MusicArtist</MudText>
|
||||||
<MudText Typo="Typo.caption">@context.Item.MusicArtist</MudText>
|
</div>
|
||||||
</MudStack>
|
<div>
|
||||||
</MudItem>
|
<MudToggleIconButton Toggled="@context.Item.IsFavorite"
|
||||||
<MudItem xs="2">
|
ToggledChanged="@(async () => await OnFavoriteToggled(context.Item))"
|
||||||
<MudStack Justify="Justify.Center" AlignItems="AlignItems.End" Style="height:100%">
|
Icon="@Icons.Material.Filled.FavoriteBorder" Color="@Color.Secondary"
|
||||||
<MudTooltip Text="@(context.Item.IsFavorite ? "Remove from favorites" : "Add to favorites")" Arrow="true" Placement="Placement.Top">
|
ToggledIcon="@Icons.Material.Filled.Favorite" ToggledColor="@Color.Secondary"
|
||||||
<MudToggleIconButton Toggled="@context.Item.IsFavorite"
|
Size="Size.Small"
|
||||||
ToggledChanged="@(async () => await OnFavoriteToggled(context.Item))"
|
ToggledSize="Size.Small"
|
||||||
Icon="@Icons.Material.Filled.FavoriteBorder" Color="@Color.Secondary"
|
Title="Add to favorites" ToggledTitle="Remove from favorites"/>
|
||||||
ToggledIcon="@Icons.Material.Filled.Favorite" ToggledColor="@Color.Secondary"
|
</div>
|
||||||
Size="Size.Small"
|
</MudStack>
|
||||||
ToggledSize="Size.Small"/>
|
|
||||||
</MudTooltip>
|
|
||||||
</MudStack>
|
|
||||||
</MudItem>
|
|
||||||
</MudGrid>
|
|
||||||
</CellTemplate>
|
</CellTemplate>
|
||||||
</Column>
|
</Column>
|
||||||
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Level" Sortable="false">
|
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Level" Sortable="false">
|
||||||
@ -76,18 +71,14 @@
|
|||||||
<Column T="SongBestData" Field="@nameof(SongBestData.BestScore)" Title="Best Score"/>
|
<Column T="SongBestData" Field="@nameof(SongBestData.BestScore)" Title="Best Score"/>
|
||||||
<Column T="SongBestData" Field="@nameof(SongBestData.BestCrown)" Title="Best Crown">
|
<Column T="SongBestData" Field="@nameof(SongBestData.BestCrown)" Title="Best Crown">
|
||||||
<CellTemplate>
|
<CellTemplate>
|
||||||
<MudTooltip Text="@(GetCrownText(context.Item.BestCrown))" Arrow="true" Placement="Placement.Top">
|
<img src="@($"/images/crown_{context.Item.BestCrown}.png")" alt="@(GetCrownText(context.Item.BestCrown))" title="@(GetCrownText(context.Item.BestCrown))" style="@IconStyle" />
|
||||||
<img src="@($"/images/crown_{context.Item.BestCrown}.png")" alt="@(context.Item.BestCrown)" style="@IconStyle"/>
|
|
||||||
</MudTooltip>
|
|
||||||
</CellTemplate>
|
</CellTemplate>
|
||||||
</Column>
|
</Column>
|
||||||
<Column T="SongBestData" Field="@nameof(SongBestData.BestScoreRank)" Title="Best Rank" Sortable="false">
|
<Column T="SongBestData" Field="@nameof(SongBestData.BestScoreRank)" Title="Best Rank" Sortable="false">
|
||||||
<CellTemplate>
|
<CellTemplate>
|
||||||
@if (context.Item.BestScoreRank is not ScoreRank.None)
|
@if (context.Item.BestScoreRank is not ScoreRank.None)
|
||||||
{
|
{
|
||||||
<MudTooltip Text="@(GetRankText(context.Item.BestScoreRank))" Arrow="true" Placement="Placement.Top">
|
<img src="@($"/images/rank_{context.Item.BestScoreRank}.png")" alt="@(GetRankText(context.Item.BestScoreRank))" title="@(GetRankText(context.Item.BestScoreRank))" style="@IconStyle" />
|
||||||
<img src="@($"/images/rank_{context.Item.BestScoreRank}.png")" alt="@(context.Item.BestScoreRank)" style="@IconStyle"/>
|
|
||||||
</MudTooltip>
|
|
||||||
}
|
}
|
||||||
</CellTemplate>
|
</CellTemplate>
|
||||||
</Column>
|
</Column>
|
||||||
|
@ -6,11 +6,19 @@ namespace TaikoWebUI.Services;
|
|||||||
|
|
||||||
public class GameDataService : IGameDataService
|
public class GameDataService : IGameDataService
|
||||||
{
|
{
|
||||||
|
private readonly string[] bodyTitles = new string[Constants.COSTUME_BODY_MAX];
|
||||||
private readonly HttpClient client;
|
private readonly HttpClient client;
|
||||||
|
private readonly string[] faceTitles = new string[Constants.COSTUME_FACE_MAX];
|
||||||
|
|
||||||
|
private readonly string[] headTitles = new string[Constants.COSTUME_HEAD_MAX];
|
||||||
|
private readonly string[] kigurumiMTitles = new string[Constants.COSTUME_KIGURUMI_MAX];
|
||||||
|
|
||||||
private readonly Dictionary<uint, MusicDetail> musicMap = new();
|
private readonly Dictionary<uint, MusicDetail> musicMap = new();
|
||||||
|
private readonly string[] puchiTitles = new string[Constants.COSTUME_PUCHI_MAX];
|
||||||
|
|
||||||
private ImmutableDictionary<uint, DanData> danMap = null!;
|
private ImmutableDictionary<uint, DanData> danMap = ImmutableDictionary<uint, DanData>.Empty;
|
||||||
|
|
||||||
|
private ImmutableHashSet<Title> titles = ImmutableHashSet<Title>.Empty;
|
||||||
|
|
||||||
public GameDataService(HttpClient client)
|
public GameDataService(HttpClient client)
|
||||||
{
|
{
|
||||||
@ -30,15 +38,171 @@ public class GameDataService : IGameDataService
|
|||||||
danData.ThrowIfNull();
|
danData.ThrowIfNull();
|
||||||
|
|
||||||
danMap = danData.ToImmutableDictionary(data => data.DanId);
|
danMap = danData.ToImmutableDictionary(data => data.DanId);
|
||||||
|
|
||||||
// To prevent duplicate entries in wordlist
|
// To prevent duplicate entries in wordlist
|
||||||
var dict = wordList.WordListEntries.GroupBy(entry => entry.Key)
|
var dict = wordList.WordListEntries.GroupBy(entry => entry.Key)
|
||||||
.ToImmutableDictionary(group => group.Key, group => group.First());
|
.ToImmutableDictionary(group => group.Key, group => group.First());
|
||||||
|
await Task.Run(() => InitializeMusicMap(musicInfo, dict, musicOrder));
|
||||||
|
|
||||||
|
await Task.Run(() => InitializeHeadTitles(dict));
|
||||||
|
await Task.Run(() => InitializeFaceTitles(dict));
|
||||||
|
await Task.Run(() => InitializeBodyTitles(dict));
|
||||||
|
await Task.Run(() => InitializePuchiTitles(dict));
|
||||||
|
await Task.Run(() => InitializeKigurumiTitles(dict));
|
||||||
|
await Task.Run(() => InitializeTitles(dict));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetMusicNameBySongId(uint songId)
|
||||||
|
{
|
||||||
|
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.SongName : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetMusicArtistBySongId(uint songId)
|
||||||
|
{
|
||||||
|
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.ArtistName : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SongGenre GetMusicGenreBySongId(uint songId)
|
||||||
|
{
|
||||||
|
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Genre : SongGenre.Variety;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMusicIndexBySongId(uint songId)
|
||||||
|
{
|
||||||
|
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Index : int.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DanData GetDanDataById(uint danId)
|
||||||
|
{
|
||||||
|
return danMap.GetValueOrDefault(danId, new DanData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMusicStarLevel(uint songId, Difficulty difficulty)
|
||||||
|
{
|
||||||
|
var success = musicMap.TryGetValue(songId, out var musicDetail);
|
||||||
|
return difficulty switch
|
||||||
|
{
|
||||||
|
Difficulty.None => throw new ArgumentException("Difficulty cannot be none"),
|
||||||
|
Difficulty.Easy => success ? musicDetail!.StarEasy : 0,
|
||||||
|
Difficulty.Normal => success ? musicDetail!.StarNormal : 0,
|
||||||
|
Difficulty.Hard => success ? musicDetail!.StarHard : 0,
|
||||||
|
Difficulty.Oni => success ? musicDetail!.StarOni : 0,
|
||||||
|
Difficulty.UraOni => success ? musicDetail!.StarUra : 0,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(difficulty), difficulty, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetHeadTitle(uint index)
|
||||||
|
{
|
||||||
|
return index < headTitles.Length ? headTitles[index] : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetKigurumiTitle(uint index)
|
||||||
|
{
|
||||||
|
return index < kigurumiMTitles.Length ? kigurumiMTitles[index] : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetBodyTitle(uint index)
|
||||||
|
{
|
||||||
|
return index < bodyTitles.Length ? bodyTitles[index] : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetFaceTitle(uint index)
|
||||||
|
{
|
||||||
|
return index < faceTitles.Length ? faceTitles[index] : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetPuchiTitle(uint index)
|
||||||
|
{
|
||||||
|
return index < puchiTitles.Length ? puchiTitles[index] : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableHashSet<Title> GetTitles()
|
||||||
|
{
|
||||||
|
return titles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeTitles(ImmutableDictionary<string, WordListEntry> dict)
|
||||||
|
{
|
||||||
|
var set = ImmutableHashSet.CreateBuilder<Title>();
|
||||||
|
for (var i = 1; i < Constants.PLAYER_TITLE_MAX; i++)
|
||||||
|
{
|
||||||
|
var key = $"syougou_{i}";
|
||||||
|
|
||||||
|
var titleWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
|
||||||
|
|
||||||
|
set.Add(new Title{
|
||||||
|
TitleName = titleWordlistItem.JapaneseText,
|
||||||
|
TitleId = i
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
titles = set.ToImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializePuchiTitles(ImmutableDictionary<string, WordListEntry> dict)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < Constants.COSTUME_PUCHI_MAX; i++)
|
||||||
|
{
|
||||||
|
var key = $"costume_puchi_{i}";
|
||||||
|
|
||||||
|
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
|
||||||
|
puchiTitles[i] = costumeWordlistItem.JapaneseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeKigurumiTitles(ImmutableDictionary<string, WordListEntry> dict)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < Constants.COSTUME_KIGURUMI_MAX; i++)
|
||||||
|
{
|
||||||
|
var key = $"costume_kigurumi_{i}";
|
||||||
|
|
||||||
|
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
|
||||||
|
kigurumiMTitles[i] = costumeWordlistItem.JapaneseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeBodyTitles(ImmutableDictionary<string, WordListEntry> dict)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < Constants.COSTUME_BODY_MAX; i++)
|
||||||
|
{
|
||||||
|
var key = $"costume_body_{i}";
|
||||||
|
|
||||||
|
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
|
||||||
|
bodyTitles[i] = costumeWordlistItem.JapaneseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeFaceTitles(ImmutableDictionary<string, WordListEntry> dict)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < Constants.COSTUME_FACE_MAX; i++)
|
||||||
|
{
|
||||||
|
var key = $"costume_face_{i}";
|
||||||
|
|
||||||
|
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
|
||||||
|
faceTitles[i] = costumeWordlistItem.JapaneseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeHeadTitles(ImmutableDictionary<string, WordListEntry> dict)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < Constants.COSTUME_HEAD_MAX; i++)
|
||||||
|
{
|
||||||
|
var key = $"costume_head_{i}";
|
||||||
|
|
||||||
|
var costumeWordlistItem = dict.GetValueOrDefault(key, new WordListEntry());
|
||||||
|
headTitles[i] = costumeWordlistItem.JapaneseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeMusicMap(MusicInfo musicInfo, ImmutableDictionary<string, WordListEntry> dict,
|
||||||
|
MusicOrder musicOrder)
|
||||||
|
{
|
||||||
foreach (var music in musicInfo.Items)
|
foreach (var music in musicInfo.Items)
|
||||||
{
|
{
|
||||||
var songNameKey = $"song_{music.Id}";
|
var songNameKey = $"song_{music.Id}";
|
||||||
var songArtistKey = $"song_sub_{music.Id}";
|
var songArtistKey = $"song_sub_{music.Id}";
|
||||||
|
|
||||||
var musicName = dict.GetValueOrDefault(songNameKey, new WordListEntry());
|
var musicName = dict.GetValueOrDefault(songNameKey, new WordListEntry());
|
||||||
var musicArtist = dict.GetValueOrDefault(songArtistKey, new WordListEntry());
|
var musicArtist = dict.GetValueOrDefault(songArtistKey, new WordListEntry());
|
||||||
|
|
||||||
@ -60,42 +224,4 @@ public class GameDataService : IGameDataService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetMusicNameBySongId(uint songId)
|
|
||||||
{
|
|
||||||
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.SongName : string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetMusicArtistBySongId(uint songId)
|
|
||||||
{
|
|
||||||
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.ArtistName : string.Empty;
|
|
||||||
}
|
|
||||||
public SongGenre GetMusicGenreBySongId(uint songId)
|
|
||||||
{
|
|
||||||
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Genre : SongGenre.Variety;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetMusicIndexBySongId(uint songId)
|
|
||||||
{
|
|
||||||
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.Index : int.MaxValue;
|
|
||||||
}
|
|
||||||
public DanData GetDanDataById(uint danId)
|
|
||||||
{
|
|
||||||
return danMap.GetValueOrDefault(danId, new DanData());
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetMusicStarLevel(uint songId, Difficulty difficulty)
|
|
||||||
{
|
|
||||||
var success = musicMap.TryGetValue(songId, out var musicDetail);
|
|
||||||
return difficulty switch
|
|
||||||
{
|
|
||||||
Difficulty.None => throw new ArgumentException("Difficulty cannot be none"),
|
|
||||||
Difficulty.Easy => success ? musicDetail!.StarEasy : 0,
|
|
||||||
Difficulty.Normal => success ? musicDetail!.StarNormal : 0,
|
|
||||||
Difficulty.Hard => success ? musicDetail!.StarHard : 0,
|
|
||||||
Difficulty.Oni => success ? musicDetail!.StarOni : 0,
|
|
||||||
Difficulty.UraOni => success ? musicDetail!.StarUra : 0,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(difficulty), difficulty, null)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,4 +1,7 @@
|
|||||||
namespace TaikoWebUI.Services;
|
using System.Collections.Immutable;
|
||||||
|
using TaikoWebUI.Shared.Models;
|
||||||
|
|
||||||
|
namespace TaikoWebUI.Services;
|
||||||
|
|
||||||
public interface IGameDataService
|
public interface IGameDataService
|
||||||
{
|
{
|
||||||
@ -15,4 +18,12 @@ public interface IGameDataService
|
|||||||
public DanData GetDanDataById(uint danId);
|
public DanData GetDanDataById(uint danId);
|
||||||
|
|
||||||
public int GetMusicStarLevel(uint songId, Difficulty difficulty);
|
public int GetMusicStarLevel(uint songId, Difficulty difficulty);
|
||||||
|
|
||||||
|
public string GetHeadTitle(uint index);
|
||||||
|
public string GetKigurumiTitle(uint index);
|
||||||
|
public string GetBodyTitle(uint index);
|
||||||
|
public string GetFaceTitle(uint index);
|
||||||
|
public string GetPuchiTitle(uint index);
|
||||||
|
|
||||||
|
public ImmutableHashSet<Title> GetTitles();
|
||||||
}
|
}
|
12
TaikoWebUI/Shared/Constants.cs
Normal file
12
TaikoWebUI/Shared/Constants.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace TaikoWebUI.Shared;
|
||||||
|
|
||||||
|
public static class Constants
|
||||||
|
{
|
||||||
|
public const int COSTUME_HEAD_MAX = 140;
|
||||||
|
public const int COSTUME_FACE_MAX = 58;
|
||||||
|
public const int COSTUME_BODY_MAX = 156;
|
||||||
|
public const int COSTUME_KIGURUMI_MAX = 154;
|
||||||
|
public const int COSTUME_PUCHI_MAX = 129;
|
||||||
|
public const int COSTUME_COLOR_MAX = 63;
|
||||||
|
public const int PLAYER_TITLE_MAX = 750;
|
||||||
|
}
|
23
TaikoWebUI/Shared/Models/Title.cs
Normal file
23
TaikoWebUI/Shared/Models/Title.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace TaikoWebUI.Shared.Models;
|
||||||
|
|
||||||
|
public class Title
|
||||||
|
{
|
||||||
|
public int TitleId { get; set; }
|
||||||
|
|
||||||
|
public string TitleName { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
if (obj is Title title)
|
||||||
|
{
|
||||||
|
return title.TitleName.Equals(TitleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return TitleName.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Autocomplete.Clients" Version="1.1.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.7" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.7" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.7" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.7" PrivateAssets="all" />
|
||||||
<PackageReference Include="MudBlazor" Version="6.0.15" />
|
<PackageReference Include="MudBlazor" Version="6.0.15" />
|
||||||
|
@ -18,4 +18,15 @@
|
|||||||
.mud-progress-linear.bar-pass-red .mud-typography {
|
.mud-progress-linear.bar-pass-red .mud-typography {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-box {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
border: 1px solid black;
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user