1
0
mirror of synced 2024-11-30 17:04:28 +01:00

Remove old version

This commit is contained in:
asesidaa 2023-03-03 12:18:46 +08:00
parent 32a1886cf4
commit 0fd5fc7227
112 changed files with 0 additions and 17732 deletions

View File

@ -1,439 +0,0 @@
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### VisualStudio template
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# Ignore card db since we should start from scratch
/db/card.db3

View File

@ -1,74 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>GCLocalServerRewrite</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CertificateManager" Version="1.0.8" />
<PackageReference Include="ChoETL" Version="1.2.1.48" />
<PackageReference Include="ConcurrentHashSet" Version="1.3.0" />
<PackageReference Include="Config.Net" Version="5.1.3" />
<PackageReference Include="Config.Net.Json" Version="4.19.0" />
<PackageReference Include="EmbedIO" Version="3.5.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0-preview.5.22301.12" />
<PackageReference Include="sqlite-net2" Version="2.1.0-preB" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" />
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.2" />
<PackageReference Include="Unosquare.Swan.Lite" Version="3.1.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="log" />
<Folder Include="event\" />
<None Remove="db\music.db3" />
<None Remove="db\music4MAX465.db3" />
<Content Include="db\music.db3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<None Remove="static\favicon.ico" />
<Content Include="db\music4MAX465.db3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Include="static\favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<None Remove="static\index.html" />
<Content Include="static\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<None Remove="static\news.png" />
<Content Include="static\news.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<None Remove="GC-local-server-rewrite.exe.config.xml" />
<None Remove="db\music4MAX.db3" />
<Content Include="db\music4MAX.db3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<None Update="config.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>
<None Update="db\music471.db3">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedProject\SharedProject.csproj" />
</ItemGroup>
</Project>

View File

@ -1,107 +0,0 @@
using GCLocalServerRewrite.common;
using GCLocalServerRewrite.server;
using SQLitePCL;
using Swan;
using Swan.Logging;
namespace GCLocalServerRewrite;
internal class Program
{
private static void Main(string[] args)
{
Batteries_V2.Init();
InitializeLogging();
LogConfigValues();
var urlPrefixes =
args.Length > 0 ? new List<string>(args) : new List<string> { "http://+:80", "https://+:443" };
using (var cts = new CancellationTokenSource())
{
Task.WaitAll(
RunWebServerAsync(urlPrefixes, cts.Token),
Task.CompletedTask,
WaitForUserBreakAsync(cts.Cancel));
}
// Clean up
"Bye".Info(nameof(Program));
Terminal.Flush();
Console.WriteLine("Press any key to exit.");
Console.ReadKey(true);
}
private static void InitializeLogging()
{
if (!Directory.Exists(PathHelper.LogRootPath))
{
Directory.CreateDirectory(PathHelper.LogRootPath);
}
Logger.RegisterLogger(new FileLogger(Path.Combine(PathHelper.LogRootPath, Configs.LOG_BASE_NAME), true));
}
/// <summary>
/// Create and run a web server.
/// </summary>
/// <param name="urlPrefixes"></param>
/// <param name="cancellationToken"></param>
private static async Task RunWebServerAsync(IEnumerable<string> urlPrefixes, CancellationToken cancellationToken)
{
using var server = Server.CreateWebServer(urlPrefixes);
await server.RunAsync(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Prompt the user to press any key;
/// when a key is next pressed, call the specified action to cancel operations.
/// </summary>
/// <param name="cancel"> Cancel Action to call </param>
private static async Task WaitForUserBreakAsync(Action cancel)
{
// Be sure to run in parallel.
await Task.Yield();
"Press x to stop the web server.".Info(nameof(Program));
WaitForKeypress();
"Stopping...".Info(nameof(Program));
cancel();
}
/// <summary>
/// Clear the console input buffer and wait for a keypress
/// </summary>
private static void WaitForKeypress()
{
ConsoleKeyInfo consoleKeyInfo;
do
{
while (Console.KeyAvailable == false)
{
Thread.Sleep(250);
}
consoleKeyInfo = Console.ReadKey(true);
} while (consoleKeyInfo.Key != ConsoleKey.X);
}
private static void LogConfigValues()
{
var paths = $"Paths: {nameof(PathHelper.HtmlRootPath)}: {PathHelper.HtmlRootPath}\n" +
$"{nameof(PathHelper.LogRootPath)}: {PathHelper.LogRootPath}\n" +
$"{nameof(PathHelper.DataBaseRootPath)}: {PathHelper.DataBaseRootPath}\n" +
$"{nameof(PathHelper.ConfigFilePath)}: {PathHelper.ConfigFilePath}\n" +
$"{nameof(PathHelper.CertRootPath)}: {PathHelper.CertRootPath}\n";
paths.Info();
var configs = "Config values: \n" +
$"{Configs.SETTINGS.Stringify()}";
configs.Info();
}
}

View File

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC 清单选项
如果想要更改 Windows 用户帐户控制级别,请使用
以下节点之一替换 requestedExecutionLevel 节点。
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
指定 requestedExecutionLevel 元素将禁用文件和注册表虚拟化。
如果你的应用程序需要此虚拟化来实现向后兼容性,则移除此
元素。
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- 设计此应用程序与其一起工作且已针对此应用程序进行测试的
Windows 版本的列表。取消评论适当的元素,
Windows 将自动选择最兼容的环境。 -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<!-- 指示该应用程序可感知 DPI 且 Windows 在 DPI 较高时将不会对其进行
自动缩放。Windows Presentation Foundation (WPF)应用程序自动感知 DPI无需
选择加入。选择加入此设置的 Windows 窗体应用程序(面向 .NET Framework 4.6)还应
在其 app.config 中将 "EnableWindowsFormsHighDpiAutoResizing" 设置设置为 "true"。
将应用程序设为感知长路径。请参阅 https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
-->
<!-- 启用 Windows 公共控件和对话框的主题(Windows XP 和更高版本) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View File

@ -1,238 +0,0 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using CertificateManager;
using CertificateManager.Models;
using Microsoft.Extensions.DependencyInjection;
using Swan;
using Swan.Logging;
namespace GCLocalServerRewrite.common;
public static class CertificateHelper
{
private const X509KeyUsageFlags ROOT_CA_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.KeyCertSign |
X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.KeyEncipherment |
X509KeyUsageFlags.DigitalSignature;
private const X509KeyStorageFlags X509_KEY_STORAGE_FLAGS_MACHINE = X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.Exportable;
private const X509KeyUsageFlags CERT_X509_KEY_USAGE_FLAGS = X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.KeyEncipherment |
X509KeyUsageFlags.DigitalSignature;
private static readonly DistinguishedName ROOT_CA_DISTINGUISHED_NAME = new()
{
CommonName = Configs.ROOT_CA_CN
};
private static readonly DistinguishedName CERT_DISTINGUISHED_NAME = new()
{
CommonName = Configs.CERT_CN
};
private static readonly BasicConstraints ROOT_CA_BASIC_CONSTRAINTS = new()
{
CertificateAuthority = true,
HasPathLengthConstraint = true,
PathLengthConstraint = 3,
Critical = true
};
public static readonly BasicConstraints CERT_BASIC_CONSTRAINTS = new()
{
CertificateAuthority = false,
HasPathLengthConstraint = false,
PathLengthConstraint = 0,
Critical = true,
};
private static readonly SubjectAlternativeName SUBJECT_ALTERNATIVE_NAME = new()
{
DnsName = Configs.DOMAINS,
IpAddress = System.Net.IPAddress.Parse(Configs.SETTINGS.ServerIp)
};
private static readonly ValidityPeriod VALIDITY_PERIOD = new()
{
ValidFrom = DateTime.UtcNow,
ValidTo = DateTime.UtcNow.AddYears(3)
};
private static readonly OidCollection OID_COLLECTION = new()
{
OidLookup.AnyPurpose
};
public static X509Certificate2 InitializeCertificate()
{
if (CertificateExists())
{
var existingCert = GetCertificate(StoreName.My, StoreLocation.LocalMachine, Configs.CERT_CN);
if (existingCert != null)
{
return existingCert;
}
"Existing CN not found! Removing old certificates and genrate new ones...".Info();
}
RemovePreviousCert(StoreName.My, StoreLocation.LocalMachine);
RemovePreviousCert(StoreName.Root, StoreLocation.LocalMachine);
var serviceProvider = new ServiceCollection()
.AddCertificateManager().BuildServiceProvider();
var createCertificates = serviceProvider.GetService<CreateCertificates>();
if (createCertificates == null)
{
throw SelfCheck.Failure("Cannot initialize CreateCertificates service!");
}
var rootCa = createCertificates.NewRsaSelfSignedCertificate(
ROOT_CA_DISTINGUISHED_NAME,
ROOT_CA_BASIC_CONSTRAINTS,
VALIDITY_PERIOD,
SUBJECT_ALTERNATIVE_NAME,
OID_COLLECTION,
ROOT_CA_X509_KEY_USAGE_FLAGS,
new RsaConfiguration()
);
var cert = createCertificates.NewRsaChainedCertificate(
CERT_DISTINGUISHED_NAME,
CERT_BASIC_CONSTRAINTS,
VALIDITY_PERIOD,
SUBJECT_ALTERNATIVE_NAME,
rootCa,
OID_COLLECTION,
CERT_X509_KEY_USAGE_FLAGS,
new RsaConfiguration()
);
var exportService = serviceProvider.GetService<ImportExportCertificate>();
if (exportService == null)
{
throw SelfCheck.Failure("Cannot initialize ImportExportCertificate service!");
}
var rootCaPfxBytes = exportService.ExportRootPfx(null, rootCa);
var certPfxBytes = exportService.ExportChainedCertificatePfx(null, cert, rootCa);
var rootCaWithPrivateKey = new X509Certificate2(rootCaPfxBytes, (string)null!,
X509_KEY_STORAGE_FLAGS_MACHINE);
var certWithPrivateKey = new X509Certificate2(certPfxBytes, (string)null!,
X509_KEY_STORAGE_FLAGS_MACHINE);
AddCertToStore(rootCaWithPrivateKey, StoreName.My, StoreLocation.LocalMachine);
AddCertToStore(rootCaWithPrivateKey, StoreName.Root, StoreLocation.LocalMachine);
AddCertToStore(certWithPrivateKey, StoreName.My, StoreLocation.LocalMachine);
Directory.CreateDirectory(PathHelper.CertRootPath);
File.WriteAllBytes(Path.Combine(PathHelper.CertRootPath, "root.pfx"), rootCaWithPrivateKey.Export(X509ContentType.Pfx));
File.WriteAllBytes(Path.Combine(PathHelper.CertRootPath, "cert.pfx"), certWithPrivateKey.Export(X509ContentType.Pfx));
return certWithPrivateKey;
}
private static void AddCertToStore(X509Certificate2 cert, StoreName storeName, StoreLocation storeLocation)
{
try
{
var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadWrite);
store.Add(cert);
store.Close();
}
catch (Exception e)
{
e.Error(e.Source ?? "", e.Message);
}
}
private static void RemovePreviousCert(StoreName storeName, StoreLocation storeLocation)
{
try
{
var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadWrite);
var result = store.Certificates.Find(X509FindType.FindByIssuerName, Configs.ROOT_CA_CN, true);
if (result.Any())
{
store.RemoveRange(result);
"Removed previous certs!".Info();
}
store.Close();
}
catch (Exception e)
{
e.Error(e.Source ?? "", e.Message);
}
}
private static bool CertificateExists()
{
try
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var result = store.Certificates.Find(X509FindType.FindByIssuerName, Configs.ROOT_CA_CN, true);
if (result.Count == 2)
{
"Certificate exists!".Info();
return true;
}
store.Close();
"Certificate not found! Will generate new certs...".Info();
return false;
}
catch (Exception e)
{
e.Error(e.Source ?? "", e.Message);
return false;
}
}
private static X509Certificate2? GetCertificate(StoreName storeName, StoreLocation storeLocation, string commonName)
{
try
{
var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadWrite);
var result = store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName,
$"CN={commonName}", true);
if (result.Any())
{
$"Certificate CN={commonName} found!".Info();
return result.First();
}
store.Close();
return null;
}
catch (Exception e)
{
e.Error(e.Source ?? "", e.Message);
return null;
}
}
}

View File

@ -1,184 +0,0 @@
using Config.Net;
namespace GCLocalServerRewrite.common;
public static class Configs
{
public const bool USE_FILE_CACHE = true;
public const string ROOT_CA_CN = "Taito Arcade Machine CA";
public const string CERT_CN = "GC local server";
public const string DB_FOLDER = "db";
public const string LOG_FOLDER = "log";
public const string CERT_FOLDER = "certs";
public const string LOG_BASE_NAME = "log";
public const string CONFIG_FILE_NAME = "config.json";
public const string STATIC_FOLDER = "static";
public const string WWWROOT = "wwwroot";
public const string API_BASE_ROUTE = "/api";
public const string OPTION_SERVICE_BASE_ROUTE = "/service/option";
public const string CARD_SERVICE_BASE_ROUTE = "/service/card";
public const string UPLOAD_SERVICE_BASE_ROUTE = "/service/upload";
public const string RESPONE_SERVICE_BASE_ROUTE = "/service/respone";
public const string INCOM_SERVICE_BASE_ROUTE = "/service/incom";
public const string UPDATE_SERVICE_BASE_ROUTE = "/update/cgi";
public const string RANK_BASE_ROUTE = "/ranking";
public const string ALIVE_BASE_ROUTE = "/alive";
public const string SERVER_BASE_ROUTE = "/server";
public const string STATIC_BASE_ROUTE = "/static";
public const string ROOT_XPATH = "/root";
public const string DATA_XPATH = $"{ROOT_XPATH}/data";
public const string CARD = "card";
public const string CARD_XPATH = $"{ROOT_XPATH}/{CARD}";
public const string CARD_DETAIL = "card_detail";
public const string CARD_DETAIL_SINGLE_XPATH = $"{ROOT_XPATH}/{CARD_DETAIL}";
public const string CARD_DETAIL_RECORD_XPATH = $"{CARD_DETAIL_SINGLE_XPATH}/record";
public const string CARD_BDATA = "card_bdata";
public const string CARD_BDATA_XPATH = $"{ROOT_XPATH}/{CARD_BDATA}";
public const string MUSIC = "music";
public const string MUSIC_XPATH = $"{ROOT_XPATH}/{MUSIC}/record";
public const string MUSIC_EXTRA = "music_extra";
public const string MUSIC_EXTRA_XPATH = $"{ROOT_XPATH}/{MUSIC_EXTRA}/record";
public const string MUSIC_AOU = "music_aou";
public const string MUSIC_AOU_XPATH = $"{ROOT_XPATH}/{MUSIC_AOU}/record";
public const string ITEM = "item";
public const string ITEM_XPATH = $"{ROOT_XPATH}/{ITEM}/record";
public const string AVATAR = "avatar";
public const string AVATAR_XPATH = $"{ROOT_XPATH}/{AVATAR}/record";
public const string SKIN = "skin";
public const string SKIN_XPATH = $"{ROOT_XPATH}/{SKIN}/record";
public const string TITLE = "title";
public const string TITLE_XPATH = $"{ROOT_XPATH}/{TITLE}/record";
public const string NAVIGATOR = "navigator";
public const string NAVIGATOR_XPATH = $"{ROOT_XPATH}/{NAVIGATOR}/record";
public const string COIN = "coin";
public const string COIN_XPATH = $"{ROOT_XPATH}/{COIN}";
public const string UNLOCK_REWARD = "unlock_reward";
public const string UNLOCK_REWARD_XPATH = $"{ROOT_XPATH}/{UNLOCK_REWARD}/record";
public const string UNLOCK_KEYNUM = "unlock_keynum";
public const string UNLOCK_KEYNUM_XPATH = $"{ROOT_XPATH}/{UNLOCK_KEYNUM}/record";
public const string SOUND_EFFECT = "sound_effect";
public const string SE_XPATH = $"{ROOT_XPATH}/{SOUND_EFFECT}/record";
public const string GET_MESSAGE = "get_message";
public const string TOTAL_TROPHY = "total_trophy";
public const string TOTAL_TROPHY_XPATH = $"{ROOT_XPATH}/{TOTAL_TROPHY}";
public const string EVENT_REWARD = "event_reward";
public const string COND = "cond";
public const string SESSION_XPATH = $"{ROOT_XPATH}/session";
public const string RANK_STATUS_XPATH = $"{ROOT_XPATH}/ranking_status";
public const string ONLINE_MATCHING_XPATH = $"{ROOT_XPATH}/online_matching/record";
public const string ONLINE_BATTLE_RESULT_XPATH = $"{ROOT_XPATH}/online_battle_result";
public const int FIRST_CONFIG_PCOL1 = 0;
public const int SECOND_CONFIG_PCOL1 = 1;
public const int CONFIG_PCOL2 = 0;
public const int CONFIG_PCOL3 = 0;
public const int FAVORITE_PCOL1 = 10;
public const int COUNT_PCOL1 = 20;
public const int SCORE_PCOL1 = 21;
public const string MATCHING_URL_BASE = "Matching";
public const string START_MATCHING_URL = "Start";
public const string UPDATE_MATCHING_URL = "Update";
public const string FINISH_MATCHING_URL = "Finish";
public static readonly List<string> DOMAINS = new()
{
"localhost",
"cert.nesys.jp",
"nesys.taito.co.jp",
"fjm170920zero.nesica.net"
};
public static readonly IAppSettings SETTINGS =
new ConfigurationBuilder<IAppSettings>().UseJsonConfig(PathHelper.ConfigFilePath).Build();
public const int DEFAULT_AVATAR_COUNT = 323;
public const int DEFAULT_NAVIGATOR_COUNT = 94;
public const int DEFAULT_ITEM_COUNT = 21;
public const int DEFAULT_TITLE_COUNT = 5273;
public const int DEFAULT_SKIN_COUNT = 21;
public const int DEFAULT_SE_COUNT = 26;
public const string DEFAULT_CARD_DB_NAME = "card.db3";
public const string DEFAULT_MUSIC_DB_NAME = "music4MAX465.db3";
public const string DEFAULT_SERVER_IP = "127.0.0.1";
public const string DEFAULT_RELAY_SERVER = "127.0.0.1";
public const int DEFAULT_RELAY_PORT = 54321;
public const string DEFAULT_EVENT_FOLDER = "event";
public const string DEFAULT_MATCHING_SERVER = "127.0.0.1:5000";
public static readonly IReadOnlyList<int> DEFAULT_UNLOCKABLE_SONGS = new[]
{
11, 13, 149, 273, 291, 320, 321, 371, 378, 384, 464, 471, 474, 475, 492, 494, 498, 520,
548, 551, 558, 561, 565, 570, 577, 583, 612, 615, 622, 632, 659, 666, 668, 670, 672, 676,
680, 682, 685, 686, 697, 700, 701, 711, 720, 749, 875, 876, 877
};
}

