commit
799eb85031
1
.gitignore
vendored
1
.gitignore
vendored
@ -396,3 +396,4 @@ FodyWeavers.xsd
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
TaikoLocalServer/wwwroot/data/music_attribute.bin
|
||||
|
95
OldSetup.md
Normal file
95
OldSetup.md
Normal file
@ -0,0 +1,95 @@
|
||||
# Old setup
|
||||
|
||||
1. Download the latest release of [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader) and install it. Make sure to setup TAL using the config.toml and set the `server` parameter to your local IP address.
|
||||
2. Download the latest release of [TaikoReverseProxy](https://github.com/shiibe/TaikoReverseProxy).
|
||||
3. In the `Data\x64\datatable` folder of the game, find the following files:
|
||||
|
||||
```
|
||||
music_attribute.bin
|
||||
musicinfo.bin
|
||||
music_order.bin
|
||||
wordlist.bin
|
||||
```
|
||||
|
||||
Extract them (you can use [7zip](https://www.7-zip.org)) and rename the extracted file like so:
|
||||
|
||||
```
|
||||
music_attribute -> music_attribute.json
|
||||
musicinfo -> musicinfo.json
|
||||
music_order -> music_order.json
|
||||
wordlist -> wordlist.json
|
||||
```
|
||||
|
||||
Then put these in TaikoLocalServer's `wwwroot/data` folder.
|
||||
|
||||
4. Modify hosts, add the following entries (this step can be done automatically with TaikoReverseProxy, check the config for it):
|
||||
|
||||
```
|
||||
server.ip tenporouter.loc
|
||||
server.ip naominet.jp
|
||||
server.ip v402-front.mucha-prd.nbgi-amnet.jp
|
||||
server.ip vsapi.taiko-p.jp
|
||||
```
|
||||
|
||||
where `server.ip` is your computer's ip (or the server's ip)
|
||||
|
||||
5. 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. Run TaikoReverseProxy and TaikoLocalServer, then run the game. You can access the WebUI by going to `https://naominet.jp:10122/` in your browser.
|
||||
|
||||
|
||||
### Server setup (for TAL<2.00 or other loaders)
|
||||
|
||||
1. Download the server from release page, extract anywhere
|
||||
|
||||
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 naominet.jp
|
||||
server.ip v402-front.mucha-prd.nbgi-amnet.jp
|
||||
server.ip vsapi.taiko-p.jp
|
||||
```
|
||||
|
||||
where `server.ip` is your computers ip (or the server's ip)
|
||||
|
||||
4. Setup [TaikoReverseProxy](https://github.com/shiibe/TaikoReverseProxy) or [Apache](#apache-setup-optional) as reverse proxy.
|
||||
|
||||
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. 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. 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 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.
|
||||
|
||||
### Apache Setup (Optional)
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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)
|
106
README.md
106
README.md
@ -8,106 +8,38 @@ This is a server for Taiko no Tatsujin Nijiiro ver 08.18
|
||||
|
||||
- A working game, with dongle and card reader emulation. You can use [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader) for these if you haven't.
|
||||
|
||||
### Tools
|
||||
|
||||
- [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader): A loader for the game with hardware emulation and other fixes.
|
||||
- [TaikoReverseProxy](https://github.com/shiibe/TaikoReverseProxy): A no-setup bundled proxy server, use as a user-friendly alternative to Apache.
|
||||
|
||||
### Quick Setup
|
||||
|
||||
With the newest release (>=2.00) of TaikoArcadeLoader, you no longer need to run AMAuthd or AMUpdater.
|
||||
With the newest release (from [this](https://github.com/BroGamer4256/TaikoArcadeLoader/tree/95d633850d89cb7099e98ffe74cd23632fe26e56) commit) of TaikoArcadeLoader, you no longer need to run AMAuthd, AMUpdater or reverse proxy.
|
||||
|
||||
1. Download the latest release of [TaikoArcadeLoader](https://github.com/BroGamer4256/TaikoArcadeLoader) and install it. Make sure to setup TAL using the config.toml and set the `server` parameter to your local IP address.
|
||||
2. Download the latest release of [TaikoReverseProxy](https://github.com/shiibe/TaikoReverseProxy).
|
||||
3. In the `Data\x64\datatable` folder of the game, find the following files:
|
||||
1. Extract the server release anywhere
|
||||
|
||||
```
|
||||
music_attribute.bin
|
||||
musicinfo.bin
|
||||
music_order.bin
|
||||
wordlist.bin
|
||||
```
|
||||
2. From the game files, copy `music_attribute.bin`, `music_order.bin`, `musicinfo.bin` and `wordlist.bin` to `wwwroot/data` folder.
|
||||
|
||||
Extract them (you can use [7zip](https://www.7-zip.org)) and rename the extracted file like so:
|
||||
3. (Optional) Instead of direct copy, extract the specified game files (using 7zip), rename them by adding the file extension `.json` and copy the jsons over.
|
||||
|
||||
```
|
||||
music_attribute -> music_attribute.json
|
||||
musicinfo -> musicinfo.json
|
||||
music_order -> music_order.json
|
||||
wordlist -> wordlist.json
|
||||
```
|
||||
4. (Optional) In `Certificates` folder, import `root.pfx` to trusted root store, `cert.pfx` to personal store. All the other import options can be kept default.
|
||||
|
||||
Then put these in TaikoLocalServer's `wwwroot/data` folder.
|
||||
5. Visit http://localhost:5000 and (Optional, only if you have done step 4) https://localhost:10122, if the web ui starts without errors, the config is fine.
|
||||
|
||||
6. Modify comfig.toml from TAL, edit the following line:
|
||||
|
||||
4. Modify hosts, add the following entries (this step can be done automatically with TaikoReverseProxy, check the config for it):
|
||||
|
||||
```
|
||||
server.ip tenporouter.loc
|
||||
server.ip naominet.jp
|
||||
server.ip v402-front.mucha-prd.nbgi-amnet.jp
|
||||
server.ip vsapi.taiko-p.jp
|
||||
```toml
|
||||
server = "https://divamodarchive.com" # Change https://divamodarchive.com to your/server's ip, like 192.168.1.100
|
||||
```
|
||||
|
||||
where `server.ip` is your computer's ip (or the server's ip)
|
||||
7. Now the game should be able to connect.
|
||||
|
||||
5. Open command prompt as admin, navigate to game root folder (where init.ps1 is). Run `regsvr32 .\AMCUS\iauthdll.dll`. It should prompt about success
|
||||
### About certificates
|
||||
|
||||
6. Run TaikoReverseProxy and TaikoLocalServer, then run the game. You can access the WebUI by going to `https://naominet.jp:10122/` in your browser.
|
||||
If you want to change the certificate used by server, just replace `cert.pfx` in `Certificates` folder. The bundled certificate includes the following DNS names:
|
||||
|
||||
|
||||
### Server setup (for TAL<2.00 or other loaders)
|
||||
|
||||
1. Download the server from release page, extract anywhere
|
||||
|
||||
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 naominet.jp
|
||||
server.ip v402-front.mucha-prd.nbgi-amnet.jp
|
||||
server.ip vsapi.taiko-p.jp
|
||||
```
|
||||
|
||||
where `server.ip` is your computers ip (or the server's ip)
|
||||
|
||||
4. Setup [TaikoReverseProxy](https://github.com/shiibe/TaikoReverseProxy) or [Apache](#apache-setup-optional) as reverse proxy.
|
||||
|
||||
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. 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. 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 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.
|
||||
|
||||
### Apache Setup (Optional)
|
||||
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
|
||||
|
||||
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"
|
||||
```
|
||||
DNS Name=nbgi-amnet.jp
|
||||
DNS Name=v402-front.mucha-prd.nbgi-amnet.jp
|
||||
DNS Name=*.mucha-prd.nbgi-amnet.jp
|
||||
DNS Name=localhost
|
||||
DNS Name=vsapi.taiko-p.jp
|
||||
```
|
||||
|
||||
4. 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)
|
||||
You will need to modify hosts file to use them.
|
||||
|
@ -5,5 +5,5 @@ public enum PlayMode
|
||||
Normal = 0,
|
||||
DanMode = 1,
|
||||
// Not sure about this
|
||||
AiBattle = 2
|
||||
AiBattle = 6
|
||||
}
|
22
SharedProject/Models/AiSectionBestData.cs
Normal file
22
SharedProject/Models/AiSectionBestData.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using SharedProject.Enums;
|
||||
|
||||
namespace SharedProject.Models;
|
||||
|
||||
public class AiSectionBestData
|
||||
{
|
||||
public int SectionIndex { get; set; }
|
||||
|
||||
public CrownType Crown { get; set; }
|
||||
|
||||
public bool IsWin { get; set; }
|
||||
|
||||
public uint Score { get; set; }
|
||||
|
||||
public uint GoodCount { get; set; }
|
||||
|
||||
public uint OkCount { get; set; }
|
||||
|
||||
public uint MissCount { get; set; }
|
||||
|
||||
public uint DrumrollCount { get; set; }
|
||||
}
|
@ -14,6 +14,11 @@ public class SongBestData
|
||||
|
||||
public Difficulty Difficulty { get; set; }
|
||||
|
||||
public int PlayCount { get; set; }
|
||||
public int ClearCount { get; set; }
|
||||
public int FullComboCount { get; set; }
|
||||
public int PerfectCount { get; set; }
|
||||
|
||||
public uint BestScore { get; set; }
|
||||
|
||||
public uint BestRate { get; set; }
|
||||
@ -37,4 +42,8 @@ public class SongBestData
|
||||
public uint HitCount { get; set; }
|
||||
|
||||
public uint DrumrollCount { get; set; }
|
||||
|
||||
public List<AiSectionBestData> AiSectionBestData { get; set; } = new();
|
||||
|
||||
public bool ShowAiData { get; set; }
|
||||
}
|
14
SharedProject/Utils/ValueHelpers.cs
Normal file
14
SharedProject/Utils/ValueHelpers.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace SharedProject.Utils;
|
||||
|
||||
public static class ValueHelpers
|
||||
{
|
||||
public static T Min<T>(T a, T b) where T : IComparable
|
||||
{
|
||||
return Comparer<T>.Default.Compare(a, b) <= 0 ? a : b;
|
||||
}
|
||||
|
||||
public static T Max<T>(T a, T b) where T : IComparable
|
||||
{
|
||||
return Comparer<T>.Default.Compare(a, b) >= 0 ? a : b;
|
||||
}
|
||||
}
|
Binary file not shown.
BIN
TaikoLocalServer/Certificates/root.pfx
Normal file
BIN
TaikoLocalServer/Certificates/root.pfx
Normal file
Binary file not shown.
@ -4,21 +4,14 @@ public static class Constants
|
||||
{
|
||||
public const string DATE_TIME_FORMAT = "yyyyMMddHHmmss";
|
||||
|
||||
public const int MUSIC_ID_MAX = 9000;
|
||||
public const int MUSIC_ID_MAX = 1599;
|
||||
|
||||
public const int CROWN_FLAG_ARRAY_SIZE = MUSIC_ID_MAX + 1;
|
||||
|
||||
public const int DONDAFUL_CROWN_FLAG_ARRAY_SIZE = MUSIC_ID_MAX + 1;
|
||||
|
||||
public const int KIWAMI_SCORE_RANK_ARRAY_SIZE = MUSIC_ID_MAX + 1;
|
||||
|
||||
public const int IKI_CORE_RANK_ARRAY_SIZE = MUSIC_ID_MAX + 1;
|
||||
|
||||
public const int MIYABI_CORE_RANK_ARRAY_SIZE = MUSIC_ID_MAX + 1;
|
||||
public const int MUSIC_ID_MAX_EXPANDED = 9000;
|
||||
|
||||
public const string DEFAULT_DB_NAME = "taiko.db3";
|
||||
|
||||
public const string MUSIC_ATTRIBUTE_FILE_NAME = "music_attribute.json";
|
||||
public const string MUSIC_ATTRIBUTE_COMPRESSED_FILE_NAME = "music_attribute.bin";
|
||||
|
||||
public const string DAN_DATA_FILE_NAME = "dan_data.json";
|
||||
|
||||
|
@ -116,7 +116,7 @@ public static class FlagCalculator
|
||||
{
|
||||
if (id >= bitArraySize)
|
||||
{
|
||||
logger.LogWarning("Id out of range!");
|
||||
logger.LogWarning("Id {Id} out of range!", id);
|
||||
continue;
|
||||
}
|
||||
bitSet.Set((int)id, true);
|
||||
|
@ -4,6 +4,8 @@ public partial class TaikoDbContext
|
||||
{
|
||||
public virtual DbSet<DanScoreDatum> DanScoreData { get; set; } = null!;
|
||||
public virtual DbSet<DanStageScoreDatum> DanStageScoreData { get; set; } = null!;
|
||||
public virtual DbSet<AiScoreDatum> AiScoreData { get; set; } = null!;
|
||||
public virtual DbSet<AiSectionScoreDatum> AiSectionScoreData { get; set; } = null!;
|
||||
|
||||
partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
|
||||
{
|
||||
@ -30,5 +32,28 @@ public partial class TaikoDbContext
|
||||
.HasForeignKey(d => new {d.Baid, d.DanId})
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AiScoreDatum>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.Baid, e.SongId, e.Difficulty });
|
||||
|
||||
entity.HasOne(d => d.Ba)
|
||||
.WithMany()
|
||||
.HasPrincipalKey(p => p.Baid)
|
||||
.HasForeignKey(d => d.Baid)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
|
||||
modelBuilder.Entity<AiSectionScoreDatum>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.Baid, e.SongId, e.Difficulty, e.SectionIndex });
|
||||
|
||||
entity.HasOne(d => d.Parent)
|
||||
.WithMany(p => p.AiSectionScoreData)
|
||||
.HasPrincipalKey(p => new {p.Baid, p.SongId, p.Difficulty })
|
||||
.HasForeignKey(d => new {d.Baid, d.SongId, d.Difficulty })
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
}
|
||||
}
|
@ -7,9 +7,9 @@ namespace TaikoLocalServer.Controllers.AmAuth;
|
||||
[Route("/sys/servlet/PowerOn")]
|
||||
public class PowerOnController : BaseController<PowerOnController>
|
||||
{
|
||||
private readonly UrlSettings settings;
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public PowerOnController(IOptions<UrlSettings> settings)
|
||||
public PowerOnController(IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ namespace TaikoLocalServer.Controllers.AmUpdater;
|
||||
|
||||
public class MuchaController : BaseController<MuchaController>
|
||||
{
|
||||
private readonly UrlSettings settings;
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public MuchaController(IOptions<UrlSettings> settings)
|
||||
public MuchaController(IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
|
@ -1,7 +1,6 @@
|
||||
using SharedProject.Models;
|
||||
using SharedProject.Models.Responses;
|
||||
using Swan.Mapping;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
|
@ -1,7 +1,4 @@
|
||||
using SharedProject.Models;
|
||||
using SharedProject.Models.Responses;
|
||||
using Swan.Mapping;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using SharedProject.Models.Responses;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
using SharedProject.Models.Requests;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
using SharedProject.Models.Responses;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using System.Collections.Immutable;
|
||||
using SharedProject.Models.Responses;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
@ -11,10 +11,14 @@ public class PlayDataController : BaseController<PlayDataController>
|
||||
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
|
||||
public PlayDataController(IUserDatumService userDatumService, ISongBestDatumService songBestDatumService)
|
||||
private readonly ISongPlayDatumService songPlayDatumService;
|
||||
|
||||
public PlayDataController(IUserDatumService userDatumService, ISongBestDatumService songBestDatumService,
|
||||
ISongPlayDatumService songPlayDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.songPlayDatumService = songPlayDatumService;
|
||||
}
|
||||
|
||||
[HttpGet("{baid}")]
|
||||
@ -27,6 +31,16 @@ public class PlayDataController : BaseController<PlayDataController>
|
||||
}
|
||||
|
||||
var songBestRecords = await songBestDatumService.GetAllSongBestAsModel(baid);
|
||||
var playLogs = await songPlayDatumService.GetSongPlayDatumByBaid(baid);
|
||||
foreach (var songBestData in songBestRecords)
|
||||
{
|
||||
var songPlayLogs = playLogs.Where(datum => datum.SongId == songBestData.SongId &&
|
||||
datum.Difficulty == songBestData.Difficulty).ToList();
|
||||
songBestData.PlayCount = songPlayLogs.Count;
|
||||
songBestData.ClearCount = songPlayLogs.Count(datum => datum.Crown >= CrownType.Clear);
|
||||
songBestData.FullComboCount = songPlayLogs.Count(datum => datum.Crown >= CrownType.Gold);
|
||||
songBestData.PerfectCount = songPlayLogs.Count(datum => datum.Crown >= CrownType.Dondaful);
|
||||
}
|
||||
var favoriteSongs = await userDatumService.GetFavoriteSongIds(baid);
|
||||
var favoriteSet = favoriteSongs.ToHashSet();
|
||||
foreach (var songBestRecord in songBestRecords.Where(songBestRecord => favoriteSet.Contains(songBestRecord.SongId)))
|
||||
|
@ -1,11 +1,6 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using SharedProject.Models;
|
||||
using SharedProject.Models.Responses;
|
||||
using SharedProject.Utils;
|
||||
using TaikoLocalServer.Services;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Api;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
@ -16,13 +15,16 @@ public class BaidController : BaseController<BaidController>
|
||||
|
||||
private readonly IDanScoreDatumService danScoreDatumService;
|
||||
|
||||
private readonly IAiDatumService aiDatumService;
|
||||
|
||||
public BaidController(IUserDatumService userDatumService, ICardService cardService,
|
||||
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService)
|
||||
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.cardService = cardService;
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.danScoreDatumService = danScoreDatumService;
|
||||
this.aiDatumService = aiDatumService;
|
||||
}
|
||||
|
||||
|
||||
@ -112,6 +114,11 @@ public class BaidController : BaseController<BaidController>
|
||||
var genericInfoFlgLength = genericInfoFlg.Any()? genericInfoFlg.Max() + 1 : 0;
|
||||
var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, Logger);
|
||||
|
||||
var aiRank = (uint)(userData.AiWinCount / 10);
|
||||
if (aiRank > 11)
|
||||
{
|
||||
aiRank = 11;
|
||||
}
|
||||
response = new BAIDResponse
|
||||
{
|
||||
Result = 1,
|
||||
@ -158,8 +165,8 @@ public class BaidController : BaseController<BaidController>
|
||||
IsDispAchievementTypeSet = true,
|
||||
LastPlayMode = userData.LastPlayMode,
|
||||
IsDispSouuchiOn = true,
|
||||
AiRank = 0,
|
||||
AiTotalWin = 0,
|
||||
AiRank = aiRank,
|
||||
AiTotalWin = (uint)userData.AiWinCount,
|
||||
Accesstoken = "123456",
|
||||
ContentInfo = GZipBytesUtil.GetGZipBytes(new byte[10])
|
||||
};
|
||||
|
@ -1,4 +1,6 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoLocalServer.Settings;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
@ -8,9 +10,12 @@ public class CrownsDataController : BaseController<CrownsDataController>
|
||||
{
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
|
||||
public CrownsDataController(ISongBestDatumService songBestDatumService)
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public CrownsDataController(ISongBestDatumService songBestDatumService, IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -21,10 +26,11 @@ public class CrownsDataController : BaseController<CrownsDataController>
|
||||
|
||||
var songBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
|
||||
|
||||
var crown = new ushort[Constants.CROWN_FLAG_ARRAY_SIZE];
|
||||
var dondafulCrown = new byte[Constants.DONDAFUL_CROWN_FLAG_ARRAY_SIZE];
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var crown = new ushort[songIdMax + 1];
|
||||
var dondafulCrown = new byte[songIdMax + 1];
|
||||
|
||||
for (var songId = 0; songId < Constants.MUSIC_ID_MAX; songId++)
|
||||
for (var songId = 0; songId < songIdMax; songId++)
|
||||
{
|
||||
var id = songId;
|
||||
dondafulCrown[songId] = songBestData
|
||||
|
@ -1,21 +1,32 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r03/chassis/getaidata.php")]
|
||||
[ApiController]
|
||||
public class GetAiDataController : BaseController<GetAiDataController>
|
||||
{
|
||||
private readonly IUserDatumService userDatumService;
|
||||
|
||||
public GetAiDataController(IUserDatumService userDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetAiData([FromBody] GetAiDataRequest request)
|
||||
public async Task<IActionResult> GetAiData([FromBody] GetAiDataRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetAiData request : {Request}", request.Stringify());
|
||||
|
||||
var user = await userDatumService.GetFirstUserDatumOrNull(request.Baid);
|
||||
user.ThrowIfNull($"User with baid {request.Baid} does not exist!");
|
||||
var response = new GetAiDataResponse
|
||||
{
|
||||
Result = 1,
|
||||
TotalWinnings = 1,
|
||||
InputMedian = "1",
|
||||
InputVariance = "0.576389"
|
||||
TotalWinnings = (uint)user.AiWinCount,
|
||||
InputMedian = "1000",
|
||||
InputVariance = "2000"
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
|
@ -1,12 +1,21 @@
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r03/chassis/getaiscore.php")]
|
||||
[ApiController]
|
||||
public class GetAiScoreController : BaseController<GetAiScoreController>
|
||||
{
|
||||
private readonly IAiDatumService aiDatumService;
|
||||
|
||||
public GetAiScoreController(IAiDatumService aiDatumService)
|
||||
{
|
||||
this.aiDatumService = aiDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Produces("application/protobuf")]
|
||||
public IActionResult GetAiScore([FromBody] GetAiScoreRequest request)
|
||||
public async Task<IActionResult> GetAiScore([FromBody] GetAiScoreRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetAiScore request : {Request}", request.Stringify());
|
||||
|
||||
@ -15,9 +24,33 @@ public class GetAiScoreController : BaseController<GetAiScoreController>
|
||||
Result = 1
|
||||
};
|
||||
|
||||
var difficulty = (Difficulty)request.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
|
||||
var aiData = await aiDatumService.GetSongAiScore(request.Baid, request.SongNo, difficulty);
|
||||
if (aiData is null)
|
||||
{
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
for (var index = 0; index < aiData.AiSectionScoreData.Count; index++)
|
||||
{
|
||||
var sectionScoreDatum = aiData.AiSectionScoreData[index];
|
||||
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData
|
||||
{
|
||||
Crown = (uint)sectionScoreDatum.Crown,
|
||||
GoodCnt = sectionScoreDatum.GoodCount,
|
||||
OkCnt = sectionScoreDatum.OkCount,
|
||||
NgCnt = sectionScoreDatum.MissCount,
|
||||
PoundCnt = sectionScoreDatum.DrumrollCount,
|
||||
Score = sectionScoreDatum.Score,
|
||||
SectionNo = (uint)index
|
||||
});
|
||||
}
|
||||
|
||||
// There's either 3 or 5 total sections
|
||||
// SectionNo doesn't seem to actually affect which section is being assigned to, only the List order matters
|
||||
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
|
||||
/*response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()
|
||||
{
|
||||
SectionNo = 1,
|
||||
Crown = (uint)CrownType.Clear,
|
||||
@ -66,7 +99,7 @@ public class GetAiScoreController : BaseController<GetAiScoreController>
|
||||
OkCnt = 50,
|
||||
NgCnt = 25,
|
||||
PoundCnt = 12
|
||||
});
|
||||
});*/
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r03/chassis/getdanodai.php")]
|
||||
[ApiController]
|
||||
|
@ -1,6 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r03/chassis/getdanscore.php")]
|
||||
[ApiController]
|
||||
|
@ -1,4 +1,5 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoLocalServer.Settings;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
@ -8,9 +9,12 @@ public class GetScoreRankController : BaseController<GetScoreRankController>
|
||||
{
|
||||
private readonly ISongBestDatumService songBestDatumService;
|
||||
|
||||
public GetScoreRankController(ISongBestDatumService songBestDatumService)
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public GetScoreRankController(ISongBestDatumService songBestDatumService, IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -18,12 +22,13 @@ public class GetScoreRankController : BaseController<GetScoreRankController>
|
||||
public async Task<IActionResult> GetScoreRank([FromBody] GetScoreRankRequest request)
|
||||
{
|
||||
Logger.LogInformation("GetScoreRank request : {Request}", request.Stringify());
|
||||
var kiwamiScores = new byte[Constants.KIWAMI_SCORE_RANK_ARRAY_SIZE];
|
||||
var miyabiScores = new ushort[Constants.MIYABI_CORE_RANK_ARRAY_SIZE];
|
||||
var ikiScores = new ushort[Constants.IKI_CORE_RANK_ARRAY_SIZE];
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var kiwamiScores = new byte[songIdMax + 1];
|
||||
var miyabiScores = new ushort[songIdMax + 1];
|
||||
var ikiScores = new ushort[songIdMax + 1];
|
||||
var songBestData = await songBestDatumService.GetAllSongBestData(request.Baid);
|
||||
|
||||
for (var songId = 0; songId < Constants.MUSIC_ID_MAX; songId++)
|
||||
for (var songId = 0; songId < songIdMax; songId++)
|
||||
{
|
||||
var id = songId;
|
||||
kiwamiScores[songId] = songBestData
|
||||
|
@ -1,6 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r03/chassis/getsongintroduction.php")]
|
||||
[ApiController]
|
||||
|
@ -1,4 +1,5 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoLocalServer.Settings;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
@ -8,9 +9,12 @@ public class InitialDataCheckController : BaseController<InitialDataCheckControl
|
||||
{
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
public InitialDataCheckController(IGameDataService gameDataService)
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public InitialDataCheckController(IGameDataService gameDataService, IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.gameDataService = gameDataService;
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -19,8 +23,9 @@ public class InitialDataCheckController : BaseController<InitialDataCheckControl
|
||||
{
|
||||
Logger.LogInformation("Initial data check request: {Request}", request.Stringify());
|
||||
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var enabledArray =
|
||||
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicList(), Constants.MUSIC_ID_MAX, Logger);
|
||||
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicList(), songIdMax, Logger);
|
||||
|
||||
var danData = new List<InitialdatacheckResponse.InformationData>();
|
||||
for (var danId = Constants.MIN_DAN_ID; danId <= Constants.MAX_DAN_ID; danId++)
|
||||
@ -46,7 +51,7 @@ public class InitialDataCheckController : BaseController<InitialDataCheckControl
|
||||
{
|
||||
Result = 1,
|
||||
IsDanplay = true,
|
||||
IsAibattle = false,
|
||||
IsAibattle = true,
|
||||
IsClose = false,
|
||||
DefaultSongFlg = enabledArray,
|
||||
AchievementSongBit = enabledArray,
|
||||
|
@ -1,7 +1,4 @@
|
||||
using TaikoLocalServer.Services;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
[Route("/v12r03/chassis/mydonentry.php")]
|
||||
[ApiController]
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
@ -20,13 +19,16 @@ public class PlayResultController : BaseController<PlayResultController>
|
||||
|
||||
private readonly IDanScoreDatumService danScoreDatumService;
|
||||
|
||||
private readonly IAiDatumService aiDatumService;
|
||||
|
||||
public PlayResultController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService,
|
||||
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService)
|
||||
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.songPlayDatumService = songPlayDatumService;
|
||||
this.songBestDatumService = songBestDatumService;
|
||||
this.danScoreDatumService = danScoreDatumService;
|
||||
this.aiDatumService = aiDatumService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -77,9 +79,7 @@ public class PlayResultController : BaseController<PlayResultController>
|
||||
|
||||
if (playMode == PlayMode.AiBattle)
|
||||
{
|
||||
// await UpdateAiBattleData(request, stageData);
|
||||
// Update AI win count here somewhere, or in UpdatePlayData?
|
||||
// I have no clue how to update input median or variance
|
||||
await UpdateAiBattleData(request, stageData);
|
||||
}
|
||||
|
||||
await UpdateBestData(request, stageData, bestData);
|
||||
@ -163,7 +163,7 @@ public class PlayResultController : BaseController<PlayResultController>
|
||||
ComboCount = stageData.ComboCnt,
|
||||
HitCount = stageData.HitCnt,
|
||||
DrumrollCount = stageData.PoundCnt,
|
||||
Crown = PlayResultToCrown(stageData),
|
||||
Crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt),
|
||||
Score = stageData.PlayScore,
|
||||
ScoreRate = stageData.ScoreRate,
|
||||
ScoreRank = (ScoreRank)stageData.ScoreRank,
|
||||
@ -229,6 +229,7 @@ public class PlayResultController : BaseController<PlayResultController>
|
||||
userdata.GenericInfoFlgArray =
|
||||
UpdateJsonUintFlagArray(userdata.GenericInfoFlgArray, playResultData.GetGenericInfoNoes, nameof(userdata.GenericInfoFlgArray));
|
||||
|
||||
userdata.AiWinCount += playResultData.AryStageInfoes.Count(data => data.IsWin);
|
||||
await userDatumService.UpdateUserDatum(userdata);
|
||||
}
|
||||
|
||||
@ -300,7 +301,7 @@ public class PlayResultController : BaseController<PlayResultController>
|
||||
});
|
||||
|
||||
// Determine whether it is dondaful crown as this is not reflected by play result
|
||||
var crown = PlayResultToCrown(stageData);
|
||||
var crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt);
|
||||
|
||||
bestDatum.UpdateBestData(crown, stageData.ScoreRank, stageData.PlayScore, stageData.ScoreRate);
|
||||
|
||||
@ -308,23 +309,69 @@ public class PlayResultController : BaseController<PlayResultController>
|
||||
}
|
||||
|
||||
// TODO: AI battle
|
||||
/*private async Task UpdateAiBattleData(PlayResultRequest request, StageData stageData)
|
||||
private async Task UpdateAiBattleData(PlayResultRequest request, StageData stageData)
|
||||
{
|
||||
for (int i = 0; i < stageData.ArySectionDatas.Count; i++)
|
||||
var difficulty = (Difficulty)stageData.Level;
|
||||
difficulty.Throw().IfOutOfRange();
|
||||
var existing = await aiDatumService.GetSongAiScore(request.BaidConf,
|
||||
stageData.SongNo, difficulty);
|
||||
|
||||
if (existing is null)
|
||||
{
|
||||
// Only update crown if it's a higher crown than the previous best crown
|
||||
var aiScoreDatum = new AiScoreDatum
|
||||
{
|
||||
Baid = request.BaidConf,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
IsWin = stageData.IsWin
|
||||
};
|
||||
for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
|
||||
{
|
||||
AddNewAiSectionScore(request, stageData, index, difficulty, aiScoreDatum);
|
||||
}
|
||||
|
||||
// Maybe have a "SectionNo" variable for which section number it is on the DB
|
||||
// compare DB.SectionNo == i
|
||||
// if any aspect of the section is higher than the previous best, update it
|
||||
// Similar to Dan best play updates
|
||||
await aiDatumService.InsertSongAiScore(aiScoreDatum);
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
private static CrownType PlayResultToCrown(StageData stageData)
|
||||
for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
|
||||
{
|
||||
var sectionData = stageData.ArySectionDatas[index];
|
||||
if (index < existing.AiSectionScoreData.Count)
|
||||
{
|
||||
existing.AiSectionScoreData[index].UpdateBest(sectionData);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddNewAiSectionScore(request,stageData,index,difficulty,existing);
|
||||
}
|
||||
}
|
||||
|
||||
await aiDatumService.UpdateSongAiScore(existing);
|
||||
}
|
||||
|
||||
private static void AddNewAiSectionScore(PlayResultRequest request, StageData stageData, int index, Difficulty difficulty,
|
||||
AiScoreDatum aiScoreDatum)
|
||||
{
|
||||
var crown = (CrownType)stageData.PlayResult;
|
||||
if (crown == CrownType.Gold && stageData.OkCnt == 0)
|
||||
var sectionData = stageData.ArySectionDatas[index];
|
||||
var aiSectionScoreDatum = new AiSectionScoreDatum
|
||||
{
|
||||
Baid = request.BaidConf,
|
||||
SongId = stageData.SongNo,
|
||||
Difficulty = difficulty,
|
||||
SectionIndex = index,
|
||||
OkCount = sectionData.OkCnt,
|
||||
MissCount = sectionData.NgCnt
|
||||
};
|
||||
aiSectionScoreDatum.UpdateBest(sectionData);
|
||||
aiScoreDatum.AiSectionScoreData.Add(aiSectionScoreDatum);
|
||||
}
|
||||
|
||||
|
||||
private static CrownType PlayResultToCrown(uint playResult, uint okCount)
|
||||
{
|
||||
var crown = (CrownType)playResult;
|
||||
if (crown == CrownType.Gold && okCount == 0)
|
||||
{
|
||||
crown = CrownType.Dondaful;
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Text.Json;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Microsoft.Extensions.Options;
|
||||
using TaikoLocalServer.Settings;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Controllers.Game;
|
||||
@ -15,11 +16,15 @@ public class UserDataController : BaseController<UserDataController>
|
||||
|
||||
private readonly IGameDataService gameDataService;
|
||||
|
||||
public UserDataController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService, IGameDataService gameDataService)
|
||||
private readonly ServerSettings settings;
|
||||
|
||||
public UserDataController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService,
|
||||
IGameDataService gameDataService, IOptions<ServerSettings> settings)
|
||||
{
|
||||
this.userDatumService = userDatumService;
|
||||
this.songPlayDatumService = songPlayDatumService;
|
||||
this.gameDataService = gameDataService;
|
||||
this.settings = settings.Value;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -28,11 +33,12 @@ public class UserDataController : BaseController<UserDataController>
|
||||
{
|
||||
Logger.LogInformation("UserData request : {Request}", request.Stringify());
|
||||
|
||||
var songIdMax = settings.EnableMoreSongs ? Constants.MUSIC_ID_MAX_EXPANDED : Constants.MUSIC_ID_MAX;
|
||||
var releaseSongArray =
|
||||
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicList(), Constants.MUSIC_ID_MAX, Logger);
|
||||
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicList(), songIdMax, Logger);
|
||||
|
||||
var uraSongArray =
|
||||
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicWithUraList(), Constants.MUSIC_ID_MAX, Logger);
|
||||
FlagCalculator.GetBitArrayFromIds(gameDataService.GetMusicWithUraList(), songIdMax, Logger);
|
||||
|
||||
var userData = await userDatumService.GetFirstUserDatumOrDefault(request.Baid);
|
||||
|
||||
|
16
TaikoLocalServer/Entities/AiScoreDatum.cs
Normal file
16
TaikoLocalServer/Entities/AiScoreDatum.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace TaikoLocalServer.Entities;
|
||||
|
||||
public class AiScoreDatum
|
||||
{
|
||||
public uint Baid { get; set; }
|
||||
|
||||
public uint SongId { get; set; }
|
||||
|
||||
public Difficulty Difficulty { get; set; }
|
||||
|
||||
public bool IsWin { get; set; }
|
||||
|
||||
public List<AiSectionScoreDatum> AiSectionScoreData { get; set; } = new();
|
||||
|
||||
public virtual Card? Ba { get; set; }
|
||||
}
|
47
TaikoLocalServer/Entities/AiSectionScoreDatum.cs
Normal file
47
TaikoLocalServer/Entities/AiSectionScoreDatum.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using SharedProject.Utils;
|
||||
|
||||
namespace TaikoLocalServer.Entities;
|
||||
|
||||
public class AiSectionScoreDatum
|
||||
{
|
||||
public uint Baid { get; set; }
|
||||
|
||||
public uint SongId { get; set; }
|
||||
|
||||
public Difficulty Difficulty { get; set; }
|
||||
|
||||
public int SectionIndex { get; set; }
|
||||
|
||||
public CrownType Crown { get; set; }
|
||||
|
||||
public bool IsWin { get; set; }
|
||||
|
||||
public uint Score { get; set; }
|
||||
|
||||
public uint GoodCount { get; set; }
|
||||
|
||||
public uint OkCount { get; set; }
|
||||
|
||||
public uint MissCount { get; set; }
|
||||
|
||||
public uint DrumrollCount { get; set; }
|
||||
|
||||
public AiScoreDatum Parent { get; set; } = null!;
|
||||
|
||||
public void UpdateBest(PlayResultDataRequest.StageData.AiStageSectionData sectionData)
|
||||
{
|
||||
var crown = (CrownType)sectionData.Crown;
|
||||
if (crown == CrownType.Gold && sectionData.OkCnt == 0)
|
||||
{
|
||||
crown = CrownType.Dondaful;
|
||||
}
|
||||
|
||||
IsWin = sectionData.IsWin ? sectionData.IsWin : IsWin;
|
||||
Crown = ValueHelpers.Max(crown, Crown);
|
||||
Score = ValueHelpers.Max(sectionData.Score, Score);
|
||||
GoodCount = ValueHelpers.Max(sectionData.GoodCnt, GoodCount);
|
||||
OkCount = ValueHelpers.Min(sectionData.OkCnt, OkCount);
|
||||
MissCount = ValueHelpers.Min(sectionData.NgCnt, MissCount);
|
||||
DrumrollCount = ValueHelpers.Max(sectionData.PoundCnt, DrumrollCount);
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
public bool DisplayAchievement { get; set; }
|
||||
public Difficulty AchievementDisplayDifficulty { get; set; }
|
||||
|
||||
public int AiWinCount { get; set; }
|
||||
public virtual Card? Ba { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -10,3 +10,5 @@ global using TaikoLocalServer.Common.Utils;
|
||||
global using TaikoLocalServer.Context;
|
||||
global using TaikoLocalServer.Entities;
|
||||
global using TaikoLocalServer.Models;
|
||||
global using TaikoLocalServer.Services;
|
||||
global using TaikoLocalServer.Services.Interfaces;
|
432
TaikoLocalServer/Migrations/20220917180457_AddAiBattle.Designer.cs
generated
Normal file
432
TaikoLocalServer/Migrations/20220917180457_AddAiBattle.Designer.cs
generated
Normal file
@ -0,0 +1,432 @@
|
||||
// <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("20220917180457_AddAiBattle")]
|
||||
partial class AddAiBattle
|
||||
{
|
||||
/// <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.AiScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty");
|
||||
|
||||
b.ToTable("AiScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiSectionScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SectionIndex")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Crown")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DrumrollCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("GoodCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("MissCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("OkCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Score")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty", "SectionIndex");
|
||||
|
||||
b.ToTable("AiSectionScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("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.AiScoreDatum", b =>
|
||||
{
|
||||
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||
.WithMany()
|
||||
.HasForeignKey("Baid")
|
||||
.HasPrincipalKey("Baid")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Ba");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiSectionScoreDatum", b =>
|
||||
{
|
||||
b.HasOne("TaikoLocalServer.Entities.AiScoreDatum", "Parent")
|
||||
.WithMany("AiSectionScoreData")
|
||||
.HasForeignKey("Baid", "SongId", "Difficulty")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Parent");
|
||||
});
|
||||
|
||||
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.AiScoreDatum", b =>
|
||||
{
|
||||
b.Navigation("AiSectionScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||
{
|
||||
b.Navigation("DanStageScoreData");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
71
TaikoLocalServer/Migrations/20220917180457_AddAiBattle.cs
Normal file
71
TaikoLocalServer/Migrations/20220917180457_AddAiBattle.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace TaikoLocalServer.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddAiBattle : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AiScoreData",
|
||||
columns: table => new
|
||||
{
|
||||
Baid = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SongId = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
Difficulty = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
IsWin = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AiScoreData", x => new { x.Baid, x.SongId, x.Difficulty });
|
||||
table.ForeignKey(
|
||||
name: "FK_AiScoreData_Card_Baid",
|
||||
column: x => x.Baid,
|
||||
principalTable: "Card",
|
||||
principalColumn: "Baid",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AiSectionScoreData",
|
||||
columns: table => new
|
||||
{
|
||||
Baid = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SongId = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
Difficulty = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
SectionIndex = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Crown = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
IsWin = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
Score = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
GoodCount = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
OkCount = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
MissCount = table.Column<uint>(type: "INTEGER", nullable: false),
|
||||
DrumrollCount = table.Column<uint>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AiSectionScoreData", x => new { x.Baid, x.SongId, x.Difficulty, x.SectionIndex });
|
||||
table.ForeignKey(
|
||||
name: "FK_AiSectionScoreData_AiScoreData_Baid_SongId_Difficulty",
|
||||
columns: x => new { x.Baid, x.SongId, x.Difficulty },
|
||||
principalTable: "AiScoreData",
|
||||
principalColumns: new[] { "Baid", "SongId", "Difficulty" },
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AiSectionScoreData");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AiScoreData");
|
||||
}
|
||||
}
|
||||
}
|
435
TaikoLocalServer/Migrations/20220919022643_AddWinCountToUserdata.Designer.cs
generated
Normal file
435
TaikoLocalServer/Migrations/20220919022643_AddWinCountToUserdata.Designer.cs
generated
Normal file
@ -0,0 +1,435 @@
|
||||
// <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("20220919022643_AddWinCountToUserdata")]
|
||||
partial class AddWinCountToUserdata
|
||||
{
|
||||
/// <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.AiScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty");
|
||||
|
||||
b.ToTable("AiScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiSectionScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SectionIndex")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Crown")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DrumrollCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("GoodCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("MissCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("OkCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Score")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty", "SectionIndex");
|
||||
|
||||
b.ToTable("AiSectionScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("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<int>("AiWinCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ColorBody")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ColorFace")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ColorLimb")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CostumeData")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CostumeFlgArray")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<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.AiScoreDatum", b =>
|
||||
{
|
||||
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||
.WithMany()
|
||||
.HasForeignKey("Baid")
|
||||
.HasPrincipalKey("Baid")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Ba");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiSectionScoreDatum", b =>
|
||||
{
|
||||
b.HasOne("TaikoLocalServer.Entities.AiScoreDatum", "Parent")
|
||||
.WithMany("AiSectionScoreData")
|
||||
.HasForeignKey("Baid", "SongId", "Difficulty")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Parent");
|
||||
});
|
||||
|
||||
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.AiScoreDatum", b =>
|
||||
{
|
||||
b.Navigation("AiSectionScoreData");
|
||||
});
|
||||
|
||||
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 AddWinCountToUserdata : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AiWinCount",
|
||||
table: "UserData",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AiWinCount",
|
||||
table: "UserData");
|
||||
}
|
||||
}
|
||||
}
|
@ -17,6 +17,65 @@ namespace TaikoLocalServer.Migrations
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.0-preview.7.22376.2");
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty");
|
||||
|
||||
b.ToTable("AiScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiSectionScoreDatum", b =>
|
||||
{
|
||||
b.Property<uint>("Baid")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("SongId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Difficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SectionIndex")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Crown")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("DrumrollCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("GoodCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsWin")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("MissCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("OkCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("Score")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Baid", "SongId", "Difficulty", "SectionIndex");
|
||||
|
||||
b.ToTable("AiSectionScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.Card", b =>
|
||||
{
|
||||
b.Property<string>("AccessCode")
|
||||
@ -197,6 +256,9 @@ namespace TaikoLocalServer.Migrations
|
||||
b.Property<uint>("AchievementDisplayDifficulty")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AiWinCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("ColorBody")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -273,6 +335,29 @@ namespace TaikoLocalServer.Migrations
|
||||
b.ToTable("UserData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiScoreDatum", b =>
|
||||
{
|
||||
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||
.WithMany()
|
||||
.HasForeignKey("Baid")
|
||||
.HasPrincipalKey("Baid")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Ba");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiSectionScoreDatum", b =>
|
||||
{
|
||||
b.HasOne("TaikoLocalServer.Entities.AiScoreDatum", "Parent")
|
||||
.WithMany("AiSectionScoreData")
|
||||
.HasForeignKey("Baid", "SongId", "Difficulty")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Parent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||
{
|
||||
b.HasOne("TaikoLocalServer.Entities.Card", "Ba")
|
||||
@ -332,6 +417,11 @@ namespace TaikoLocalServer.Migrations
|
||||
b.Navigation("Ba");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.AiScoreDatum", b =>
|
||||
{
|
||||
b.Navigation("AiSectionScoreData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("TaikoLocalServer.Entities.DanScoreDatum", b =>
|
||||
{
|
||||
b.Navigation("DanStageScoreData");
|
||||
|
@ -1,87 +1,125 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Authentication;
|
||||
using Microsoft.AspNetCore.HttpLogging;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using TaikoLocalServer.Middlewares;
|
||||
using TaikoLocalServer.Services;
|
||||
using TaikoLocalServer.Services.Extentions;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using TaikoLocalServer.Settings;
|
||||
using Throw;
|
||||
using Serilog;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
// Manually enable tls 1.0
|
||||
builder.WebHost.UseKestrel(kestrelOptions =>
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.Console()
|
||||
.CreateBootstrapLogger();
|
||||
|
||||
var version = Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
|
||||
.InformationalVersion;
|
||||
Log.Information("TaikoLocalServer version {Version}", version);
|
||||
|
||||
Log.Information("Server starting up...");
|
||||
|
||||
try
|
||||
{
|
||||
kestrelOptions.ConfigureHttpsDefaults(httpsOptions =>
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
// Manually enable tls 1.0
|
||||
builder.WebHost.UseKestrel(kestrelOptions =>
|
||||
{
|
||||
httpsOptions.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
|
||||
kestrelOptions.ConfigureHttpsDefaults(options =>
|
||||
options.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13);
|
||||
});
|
||||
});
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddOptions();
|
||||
builder.Services.AddSingleton<IGameDataService, GameDataService>();
|
||||
builder.Services.Configure<UrlSettings>(builder.Configuration.GetSection(nameof(UrlSettings)));
|
||||
builder.Services.AddControllers().AddProtoBufNet();
|
||||
builder.Services.AddDbContext<TaikoDbContext>(option =>
|
||||
{
|
||||
var dbName = builder.Configuration["DbFileName"];
|
||||
if (string.IsNullOrEmpty(dbName))
|
||||
builder.Host.UseSerilog((context, configuration) =>
|
||||
{
|
||||
dbName = Constants.DEFAULT_DB_NAME;
|
||||
configuration.WriteTo.Console().ReadFrom.Configuration(context.Configuration);
|
||||
});
|
||||
|
||||
if (builder.Configuration.GetValue<bool>("ServerSettings:EnableMoreSongs"))
|
||||
{
|
||||
Log.Warning("Song limit expanded! Currently the game has issue loading crown/score rank and " +
|
||||
"probably more server related data for songs with id > 1599. " +
|
||||
"Also, the game can have random crashes because of that! Use at your own risk!");
|
||||
}
|
||||
var path = Path.Combine(PathHelper.GetRootPath(), dbName);
|
||||
option.UseSqlite($"Data Source={path}");
|
||||
});
|
||||
builder.Services.AddHttpLogging(options =>
|
||||
{
|
||||
options.LoggingFields = HttpLoggingFields.RequestProperties |
|
||||
HttpLoggingFields.ResponseStatusCode;
|
||||
});
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("DevCorsPolicy", policy =>
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddOptions();
|
||||
builder.Services.AddSingleton<IGameDataService, GameDataService>();
|
||||
builder.Services.Configure<ServerSettings>(builder.Configuration.GetSection(nameof(ServerSettings)));
|
||||
builder.Services.AddControllers().AddProtoBufNet();
|
||||
builder.Services.AddDbContext<TaikoDbContext>(option =>
|
||||
{
|
||||
policy
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
var dbName = builder.Configuration["DbFileName"];
|
||||
if (string.IsNullOrEmpty(dbName))
|
||||
{
|
||||
dbName = Constants.DEFAULT_DB_NAME;
|
||||
}
|
||||
|
||||
var path = Path.Combine(PathHelper.GetRootPath(), dbName);
|
||||
option.UseSqlite($"Data Source={path}");
|
||||
});
|
||||
});
|
||||
builder.Services.AddTaikoDbServices();
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAllCorsPolicy", policy =>
|
||||
{
|
||||
policy
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
builder.Services.AddTaikoDbServices();
|
||||
|
||||
var app = builder.Build();
|
||||
var app = builder.Build();
|
||||
|
||||
// Migrate db
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<TaikoDbContext>();
|
||||
db.Database.Migrate();
|
||||
// Migrate db
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<TaikoDbContext>();
|
||||
db.Database.Migrate();
|
||||
}
|
||||
|
||||
app.UseSerilogRequestLogging(options =>
|
||||
{
|
||||
options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms, " +
|
||||
"request host: {RequestHost}";
|
||||
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
|
||||
{
|
||||
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
|
||||
};
|
||||
});
|
||||
|
||||
var gameDataService = app.Services.GetService<IGameDataService>();
|
||||
gameDataService.ThrowIfNull();
|
||||
await gameDataService.InitializeAsync();
|
||||
|
||||
// For reverse proxy
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
|
||||
app.UseCors("AllowAllCorsPolicy");
|
||||
// For blazor hosting
|
||||
app.UseBlazorFrameworkFiles();
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
|
||||
|
||||
app.UseHttpLogging();
|
||||
app.MapControllers();
|
||||
app.MapFallbackToFile("index.html");
|
||||
|
||||
app.UseWhen(
|
||||
context => context.Request.Path.StartsWithSegments("/sys/servlet/PowerOn", StringComparison.InvariantCulture),
|
||||
applicationBuilder => applicationBuilder.UseAllNetRequestMiddleware());
|
||||
|
||||
app.Run();
|
||||
}
|
||||
|
||||
var gameDataService = app.Services.GetService<IGameDataService>();
|
||||
gameDataService.ThrowIfNull();
|
||||
await gameDataService.InitializeAsync();
|
||||
|
||||
// For reverse proxy
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
catch (Exception ex)
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
|
||||
app.UseCors("DevCorsPolicy");
|
||||
// For blazor hosting
|
||||
app.UseBlazorFrameworkFiles();
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
|
||||
|
||||
app.UseHttpLogging();
|
||||
app.MapControllers();
|
||||
app.MapFallbackToFile("index.html");
|
||||
|
||||
app.UseWhen(context => context.Request.Path.StartsWithSegments("/sys/servlet/PowerOn", StringComparison.InvariantCulture),
|
||||
applicationBuilder => applicationBuilder.UseAllNetRequestMiddleware());
|
||||
|
||||
app.Run();
|
||||
Log.Fatal(ex, "Unhandled exception");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.Information("Shut down complete");
|
||||
Log.CloseAndFlush();
|
||||
}
|
49
TaikoLocalServer/Services/AiDatumService.cs
Normal file
49
TaikoLocalServer/Services/AiDatumService.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
|
||||
public class AiDatumService : IAiDatumService
|
||||
{
|
||||
private readonly TaikoDbContext context;
|
||||
|
||||
public AiDatumService(TaikoDbContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public async Task<List<AiScoreDatum>> GetAllAiScoreById(uint baid)
|
||||
{
|
||||
return await context.AiScoreData.Where(datum => datum.Baid == baid)
|
||||
.Include(datum => datum.AiSectionScoreData)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AiScoreDatum?> GetSongAiScore(uint baid, uint songId, Difficulty difficulty)
|
||||
{
|
||||
return await context.AiScoreData.Where(datum => datum.Baid == baid &&
|
||||
datum.SongId == songId &&
|
||||
datum.Difficulty == difficulty)
|
||||
.Include(datum => datum.AiSectionScoreData)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateSongAiScore(AiScoreDatum datum)
|
||||
{
|
||||
var existing = await context.AiScoreData.FindAsync(datum.Baid, datum.SongId, datum.Difficulty);
|
||||
existing.ThrowIfNull("Cannot update a non-existing ai score!");
|
||||
|
||||
context.AiScoreData.Update(datum);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task InsertSongAiScore(AiScoreDatum datum)
|
||||
{
|
||||
var existing = await context.AiScoreData.FindAsync(datum.Baid, datum.SongId, datum.Difficulty);
|
||||
if (existing is not null)
|
||||
{
|
||||
throw new ArgumentException("Ai score already exists!", nameof(datum));
|
||||
}
|
||||
context.AiScoreData.Add(datum);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using SharedProject.Models;
|
||||
using Swan.Mapping;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
namespace TaikoLocalServer.Services;
|
||||
|
||||
public class DanScoreDatumService : IDanScoreDatumService
|
||||
{
|
||||
@ -30,7 +28,7 @@ public class DanScoreDatumService : IDanScoreDatumService
|
||||
var existing = await context.DanScoreData.FindAsync(datum.Baid, datum.DanId);
|
||||
if (existing is null)
|
||||
{
|
||||
await context.DanScoreData.AddAsync(datum);
|
||||
context.DanScoreData.Add(datum);
|
||||
await context.SaveChangesAsync();
|
||||
return;
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Services.Extentions;
|
||||
namespace TaikoLocalServer.Services.Extentions;
|
||||
|
||||
public static class ServiceExtensions
|
||||
{
|
||||
@ -11,6 +9,7 @@ public static class ServiceExtensions
|
||||
services.AddScoped<ISongPlayDatumService, SongPlayDatumService>();
|
||||
services.AddScoped<ISongBestDatumService, SongBestDatumService>();
|
||||
services.AddScoped<IDanScoreDatumService, DanScoreDatumService>();
|
||||
services.AddScoped<IAiDatumService, AiDatumService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Text.Json;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
using SharedProject.Models;
|
||||
using Swan.Mapping;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
@ -54,6 +54,10 @@ public class GameDataService : IGameDataService
|
||||
var danDataPath = Path.Combine(dataPath, Constants.DAN_DATA_FILE_NAME);
|
||||
var songIntroDataPath = Path.Combine(dataPath, Constants.INTRO_DATA_FILE_NAME);
|
||||
|
||||
if (!File.Exists(musicAttributePath))
|
||||
{
|
||||
TryDecompressMusicAttribute();
|
||||
}
|
||||
await using var musicAttributeFile = File.OpenRead(musicAttributePath);
|
||||
await using var danDataFile = File.OpenRead(danDataPath);
|
||||
await using var songIntroDataFile = File.OpenRead(songIntroDataPath);
|
||||
@ -69,6 +73,18 @@ public class GameDataService : IGameDataService
|
||||
InitializeIntroData(introData);
|
||||
}
|
||||
|
||||
private static void TryDecompressMusicAttribute()
|
||||
{
|
||||
var dataPath = PathHelper.GetDataPath();
|
||||
var musicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_FILE_NAME);
|
||||
var compressedMusicAttributePath = Path.Combine(dataPath, Constants.MUSIC_ATTRIBUTE_COMPRESSED_FILE_NAME);
|
||||
|
||||
using var compressed = File.Open(compressedMusicAttributePath, FileMode.Open);
|
||||
using var output = File.Create(musicAttributePath);
|
||||
|
||||
GZip.Decompress(compressed, output, true);
|
||||
}
|
||||
|
||||
private void InitializeIntroData(List<SongIntroductionData>? introData)
|
||||
{
|
||||
introData.ThrowIfNull("Shouldn't happen!");
|
||||
|
12
TaikoLocalServer/Services/Interfaces/IAiDatumService.cs
Normal file
12
TaikoLocalServer/Services/Interfaces/IAiDatumService.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
public interface IAiDatumService
|
||||
{
|
||||
public Task<List<AiScoreDatum>> GetAllAiScoreById(uint baid);
|
||||
|
||||
public Task<AiScoreDatum?> GetSongAiScore(uint baid, uint songId, Difficulty difficulty);
|
||||
|
||||
public Task UpdateSongAiScore(AiScoreDatum datum);
|
||||
|
||||
public Task InsertSongAiScore(AiScoreDatum datum);
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
using SharedProject.Models;
|
||||
using Swan.Mapping;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
@ -32,7 +31,7 @@ public class SongBestDatumService : ISongBestDatumService
|
||||
return;
|
||||
}
|
||||
|
||||
await context.SongBestData.AddAsync(datum);
|
||||
context.SongBestData.Add(datum);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@ -43,6 +42,10 @@ public class SongBestDatumService : ISongBestDatumService
|
||||
|
||||
var result = songbestDbData.Select(datum => datum.CopyPropertiesToNew<SongBestData>()).ToList();
|
||||
|
||||
var aiSectionBest = await context.AiScoreData.Where(datum => datum.Baid == baid)
|
||||
.Include(datum => datum.AiSectionScoreData)
|
||||
.ToListAsync();
|
||||
|
||||
var playLogs = await context.SongPlayData.Where(datum => datum.Baid == baid).ToListAsync();
|
||||
foreach (var bestData in result)
|
||||
{
|
||||
@ -65,6 +68,16 @@ public class SongBestDatumService : ISongBestDatumService
|
||||
nameof(SongPlayDatum.DrumrollCount),
|
||||
nameof(SongPlayDatum.ComboCount)
|
||||
);
|
||||
|
||||
var aiSection = aiSectionBest.FirstOrDefault(datum => datum.Difficulty == bestData.Difficulty &&
|
||||
datum.SongId == bestData.SongId);
|
||||
if (aiSection is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bestData.AiSectionBestData = aiSection.AiSectionScoreData
|
||||
.Select(datum => datum.CopyPropertiesToNew<AiSectionBestData>()).ToList();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -1,6 +1,4 @@
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
namespace TaikoLocalServer.Services;
|
||||
|
||||
class SongPlayDatumService : ISongPlayDatumService
|
||||
{
|
||||
@ -18,7 +16,7 @@ class SongPlayDatumService : ISongPlayDatumService
|
||||
|
||||
public async Task AddSongPlayDatum(SongPlayDatum datum)
|
||||
{
|
||||
await context.SongPlayData.AddAsync(datum);
|
||||
context.SongPlayData.Add(datum);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using System.Text.Json;
|
||||
using TaikoLocalServer.Services.Interfaces;
|
||||
using Throw;
|
||||
|
||||
namespace TaikoLocalServer.Services;
|
||||
|
@ -1,8 +1,10 @@
|
||||
namespace TaikoLocalServer.Settings;
|
||||
|
||||
public class UrlSettings
|
||||
public class ServerSettings
|
||||
{
|
||||
public string MuchaUrl { get; set; } = string.Empty;
|
||||
|
||||
public string GameUrl { get; set; } = string.Empty;
|
||||
|
||||
public bool EnableMoreSongs { get; set; }
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Version>0.3.0-alpha</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -11,16 +12,18 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0-preview.7.22376.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-preview.7.22376.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0-preview.7.22376.2">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0-rc.1.22426.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-rc.1.22426.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0-rc.1.22426.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="protobuf-net" Version="3.1.17" />
|
||||
<PackageReference Include="protobuf-net.AspNetCore" Version="3.1.17" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="6.1.0-dev-00281" />
|
||||
<PackageReference Include="Serilog.Expressions" Version="3.4.1-dev-00095" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.0" />
|
||||
<PackageReference Include="Swan.Core" Version="6.0.2-beta.90" />
|
||||
<PackageReference Include="Swan.Logging" Version="6.0.2-beta.69" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
@ -32,18 +35,24 @@
|
||||
<None Update="Certificates\cert.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Certificates\root.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\data\music_attribute.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\dan_data.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\intro_data.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\music_attribute.bin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\music_attribute.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Information"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,56 @@
|
||||
{
|
||||
"UrlSettings": {
|
||||
"ServerSettings": {
|
||||
"MuchaUrl": "https://v402-front.mucha-prd.nbgi-amnet.jp:10122",
|
||||
"GameUrl": "vsapi.taiko-p.jp"
|
||||
"GameUrl": "vsapi.taiko-p.jp",
|
||||
"EnableMoreSongs": false
|
||||
},
|
||||
|
||||
"DbFileName" : "taiko.db3",
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
"Serilog": {
|
||||
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
|
||||
}
|
||||
"Override": {
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"Filter": [
|
||||
{
|
||||
"Name": "ByExcluding",
|
||||
"Args": {
|
||||
"expression": "@mt = 'An unhandled exception has occurred while executing the request.'"
|
||||
}
|
||||
}
|
||||
],
|
||||
"WriteTo": [
|
||||
{
|
||||
"Name": "File",
|
||||
"Args": { "path": "./Logs/log-.txt", "rollingInterval": "Day" }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"AllowedHosts": "*",
|
||||
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
"Server": {
|
||||
"BaseServer": {
|
||||
"Url": "http://0.0.0.0:5000"
|
||||
},
|
||||
"AmAuthServer": {
|
||||
"Url": "http://0.0.0.0:80"
|
||||
},
|
||||
"MuchaServer": {
|
||||
"Url": "https://0.0.0.0:10122"
|
||||
},
|
||||
"GameServer1": {
|
||||
"Url": "https://0.0.0.0:54430"
|
||||
},
|
||||
"GameServer2": {
|
||||
"Url": "https://0.0.0.0:54431"
|
||||
}
|
||||
},
|
||||
"Certificates": {
|
||||
|
@ -68,9 +68,8 @@
|
||||
FullWidth="true"
|
||||
AnchorOrigin="Origin.BottomCenter"
|
||||
TransformOrigin="Origin.TopCenter">
|
||||
<MudMenuItem Href="@($"Cards/{user.Baid}/TaikoMode")">Taiko Mode</MudMenuItem>
|
||||
<MudMenuItem Href="@($"Cards/{user.Baid}/HighScores")">High Scores</MudMenuItem>
|
||||
<MudMenuItem Href="@($"Cards/{user.Baid}/DaniDojo")">Dani Dojo</MudMenuItem>
|
||||
@*<MudMenuItem Href="@($"Cards/{user.Baid}/AIBattle")">AI Battle</MudMenuItem>*@
|
||||
</MudMenu>
|
||||
</MudStack>
|
||||
</MudCardActions>
|
||||
|
@ -1,11 +1,11 @@
|
||||
@inject IGameDataService GameDataService
|
||||
@inject HttpClient Client
|
||||
|
||||
@page "/Cards/{baid:int}/TaikoMode"
|
||||
@page "/Cards/{baid:int}/HighScores"
|
||||
|
||||
<MudBreadcrumbs Items="breadcrumbs" Class="px-0"></MudBreadcrumbs>
|
||||
|
||||
<h1>Taiko Mode</h1>
|
||||
<h1>High Scores</h1>
|
||||
<MudText Typo="Typo.caption">Card: @Baid</MudText>
|
||||
|
||||
<MudGrid Class="my-8">
|
||||
@ -29,7 +29,7 @@
|
||||
Icon="@GetDifficultyIcon(difficulty)">
|
||||
@if (songBestDataMap.ContainsKey(difficulty))
|
||||
{
|
||||
<MudDataGrid Items="@songBestDataMap[difficulty]"
|
||||
<MudDataGrid Items="@songBestDataMap[difficulty]" RowClick="(DataGridRowClickEventArgs<SongBestData> _) => { return;}"
|
||||
ColumnResizeMode="ResizeMode.None" RowsPerPage="25" Elevation="0">
|
||||
<Columns>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.SongId)" Title="Song" StickyLeft="true">
|
||||
@ -68,7 +68,7 @@
|
||||
</MudChip>
|
||||
</CellTemplate>
|
||||
</Column>
|
||||
<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">
|
||||
<CellTemplate>
|
||||
<img src="@($"/images/crown_{context.Item.BestCrown}.png")" alt="@(GetCrownText(context.Item.BestCrown))" title="@(GetCrownText(context.Item.BestCrown))" style="@IconStyle" />
|
||||
@ -82,13 +82,73 @@
|
||||
}
|
||||
</CellTemplate>
|
||||
</Column>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.GoodCount)" Title="Good" Sortable="false"/>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.OkCount)" Title="OK" Sortable="false"/>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.MissCount)" Title="Bad" Sortable="false"/>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.GoodCount)" Title="Good" Sortable="false" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.OkCount)" Title="OK" Sortable="false" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.MissCount)" Title="Bad" Sortable="false" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.DrumrollCount)" Title="Drumroll" Sortable="false"/>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.ComboCount)" Title="MAX Combo" Sortable="false"/>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.LastPlayTime)" Title="Last Played"/>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.ComboCount)" Title="MAX Combo" Sortable="false" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.ShowAiData)" Title="AI Battle Data">
|
||||
<CellTemplate>
|
||||
<MudButton Variant="Variant.Outlined" Size="Size.Small"
|
||||
OnClick="@(() => ToggleShowAiData(context.Item))"
|
||||
Disabled="@(!IsAiDataPresent(context.Item))">
|
||||
@(context.Item.ShowAiData ? "Hide" : "Show")
|
||||
</MudButton>
|
||||
</CellTemplate>
|
||||
</Column>
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.LastPlayTime)" Title="Last Played" Hideable="true" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.PlayCount)" Title="Total Plays" Hideable="true" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.ClearCount)" Title="Total Clears" Hideable="true" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.FullComboCount)" Title="Total Full Combos" Hideable="true" />
|
||||
<Column T="SongBestData" Field="@nameof(SongBestData.PerfectCount)" Title="Total Donderful Combos" Hideable="true" />
|
||||
</Columns>
|
||||
<ChildRowContent>
|
||||
@if (context.Item.ShowAiData)
|
||||
{
|
||||
<tr>
|
||||
<td colspan="1" class="pa-3 ai-battle-td">
|
||||
<MudText Typo="Typo.body2" Style="font-weight: bold">
|
||||
AI Battle Data
|
||||
</MudText>
|
||||
</td>
|
||||
<td colspan="16">
|
||||
<MudTable Elevation="0" ReadOnly="true"
|
||||
Items="@context.Item.AiSectionBestData" Context="aiSectionContext">
|
||||
<HeaderContent>
|
||||
<MudTh>Section No.</MudTh>
|
||||
<MudTh>Result</MudTh>
|
||||
<MudTh>Score</MudTh>
|
||||
<MudTh>Crown</MudTh>
|
||||
<MudTh>Good</MudTh>
|
||||
<MudTh>OK</MudTh>
|
||||
<MudTh>Bad</MudTh>
|
||||
<MudTh>Drumroll</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
<MudTd>@(aiSectionContext.SectionIndex + 1)</MudTd>
|
||||
<MudTd>
|
||||
@if (@aiSectionContext.IsWin) {
|
||||
<img src="@($"/images/ai_Win.png")" alt="Win" title="Win" style="@IconStyle" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<img src="@($"/images/ai_Lose.png")" alt="Lose" title="Lose" style="@IconStyle" />
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd>@aiSectionContext.Score</MudTd>
|
||||
<MudTd>
|
||||
<img src="@($"/images/ai_crown_{aiSectionContext.Crown}.png")" alt="@(GetCrownText(aiSectionContext.Crown))" title="@(GetCrownText(aiSectionContext.Crown))" style="@IconStyle" />
|
||||
</MudTd>
|
||||
<MudTd>@aiSectionContext.GoodCount</MudTd>
|
||||
<MudTd>@aiSectionContext.OkCount</MudTd>
|
||||
<MudTd>@aiSectionContext.MissCount</MudTd>
|
||||
<MudTd>@aiSectionContext.DrumrollCount</MudTd>
|
||||
</RowTemplate>
|
||||
</MudTable>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</ChildRowContent>
|
||||
<PagerContent>
|
||||
<MudDataGridPager T="SongBestData"/>
|
||||
</PagerContent>
|
@ -1,6 +1,9 @@
|
||||
namespace TaikoWebUI.Pages;
|
||||
using static MudBlazor.Colors;
|
||||
using System;
|
||||
|
||||
public partial class TaikoMode
|
||||
namespace TaikoWebUI.Pages;
|
||||
|
||||
public partial class HighScores
|
||||
{
|
||||
[Parameter]
|
||||
public int Baid { get; set; }
|
||||
@ -41,7 +44,7 @@ public partial class TaikoMode
|
||||
|
||||
|
||||
breadcrumbs.Add(new BreadcrumbItem($"Card: {Baid}", href: null, disabled: true));
|
||||
breadcrumbs.Add(new BreadcrumbItem("Taiko Mode", href: $"/Cards/{Baid}/TaikoMode", disabled: false));
|
||||
breadcrumbs.Add(new BreadcrumbItem("High Scores", href: $"/Cards/{Baid}/HighScores", disabled: false));
|
||||
}
|
||||
|
||||
private async Task OnFavoriteToggled(SongBestData data)
|
||||
@ -133,7 +136,19 @@ public partial class TaikoMode
|
||||
SongGenre.Variety => "background: #1dc83b; color: #fff",
|
||||
SongGenre.Classical => "background: #bfa356",
|
||||
_ => ""
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private static void ToggleShowAiData(SongBestData data)
|
||||
{
|
||||
data.ShowAiData = !data.ShowAiData;
|
||||
}
|
||||
|
||||
private static bool IsAiDataPresent(SongBestData data)
|
||||
{
|
||||
var aiData = data.AiSectionBestData;
|
||||
|
||||
return aiData.Count > 0;
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ builder.RootComponents.Add<HeadOutlet>("head::after");
|
||||
|
||||
builder.Services.AddSingleton(sp => new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(builder.Configuration.GetValue<string>("BaseUrl"))
|
||||
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
|
||||
});
|
||||
builder.Services.AddMudServices();
|
||||
builder.Services.AddSingleton<IGameDataService, GameDataService>();
|
||||
@ -15,10 +15,6 @@ builder.Services.AddSingleton<IGameDataService, GameDataService>();
|
||||
var host = builder.Build();
|
||||
|
||||
var gameDataService = host.Services.GetRequiredService<IGameDataService>();
|
||||
#if DEBUG
|
||||
await gameDataService.InitializeAsync(builder.Configuration.GetValue<string>("DataBaseUrl"));
|
||||
#else
|
||||
await gameDataService.InitializeAsync(builder.Configuration.GetValue<string>("BaseUrl"));
|
||||
#endif
|
||||
await gameDataService.InitializeAsync(builder.HostEnvironment.BaseAddress);
|
||||
|
||||
await host.RunAsync();
|
@ -1,4 +1,7 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using ICSharpCode.SharpZipLib.GZip;
|
||||
using Swan.Mapping;
|
||||
using TaikoWebUI.Shared.Models;
|
||||
|
||||
@ -27,16 +30,13 @@ public class GameDataService : IGameDataService
|
||||
|
||||
public async Task InitializeAsync(string dataBaseUrl)
|
||||
{
|
||||
var musicInfo = await client.GetFromJsonAsync<MusicInfo>($"{dataBaseUrl}/data/musicinfo.json");
|
||||
var wordList = await client.GetFromJsonAsync<WordList>($"{dataBaseUrl}/data/wordlist.json");
|
||||
var musicOrder = await client.GetFromJsonAsync<MusicOrder>($"{dataBaseUrl}/data/music_order.json");
|
||||
dataBaseUrl = dataBaseUrl.TrimEnd('/');
|
||||
var musicInfo = await GetData<MusicInfo>(dataBaseUrl, Constants.MUSIC_INFO_BASE_NAME);
|
||||
var wordList = await GetData<WordList>(dataBaseUrl, Constants.WORD_LIST_BASE_NAME);
|
||||
var musicOrder = await GetData<MusicOrder>(dataBaseUrl, Constants.MUSIC_ORDER_BASE_NAME);
|
||||
var danData = await client.GetFromJsonAsync<List<DanData>>($"{dataBaseUrl}/data/dan_data.json");
|
||||
|
||||
musicInfo.ThrowIfNull();
|
||||
wordList.ThrowIfNull();
|
||||
musicOrder.ThrowIfNull();
|
||||
danData.ThrowIfNull();
|
||||
|
||||
danMap = danData.ToImmutableDictionary(data => data.DanId);
|
||||
|
||||
// To prevent duplicate entries in wordlist
|
||||
@ -52,6 +52,36 @@ public class GameDataService : IGameDataService
|
||||
await Task.Run(() => InitializeTitles(dict));
|
||||
}
|
||||
|
||||
private async Task<T> GetData<T>(string dataBaseUrl, string fileBaseName) where T : notnull
|
||||
{
|
||||
T? data;
|
||||
try
|
||||
{
|
||||
data = await client.GetFromJsonAsync<T>($"{dataBaseUrl}/data/{fileBaseName}.json");
|
||||
data.ThrowIfNull();
|
||||
return data;
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
if (e.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
await using var compressed = await client.GetStreamAsync($"{dataBaseUrl}/data/{fileBaseName}.bin");
|
||||
await using var gZipInputStream = new GZipInputStream(compressed);
|
||||
using var decompressed = new MemoryStream();
|
||||
|
||||
// Decompress
|
||||
await gZipInputStream.CopyToAsync(decompressed);
|
||||
|
||||
// Reset stream for reading
|
||||
decompressed.Position = 0;
|
||||
data = await JsonSerializer.DeserializeAsync<T>(decompressed);
|
||||
data.ThrowIfNull();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
public string GetMusicNameBySongId(uint songId)
|
||||
{
|
||||
return musicMap.TryGetValue(songId, out var musicDetail) ? musicDetail.SongName : string.Empty;
|
||||
|
@ -9,4 +9,8 @@ public static class Constants
|
||||
public const int COSTUME_PUCHI_MAX = 129;
|
||||
public const int COSTUME_COLOR_MAX = 63;
|
||||
public const int PLAYER_TITLE_MAX = 750;
|
||||
|
||||
public const string MUSIC_INFO_BASE_NAME = "musicinfo";
|
||||
public const string WORD_LIST_BASE_NAME = "wordlist";
|
||||
public const string MUSIC_ORDER_BASE_NAME = "music_order";
|
||||
}
|
@ -8,9 +8,10 @@
|
||||
|
||||
<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.DevServer" Version="6.0.7" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.9" PrivateAssets="all" />
|
||||
<PackageReference Include="MudBlazor" Version="6.0.15" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.0" />
|
||||
<PackageReference Include="Swan.Core" Version="6.0.2-beta.90" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -19,7 +20,19 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="wwwroot\appsettings.json">
|
||||
<Content Update="wwwroot\data\musicinfo.bin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\music_order.bin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\wordlist.bin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\music_attribute.bin">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\music_order.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\musicinfo.json">
|
||||
@ -28,9 +41,6 @@
|
||||
<Content Update="wwwroot\data\wordlist.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\data\music_order.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"DataBaseUrl": "http://localhost:5000"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"BaseUrl": "http://localhost:5000"
|
||||
}
|
BIN
TaikoWebUI/wwwroot/images/ai_Lose.png
Normal file
BIN
TaikoWebUI/wwwroot/images/ai_Lose.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
TaikoWebUI/wwwroot/images/ai_Win.png
Normal file
BIN
TaikoWebUI/wwwroot/images/ai_Win.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
TaikoWebUI/wwwroot/images/ai_crown_Clear.png
Normal file
BIN
TaikoWebUI/wwwroot/images/ai_crown_Clear.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
TaikoWebUI/wwwroot/images/ai_crown_Dondaful.png
Normal file
BIN
TaikoWebUI/wwwroot/images/ai_crown_Dondaful.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
TaikoWebUI/wwwroot/images/ai_crown_Gold.png
Normal file
BIN
TaikoWebUI/wwwroot/images/ai_crown_Gold.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
BIN
TaikoWebUI/wwwroot/images/ai_crown_None.png
Normal file
BIN
TaikoWebUI/wwwroot/images/ai_crown_None.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
@ -30,3 +30,22 @@
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.columns-panel {
|
||||
column-count: 2;
|
||||
}
|
||||
|
||||
.ai-battle-td {
|
||||
position: sticky;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 99;
|
||||
background: #FAFAFA;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 600px) {
|
||||
.ai-battle-td {
|
||||
display: revert;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user