View File

@ -1,61 +0,0 @@
using GCLocalServerRewrite.models;
using SharedProject.models;
namespace GCLocalServerRewrite.common;
public static class Converters
{
public static OnlineMatchingData ConvertFromEntry(OnlineMatchingEntry entry)
{
return new OnlineMatchingData
{
AvatarId = entry.AvatarId,
CardId = entry.CardId,
ClassId = entry.ClassId,
EntryNo = entry.EntryNo,
EntryStart = entry.EntryStart,
EventId = entry.EventId,
GroupId = entry.GroupId,
MachineId = entry.MachineId,
MatchingId = entry.MatchingId,
MatchingRemainingTime = entry.MatchingRemainingTime,
Pref = entry.Pref,
Status = entry.Status,
MatchingTimeout = entry.MatchingTimeout,
MessageId = entry.MessageId,
PlayerName = entry.PlayerName,
PrefId = entry.PrefId,
TenpoId = entry.TenpoId,
TenpoName = entry.TenpoName,
TitleId = entry.TitleId,
MatchingWaitTime = entry.MatchingWaitTime
};
}
public static OnlineMatchingEntry ConvertFromData(OnlineMatchingData entry)
{
return new OnlineMatchingEntry
{
AvatarId = entry.AvatarId,
CardId = entry.CardId,
ClassId = entry.ClassId,
EntryNo = entry.EntryNo,
EntryStart = entry.EntryStart,
EventId = entry.EventId,
GroupId = entry.GroupId,
MachineId = entry.MachineId,
MatchingId = entry.MatchingId,
MatchingRemainingTime = entry.MatchingRemainingTime,
Pref = entry.Pref,
Status = entry.Status,
MatchingTimeout = entry.MatchingTimeout,
MessageId = entry.MessageId,
PlayerName = entry.PlayerName,
PrefId = entry.PrefId,
TenpoId = entry.TenpoId,
TenpoName = entry.TenpoName,
TitleId = entry.TitleId,
MatchingWaitTime = entry.MatchingWaitTime
};
}
}

View File

@ -1,40 +0,0 @@
using SQLite.Net2;
namespace GCLocalServerRewrite.common;
public static class DatabaseHelper
{
/// <summary>
/// Static method to allow local data services to initialise their associated database conveniently.
/// </summary>
/// <param name="databaseName">The SQLite database name</param>
/// <param name="tables">The SQLite database tables to create (if required)</param>
/// <returns>An initialised SQLite database connection</returns>
public static SQLiteConnection InitializeLocalDatabase(string databaseName, params Type[] tables)
{
if (!Directory.Exists(PathHelper.DataBaseRootPath))
{
Directory.CreateDirectory(PathHelper.DataBaseRootPath);
}
var databasePath = Path.Combine(PathHelper.DataBaseRootPath, databaseName);
var database = new SQLiteConnection(databasePath);
foreach (var table in tables)
{
database.CreateTable(table);
}
return database;
}
public static SQLiteConnection ConnectDatabase(string databaseName)
{
var databasePath = Path.Combine(PathHelper.DataBaseRootPath, databaseName);
var database = new SQLiteConnection(databasePath);
return database;
}
}

View File

@ -1,53 +0,0 @@
using Config.Net;
namespace GCLocalServerRewrite.common;
public interface IAppSettings
{
[Option(DefaultValue = Configs.DEFAULT_AVATAR_COUNT)]
int AvatarCount { get; }
[Option(DefaultValue = Configs.DEFAULT_NAVIGATOR_COUNT)]
int NavigatorCount { get; }
[Option(DefaultValue = Configs.DEFAULT_ITEM_COUNT)]
int ItemCount { get; }
[Option(DefaultValue = Configs.DEFAULT_TITLE_COUNT)]
int TitleCount { get; }
[Option(DefaultValue = Configs.DEFAULT_SKIN_COUNT)]
int SkinCount { get; }
[Option(DefaultValue = Configs.DEFAULT_SE_COUNT)]
int SeCount { get; }
[Option(DefaultValue = Configs.DEFAULT_MUSIC_DB_NAME)]
string MusicDbName { get; }
[Option(DefaultValue = Configs.DEFAULT_CARD_DB_NAME)]
string CardDbName { get; }
[Option(DefaultValue = Configs.DEFAULT_SERVER_IP)]
string ServerIp { get; }
[Option(DefaultValue = Configs.DEFAULT_EVENT_FOLDER)]
string EventFolder { get; }
[Option(DefaultValue = Configs.DEFAULT_RELAY_SERVER)]
string RelayServer { get; }
[Option(DefaultValue = Configs.DEFAULT_RELAY_PORT)]
int RelayPort { get; }
[Option(DefaultValue = false)]
bool DownloadEvents { get; }
[Option(DefaultValue = Configs.DEFAULT_MATCHING_SERVER)]
string MatchingServer { get; }
[Option(DefaultValue = null)]
IEnumerable<int>? UnlockableSongIds { get; }
IEnumerable<IOptionServiceResponse> ResponseData { get; }
}

View File

@ -1,14 +0,0 @@
namespace GCLocalServerRewrite.common;
public interface IOptionServiceResponse
{
string FileName { get; }
long NotBeforeUnixTime { get; }
long NotAfterUnixTime { get; }
string Md5 { get; }
int Index { get; }
}

View File

@ -1,52 +0,0 @@
using Swan;
using System.Diagnostics;
namespace GCLocalServerRewrite.common;
public static class PathHelper
{
/// <summary>
/// Gets the local path of html/static files.
/// </summary>
public static string HtmlRootPath => Path.Combine(BasePath, Configs.STATIC_FOLDER, Configs.WWWROOT);
/// <summary>
/// Root path for database, when debug, it's under source root, when release, it's the exe dir
/// </summary>
public static string DataBaseRootPath => Path.Combine(BasePath, Configs.DB_FOLDER);
public static string LogRootPath => Path.Combine(BasePath, Configs.LOG_FOLDER);
public static string CertRootPath => Path.Combine(BasePath, Configs.CERT_FOLDER);
public static string ConfigFilePath => Path.Combine(BasePath, Configs.CONFIG_FILE_NAME);
private static string BasePath
{
get
{
var assemblyPath = Environment.ProcessPath;
if (assemblyPath == null)
{
throw SelfCheck.Failure("Cannot get assembly path!!!");
}
#if DEBUG
var parentFullName = Directory.GetParent(assemblyPath)?.Parent?.Parent?.Parent?.FullName;
Debug.Assert(parentFullName != null, $"{nameof(parentFullName)} != null");
return parentFullName;
#else
var parent = Directory.GetParent(assemblyPath);
if (parent == null)
{
throw SelfCheck.Failure("Cannot get assembly parent path!!!");
}
return parent.ToString();
#endif
}
}
}

View File

@ -1,125 +0,0 @@
{
"AvatarCount": 356,
"NavigatorCount": 118,
"ItemCount": 21,
"SkinCount": 21,
"SeCount": 26,
"TitleCount": 5530,
"CardDbName": "card.db3",
"MusicDbName": "music471omni.db3",
"ServerIp": "127.0.0.1",
"EventFolder": "event",
"DownloadEvents": false,
"RelayServer": "127.0.0.1",
"RelayPort": 3333,
"MatchingServer": "127.0.0.1:5038",
"UnlockableSongIds": [
11,
13,
149,
273,
291,
320,
321,
371,
378,
384,
464,
471,
474,
475,
492,
494,
498,
520,
548,
551,
558,
561,
565,
570,
577,
583,
612,
615,
622,
632,
659,
666,
668,
670,
672,
676,
680,
682,
685,
686,
697,
700,
701,
711,
720,
749,
875,
876,
877
],
"ResponseData": [
{
"FileName": "/event_103_20201125.evt",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "27b503145a62e46f5f611b6f8a91e4f3",
"Index": 0
},
{
"FileName": "/event_20201125_reg.jpg",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "8e3fe25bf50dcbed13dbb54cc18b1efa",
"Index": 1
},
{
"FileName": "/event_20201125_sgreg.png",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "e0abb0503fe0c530d8a68e36994264c6",
"Index": 2
},
{
"FileName": "/news_big_20201125_0.jpg",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "4a0f66431f6449279dc046149d1dd882",
"Index": 0
},
{
"FileName": "/news_big_20201125_2.jpg",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "8e3fe25bf50dcbed13dbb54cc18b1efa",
"Index": 2
},
{
"FileName": "/news_small_20201125_1.jpg",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "e20135bcd41c98875aec2b52eb9fcd06",
"Index": 1
},
{
"FileName": "/telop_20201125.txt",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "ee228de44d6656a9ec0bb7f1a0ca64e1",
"Index": 0
},
{
"FileName": "/event_unlock_20201125.cmp",
"NotBeforeUnixTime": 1335677127,
"NotAfterUnixTime": 1966397127,
"Md5": "534a253e3de8360c2beff49a5f120105",
"Index": 8
}
]
}

View File

@ -1,34 +0,0 @@
using System.Net;
using System.Net.Mime;
using System.Text;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
namespace GCLocalServerRewrite.controllers;
public class AliveController : WebApiController
{
[Route(HttpVerbs.Get, "/i.php")]
// ReSharper disable once UnusedMember.Global
public string Check()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Html;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "REMOTE ADDRESS: 127.0.0.1\n" +
"SERVER NAME:nesys.home\n" +
"SERVER ADDR:239.1.1.1";
}
[Route(HttpVerbs.Get, "/{id}/Alive.txt")]
// ReSharper disable once UnusedMember.Global
public void AliveFile()
{
HttpContext.Response.SetEmptyResponse((int)HttpStatusCode.OK);
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
}
}

View File

@ -1,340 +0,0 @@
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using GCLocalServerRewrite.common;
using GCLocalServerRewrite.models;
using SharedProject.common;
using SharedProject.enums;
using SharedProject.models;
using SQLite.Net2;
using Swan.Logging;
namespace GCLocalServerRewrite.controllers;
public class ApiController : WebApiController
{
private readonly SQLiteConnection cardSqLiteConnection;
private readonly Dictionary<int, Music> musics;
private readonly Dictionary<int, MusicExtra> musicExtras;
public ApiController()
{
cardSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.SETTINGS.CardDbName);
var musicSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.SETTINGS.MusicDbName);
musics = musicSqLiteConnection.Table<Music>().ToDictionary(music => music.MusicId);
musicExtras = musicSqLiteConnection.Table<MusicExtra>().ToDictionary(music => music.MusicId);
}
[Route(HttpVerbs.Get, "/Users")]
// ReSharper disable once UnusedMember.Global
public List<User> GetUsers()
{
var result = cardSqLiteConnection.Table<Card>().ToList().ConvertAll(card => new User
{
CardId = card.CardId,
PlayerName = card.PlayerName
});
return result;
}
[Route(HttpVerbs.Post, "/Users/SetPlayerName")]
public bool SetPlayerName([JsonData] User data)
{
var existing = cardSqLiteConnection.Table<Card>().Where(card => card.CardId == data.CardId);
if (!existing.Any())
{
$"Trying to update non existing user's name! Card id {data.CardId}".Warn();
return false;
}
var user = existing.First();
user.PlayerName = data.PlayerName;
return cardSqLiteConnection.Update(user) == 1;
}
[Route(HttpVerbs.Post, "/UserDetail/SetMusicFavorite")]
// ReSharper disable once UnusedMember.Global
public bool SetFavorite([JsonData] MusicFavoriteData data)
{
var existing = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == data.CardId
&& detail.Pcol1 == Configs.FAVORITE_PCOL1
&& detail.Pcol2 == data.MusicId);
if (!existing.Any())
{
$"Trying to update non existing song's favorite! Card id {data.CardId}, music id {data.MusicId}".Warn();
return false;
}
var cardDetail = existing.First();
cardDetail.Fcol1 = data.IsFavorite ? 1 : 0;
var result = cardSqLiteConnection.Update(cardDetail);
return result == 1;
}
[Route(HttpVerbs.Post, "/UserDetail/SetPlayOption")]
// ReSharper disable once UnusedMember.Global
public bool SetPlayOption([JsonData] PlayOption data)
{
var firstConfig = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == data.CardId
&& detail.Pcol1 == Configs.FIRST_CONFIG_PCOL1
&& detail.Pcol2 == Configs.CONFIG_PCOL2
&& detail.Pcol3 == Configs.CONFIG_PCOL3);
var secondConfig = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == data.CardId
&& detail.Pcol1 == Configs.SECOND_CONFIG_PCOL1
&& detail.Pcol2 == Configs.CONFIG_PCOL2
&& detail.Pcol3 == Configs.CONFIG_PCOL3);
if (!firstConfig.Any() || !secondConfig.Any())
{
$"Trying to update non existing card's config! Card id {data.CardId}".Warn();
return false;
}
var firstDetail = firstConfig.First();
firstDetail.ScoreUi1 = (long)data.FastSlowIndicator;
firstDetail.ScoreUi2 = (long)data.FeverTrance;
firstDetail.ScoreI1 = data.AvatarId;
firstDetail.Fcol2 = (int)data.TitleId;
var secondDetail = secondConfig.First();
secondDetail.ScoreI1 = data.NavigatorId;
var firstResult = cardSqLiteConnection.Update(firstDetail);
var secondResult = cardSqLiteConnection.Update(secondDetail);
return firstResult == 1 && secondResult == 1;
}
[Route(HttpVerbs.Get, "/UserDetail/{cardId}")]
// ReSharper disable once UnusedMember.Global
public UserDetail? GetUserDetail(long cardId)
{
var cardResult = cardSqLiteConnection.Table<Card>().Where(card => card.CardId == cardId);
if (!cardResult.Any())
{
$"Getting detail for non exisisting card! Card id is {cardId}".Warn();
return null;
}
var card = cardResult.First();
return ToUserDetail(card);
}
private UserDetail? ToUserDetail(Card card)
{
if (!cardSqLiteConnection.Table<CardDetail>().Select(detail => detail.CardId == card.CardId).Any())
{
return null;
}
var userDetail = new UserDetail
{
CardId = card.CardId,
PlayerName = card.PlayerName
};
var songPlayDataDict = new Dictionary<int, SongPlayData>();
ProcessCardDetail(userDetail, songPlayDataDict);
userDetail.SongPlayDataList = songPlayDataDict.Values.ToList();
userDetail.TotalSongCount = musics.Count;
userDetail.TotalStageCount = userDetail.TotalSongCount * 3 + musicExtras.Count;
userDetail.AverageScore = (int)(userDetail.TotalScore / userDetail.PlayedStageCount);
userDetail.PlayedSongCount = songPlayDataDict.Count;
return userDetail;
}
private void ProcessCardDetail(UserDetail userDetail, IDictionary<int, SongPlayData> songPlayDataDict)
{
var firstOption = cardSqLiteConnection.Table<CardDetail>()
.FirstOrDefault(detail => detail.CardId == userDetail.CardId
&& detail.Pcol1 == Configs.FIRST_CONFIG_PCOL1
&& detail.Pcol2 == Configs.CONFIG_PCOL2
&& detail.Pcol3 == Configs.CONFIG_PCOL3
, new CardDetail
{
CardId = userDetail.CardId
});
var secondOption = cardSqLiteConnection.Table<CardDetail>()
.FirstOrDefault(detail => detail.CardId == userDetail.CardId
&& detail.Pcol1 == Configs.SECOND_CONFIG_PCOL1
&& detail.Pcol2 == Configs.CONFIG_PCOL2
&& detail.Pcol3 == Configs.CONFIG_PCOL3
, new CardDetail
{
CardId = userDetail.CardId
});
SetOptions(firstOption, secondOption, userDetail);
var songCounts = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == userDetail.CardId && detail.Pcol1 == Configs.COUNT_PCOL1);
foreach (var detail in songCounts)
{
SetCounts(detail, songPlayDataDict, userDetail);
}
var songScores = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == userDetail.CardId && detail.Pcol1 == Configs.SCORE_PCOL1);
foreach (var detail in songScores)
{
SetDetails(detail, songPlayDataDict, userDetail);
}
var favorites = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == userDetail.CardId && detail.Pcol1 == Configs.FAVORITE_PCOL1)
.ToDictionary(detail => detail.Pcol2);
foreach (var (musicId, songPlayData) in songPlayDataDict)
{
songPlayData.IsFavorite = favorites[musicId].Fcol1 != 0;
}
}
private static void SetOptions(CardDetail firstOptionCardDetail, CardDetail secondOptionCardDetail, UserDetail userDetail)
{
var fastSlow = (int)firstOptionCardDetail.ScoreUi1;
var feverTrance = (int)firstOptionCardDetail.ScoreUi2;
if (!Enum.IsDefined(typeof(PlayOptions.FastSlowIndicator), fastSlow))
{
fastSlow = (int)PlayOptions.FastSlowIndicator.NotUsed;
}
if (!Enum.IsDefined(typeof(PlayOptions.FeverTranceShow), feverTrance))
{
feverTrance = (int)PlayOptions.FeverTranceShow.Show;
}
userDetail.PlayOption = new PlayOption
{
CardId = firstOptionCardDetail.CardId,
FastSlowIndicator = (PlayOptions.FastSlowIndicator)fastSlow,
FeverTrance = (PlayOptions.FeverTranceShow)feverTrance,
AvatarId = firstOptionCardDetail.ScoreI1,
TitleId = firstOptionCardDetail.Fcol2,
NavigatorId = secondOptionCardDetail.ScoreI1
};
}
private void SetDetails(CardDetail cardDetail, IDictionary<int, SongPlayData> songPlayDataDict,
UserDetail userDetail)
{
var musicId = cardDetail.Pcol2;
AddSongPlayDataIfNotExist(songPlayDataDict, musicId);
for (var i = 0; i < SharedConstants.DIFFICULTY_COUNT; i++)
{
var songPlayDetailData = songPlayDataDict[musicId].SongPlaySubDataList[i];
songPlayDetailData.Difficulty = (Difficulty)i;
if (i != cardDetail.Pcol3)
{
continue;
}
songPlayDetailData.Score = (int)cardDetail.ScoreUi1;
songPlayDetailData.MaxChain = (int)cardDetail.ScoreUi3;
userDetail.TotalScore += cardDetail.ScoreUi1;
if (cardDetail.ScoreUi1 >= SharedConstants.S_SCORE_THRESHOLD)
{
userDetail.SAboveStageCount++;
}
if (cardDetail.ScoreUi1 >= SharedConstants.S_PLUS_SCORE_THRESHOLD)
{
userDetail.SPlusAboveStageCount++;
}
if (cardDetail.ScoreUi1 >= SharedConstants.S_PLUS_PLUS_SCORE_THRESHOLD)
{
userDetail.SPlusPlusAboveStageCount++;
}
}
}
private void SetCounts(CardDetail cardDetail, IDictionary<int, SongPlayData> songPlayDataDict, UserDetail userDetail)
{
var musicId = cardDetail.Pcol2;
AddSongPlayDataIfNotExist(songPlayDataDict, musicId);
for (var i = 0; i < SharedConstants.DIFFICULTY_COUNT; i++)
{
var songPlayDetailData = songPlayDataDict[musicId].SongPlaySubDataList[i];
songPlayDetailData.Difficulty = (Difficulty)i;
if (i != cardDetail.Pcol3)
{
continue;
}
songPlayDetailData.PlayCount = (int)cardDetail.ScoreUi1;
songPlayDetailData.LastPlayTime = cardDetail.LastPlayTime;
songPlayDetailData.ClearState = ClearState.Failed;
userDetail.PlayedStageCount++;
if (cardDetail.ScoreUi2 > 0)
{
userDetail.ClearedStageCount++;
songPlayDetailData.ClearState = ClearState.Clear;
}
if (cardDetail.ScoreUi3 > 0)
{
userDetail.NoMissStageCount++;
songPlayDetailData.ClearState = ClearState.NoMiss;
}
if (cardDetail.ScoreUi4 > 0)
{
userDetail.FullChainStageCount++;
songPlayDetailData.ClearState = ClearState.FullChain;
}
if (cardDetail.ScoreUi6 > 0)
{
userDetail.PerfectStageCount++;
songPlayDetailData.ClearState = ClearState.Perfect;
}
}
}
private void AddSongPlayDataIfNotExist(IDictionary<int, SongPlayData> songPlayDataDict, int musicId)
{
if (songPlayDataDict.ContainsKey(musicId))
{
return;
}
var musicData = musics[musicId];
var songPlayData = new SongPlayData
{
Artist = musicData.Artist ?? string.Empty,
Title = musicData.Title ?? string.Empty,
MusicId = musicId,
SongPlaySubDataList = new SongPlayDetailData[SharedConstants.DIFFICULTY_COUNT]
};
for (var i = 0; i < SharedConstants.DIFFICULTY_COUNT; i++)
{
songPlayData.SongPlaySubDataList[i] = new SongPlayDetailData();
}
songPlayDataDict[musicId] = songPlayData;
}
}

View File

@ -1,806 +0,0 @@
using System.Net.Http.Json;
using System.Net.Mime;
using System.Text;
using System.Xml.Linq;
using ChoETL;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using GCLocalServerRewrite.common;
using GCLocalServerRewrite.models;
using SharedProject.models;
using SQLite.Net2;
using Swan;
using Swan.Logging;
using Avatar=GCLocalServerRewrite.models.Avatar;
using Navigator=GCLocalServerRewrite.models.Navigator;
using Title=GCLocalServerRewrite.models.Title;
namespace GCLocalServerRewrite.controllers;
public class CardServiceController : WebApiController
{
private readonly SQLiteConnection cardSqLiteConnection;
private readonly SQLiteConnection musicSqLiteConnection;
public CardServiceController()
{
cardSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.SETTINGS.CardDbName);
musicSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.SETTINGS.MusicDbName);
}
[Route(HttpVerbs.Post, "/cardn.cgi")]
// ReSharper disable once UnusedMember.Global
public async Task<string> CardService([FormField] int gid, [FormField("mac_addr")] string mac, [FormField] int type,
[FormField("card_no")] long cardId, [FormField("data")] string xmlData, [FormField("cmd_str")] int cmdType)
{
HttpContext.Response.ContentType = MediaTypeNames.Application.Octet;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return await ProcessCommand(cmdType, mac, cardId, xmlData, type);
}
private async Task<string> ProcessCommand(int cmdType, string mac, long cardId, string xmlData, int type)
{
if (!Enum.IsDefined(typeof(Command), cmdType))
{
throw new ArgumentOutOfRangeException(nameof(cmdType), cmdType, $"Cmd type is unknown!\n Data is {xmlData}");
}
var command = (Command)cmdType;
return command switch
{
Command.CardReadRequest or Command.CardWriteRequest => await ProcessCardRequest(mac, cardId, xmlData, type),
Command.ReissueRequest => ProcessReissueRequest(),
Command.RegisterRequest => ProcessRegisterRequest(cardId, xmlData),
_ => throw new ArgumentOutOfRangeException(nameof(command), command, "Command unknown, should never happen!")
};
}
private string ProcessRegisterRequest(long cardId, string xmlData)
{
$"Get card register request, data is \n{xmlData}".Info();
Write<Card>(cardId, xmlData);
return ConstructResponse(xmlData);
}
private static string ProcessReissueRequest()
{
"Get reissue request, returning not reissue".Info();
return ConstructResponse("", ReturnCode.NotReissue);
}
private async Task<string> ProcessCardRequest(string mac, long cardId, string xmlData, int type)
{
if (!Enum.IsDefined(typeof(CardRequestType), type))
{
throw new ArgumentOutOfRangeException(nameof(type), type, "Card request type is unknown!");
}
var requestType = (CardRequestType)type;
$"Getting card request, type is {requestType}".Info();
switch (requestType)
{
#region ReadRequests
case CardRequestType.ReadCard:
{
var response = Card(cardId, out var returnCode);
return ConstructResponse(response, returnCode);
}
case CardRequestType.ReadCardDetail:
{
var cardDetail = CardDetail(cardId, xmlData);
return ConstructResponse(cardDetail);
}
case CardRequestType.ReadCardDetails:
{
return ConstructResponse(CardDetails(cardId));
}
case CardRequestType.ReadCardBData:
{
return ConstructResponse(CardBData(cardId));
}
case CardRequestType.ReadAvatar:
{
return ConstructResponse(
GetStaticCount<Avatar>(cardId, Configs.SETTINGS.AvatarCount, Configs.AVATAR_XPATH));
}
case CardRequestType.ReadItem:
{
return ConstructResponse(
GetStaticCount<Item>(cardId, Configs.SETTINGS.ItemCount, Configs.ITEM_XPATH));
}
case CardRequestType.ReadSkin:
{
return ConstructResponse(
GetStaticCount<Skin>(cardId, Configs.SETTINGS.SkinCount, Configs.SKIN_XPATH));
}
case CardRequestType.ReadTitle:
{
return ConstructResponse(
GetStaticCount<Title>(cardId, Configs.SETTINGS.TitleCount, Configs.TITLE_XPATH));
}
case CardRequestType.ReadMusic:
{
return ConstructResponse(MusicUnlock());
}
case CardRequestType.ReadEventReward:
{
return ConstructResponse(
GenerateEmptyXml(Configs.EVENT_REWARD));
}
case CardRequestType.ReadNavigator:
{
return ConstructResponse(
GetStaticCount<Navigator>(cardId, Configs.SETTINGS.NavigatorCount, Configs.NAVIGATOR_XPATH));
}
case CardRequestType.ReadMusicExtra:
{
return ConstructResponse(MusicExtra());
}
case CardRequestType.ReadMusicAou:
{
return ConstructResponse(MusicAouUnlock());
}
case CardRequestType.ReadCoin:
{
return ConstructResponse(Coin(cardId));
}
case CardRequestType.ReadUnlockReward:
{
return ConstructResponse(UnlockReward(cardId));
}
case CardRequestType.ReadUnlockKeynum:
{
return ConstructResponse(UnlockKeynum(cardId));
}
case CardRequestType.ReadSoundEffect:
{
return ConstructResponse(
GetStaticCount<SoundEffect>(cardId, Configs.SETTINGS.SeCount, Configs.SE_XPATH));
}
case CardRequestType.ReadGetMessage:
{
return ConstructResponse(GenerateEmptyXml(Configs.GET_MESSAGE));
}
case CardRequestType.ReadCond:
{
return ConstructResponse(GenerateEmptyXml(Configs.COND));
}
case CardRequestType.ReadTotalTrophy:
{
return ConstructResponse(TotalTrophy(cardId));
}
case CardRequestType.StartSession:
case CardRequestType.GetSession:
{
return ConstructResponse(GetSession(cardId, mac));
}
#endregion
#region WriteRequests
case CardRequestType.WriteCard:
{
$"Card Write data is {xmlData}".Info();
Write<Card>(cardId, xmlData);
return ConstructResponse(xmlData);
}
case CardRequestType.WriteCardDetail:
{
$"Card Detail Write data is {xmlData}".Info();
WriteCardDetail(cardId, xmlData);
return ConstructResponse(xmlData);
}
case CardRequestType.WriteCardBData:
{
$"Card BData Write data is {xmlData}".Info();
Write<CardBData>(cardId, xmlData);
WriteCardPlayCount(cardId);
return ConstructResponse(xmlData);
}
// TODO: Maybe one day implement these
case CardRequestType.WriteAvatar:
case CardRequestType.WriteItem:
case CardRequestType.WriteTitle:
case CardRequestType.WriteMusicDetail:
case CardRequestType.WriteNavigator:
case CardRequestType.WriteCoin:
case CardRequestType.WriteSkin:
case CardRequestType.WriteUnlockKeynum:
case CardRequestType.WriteSoundEffect:
{
$"Card Write data is {xmlData}".Info();
return ConstructResponse(xmlData);
}
#endregion
#region OnlineMatching
case CardRequestType.StartOnlineMatching:
{
$"Start Online Matching, data is {xmlData}".Info();
var resultString = await StartOnlineMatching(cardId, xmlData);
return ConstructResponse(resultString);
}
case CardRequestType.UpdateOnlineMatching:
{
$"Update Online Matching, data is {xmlData}".Info();
var resultString = await UpdateOnlineMatching(cardId, xmlData);
return ConstructResponse(resultString);
}
case CardRequestType.UploadOnlineMatchingResult:
{
$"Get Online Matching result, data is {xmlData}".Info();
var resultString = await UploadOnlineMatchingResult(cardId, xmlData);
return ConstructResponse(resultString);
}
#endregion
default:
throw new ArgumentOutOfRangeException(nameof(requestType), requestType, "Request type not captured, should never happen!");
}
}
#region ReadImplementation
private string Card(long cardId, out ReturnCode returnCode)
{
var result = cardSqLiteConnection.Table<Card>().Where(card => card.CardId == cardId);
if (!result.Any())
{
returnCode = ReturnCode.CardNotRegistered;
return string.Empty;
}
var card = result.First();
returnCode = ReturnCode.Ok;
return GenerateSingleXml(card, Configs.CARD_XPATH);
}
private string CardDetail(long cardId, string xmlData)
{
var reader = new ChoXmlReader<CardDetailReadData>(new StringReader(xmlData));
var data = reader.Read();
var result = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == cardId &&
detail.Pcol1 == data.Pcol1 && detail.Pcol2 == data.Pcol2 &&
detail.Pcol3 == data.Pcol3);
if (!result.Any())
{
return GenerateEmptyXml(Configs.CARD_DETAIL);
}
var cardDetail = result.First();
return GenerateSingleXml(cardDetail, Configs.CARD_DETAIL_SINGLE_XPATH);
}
private string CardDetails(long cardId)
{
var result = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == cardId);
if (!result.Any())
{
return GenerateEmptyXml(Configs.CARD_DETAIL);
}
var cardDetails = result.ToList();
return GenerateRecordsXml(cardDetails, Configs.CARD_DETAIL_RECORD_XPATH);
}
private string CardBData(long cardId)
{
var result = cardSqLiteConnection.Table<CardBData>()
.Where(detail => detail.CardId == cardId);
if (!result.Any())
{
return GenerateEmptyXml(Configs.CARD_BDATA);
}
var cardBData = result.First();
return GenerateSingleXml(cardBData, Configs.CARD_BDATA_XPATH);
}
private static string GetStaticCount<T>(long cardId, int count, string xpath)
where T : Record, IIdModel, ICardIdModel, new()
{
var models = new List<T>();
for (var id = 1; id <= count; id++)
{
var model = new T();
model.SetId(id);
model.SetCardId(cardId);
models.Add(model);
}
return GenerateRecordsXml(models, xpath);
}
private static string GetSession(long cardId, string mac)
{
var session = new Session
{
CardId = cardId,
Mac = mac,
SessionId = "12345678901234567890123456789012",
Expires = 9999,
PlayerId = 1
};
return GenerateSingleXml(session, Configs.SESSION_XPATH);
}
private static string TotalTrophy(long cardId)
{
var trophy = new TotalTrophy
{
CardId = cardId,
TrophyNum = 9
};
return GenerateSingleXml(trophy, Configs.TOTAL_TROPHY_XPATH);
}
private static string Coin(long cardId)
{
var coin = new Coin
{
CardId = cardId,
CurrentCoins = 999999,
TotalCoins = 999999,
MonthlyCoins = 999999
};
return GenerateSingleXml(coin, Configs.COIN_XPATH);
}
private static string UnlockReward(long cardId)
{
var unlockRewards = new List<UnlockReward>
{
new()
{
CardId = cardId,
RewardType = 1,
RewardId = 1,
TargetId = 1,
TargetNum = 1,
KeyNum = 3
}
};
return GenerateRecordsXml(unlockRewards, Configs.UNLOCK_REWARD_XPATH);
}
private static string UnlockKeynum(long cardId)
{
var unlockKeynums = new List<UnlockKeynum>
{
new()
{
CardId = cardId,
RewardId = 1,
KeyNum = 0,
RewardCount = 1
}
};
return GenerateRecordsXml(unlockKeynums, Configs.UNLOCK_KEYNUM_XPATH);
}
private string MusicUnlock()
{
var result = musicSqLiteConnection.Table<Music>().ToList();
return GenerateRecordsXml(result, Configs.MUSIC_XPATH);
}
private string MusicAouUnlock()
{
var result = musicSqLiteConnection.Table<MusicAou>().ToList();
return !result.Any() ? GenerateEmptyXml(Configs.MUSIC_AOU) : GenerateRecordsXml(result, Configs.MUSIC_AOU_XPATH);
}
private string MusicExtra()
{
var result = musicSqLiteConnection.Table<MusicExtra>().ToList();
return GenerateRecordsXml(result, Configs.MUSIC_EXTRA_XPATH);
}
#endregion
#region HelperMethods
private static string ConstructResponse(string xml, ReturnCode returnCode = ReturnCode.Ok)
{
var returnCodeInt = (int)returnCode;
if (returnCodeInt == 1)
{
return $"{returnCodeInt}\n" +
"1,1\n" +
xml;
}
return $"{returnCodeInt}";
}
private static string GenerateEmptyXml(string fieldName)
{
var xml = new XDocument(new XElement("root",
new XElement(fieldName)));
xml.Declaration = new XDeclaration("1.0", "UTF-8", null);
return xml.ToString();
}
private static string GenerateSingleXml<T>(T obj, string xpath) where T : class
{
var sb = new StringBuilder();
using (var writer = new ChoXmlWriter<T>(sb))
{
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.UseXmlSerialization = true;
writer.WithXPath(xpath);
writer.Write(obj);
}
return sb.ToString();
}
private static string GenerateRecordsXml<T>(IReadOnlyList<T> list, string xpath) where T : Record
{
var stringBuilder = new StringBuilder();
for (var i = 0; i < list.Count; i++)
{
var obj = list[i];
obj.RecordId = i + 1;
}
using (var writer = new ChoXmlWriter<T>(stringBuilder))
{
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.UseXmlSerialization = true;
writer.WithXPath(xpath);
writer.Write(list);
}
return stringBuilder.ToString();
}
#endregion
#region WriteImplementation
private void Write<T>(long cardId, string xmlData) where T : class, ICardIdModel
{
var reader = new ChoXmlReader<T>(new StringReader(xmlData)).WithXPath(Configs.DATA_XPATH);
var writeObject = reader.Read();
if (writeObject == null)
{
throw new HttpRequestException();
}
writeObject.SetCardId(cardId);
var rowsAffected = cardSqLiteConnection.InsertOrReplace(writeObject, typeof(T));
if (rowsAffected == 0)
{
throw new ApplicationException("Update database failed!");
}
$"Updated {typeof(T)}".Info();
}
private void WriteCardPlayCount(long cardId)
{
var record = cardSqLiteConnection.Table<CardPlayCount>().Where(count => count.CardId == cardId);
if (!record.Any())
{
$"Created new play count data for card {cardId}".Info();
var playCount = new CardPlayCount
{
CardId = cardId,
PlayCount = 1,
LastPlayed = DateTime.Now
};
cardSqLiteConnection.InsertOrReplace(playCount);
return;
}
var data = record.First();
var now = DateTime.Now;
var lastPlayedTime = data.LastPlayed;
if (now <= lastPlayedTime)
{
$"Current time {now} is less than or equal to last played time! Clock skew detected!".Warn();
data.PlayCount = 0;
data.LastPlayed = DateTime.Now;
cardSqLiteConnection.InsertOrReplace(data);
return;
}
DateTime start;
DateTime end;
if (now.Hour >= 8)
{
start = DateTime.Today.AddHours(8);
end = start.AddHours(24);
}
else
{
end = DateTime.Today.AddHours(8);
start = end.AddHours(-24);
}
data.PlayCount = lastPlayedTime.IsBetween(start, end) ? data.PlayCount + 1 : 0;
cardSqLiteConnection.InsertOrReplace(data);
$"Updated card play count, current count is {data.PlayCount}".Info();
}
private void WriteCardDetail(long cardId, string xmlData)
{
var result = cardSqLiteConnection.Table<CardDetail>()
.Where(detail => detail.CardId == cardId);
// Unlock all unlockable songs in card details table when write card detail for the first time
if (!result.Any())
{
UnlockSongs(cardId);
}
var reader = new ChoXmlReader<CardDetail>(new StringReader(xmlData)).WithXPath(Configs.DATA_XPATH);
var cardDetail = reader.Read();
if (cardDetail is null)
{
throw new HttpRequestException("Write object is null");
}
cardDetail.SetCardId(cardId);
cardDetail.LastPlayTime = DateTime.Now;
var rowsAffected = cardSqLiteConnection.InsertOrReplace(cardDetail);
if (rowsAffected == 0)
{
throw new ApplicationException("Update database failed!");
}
"Updated card detail".Info();
}
private void UnlockSongs(long cardId)
{
var unlockableSongIds = Configs.SETTINGS.UnlockableSongIds;
if (unlockableSongIds is null)
{
unlockableSongIds = Configs.DEFAULT_UNLOCKABLE_SONGS;
}
var detailList = unlockableSongIds.Select(id => new CardDetail
{
CardId = cardId,
Pcol1 = 10,
Pcol2 = id,
Pcol3 = 0,
ScoreUi2 = 1,
ScoreUi6 = 1,
LastPlayTime = DateTime.Now
})
.ToList();
cardSqLiteConnection.InsertOrIgnoreAll(detailList);
}
#endregion
#region OnlineMatchingImplementation
private static async Task<string> StartOnlineMatching(long cardId, string xmlData)
{
var reader = new ChoXmlReader<OnlineMatchingEntry>(new StringReader(xmlData)).WithXPath(Configs.DATA_XPATH);
var entry = reader.Read();
var request = Converters.ConvertFromEntry(entry) ;
request.CardId = cardId;
request.EntryStart = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
request.MatchingTimeout = 20;
request.MatchingRemainingTime = 3;
request.MatchingWaitTime = 10;
request.Status = 1;
var client = new HttpClient();
var url = $"http://{Configs.SETTINGS.MatchingServer}/{Configs.MATCHING_URL_BASE}/{Configs.START_MATCHING_URL}";
try
{
var response = await client.PostAsJsonAsync(url, request);
var dataList = await response.Content.ReadFromJsonAsync<List<OnlineMatchingData>>();
if (dataList is null)
{
throw new HttpRequestException("Start matching request fail");
}
var result = dataList.ConvertAll(input => Converters.ConvertFromData(input));
return GenerateRecordsXml(result, Configs.ONLINE_MATCHING_XPATH);
}
catch (Exception e)
{
e.Error("", "Http request failed");
throw;
}
}
private static async Task<string> UpdateOnlineMatching(long cardId, string xmlData)
{
var reader = new ChoXmlReader<OnlineMatchingUpdateData>(new StringReader(xmlData));
var data = reader.Read();
var request = new OnlineMatchingUpdateRequest
{
Action = data.Action,
CardId = cardId,
EventId = data.EventId,
MatchingId = data.MatchingId,
MessageId = data.MessageId
};
var client = new HttpClient();
var url = $"http://{Configs.SETTINGS.MatchingServer}/{Configs.MATCHING_URL_BASE}/{Configs.UPDATE_MATCHING_URL}";
try
{
var response = await client.PostAsJsonAsync(url, request);
var dataList = await response.Content.ReadFromJsonAsync<List<OnlineMatchingEntry>>();
if (dataList is null)
{
throw new HttpRequestException("Update matching request fail");
}
return GenerateRecordsXml(dataList, Configs.ONLINE_MATCHING_XPATH);
}
catch (Exception e)
{
e.Error("", "Http request failed");
throw;
}
}
private static async Task<string> UploadOnlineMatchingResult(long cardId, string xmlData)
{
var reader = new ChoXmlReader<OnlineMatchingResultData>(new StringReader(xmlData));
var data = reader.Read();
var request = new OnlineMatchingFinishRequest
{
CardId = cardId,
MatchingId = data.MatchingId
};
var client = new HttpClient();
var url = $"http://{Configs.SETTINGS.MatchingServer}/{Configs.MATCHING_URL_BASE}/{Configs.UPDATE_MATCHING_URL}";
try
{
var response = await client.PostAsJsonAsync(url, request);
var success = await response.Content.ReadFromJsonAsync<bool>();
var result = new OnlineMatchingResult
{
Status = success ? 1:0
};
return GenerateSingleXml(result, Configs.ONLINE_BATTLE_RESULT_XPATH);
}
catch (Exception e)
{
e.Error("", "Http request failed");
throw;
}
/*var entries = OnlineMatchingEntries[0xDEADBEEF];
var entry = entries.Find(matchingEntry => matchingEntry.CardId == cardId);
if (entry is null)
{
throw new HttpException(400,"Entry for this card id does not exist!");
}
if (entry.MatchingId != data.MatchingId)
{
throw new HttpException(400,"Matching Id mismatch!");
}
OnlineMatchingEntries[0xDEADBEEF].Remove(entry);
var result = new OnlineMatchingResult
{
Status = 1
};
return GenerateSingleXml(result, Configs.ONLINE_BATTLE_RESULT_XPATH);*/
}
#endregion
private enum CardRequestType
{
// Read data
ReadCard = 259,
ReadCardDetail = 260,
ReadCardDetails = 261,
ReadCardBData = 264,
ReadAvatar = 418,
ReadItem = 420,
ReadSkin = 422,
ReadTitle = 424,
ReadMusic = 428,
ReadEventReward = 441,
ReadNavigator = 443,
ReadMusicExtra = 465,
ReadMusicAou = 467,
ReadCoin = 468,
ReadUnlockReward = 507,
ReadUnlockKeynum = 509,
ReadSoundEffect = 8458,
ReadGetMessage = 8461,
ReadCond = 8465,
ReadTotalTrophy = 8468,
// Sessions
GetSession = 401,
StartSession = 402,
// Write data
WriteCard = 771,
WriteCardDetail = 772,
WriteCardBData = 776,
WriteAvatar = 929,
WriteItem = 931,
WriteTitle = 935,
WriteMusicDetail = 941,
WriteNavigator = 954,
WriteCoin = 980,
WriteSkin = 933,
WriteUnlockKeynum = 1020,
WriteSoundEffect = 8969,
// Online matching
StartOnlineMatching = 8705,
UpdateOnlineMatching = 8961,
UploadOnlineMatchingResult = 8709
}
private enum Command
{
CardReadRequest = 256,
CardWriteRequest = 768,
RegisterRequest = 512,
ReissueRequest = 1536
}
private enum ReturnCode
{
/// <summary>
/// 処理は正常に完了しました in debug string
/// </summary>
Ok = 1,
/// <summary>
/// 未登録のカードです in debug string
/// </summary>
CardNotRegistered = 23,
/// <summary>
/// 再発行予約がありません in debug string
/// </summary>
NotReissue = 27
}
}

View File

@ -1,32 +0,0 @@
using System.Net.Mime;
using System.Text;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
namespace GCLocalServerRewrite.controllers;
public class IncomServiceController : WebApiController
{
[Route(HttpVerbs.Post, "/incom.php")]
// ReSharper disable once UnusedMember.Global
public string IncomService()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "1+1";
}
[Route(HttpVerbs.Post, "/incomALL.php")]
// ReSharper disable once UnusedMember.Global
public string IncomAllService()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "1+1";
}
}

View File

@ -1,70 +0,0 @@
using System.Net.Mime;
using System.Text;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using GCLocalServerRewrite.common;
using GCLocalServerRewrite.models;
using SQLite.Net2;
using Swan;
using Swan.Logging;
namespace GCLocalServerRewrite.controllers;
public class OptionServiceController : WebApiController
{
private readonly SQLiteConnection cardSqLiteConnection;
public OptionServiceController()
{
cardSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.SETTINGS.CardDbName);
}
[Route(HttpVerbs.Get, "/PlayInfo.php")]
public string OptionService([QueryField("card_id")] long cardId)
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "1\n" +
$"{GetPlayCount(cardId)}";
}
private int GetPlayCount(long cardId)
{
var record = cardSqLiteConnection.Table<CardPlayCount>().Where(count => count.CardId == cardId);
if (!record.Any())
{
return 0;
}
var now = DateTime.Now;
var data = record.First();
var lastPlayedTime = data.LastPlayed;
if (now <= lastPlayedTime)
{
$"Current time {now} is less than or equal to last played time! Clock skew detected!".Warn();
return 0;
}
DateTime start;
DateTime end;
if (now.Hour >= 8)
{
start = DateTime.Today.AddHours(8);
end = start.AddHours(24);
}
else
{
end = DateTime.Today.AddHours(8);
start = end.AddHours(-24);
}
return lastPlayedTime.IsBetween(start, end) ? data.PlayCount : 0;
}
}

View File

@ -1,178 +0,0 @@
using System.Net.Mime;
using System.Text;
using System.Xml;
using ChoETL;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using GCLocalServerRewrite.common;
using GCLocalServerRewrite.models;
using SQLite.Net2;
using Swan;
using Swan.Logging;
// ReSharper disable UnusedMember.Global
namespace GCLocalServerRewrite.controllers;
public class RankController : WebApiController
{
private readonly SQLiteConnection cardSqLiteConnection;
private readonly SQLiteConnection musicSqLiteConnection;
public RankController()
{
cardSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.SETTINGS.CardDbName);
musicSqLiteConnection = DatabaseHelper.ConnectDatabase(Configs.SETTINGS.MusicDbName);
}
[Route(HttpVerbs.Get, "/ranking.php")]
public string Rank([QueryField("cmd_type")] int type)
{
HttpContext.Response.ContentType = MediaTypeNames.Application.Octet;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
if (!Enum.IsDefined(typeof(RankType), type))
{
throw new ArgumentOutOfRangeException(nameof(type), type, "Rank type out of range");
}
var requestType = (RankType)type;
switch (requestType)
{
case RankType.GlobalRank:
case RankType.UnknownRank1:
case RankType.UnknownRank2:
case RankType.UnknownRank3:
$"Getting rank request, type is {requestType}".Info();
return ConstructResponse(RankTemp("score_rank"));
case RankType.PlayNumRank:
$"Getting rank request, type is {requestType}".Info();
return ConstructResponse(PlayNumRank());
case RankType.EventRank:
$"Getting rank request, type is {requestType}".Info();
return ConstructResponse(RankTemp("event_rank"));
default:
#pragma warning disable CA2208
throw new ArgumentOutOfRangeException(nameof(requestType));
#pragma warning restore CA2208
}
}
private static string ConstructResponse(string xml)
{
return "1\n" +
xml;
}
// TODO: Add proper rank support
private static string RankTemp(string rankType)
{
var rankStatus = new RankStatus
{
Rows = 0,
Status = 0
};
var sb = new StringBuilder();
using (var writer = new ChoXmlWriter<RankStatus>(sb))
{
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.UseXmlSerialization = true;
writer.WithXPath(Configs.RANK_STATUS_XPATH);
writer.Write(rankStatus);
}
var document = new XmlDocument();
document.LoadXml(sb.ToString());
var root = document.DocumentElement;
if (root == null)
{
throw SelfCheck.Failure("Internal XML error!");
}
root.AppendChild(document.CreateElement(rankType));
var stringWriter = new StringWriter();
var xmlTextWriter = new XmlTextWriter(stringWriter);
document.WriteTo(xmlTextWriter);
return stringWriter.ToString();
}
private string PlayNumRank()
{
var rankStatus = new RankStatus
{
Rows = 30,
TableName = "play_num_rank",
Status = 1,
EndDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
};
var playNumRankContainer = new PlayNumRankContainer
{
PlayNumRankRecords = GetPlayNumRankRecords(),
RankStatus = rankStatus
};
var sb = new StringBuilder();
using (var writer = new ChoXmlWriter<PlayNumRankContainer>(sb))
{
writer.Configuration.UseXmlSerialization = true;
writer.Configuration.OmitXmlDeclaration = false;
writer.Configuration.IgnoreRootName = true;
writer.Write(playNumRankContainer);
}
return sb.ToString();
}
private List<PlayNumRankRecord> GetPlayNumRankRecords()
{
var records = new List<PlayNumRankRecord>();
var musics = musicSqLiteConnection.Table<Music>().ToList().OrderBy(arg => Guid.NewGuid()).Take(30).ToList();
for (var i = 0; i < musics.Count; i++)
{
var music = musics[i];
var index = i + 1;
var record = new PlayNumRankRecord
{
Id = index,
Rank = index,
Rank2 = index + 1,
PrevRank = musics.Count - i,
PrevRank2 = index + 1,
Artist = music.Artist,
Pcol1 = music.MusicId,
ScoreBi1 = index,
Title = music.Title
};
records.Add(record);
}
return records;
}
private enum RankType
{
GlobalRank = 4119,
PlayNumRank = 6657,
EventRank = 6661,
UnknownRank1 = 6666,
UnknownRank2 = 6667,
UnknownRank3 = 4098
}
}

View File

@ -1,21 +0,0 @@
using System.Net.Mime;
using System.Text;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
namespace GCLocalServerRewrite.controllers;
public class ResponeServiceController : WebApiController
{
[Route(HttpVerbs.Post, "/respone.php")]
// ReSharper disable once UnusedMember.Global
public string ResponeService()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "1";
}
}

View File

@ -1,152 +0,0 @@
using System.Collections.Specialized;
using System.Net.Mime;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using GCLocalServerRewrite.common;
using Swan.Logging;
// ReSharper disable UnusedMember.Global
namespace GCLocalServerRewrite.controllers;
public class ServerController : WebApiController
{
private static readonly string DataUrl = $"https://cert.nesys.jp/{Configs.STATIC_FOLDER}/{Configs.SETTINGS.EventFolder}";
[Route(HttpVerbs.Get, "/certify.php")]
public string Certify([QueryData] NameValueCollection parameters)
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
var gid = parameters["gid"];
var mac = parameters["mac"];
var random = parameters["r"];
var hash = parameters["md"];
if (gid == null)
{
return QuitWithError(ErrorCode.ErrorNoGid);
}
if (mac == null)
{
return QuitWithError(ErrorCode.ErrorNoMac);
}
if (random == null)
{
return QuitWithError(ErrorCode.ErrorNoRandom);
}
if (hash == null)
{
return QuitWithError(ErrorCode.ErrorNoHash);
}
if (!MacValid(mac))
{
return QuitWithError(ErrorCode.ErrorInvalidMac);
}
if (!Md5Valid(hash))
{
return QuitWithError(ErrorCode.ErrorInvalidHash);
}
using var md5 = MD5.Create();
var ticket = string.Join(string.Empty,
md5.ComputeHash(Encoding.UTF8.GetBytes(gid)).Select(b => b.ToString("x2")));
var response = $"host=card_id=7020392000147361,relay_addr={Configs.SETTINGS.RelayServer},relay_port={Configs.SETTINGS.RelayPort}\n" +
"no=1337\n" +
"name=123\n" +
"pref=nesys\n" +
"addr=nesys@home\n" +
"x-next-time=15\n" +
$"x-img=http://{Configs.SETTINGS.ServerIp}{Configs.STATIC_BASE_ROUTE}/news.png\n" +
$"x-ranking=http://{Configs.SETTINGS.ServerIp}{Configs.RANK_BASE_ROUTE}/ranking.php\n" +
$"ticket={ticket}";
return response;
}
[Route(HttpVerbs.Get, "/cursel.php")]
public string Cursel()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "1\n";
}
[Route(HttpVerbs.Get, "/data.php")]
public string Data()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
var responseList = Configs.SETTINGS.ResponseData.ToList();
var count = responseList.Count;
var dataString = new StringBuilder();
for (var i = 0; i < count; i++)
{
var data = responseList[i];
var fileUrl = data.FileName.StartsWith("/") ? $"{DataUrl}{data.FileName}" : $"{DataUrl}/{data.FileName}";
dataString.Append($"{i},{fileUrl},{data.NotBeforeUnixTime},{data.NotAfterUnixTime},{data.Md5},{data.Index}");
dataString.Append('\n');
}
return $"count={count}\n" +
"nexttime=1\n" +
(Configs.SETTINGS.DownloadEvents ? dataString.ToString().TrimEnd('\n') : "");
}
[Route(HttpVerbs.Get, "/gameinfo.php")]
public string GameInfo()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "0\n" +
"3\n" +
"301000,test1\n" +
"302000,test2\n" +
"303000,test3";
}
private static bool MacValid(string mac)
{
return Regex.IsMatch(mac, "^[a-fA-F0-9]{12}$");
}
private static bool Md5Valid(string md5)
{
return Regex.IsMatch(md5, "^[a-fA-F0-9]{32}$");
}
private static string QuitWithError(ErrorCode errorCode)
{
return $"error={(int)errorCode}";
}
private enum ErrorCode
{
ErrorNoGid,
ErrorNoMac,
ErrorNoRandom,
ErrorNoHash,
ErrorInvalidMac,
ErrorInvalidHash
}
}

View File

@ -1,24 +0,0 @@
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using Swan.Logging;
namespace GCLocalServerRewrite.controllers
{
// TODO: Add proper update check response
public class UpdateController : WebApiController
{
[Route(HttpVerbs.Get, "/check.php")]
public string CheckUpdate()
{
var parameters = HttpContext.GetRequestQueryData();
foreach (var key in parameters.AllKeys)
{
$"Key {key}: {parameters[key]}".Info();
}
HttpContext.Response.StatusCode = 404;
return "Not found";
}
}
}

View File

@ -1,22 +0,0 @@
using System.Net.Mime;
using System.Text;
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
namespace GCLocalServerRewrite.controllers;
public class UploadServiceController : WebApiController
{
[Route(HttpVerbs.Post, "/upload.php")]
// ReSharper disable once UnusedMember.Global
public string UploadService()
{
HttpContext.Response.ContentType = MediaTypeNames.Text.Plain;
HttpContext.Response.ContentEncoding = new UTF8Encoding(false);
HttpContext.Response.KeepAlive = true;
return "1\n" +
"OK";
}
}

Binary file not shown.

View File

@ -1,34 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class Avatar : Record , IIdModel, ICardIdModel
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "avatar_id")]
public int AvatarId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
[XmlElement("new_flag")]
public int NewFlag { get; set; } = 1;
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public void SetId(int id)
{
AvatarId = id;
}
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,60 +0,0 @@
using System.Xml.Serialization;
using ChoETL;
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
[Table("card_main")]
public class Card : ICardIdModel
{
[PrimaryKey]
[Column("card_id")]
[ChoXmlElementRecordField(FieldName = "card_id")]
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[Column("player_name")]
[ChoXmlElementRecordField(FieldName = "player_name")]
[XmlElement(ElementName = "player_name")]
public string PlayerName { get; set; } = string.Empty;
[Column("score_i1")]
[ChoXmlElementRecordField(FieldName = "score_i1")]
[XmlElement("score_i1")]
public int Score { get; set; }
[Column("fcol1")]
[ChoXmlElementRecordField(FieldName = "fcol1")]
[XmlElement("fcol1")]
public int Fcol1 { get; set; }
[Column("fcol2")]
[ChoXmlElementRecordField(FieldName = "fcol2")]
[XmlElement("fcol2")]
public int Fcol2 { get; set; }
[Column("fcol3")]
[ChoXmlElementRecordField(FieldName = "fcol3")]
[XmlElement("fcol3")]
public int Fcol3 { get; set; }
[Column("achieve_status")]
[ChoXmlElementRecordField(FieldName = "achieve_status")]
[XmlElement("achieve_status")]
public string AchieveStatus { get; set; } = string.Empty;
[Column("created")]
[ChoXmlElementRecordField(FieldName = "created")]
[XmlElement("created")]
public string Created { get; set; } = "2017-01-01 08:00:00";
[Column("modified")]
[ChoXmlElementRecordField(FieldName = "modified")]
[XmlElement("modified")]
public string Modified { get; set; } = "2017-01-01 08:00:00";
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,30 +0,0 @@
using System.Xml.Serialization;
using ChoETL;
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
[Table("card_bdata")]
public class CardBData : ICardIdModel
{
[PrimaryKey]
[Column("card_id")]
[ChoXmlElementRecordField(FieldName = "card_id")]
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[Column("bdata")]
[ChoXmlElementRecordField(FieldName = "bdata")]
[XmlElement(ElementName = "bdata")]
public string BData { get; set; } = string.Empty;
[Column("bdata_size")]
[ChoXmlElementRecordField(FieldName = "bdata_size")]
[XmlElement(ElementName = "bdata_size")]
public int BDataSize { get; set; }
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,102 +0,0 @@
using System.Xml.Serialization;
using ChoETL;
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
[Table("card_detail")]
public class CardDetail : Record, ICardIdModel
{
[PrimaryKey]
[Column("card_id")]
[ChoXmlElementRecordField(FieldName = "card_id")]
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[PrimaryKey]
[Column("pcol1")]
[ChoXmlElementRecordField(FieldName = "pcol1")]
[XmlElement(ElementName = "pcol1")]
public int Pcol1 { get; set; }
[PrimaryKey]
[Column("pcol2")]
[ChoXmlElementRecordField(FieldName = "pcol2")]
[XmlElement(ElementName = "pcol2")]
public int Pcol2 { get; set; }
[PrimaryKey]
[Column("pcol3")]
[ChoXmlElementRecordField(FieldName = "pcol3")]
[XmlElement(ElementName = "pcol3")]
public int Pcol3 { get; set; }
[Column("score_i1")]
[ChoXmlElementRecordField(FieldName = "score_i1")]
[XmlElement(ElementName = "score_i1")]
public long ScoreI1 { get; set; }
[Column("score_ui1")]
[ChoXmlElementRecordField(FieldName = "score_ui1")]
[XmlElement(ElementName = "score_ui1")]
public long ScoreUi1 { get; set; }
[Column("score_ui2")]
[ChoXmlElementRecordField(FieldName = "score_ui2")]
[XmlElement(ElementName = "score_ui2")]
public long ScoreUi2 { get; set; }
[Column("score_ui3")]
[ChoXmlElementRecordField(FieldName = "score_ui3")]
[XmlElement(ElementName = "score_ui3")]
public long ScoreUi3 { get; set; }
[Column("score_ui4")]
[ChoXmlElementRecordField(FieldName = "score_ui4")]
[XmlElement(ElementName = "score_ui4")]
public long ScoreUi4 { get; set; }
[Column("score_ui5")]
[ChoXmlElementRecordField(FieldName = "score_ui5")]
[XmlElement(ElementName = "score_ui5")]
public long ScoreUi5 { get; set; }
[Column("score_ui6")]
[ChoXmlElementRecordField(FieldName = "score_ui6")]
[XmlElement(ElementName = "score_ui6")]
public long ScoreUi6 { get; set; }
[Column("score_bi1")]
[ChoXmlElementRecordField(FieldName = "score_bi1")]
[XmlElement(ElementName = "score_bi1")]
public long ScoreBi1 { get; set; }
[Column("last_play_tenpo_id")]
[ChoXmlElementRecordField(FieldName = "last_play_tenpo_id")]
[XmlElement(ElementName = "last_play_tenpo_id")]
public string LastPlayShopId { get; set; } = "1337";
[Column("fcol1")]
[ChoXmlElementRecordField(FieldName = "fcol1")]
[XmlElement("fcol1")]
public int Fcol1 { get; set; }
[Column("fcol2")]
[ChoXmlElementRecordField(FieldName = "fcol2")]
[XmlElement("fcol2")]
public int Fcol2 { get; set; }
[Column("fcol3")]
[ChoXmlElementRecordField(FieldName = "fcol3")]
[XmlElement("fcol3")]
public int Fcol3 { get; set; }
[Column("last_play_time")]
[XmlIgnore]
public DateTime LastPlayTime { get; set; } = DateTime.MinValue;
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,20 +0,0 @@
using System.Xml.Serialization;
using ChoETL;
namespace GCLocalServerRewrite.models;
[ChoXmlRecordObject(XPath = "/root/data")]
public class CardDetailReadData
{
[XmlElement(ElementName = "pcol1")]
[ChoXmlElementRecordField(FieldName = "pcol1")]
public int Pcol1 { get; set; }
[XmlElement(ElementName = "pcol2")]
[ChoXmlElementRecordField(FieldName = "pcol2")]
public int Pcol2 { get; set; }
[XmlElement(ElementName = "pcol3")]
[ChoXmlElementRecordField(FieldName = "pcol3")]
public int Pcol3 { get; set; }
}

View File

@ -1,16 +0,0 @@
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
public class CardPlayCount
{
[PrimaryKey]
[Column("card_id")]
public long CardId { get; set; }
[Column("play_count")]
public int PlayCount { get; set; }
[Column("last_played_time")]
public DateTime LastPlayed { get; set; }
}

View File

@ -1,24 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class Coin
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "current")]
public int CurrentCoins { get; set; }
[XmlElement(ElementName = "total")]
public int TotalCoins { get; set; }
[XmlElement("monthly")]
public int MonthlyCoins { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
}

View File

@ -1,6 +0,0 @@
namespace GCLocalServerRewrite.models;
public interface ICardIdModel
{
public void SetCardId(long cardId);
}

View File

@ -1,6 +0,0 @@
namespace GCLocalServerRewrite.models;
public interface IIdModel
{
public void SetId(int id);
}

View File

@ -1,37 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class Item : Record, IIdModel, ICardIdModel
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "item_id")]
public int ItemId { get; set; }
[XmlElement(ElementName = "item_num")]
public int ItemNum { get; set; } = 90;
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public void SetId(int id)
{
ItemId = id;
}
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,47 +0,0 @@
using System.Xml.Serialization;
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
[Table("music_unlock")]
public class Music : Record
{
[PrimaryKey]
[Column("music_id")]
[XmlElement("music_id")]
public int MusicId { get; set; }
[Column("title")]
[XmlElement(ElementName = "title")]
public string? Title { get; set; } = string.Empty;
[Column("artist")]
[XmlElement(ElementName = "artist")]
public string? Artist
{
get => _artist;
set => _artist = value ?? string.Empty;
}
private string? _artist = string.Empty;
[Column("release_date")]
[XmlElement(ElementName = "release_date")]
public string ReleaseDate { get; set; } = "2013-01-01 08:00:00";
[Column("end_date")]
[XmlElement(ElementName = "end_date")]
public string EndDate { get; set; } = "2030-01-01 08:00:00";
[Column("new_flag")]
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[Column("use_flag")]
[XmlElement("use_flag")]
public int UseFlag { get; set; }
[Ignore]
[XmlElement("calc_flag")]
public uint CalcFlag { get; set; } = 2;
}

View File

@ -1,17 +0,0 @@
using System.Xml.Serialization;
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
[Table("music_aou")]
public class MusicAou : Record
{
[PrimaryKey]
[Column("music_id")]
[XmlElement("music_id")]
public int MusicId { get; set; }
[Column("use_flag")]
[XmlElement("use_flag")]
public int UseFlag { get; set; }
}

View File

@ -1,17 +0,0 @@
using System.Xml.Serialization;
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
[Table("music_extra")]
public class MusicExtra : Record
{
[PrimaryKey]
[Column("music_id")]
[XmlElement("music_id")]
public int MusicId { get; set; }
[Column("use_flag")]
[XmlElement("use_flag")]
public int UseFlag { get; set; }
}

View File

@ -1,34 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class Navigator : Record, IIdModel, ICardIdModel
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "navigator_id")]
public int NavigatorId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public void SetId(int id)
{
NavigatorId = id;
}
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,96 +0,0 @@
using System.Xml.Serialization;
using ChoETL;
namespace GCLocalServerRewrite.models;
public class OnlineMatchingEntry: Record
{
[XmlElement(ElementName = "machine_id")]
[ChoXmlElementRecordField(FieldName = "machine_id")]
public long MachineId { get; set; }
[XmlElement(ElementName = "event_id")]
[ChoXmlElementRecordField(FieldName = "event_id")]
public long EventId { get; set; }
[XmlElement(ElementName = "matching_id")]
[ChoXmlElementRecordField(FieldName = "matching_id")]
public long MatchingId { get; set; }
[XmlElement(ElementName = "entry_no")]
[ChoXmlElementRecordField(FieldName = "entry_no")]
public long EntryNo { get; set; }
[XmlElement(ElementName = "entry_start")]
[ChoXmlElementRecordField(FieldName = "entry_start")]
public string EntryStart { get; set; } = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
[XmlElement(ElementName = "status")]
[ChoXmlElementRecordField(FieldName = "status")]
public long Status { get; set; } = 1;
[XmlElement(ElementName = "card_id")]
[ChoXmlElementRecordField(FieldName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "player_name")]
[ChoXmlElementRecordField(FieldName = "player_name")]
public string PlayerName { get; set; } = string.Empty;
[XmlElement(ElementName = "avatar_id")]
[ChoXmlElementRecordField(FieldName = "avatar_id")]
public long AvatarId { get; set; }
[XmlElement(ElementName = "title_id")]
[ChoXmlElementRecordField(FieldName = "title_id")]
public long TitleId { get; set; }
[XmlElement(ElementName = "class_id")]
[ChoXmlElementRecordField(FieldName = "class_id")]
public long ClassId { get; set; }
[XmlElement(ElementName = "group_id")]
[ChoXmlElementRecordField(FieldName = "group_id")]
public long GroupId { get; set; }
[XmlElement(ElementName = "tenpo_id")]
[ChoXmlElementRecordField(FieldName = "tenpo_id")]
public long TenpoId { get; set; }
[XmlElement(ElementName = "tenpo_name")]
[ChoXmlElementRecordField(FieldName = "tenpo_name")]
public string TenpoName { get; set; } = "1337";
[XmlElement(ElementName = "pref_id")]
[ChoXmlElementRecordField(FieldName = "pref_id")]
public long PrefId { get; set; }
[XmlElement(ElementName = "pref")]
[ChoXmlElementRecordField(FieldName = "pref")]
public string Pref { get; set; } = "nesys";
[XmlElement(ElementName = "message_id")]
[ChoXmlElementRecordField(FieldName = "message_id")]
public long MessageId { get; set; }
/// <summary>
/// Communication timeout?
/// </summary>
[XmlElement(ElementName = "matching_timeout")]
[ChoXmlElementRecordField(FieldName = "matching_timeout")]
public long MatchingTimeout { get; set; } = 99;
/// <summary>
/// Wait time
/// </summary>
[XmlElement(ElementName = "matching_wait_time")]
[ChoXmlElementRecordField(FieldName = "matching_wait_time")]
public long MatchingWaitTime { get; set; } = 10;
/// <summary>
/// Seems not used
/// </summary>
[XmlElement(ElementName = "matching_remaining_time")]
[ChoXmlElementRecordField(FieldName = "matching_remaining_time")]
public long MatchingRemainingTime { get; set; } = 89;
}

View File

@ -1,9 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class OnlineMatchingResult
{
[XmlElement(ElementName = "status")]
public int Status { get; set; }
}

View File

@ -1,56 +0,0 @@
using ChoETL;
namespace GCLocalServerRewrite.models;
[ChoXmlRecordObject(XPath = "/root/data")]
public class OnlineMatchingResultData
{
[ChoXmlElementRecordField(FieldName = "event_id")]
public long EventId { get; set; }
[ChoXmlElementRecordField(FieldName = "matching_id")]
public long MatchingId { get; set; }
[ChoXmlElementRecordField(FieldName = "class_id")]
public long ClassId { get; set; }
[ChoXmlElementRecordField(FieldName = "group_id")]
public long GroupId { get; set; }
[ChoXmlElementRecordField(FieldName = "result_score")]
public long ResultScore { get; set; }
[ChoXmlElementRecordField(FieldName = "result_star")]
public long ResultStar { get; set; }
[ChoXmlElementRecordField(FieldName = "result_rank")]
public long ResultRank { get; set; }
[ChoXmlElementRecordField(FieldName = "music_id_1st")]
public long MusicIdFirst { get; set; }
[ChoXmlElementRecordField(FieldName = "music_id_2nd")]
public long MusicIdSecond { get; set; }
[ChoXmlElementRecordField(FieldName = "music_id_3rd")]
public long MusicIdThird { get; set; }
[ChoXmlElementRecordField(FieldName = "difficulty_lv_1st")]
public long DifficultyFirst { get; set; }
[ChoXmlElementRecordField(FieldName = "difficulty_lv_2nd")]
public long DifficultySecond { get; set; }
[ChoXmlElementRecordField(FieldName = "difficulty_lv_3rd")]
public long DifficultyThird { get; set; }
[ChoXmlElementRecordField(FieldName = "item_id_1st")]
public long ItemIdFirst { get; set; }
[ChoXmlElementRecordField(FieldName = "item_id_2nd")]
public long ItemIdSecond { get; set; }
[ChoXmlElementRecordField(FieldName = "item_id_3rd")]
public long ItemIdThird { get; set; }
}

View File

@ -1,20 +0,0 @@
using ChoETL;
namespace GCLocalServerRewrite.models;
[ChoXmlRecordObject(XPath = "/root/data")]
// ReSharper disable once ClassNeverInstantiated.Global
public class OnlineMatchingUpdateData
{
[ChoXmlElementRecordField(FieldName = "action")]
public long Action { get; set; }
[ChoXmlElementRecordField(FieldName = "event_id")]
public long EventId { get; set; }
[ChoXmlElementRecordField(FieldName = "matching_id")]
public long MatchingId { get; set; }
[ChoXmlElementRecordField(FieldName = "message_id")]
public long MessageId { get; set; }
}

View File

@ -1,13 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
[XmlRoot("root")]
public class PlayNumRankContainer
{
[XmlArray("play_num_rank")]
public List<PlayNumRankRecord> PlayNumRankRecords { get; set; } = new();
[XmlElement("ranking_status")]
public RankStatus? RankStatus { get; set; }
}

View File

@ -1,34 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
[XmlType("record")]
public class PlayNumRankRecord
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlElement("rank")]
public int Rank { get; set; }
[XmlElement("rank2")]
public int Rank2 { get; set; }
[XmlElement("prev_rank")]
public int PrevRank { get; set; }
[XmlElement("prev_rank2")]
public int PrevRank2 { get; set; }
[XmlElement("pcol1")]
public int Pcol1 { get; set; }
[XmlElement("score_bi1")]
public int ScoreBi1 { get; set; }
[XmlElement("title")]
public string? Title { get; set; }
[XmlElement("artist")]
public string? Artist { get; set; }
}

View File

@ -1,22 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
[XmlType("ranking_status")]
public class RankStatus
{
[XmlElement("table_name")]
public string TableName { get; set; } = "rank_table";
[XmlElement("start_date")]
public string StartDate { get; set; } = "2021-05-30";
[XmlElement("end_date")]
public string EndDate { get; set; } = "2022-06-08";
[XmlElement("status")]
public int Status { get; set; }
[XmlElement("rows")]
public int Rows { get; set; }
}

View File

@ -1,11 +0,0 @@
using System.Xml.Serialization;
using SQLite.Net2;
namespace GCLocalServerRewrite.models;
public class Record
{
[XmlAttribute(AttributeName = "id")]
[Ignore]
public int RecordId { get; set; }
}

View File

@ -1,21 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class Session
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "mac_addr")]
public string Mac { get; set; } = "000000000000";
[XmlElement(ElementName = "session_id")]
public string SessionId { get; set; } = "12345678901234567890123456789012";
[XmlElement(ElementName = "expires")]
public int Expires { get; set; }
[XmlElement(ElementName = "player_id")]
public int PlayerId { get; set; }
}

View File

@ -1,34 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class Skin : Record, IIdModel, ICardIdModel
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "skin_id")]
public int SkinId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public void SetId(int id)
{
SkinId = id;
}
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,34 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class SoundEffect : Record, IIdModel, ICardIdModel
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "sound_effect_id")]
public int SeId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public void SetId(int id)
{
SeId = id;
}
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,34 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class Title : Record, IIdModel, ICardIdModel
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "title_id")]
public int TitleId { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
[XmlElement("new_flag")]
public int NewFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
public void SetId(int id)
{
TitleId = id;
}
public void SetCardId(long cardId)
{
CardId = cardId;
}
}

View File

@ -1,12 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class TotalTrophy
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "total_trophy_num")]
public int TrophyNum { get; set; }
}

View File

@ -1,33 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class UnlockKeynum : Record
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "reward_id")]
public int RewardId { get; set; }
[XmlElement(ElementName = "key_num")]
public int KeyNum { get; set; }
[XmlElement(ElementName = "reward_count")]
public int RewardCount { get; set; }
[XmlElement("expired_flag")]
public int ExpiredFlag { get; set; }
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
[XmlElement("cash_flag")]
public int CashFlag { get; set; }
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
}

View File

@ -1,57 +0,0 @@
using System.Xml.Serialization;
namespace GCLocalServerRewrite.models;
public class UnlockReward : Record
{
[XmlElement(ElementName = "card_id")]
public long CardId { get; set; }
[XmlElement(ElementName = "reward_id")]
public int RewardId { get; set; }
[XmlElement(ElementName = "reward_type")]
public int RewardType { get; set; }
[XmlElement(ElementName = "open_date")]
public string OpenDate { get; set; } = "2021-05-30";
[XmlElement(ElementName = "close_date")]
public string CloseDate { get; set; } = "2030-05-30";
[XmlElement(ElementName = "open_time")]
public string OpenTime { get; set; } = "00:00:01";
[XmlElement(ElementName = "close_time")]
public string CloseTime { get; set; } = "23:59:59";
[XmlElement(ElementName = "target_id")]
public int TargetId { get; set; }
[XmlElement(ElementName = "target_num")]
public int TargetNum { get; set; }
[XmlElement(ElementName = "key_num")]
public int KeyNum { get; set; }
[XmlElement("display_flag")]
public int DisplayFlag { get; set; } = 1;
[XmlElement("use_flag")]
public int UseFlag { get; set; } = 1;
[XmlElement("limited_flag")]
public int LimitedFlag { get; set; }
[XmlElement("open_unixtime")]
public long OpenUnixTime { get; set; } = 1622304001;
[XmlElement("close_unixtime")]
public long CloseUnixTime { get; set; } = 1906387199;
[XmlElement("created")]
public string Created { get; set; } = "1";
[XmlElement("modified")]
public string Modified { get; set; } = "1";
}

View File

@ -1,101 +0,0 @@
using System.Security.Cryptography.X509Certificates;
using EmbedIO;
using EmbedIO.Actions;
using EmbedIO.Files;
using EmbedIO.WebApi;
using GCLocalServerRewrite.common;
using GCLocalServerRewrite.controllers;
using GCLocalServerRewrite.models;
using Swan.Logging;
using System.Text;
using Swan;
namespace GCLocalServerRewrite.server;
public class Server
{
public static WebServer CreateWebServer(IEnumerable<string> urlPrefixes)
{
InitializeDatabase();
X509Certificate2 cert;
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
cert = CertificateHelper.InitializeCertificate();
}
else
{
var certPath = Path.Combine(PathHelper.CertRootPath, "cert.pfx");
var certPassword = string.Empty;
var collection = new X509Certificate2Collection();
collection.Import(certPath, null, X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.Exportable);
if (!collection.Any())
{
SelfCheck.Failure("Failed to import certificate!!!");
}
cert = collection.First();
}
var server = new WebServer(webServerOptions => webServerOptions
.WithUrlPrefixes(urlPrefixes)
.WithCertificate(cert)
.WithMode(HttpListenerMode.EmbedIO))
.WithLocalSessionManager()
.WithCors()
.WithWebApi(Configs.API_BASE_ROUTE, module => module.WithController<ApiController>())
.WithWebApi(Configs.CARD_SERVICE_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<CardServiceController>())
.WithWebApi(Configs.OPTION_SERVICE_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<OptionServiceController>())
.WithWebApi(Configs.UPLOAD_SERVICE_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<UploadServiceController>())
.WithWebApi(Configs.RESPONE_SERVICE_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<ResponeServiceController>())
.WithWebApi(Configs.INCOM_SERVICE_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<IncomServiceController>())
.WithWebApi(Configs.ALIVE_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<AliveController>())
.WithWebApi(Configs.SERVER_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<ServerController>())
.WithWebApi(Configs.RANK_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<RankController>())
.WithWebApi(Configs.UPDATE_SERVICE_BASE_ROUTE, ResponseSerializer.None(true),
module => module.WithController<UpdateController>())
.WithStaticFolder(Configs.STATIC_BASE_ROUTE, PathHelper.HtmlRootPath, true, m => m
.WithContentCaching(Configs.USE_FILE_CACHE))
// Add static files after other modules to avoid conflicts
.WithStaticFolder("/", PathHelper.HtmlRootPath, true, m =>
{
m.WithContentCaching(Configs.USE_FILE_CACHE);
m.OnMappingFailed = async (context, info) =>
{
var htmlContents = await File.ReadAllTextAsync(Path.Combine(PathHelper.HtmlRootPath, "index.html"));
context.Response.StatusCode = 200;
await context.SendStringAsync(htmlContents, "text/html", Encoding.UTF8);
};
})
.WithModule(new ActionModule("/", HttpVerbs.Any,
ctx => ctx.SendDataAsync(new { Message = "Error" })));
server.AddCustomMimeType(".dll", "application/octet-stream");
server.AddCustomMimeType(".blat", "application/octet-stream");
server.AddCustomMimeType(".dat", "application/octet-stream");
server.AddCustomMimeType(".json", "application/json");
server.AddCustomMimeType(".wasm", "application/wasm");
server.AddCustomMimeType(".woff", "application/font-woff");
server.AddCustomMimeType(".woff2", "application/font-woff2");
// Listen for state changes.
server.StateChanged += (_, e) => $"WebServer New State - {e.NewState}".Info();
return server;
}
private static void InitializeDatabase()
{
DatabaseHelper.InitializeLocalDatabase(Configs.SETTINGS.CardDbName,
typeof(Card), typeof(CardBData), typeof(CardDetail), typeof(CardPlayCount));
DatabaseHelper.InitializeLocalDatabase(Configs.SETTINGS.MusicDbName,
typeof(Music), typeof(MusicAou), typeof(MusicExtra));
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
Hello world!
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 818 B

View File

@ -1,131 +0,0 @@
using MatchServer.Storage;
using Microsoft.AspNetCore.Mvc;
using SharedProject.models;
namespace MatchServer.Controllers;
[ApiController]
[Route("[controller]")]
public class MatchingController : ControllerBase
{
private readonly ILogger<MatchingController> logger;
public MatchingController(ILogger<MatchingController> logger) {
this.logger = logger;
}
[HttpPost("Start")]
public ActionResult<IEnumerable<OnlineMatchingData>> StartOnlineMatching(OnlineMatchingData startData)
{
logger.LogInformation("Start matching, card id {CardId}, player name {PlayerName}", startData.CardId, startData.PlayerName);
var matchingDb = MatchingDb.GetInstance;
lock (matchingDb.DbLock)
{
foreach (var (matchingId, entries) in matchingDb.MatchingDictionary)
{
if (entries.Count >= 4)
{
continue;
}
if (entries.Any(data => data.CardId == startData.CardId))
{
logger.LogWarning("Card id {CardId} already exists in matching id {MatchingId}", startData.CardId, matchingId);
continue;
}
entries.Add(startData);
startData.MatchingId = matchingId;
startData.EntryNo = entries.Count - 1;
break;
}
if (startData.MatchingId != 0)
{
return Ok(matchingDb.MatchingDictionary[startData.MatchingId]);
}
startData.MatchingId = matchingDb.MatchingDictionary.Count + 1;
startData.EntryNo = 0;
matchingDb.MatchingDictionary[startData.MatchingId] = new List<OnlineMatchingData>
{
startData
};
return Ok(matchingDb.MatchingDictionary[startData.MatchingId]);
}
}
[HttpPost("Update")]
public ActionResult<IEnumerable<OnlineMatchingUpdateRequest>> UpdateOnlineMatching(OnlineMatchingUpdateRequest updateData)
{
var matchingDb = MatchingDb.GetInstance;
var matchingId = updateData.MatchingId;
logger.LogInformation("Update matching, card id is {CardId}, matching id is {MatchingId}", updateData.CardId, updateData.MatchingId);
if (!matchingDb.MatchingDictionary.ContainsKey(matchingId))
{
return BadRequest();
}
var dataList = matchingDb.MatchingDictionary[matchingId];
var data = dataList.Find(data => data.CardId == updateData.CardId);
if (data is null)
{
return BadRequest();
}
data.MessageId = updateData.MessageId;
if (data.MatchingRemainingTime <= 0)
{
data.Status = 3;
return Ok(dataList);
}
data.MatchingRemainingTime--;
return Ok(dataList);
}
[HttpPost("Finish")]
public ActionResult<bool> FinishOnlineMatching(OnlineMatchingFinishRequest request)
{
var matchingDb = MatchingDb.GetInstance;
var matchingId = request.MatchingId;
lock (matchingDb.DbLock)
{
if (!matchingDb.MatchingDictionary.ContainsKey(matchingId))
{
return BadRequest();
}
var dataList = matchingDb.MatchingDictionary[matchingId];
var index = dataList.FindIndex(data => data.CardId == request.CardId);
dataList.RemoveAt(index);
}
return Ok(true);
}
[HttpGet("Debug")]
public ActionResult<Dictionary<long, List<OnlineMatchingUpdateRequest>>> InspectOnlineMatching()
{
var matchingDb = MatchingDb.GetInstance;
return Ok(matchingDb.MatchingDictionary);
}
[HttpGet("Clear")]
public ActionResult<bool> Clear()
{
var matchingDb = MatchingDb.GetInstance;
lock (matchingDb.DbLock)
{
matchingDb.MatchingDictionary.Clear();
}
return Ok(true);
}
}

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedProject\SharedProject.csproj" />
</ItemGroup>
</Project>

View File

@ -1,21 +0,0 @@
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapControllers();
app.Run();

View File

@ -1,31 +0,0 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:45319",
"sslPort": 44313
}
},
"profiles": {
"MatchServer": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7023;http://localhost:5038",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,22 +0,0 @@
using SharedProject.models;
namespace MatchServer.Storage;
public class MatchingDb
{
public Dictionary<long, List<OnlineMatchingData>> MatchingDictionary;
public object DbLock = new();
static MatchingDb()
{
}
private MatchingDb()
{
MatchingDictionary = new Dictionary<long, List<OnlineMatchingData>>();
}
public static MatchingDb GetInstance { get; } = new MatchingDb();
}

View File

@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -1,11 +0,0 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

View File

@ -1,12 +0,0 @@
<linker>
<assembly fullname="MudBlazor" preserve="all">
<type fullname="MudBlazor.FilterOperator" preserve="all" />
<type fullname="MudBlazor.FilterOperator/String" preserve="all" />
<type fullname="MudBlazor.FilterOperator/Number" preserve="all" />
<type fullname="MudBlazor.FilterOperator/Enum" preserve="all" />
<type fullname="MudBlazor.FilterOperator/Boolean" preserve="all" />
<type fullname="MudBlazor.FilterOperator/DateTime" preserve="all" />
<type fullname="MudBlazor.FilterDefinition`1" preserve="all" />
<type fullname="MudBlazor.Filter" preserve="all" />
</assembly>
</linker>

View File

@ -1,81 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<RunAOTCompilation>False</RunAOTCompilation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<RunAOTCompilation>True</RunAOTCompilation>
</PropertyGroup>
<PropertyGroup>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
</PropertyGroup>
<ItemGroup>
<None Remove="ILLink.Descriptors.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ILLink.Descriptors.xml">
<LogicalName>ILLink.Descriptors.xml</LogicalName>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="GenFu" Version="1.6.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.10" PrivateAssets="all" />
<PackageReference Include="MudBlazor" Version="6.0.17" />
<PackageReference Include="protobuf-net" Version="3.1.22" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedProject\SharedProject.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\data\avatar.dat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\data\avatar.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\data\navigator.dat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\data\navigator.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\data\title.dat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\data\title.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
<Content Update="wwwroot\news.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Utils" />
</ItemGroup>
</Project>

View File

@ -1,25 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31717.71
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MudAdmin", "MudAdmin.csproj", "{D6DF8A15-5B8D-4302-90D2-FC71264B18CC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D6DF8A15-5B8D-4302-90D2-FC71264B18CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6DF8A15-5B8D-4302-90D2-FC71264B18CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6DF8A15-5B8D-4302-90D2-FC71264B18CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6DF8A15-5B8D-4302-90D2-FC71264B18CC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {58C61F27-917C-435B-8900-9B3CE8F8DCD4}
EndGlobalSection
EndGlobal

View File

@ -1,73 +0,0 @@
@using SharedProject.common
@using System.Text.RegularExpressions
@inject HttpClient Client
@inject ILogger<ChangePlayerNameDialog> Logger
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">
Change Player Name
</MudText>
</TitleContent>
<DialogContent>
<MudForm @bind-IsValid="IsValid">
<MudTextField Value="@Data.CardId" Label="CardId" ReadOnly="true"/>
<MudTextField @bind-Value="Data.PlayerName"
Immediate="true"
Counter="SharedConstants.MAX_PLAYER_NAME_LENGTH"
MaxLength="SharedConstants.MAX_PLAYER_NAME_LENGTH"
Validation="ValidatePlayerName"/>
</MudForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit" Disabled="@(!IsValid)">Confirm</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; } = null!;
[Parameter]
public SharedProject.models.User Data { get; set; } = null!;
private string originalName = string.Empty;
private bool IsValid { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
originalName = new string(Data.PlayerName);
}
async Task Submit()
{
Logger.LogInformation("Data is {cardId}, {name}", Data.CardId, Data.PlayerName);
var response = await Client.PostAsJsonAsync("api/Users/SetPlayerName", Data);
var result = await response.Content.ReadFromJsonAsync<bool>();
Logger.LogInformation("SetPlayerName result is {result}", result);
MudDialog.Close(DialogResult.Ok(result));
}
void Cancel()
{
Data.PlayerName = originalName;
MudDialog.Cancel();
}
private static string? ValidatePlayerName(string playerName)
{
const string pattern = @"^[a-zA-Z0-9!?,./\-+:<>_\\@*#&=() ]{1,8}$";
return playerName.Length switch
{
0 => "Player name cannot be empty!",
> 8 => "Player name cannot be longer than 8 characters!",
_ => !Regex.IsMatch(playerName, pattern) ? "Player name contains invalid character!" : null
};
}
}

View File

@ -1,57 +0,0 @@
@using SharedProject.models
@inject HttpClient Client
@inject ILogger<FavoriteDialog> Logger
<MudDialog>
<TitleContent>
@if (!Data.IsFavorite)
{
<MudText Typo="Typo.h6">
<MudIcon Icon="@Icons.Material.Filled.BookmarkAdd" Color="Color.Secondary"/>
Add to favorite?
</MudText>
}
else
{
<MudText Typo="Typo.h6">
<MudIcon Icon="@Icons.Material.Filled.BookmarkRemove" Color="Color.Secondary"/>
Remove from favorite?
</MudText>
}
</TitleContent>
<DialogContent>
<MudTextField Value="@Data.Title" Label="Song Title" ReadOnly="true"/>
<MudTextField Value="@Data.Artist" Label="Artist Name" ReadOnly="true"/>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="Color.Primary" OnClick="Submit">Confirm</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
MudDialogInstance MudDialog { get; set; } = null!;
[Parameter]
public SongPlayData Data { get; set; } = null!;
[Parameter]
public long CardId { get; set; }
async Task Submit()
{
var postData = new MusicFavoriteData
{
CardId = CardId,
IsFavorite = !Data.IsFavorite,
MusicId = Data.MusicId
};
Logger.LogInformation("Data is {cardId}, {musicId}", CardId, Data.MusicId);
var response = await Client.PostAsJsonAsync("api/UserDetail/SetMusicFavorite", postData);
var result = await response.Content.ReadFromJsonAsync<bool>();
Logger.LogInformation("Favorite result is {result}", result);
MudDialog.Close(DialogResult.Ok(result));
}
void Cancel() => MudDialog.Cancel();
}

View File

@ -1,6 +0,0 @@
@page "/"
<PageTitle>Index</PageTitle>
<MudText Typo="Typo.h3" GutterBottom="true">Admin page for GC local server</MudText>
<MudText Class="mb-8">Powered by MudBlazor</MudText>

View File

@ -1,305 +0,0 @@
@page "/user/{CardId:long}"
@using SharedProject.models
@using SharedProject.enums
<PageTitle>User</PageTitle>
<MudContainer>
@if (pageLoading)
{
<MudSkeleton Width="1184px" Height="57px"/>
<MudSkeleton Width="1184px" Height="57px"/>
<MudSkeleton Width="1184px" Height="57px"/>
}
else
{
if (userDetail is null)
{
<MudText Typo="Typo.h3">No Data</MudText>
}
else
{
<MudExpansionPanels>
<MudExpansionPanel Text="Total Result">
<MudList>
<MudListSubheader>Player Name: @userDetail.PlayerName</MudListSubheader>
<MudListItem>Total Score: @userDetail.TotalScore</MudListItem>
<MudListItem>Average Score: @userDetail.AverageScore</MudListItem>
<MudListItem>Played Song Count: @userDetail.PlayedSongCount / @userDetail.TotalSongCount</MudListItem>
<MudListItem>Cleared Stage Count: @userDetail.ClearedStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>No Miss Stage Count: @userDetail.NoMissStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>Full Chain Stage Count: @userDetail.FullChainStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>Perfect Stage Count: @userDetail.PerfectStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S and Above Stage Count: @userDetail.SAboveStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S+ and Above Stage Count: @userDetail.SPlusAboveStageCount / @userDetail.TotalStageCount</MudListItem>
<MudListItem>S++ and Above Stage Count: @userDetail.SPlusPlusAboveStageCount / @userDetail.TotalStageCount</MudListItem>
</MudList>
</MudExpansionPanel>
<MudExpansionPanel Text="PlayOptions">
<MudStack>
<MudSelect @bind-Value="@playOption.FastSlowIndicator"
Label="FAST/SLOW show setting">
@foreach (var item in Enum.GetValues<PlayOptions.FastSlowIndicator>())
{
<MudSelectItem Value="@item">@item.GetHelpText()</MudSelectItem>
}
</MudSelect>
<MudSelect @bind-Value="@playOption.FeverTrance"
Label="FEVER/TRANCE show setting">
@foreach (var item in Enum.GetValues<PlayOptions.FeverTranceShow>())
{
<MudSelectItem Value="@item">@item.GetHelpText()</MudSelectItem>
}
</MudSelect>
<MudStack Row="true">
<MudField Label="Navigator setting">
@NavigatorIdToString(playOption.NavigatorId)
</MudField>
<MudButton Variant="Variant.Text"
OnClick="OnChangeNavigatorButtonClick">
Change Navigator
</MudButton>
</MudStack>
<MudStack Row="true">
<MudField Label="Avatar setting">
@AvatarIdToString(playOption.AvatarId)
</MudField>
<MudButton Variant="Variant.Text"
OnClick="OnChangeAvatarButtonClick">
Change Avatar
</MudButton>
</MudStack>
<MudStack Row="true">
<MudField Label="Title setting">
@TitleIdToString(playOption.TitleId)
</MudField>
<MudButton Variant="Variant.Text"
OnClick="OnChangeTitleButtonClick">
Change Title
</MudButton>
</MudStack>
<MudOverlay @bind-Visible="IsNavigatorOverlayVisible"
DarkBackground="true">
<MudPaper>
<MudContainer MaxWidth="MaxWidth.Large">
<MudStack>
<MudDataGrid T="Navigator"
MultiSelection="false" @bind-SelectedItem="SelectedNavigator"
FixedHeader="true" FixedFooter="true" Height="70vh"
Items="navigatorDictionary.Values" ReadOnly="true" Hover="true"
Sortable="true" Filterable="true" Hideable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Navigators</MudText>
<MudSpacer/>
</ToolBarContent>
<Columns>
<Column T="Navigator" Field="@nameof(Navigator.Id)"/>
<Column T="Navigator" Field="@nameof(Navigator.IdString)"/>
<Column T="Navigator" Field="@nameof(Navigator.NameEntryString)" Title="Navigator Name"
SortBy="NameEntrySortByFunc"/>
<Column T="Navigator" Field="@nameof(Navigator.ToolTipJp)" Title="Navigator Description (Japanese)"/>
<Column T="Navigator" Field="@nameof(Navigator.ToolTipEn)" Title="Navigator Description (English)"/>
<Column T="Navigator" Field="@nameof(Navigator.Genre)" Title="Navigator Type"/>
</Columns>
<PagerContent>
<MudDataGridPager T="Navigator"/>
</PagerContent>
</MudDataGrid>
<MudText Align="Align.Left">
Currently selected navigator:<br>
@SelectedNavigator
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
OnClick="OnNavigatorOverlayClosed">
Close
</MudButton>
</MudStack>
</MudContainer>
</MudPaper>
</MudOverlay>
<MudOverlay @bind-Visible="IsAvatarOverlayVisible"
DarkBackground="true">
<MudPaper>
<MudContainer MaxWidth="MaxWidth.Large">
<MudStack>
<MudDataGrid T="Avatar"
MultiSelection="false" @bind-SelectedItem="SelectedAvatar"
FixedHeader="true" FixedFooter="true" Height="70vh"
Items="avatarDictionary.Values" ReadOnly="true" Hover="true"
Sortable="true" Filterable="true" Hideable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Avatars</MudText>
<MudSpacer/>
</ToolBarContent>
<Columns>
<Column T="Avatar" Field="@nameof(Avatar.Id)"/>
<Column T="Avatar" Field="@nameof(Avatar.IdString)"/>
<Column T="Avatar" Field="@nameof(Avatar.FullName)" Title="Avatar Name"/>
<Column T="Avatar" Field="@nameof(Avatar.AcquireMethodJp)" Title="Acquire Method (Japanese)"/>
<Column T="Avatar" Field="@nameof(Avatar.AcquireMethodEn)" Title="Acquire Method (English)"/>
</Columns>
<PagerContent>
<MudDataGridPager T="Avatar"/>
</PagerContent>
</MudDataGrid>
<MudText Align="Align.Left">
Currently selected avatar:<br>
@SelectedAvatar
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
OnClick="OnAvatarOverlayClosed">
Close
</MudButton>
</MudStack>
</MudContainer>
</MudPaper>
</MudOverlay>
<MudOverlay @bind-Visible="IsTitleOverlayVisible"
DarkBackground="true">
<MudPaper>
<MudContainer MaxWidth="MaxWidth.Large">
<MudStack>
<MudDataGrid T="Title"
MultiSelection="false" @bind-SelectedItem="SelectedTitle"
FixedHeader="true" FixedFooter="true" Height="70vh"
Items="titleDictionary.Values" ReadOnly="true" Hover="true"
Sortable="true" Filterable="true" Hideable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Titles</MudText>
<MudSpacer/>
</ToolBarContent>
<Columns>
<Column T="Title" Field="@nameof(Title.Id)"/>
<Column T="Title" Field="@nameof(Title.IdString)"/>
<Column T="Title" Field="@nameof(Title.NameJp)" Title="Title Name"/>
<Column T="Title" Field="@nameof(Title.UnlockRequirementJp)" Title="Unlock Requirement (Japanese)"/>
<Column T="Title" Field="@nameof(Title.UnlockRequirementEng)" Title="Unlock Requirement (English)"/>
<Column T="Title" Field="@nameof(Title.Type)" Title="Title Type"/>
</Columns>
<PagerContent>
<MudDataGridPager T="Title"/>
</PagerContent>
</MudDataGrid>
<MudText Align="Align.Left">
Currently selected title:<br>
@SelectedTitle
</MudText>
<MudButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true"
OnClick="OnTitleOverlayClosed">
Close
</MudButton>
</MudStack>
</MudContainer>
</MudPaper>
</MudOverlay>
<MudButton Disabled="@isSavingOptions"
OnClick="SaveOptions"
Variant="Variant.Filled"
Color="Color.Info">
@if (isSavingOptions)
{
<MudProgressCircular Class="ms-n1" Size="Size.Small" Indeterminate="true"/>
<MudText Class="ms-2">Saving...</MudText>
}
else
{
<MudIcon Icon="@Icons.Filled.Save"></MudIcon>
<MudText>Save</MudText>
}
</MudButton>
</MudStack>
</MudExpansionPanel>
<MudExpansionPanel Text="SongPlayData">
<MudDataGrid T="SongPlayData"
Items="@songPlayDataList"
Sortable="true"
Filterable="true">
<ToolBarContent>
<MudText Typo="Typo.h6">Played Songs</MudText>
</ToolBarContent>
<Columns>
<Column T="SongPlayData" Sortable="false" Filterable="false">
<CellTemplate>
<MudButton Variant="Variant.Outlined" Size="Size.Small"
OnClick="@(() => OnShowDetailsClick(context.Item))">
@(context.Item.ShowDetails ? "Hide" : "Show") Song Play Details
</MudButton>
</CellTemplate>
</Column>
<Column T="SongPlayData" Field="@nameof(SongPlayData.IsFavorite)" Sortable="false" Title="Favorite">
<CellTemplate>
<MudToggleIconButton Toggled="@context.Item.IsFavorite"
ToggledChanged="@(() => OnFavoriteToggled(context.Item))"
Icon="@Icons.Material.Filled.FavoriteBorder"
Color="@Color.Secondary"
Title="Add to favorite"
ToggledIcon="@Icons.Material.Filled.Favorite"
ToggledColor="@Color.Secondary"
ToggledTitle="Remove from favorite"/>
</CellTemplate>
</Column>
<Column T="SongPlayData" Field="@nameof(SongPlayData.Title)" Title="Song Title"/>
<Column T="SongPlayData" Field="@nameof(SongPlayData.Artist)" Title="Artist"/>
<Column T="SongPlayData" Field="@nameof(SongPlayData.TotalPlayCount)" Title="Total Play Count"/>
<Column T="SongPlayData" Field="@nameof(SongPlayData.LastPlayTime)" Title="Last Play Time"/>
</Columns>
<ChildRowContent>
@if (context.Item.ShowDetails)
{
<MudTr>
<td colspan="5">
<MudCard Elevation="0">
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.body1">Song Play Details</MudText>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent Class="pa-0">
<MudTable Items="@context.Item.SongPlaySubDataList"
Context="SongPlayDetail"
Elevation="0"
Filter="data => data.ClearState != ClearState.NotPlayed">
<HeaderContent>
<MudTh>Difficulty</MudTh>
<MudTh>Clear State</MudTh>
<MudTh>Play Count</MudTh>
<MudTh>Rating</MudTh>
<MudTh>Score</MudTh>
<MudTh>Max Chain</MudTh>
<MudTh>Last Play Time</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Difficulty">@SongPlayDetail.Difficulty</MudTd>
<MudTd DataLabel="Clear State">
@(SongPlayDetail.Score != 1000000 ? SongPlayDetail.ClearState : ClearState.Perfect)
</MudTd>
<MudTd DataLabel="Play Count">@SongPlayDetail.PlayCount</MudTd>
<MudTd DataLabel="Rating">@CalculateRating(SongPlayDetail.Score)</MudTd>
<MudTd DataLabel="Score">@SongPlayDetail.Score</MudTd>
<MudTd DataLabel="Max Chain">@SongPlayDetail.MaxChain</MudTd>
<MudTd DataLabel="Last Play Time">@SongPlayDetail.LastPlayTime</MudTd>
</RowTemplate>
</MudTable>
</MudCardContent>
</MudCard>
</td>
</MudTr>
}
</ChildRowContent>
<PagerContent>
<MudDataGridPager T="SongPlayData"/>
</PagerContent>
</MudDataGrid>
</MudExpansionPanel>
</MudExpansionPanels>
}
}
</MudContainer>

View File

@ -1,228 +0,0 @@
using System.Net.Http.Json;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using ProtoBuf;
using SharedProject.common;
using SharedProject.models;
namespace MudAdmin.Pages;
public partial class User
{
[Inject]
public HttpClient Client { get; set; } = null!;
[Inject]
public IDialogService DialogService { get; set; } = null!;
[Inject]
public ILogger<User> Logger { get; set; } = null!;
private Dictionary<long, Avatar> avatarDictionary = new();
private bool isSavingOptions;
private Dictionary<long, Navigator> navigatorDictionary = new();
private bool pageLoading = true;
private PlayOption playOption = new();
private List<SongPlayData> songPlayDataList = new();
private Dictionary<long, Title> titleDictionary = new();
private UserDetail? userDetail;
[Parameter]
public long CardId { get; set; }
private bool IsTitleOverlayVisible { get; set; }
private bool IsNavigatorOverlayVisible { get; set; }
private bool IsAvatarOverlayVisible { get; set; }
private Title? SelectedTitle { get; set; }
private Avatar? SelectedAvatar { get; set; }
private Navigator? SelectedNavigator { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
userDetail = await Client.GetFromJsonAsync<UserDetail>($"api/UserDetail/{CardId}");
if (userDetail is null)
{
pageLoading = false;
return;
}
songPlayDataList = userDetail.SongPlayDataList ?? new List<SongPlayData>();
playOption = userDetail.PlayOption;
var navigatorStream = await Client.GetStreamAsync(SharedConstants.NAVIGATOR_DAT_URI);
var navigators = Serializer.Deserialize<Navigators>(navigatorStream);
if (navigators?.NavigatorList != null)
{
navigatorDictionary = navigators.NavigatorList.ToDictionary(navigator => (long)navigator.Id);
}
var avatarStream = await Client.GetStreamAsync(SharedConstants.AVATAR_DAT_URI);
var avatars = Serializer.Deserialize<List<Avatar>>(avatarStream);
if (avatars != null)
{
avatarDictionary = avatars.ToDictionary(avatar => (long)avatar.Id);
}
var titleStream = await Client.GetStreamAsync(SharedConstants.TITLE_DAT_URI);
var titles = Serializer.Deserialize<List<Title>>(titleStream);
if (titles != null)
{
titleDictionary = titles.ToDictionary(title => (long)title.Id);
}
SetSelected();
pageLoading = false;
}
private void SetSelected()
{
SelectedTitle = titleDictionary.GetValueOrDefault(playOption.TitleId);
SelectedAvatar = avatarDictionary.GetValueOrDefault(playOption.AvatarId);
SelectedNavigator = navigatorDictionary.GetValueOrDefault(playOption.NavigatorId);
}
private void OnShowDetailsClick(SongPlayData data)
{
data.ShowDetails = !data.ShowDetails;
}
private async Task SaveOptions()
{
isSavingOptions = true;
var postData = new PlayOption
{
CardId = CardId,
FastSlowIndicator = playOption.FastSlowIndicator,
FeverTrance = playOption.FeverTrance,
AvatarId = playOption.AvatarId,
NavigatorId = playOption.NavigatorId,
TitleId = playOption.TitleId
};
var result = await Client.PostAsJsonAsync("api/UserDetail/SetPlayOption", postData);
isSavingOptions = false;
}
private static string CalculateRating(int score)
{
var grade = SharedConstants.Grades.Where(g => g.Score <= score).Select(g => g.Grade).Last();
return grade;
}
private async Task OnFavoriteToggled(SongPlayData data)
{
var options = new DialogOptions
{
CloseOnEscapeKey = false,
DisableBackdropClick = true,
FullWidth = true
};
var parameters = new DialogParameters();
parameters.Add("Data", data);
parameters.Add("CardId", CardId);
var dialog = DialogService.Show<FavoriteDialog>("Favorite", parameters, options);
var result = await dialog.Result;
if (result.Cancelled)
{
return;
}
if ((bool)result.Data)
{
Logger.LogInformation("Changed!");
data.IsFavorite = !data.IsFavorite;
}
}
private Task<IEnumerable<long>> SearchAvatar(string value)
{
var result = string.IsNullOrEmpty(value) ? avatarDictionary.Keys : avatarDictionary.Where(pair => pair.Value.ToString().Contains(value, StringComparison.InvariantCultureIgnoreCase)).Select(pair => pair.Key);
return Task.FromResult(result);
}
private Task<IEnumerable<long>> SearchTitle(string value)
{
var result = string.IsNullOrEmpty(value) ? titleDictionary.Keys : titleDictionary.Where(pair => pair.Value.ToString().Contains(value, StringComparison.InvariantCultureIgnoreCase)).Select(pair => pair.Key);
return Task.FromResult(result);
}
private Task<IEnumerable<long>> SearchNavigator(string value)
{
var result = string.IsNullOrEmpty(value) ? navigatorDictionary.Keys : navigatorDictionary.Where(pair => pair.Value.ToString().Contains(value, StringComparison.InvariantCultureIgnoreCase)).Select(pair => pair.Key);
return Task.FromResult(result);
}
private string AvatarIdToString(long id)
{
return avatarDictionary.ContainsKey(id) ? avatarDictionary[id].ToString() : $"No Data for {id}!";
}
private string NavigatorIdToString(long id)
{
return navigatorDictionary.ContainsKey(id) ? navigatorDictionary[id].ToString() : $"No Data for {id}!";
}
private string TitleIdToString(long id)
{
return titleDictionary.ContainsKey(id) ? titleDictionary[id].ToString() : $"No Data for {id}!";
}
private void OnChangeTitleButtonClick()
{
IsTitleOverlayVisible = true;
}
private void OnChangeNavigatorButtonClick()
{
IsNavigatorOverlayVisible = true;
}
private void OnChangeAvatarButtonClick()
{
IsAvatarOverlayVisible = true;
}
private void OnTitleOverlayClosed()
{
IsTitleOverlayVisible = false;
if (SelectedTitle != null)
{
playOption.TitleId = SelectedTitle.Id;
}
}
private void OnNavigatorOverlayClosed()
{
IsNavigatorOverlayVisible = false;
if (SelectedNavigator != null)
{
playOption.NavigatorId = SelectedNavigator.Id;
}
}
private void OnAvatarOverlayClosed()
{
IsAvatarOverlayVisible = false;
if (SelectedAvatar != null)
{
playOption.AvatarId = SelectedAvatar.Id;
}
}
private static object NameEntrySortByFunc(Navigator navigator)
{
return navigator.NameEntry1?.ToString().ToLowerInvariant() ?? string.Empty;
}
}

View File

@ -1,98 +0,0 @@
@page "/users"
@using models = SharedProject.models
@inject NavigationManager NavigationManager
@inject HttpClient Client
@inject IDialogService DialogService
@inject ILogger<Users> Logger
<PageTitle>Users</PageTitle>
<MudContainer>
@if (users is null)
{
<MudGrid>
@for (var i = 0; i < 5; i++)
{
<MudItem>
<MudCard>
<MudCardHeader>
<CardHeaderContent>
<MudSkeleton SkeletonType="SkeletonType.Rectangle" Height="32px"/>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent>
<MudSkeleton Width="80px" Height="32px"/>
<MudSkeleton Width="147px" Height="28px"/>
</MudCardContent>
<MudCardActions>
<MudSkeleton SkeletonType="SkeletonType.Rectangle" Width="99px" Height="25px" Class="ml-2"/>
</MudCardActions>
</MudCard>
</MudItem>
}
</MudGrid>
}
else if (users.Count != 0)
{
<MudGrid>
@foreach (var user in users)
{
<MudItem>
<MudCard>
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.h5">@user.PlayerName</MudText>
</CardHeaderContent>
<CardHeaderActions>
<MudIconButton Icon="@Icons.Filled.Edit" Color="Color.Default"
OnClick="() => OnEditPlayerNameClicked(user)"/>
</CardHeaderActions>
</MudCardHeader>
<MudCardContent>
<MudText Typo="Typo.h6">Card ID</MudText>
<MudText>@user.CardId</MudText>
</MudCardContent>
<MudCardActions>
<MudButton Variant="Variant.Text" Color="Color.Primary" OnClick="() => OnCheckDetailClick(user)">Check detail</MudButton>
</MudCardActions>
</MudCard>
</MudItem>
}
</MudGrid>
}
else
{
<MudText Typo="Typo.h3">No Data</MudText>
}
</MudContainer>
@code {
private List<models.User>? users;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
users = await Client.GetFromJsonAsync<List<models.User>>("api/Users") ?? new List<models.User>();
}
private void OnCheckDetailClick(models.User user)
{
NavigationManager.NavigateTo($"user/{user.CardId}");
}
private async Task OnEditPlayerNameClicked(models.User user)
{
var options = new DialogOptions
{
CloseOnEscapeKey = false,
DisableBackdropClick = true,
FullWidth = true
};
var parameters = new DialogParameters();
parameters.Add("Data", user);
var dialog = DialogService.Show<ChangePlayerNameDialog>("Favorite", parameters, options);
var result = await dialog.Result;
}
}

View File

@ -1,17 +0,0 @@
using System.Text.Json;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MudAdmin;
using MudBlazor.Services;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient
{
BaseAddress = new Uri(builder.Configuration.GetValue<string>("BaseUrl"))
});
builder.Services.AddMudServices();
await builder.Build().RunAsync();

View File

@ -1,30 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:65283",
"sslPort": 44398
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Mudblazor.Template": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,60 +0,0 @@
@inherits LayoutComponentBase
<MudThemeProvider @ref="@MudThemeProvider" @bind-IsDarkMode="@IsDarkMode"/>
<MudDialogProvider/>
<MudSnackbarProvider/>
<MudLayout>
<MudAppBar Elevation="0" Dense="true">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start"
OnClick="@((e) => DrawerToggle())"/>
<MudSpacer/>
<MudTooltip Duration="1000" Text="Powered by MudBlazor">
<MudIconButton Icon="@Icons.Custom.Brands.MudBlazor" Color="Color.Inherit"
Link="https://mudblazor.com/" Target="_blank"/>
</MudTooltip>
<MudTooltip Duration="1000" Text="Github Repository">
<MudIconButton Icon="@Icons.Custom.Brands.GitHub" Color="Color.Inherit"
Link="https://github.com/asesidaa/GC-local-server-rewrite/" Target="_blank"/>
</MudTooltip>
<MudTooltip Duration="1000" Text="@(IsDarkMode ? "Switch to Light Mode" : "Switch to DarkMode")">
<MudToggleIconButton @bind-Toggled="@IsDarkMode"
Icon="@Icons.Material.Outlined.DarkMode" Color="Color.Inherit"
ToggledIcon="@Icons.Material.Filled.LightMode"/>
</MudTooltip>
</MudAppBar>
<MudDrawer @bind-Open="drawerOpen" Elevation="1">
<MudDrawerHeader>
<MudText Typo="Typo.h6">GC local server Admin</MudText>
</MudDrawerHeader>
<NavMenu/>
</MudDrawer>
<MudMainContent>
<MudContainer MaxWidth="MaxWidth.Large" Class="my-16 pt-16">
@Body
</MudContainer>
</MudMainContent>
</MudLayout>
@code {
bool drawerOpen = true;
public bool IsDarkMode { get; set; }
public MudThemeProvider MudThemeProvider { get; set; } = null!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
IsDarkMode = await MudThemeProvider.GetSystemPreference();
StateHasChanged();
}
}
void DrawerToggle()
{
drawerOpen = !drawerOpen;
}
}

View File

@ -1,4 +0,0 @@
<MudNavMenu>
<MudNavLink Href="" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Home">Home</MudNavLink>
<MudNavLink Href="Cards" Match="NavLinkMatch.Prefix" Icon="@Icons.Material.Filled.List">Cards</MudNavLink>
</MudNavMenu>

View File

@ -1,11 +0,0 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using MudBlazor
@using MudAdmin
@using MudAdmin.Shared

View File

@ -1,10 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
},
"BaseUrl": "https://127.0.0.1"
}

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,25 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>MudAdmin</title>
<base href="/" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 818 B

View File

@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>SharedProject</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="protobuf-net" Version="3.1.22" />
</ItemGroup>
</Project>

View File

@ -1,9 +0,0 @@
namespace SharedProject.common;
public record ScoreGradeMap(int Score, string Grade)
{
public override string ToString()
{
return $"{{ Score = {Score}, Grade = {Grade} }}";
}
}

View File

@ -1,42 +0,0 @@
namespace SharedProject.common;
public static class SharedConstants
{
public const int DIFFICULTY_COUNT = 4;
public const int E_SCORE_THRESHOLD = 0;
public const int D_SCORE_THRESHOLD = 300000;
public const int C_SCORE_THRESHOLD = 500000;
public const int B_SCORE_THRESHOLD = 700000;
public const int A_SCORE_THRESHOLD = 800000;
public const int S_SCORE_THRESHOLD = 900000;
public const int S_PLUS_SCORE_THRESHOLD = 950000;
public const int S_PLUS_PLUS_SCORE_THRESHOLD = 990000;
public const int MAX_PLAYER_NAME_LENGTH = 8;
public const string NAVIGATOR_DAT_URI = "data/navigator.dat";
public const string AVATAR_DAT_URI = "data/avatar.dat";
public const string TITLE_DAT_URI = "data/title.dat";
public static readonly ScoreGradeMap[] Grades =
{
new(E_SCORE_THRESHOLD, "E"),
new(D_SCORE_THRESHOLD, "D"),
new(C_SCORE_THRESHOLD, "C"),
new(B_SCORE_THRESHOLD, "B"),
new(A_SCORE_THRESHOLD, "A"),
new(S_SCORE_THRESHOLD, "S"),
new(S_PLUS_SCORE_THRESHOLD, "S+"),
new(S_PLUS_PLUS_SCORE_THRESHOLD, "S++")
};
}

View File

@ -1,11 +0,0 @@
namespace SharedProject.enums;
public enum ClearState
{
NotPlayed = 0,
Failed,
Clear,
NoMiss,
FullChain,
Perfect
}

View File

@ -1,9 +0,0 @@
namespace SharedProject.enums;
public enum Difficulty
{
Simple = 0,
Normal = 1,
Hard = 2,
Extra = 3
}

View File

@ -1,25 +0,0 @@
namespace SharedProject.enums;
public static class Extensions
{
public static string GetHelpText(this PlayOptions.FastSlowIndicator indicator)
{
return indicator switch
{
PlayOptions.FastSlowIndicator.NotUsed => "Do not show FAST/SLOW indicator",
PlayOptions.FastSlowIndicator.NearAvatar => "Show FAST/SLOW indicator near avatar",
PlayOptions.FastSlowIndicator.NearJudgement => "Show FAST/SLOW indicator near judgement text",
_ => throw new ArgumentOutOfRangeException(nameof(indicator), indicator, null)
};
}
public static string GetHelpText(this PlayOptions.FeverTranceShow show)
{
return show switch
{
PlayOptions.FeverTranceShow.NotUsed => "Do not show FEVER/TRANCE",
PlayOptions.FeverTranceShow.Show => "Show FEVER/TRANCE",
_ => throw new ArgumentOutOfRangeException(nameof(show), show, null)
};
}
}

View File

@ -1,11 +0,0 @@
using ProtoBuf;
namespace SharedProject.enums;
[ProtoContract]
public enum NavigatorDefaultAvailability
{
NotAvailable = 0,
Available = 1,
AvailableWithVoice = 2,
}

View File

@ -1,14 +0,0 @@
using ProtoBuf;
namespace SharedProject.enums;
[ProtoContract]
public enum NavigatorGenre
{
Default = 1,
Original = 2,
Game = 3,
Touhou = 4,
Vocaloid = 5,
Collab = 6,
}

View File

@ -1,17 +0,0 @@
namespace SharedProject.enums;
public static class PlayOptions
{
public enum FastSlowIndicator
{
NotUsed = 3,
NearAvatar = 1,
NearJudgement = 2
}
public enum FeverTranceShow
{
NotUsed = 2,
Show = 1
}
}

View File

@ -1,39 +0,0 @@
using ProtoBuf;
namespace SharedProject.enums;
[ProtoContract]
public enum TitleUnlockType
{
Invalid = 0,
Default = 1,
Clear = 2,
NoMiss = 3,
FullChain = 4,
SRankSimpleStages = 5,
SRankNormalStages = 6,
SRankHardStages = 7,
SRankExtraStages = 8,
SRankAllDifficulties = 9,
SPlusRankAllDifficulties = 10,
SPlusPlusRankAllDifficulties = 11,
Event = 12,
Prefecture = 13,
ChainMilestone = 14,
Adlibs = 15,
ConsecutiveNoMiss = 16,
ClearsUsingItems = 17,
Avatars = 18,
MultiplayerStarsTotal = 19,
SongSet20 = 20,
SongSet21 = 21,
SongSet22 = 22,
SongSet23 = 23,
SongSet24 = 24,
SongSet25 = 25,
SongSet26 = 26,
ProfileLevel = 27,
Perfect = 28,
OnlineMatching = 29,
Trophies = 30,
}

View File

@ -1,32 +0,0 @@
using ProtoBuf;
namespace SharedProject.models;
[ProtoContract]
public class Avatar
{
[ProtoMember(1)]
public uint Id { get; set; }
[ProtoMember(2)]
public string? IdString { get; set; }
[ProtoMember(3)]
public string? FullName { get; set; }
[ProtoMember(4)]
public string? Name { get; set; }
[ProtoMember(5)]
public string? Variant { get; set; }
[ProtoMember(6)]
public string? AcquireMethodJp { get; set; }
[ProtoMember(7)]
public string? AcquireMethodEn { get; set; }
public override string ToString() {
return $"{Id}: {FullName}, {AcquireMethodEn}";
}
}

View File

@ -1,15 +0,0 @@
using System.Text.Json.Serialization;
namespace SharedProject.models;
public class MusicFavoriteData
{
[JsonPropertyName(nameof(CardId))]
public long CardId { get; set; }
[JsonPropertyName(nameof(MusicId))]
public int MusicId { get; set; }
[JsonPropertyName(nameof(IsFavorite))]
public bool IsFavorite { get; set; }
}

Some files were not shown because too many files have changed in this diff Show More