This commit is contained in:
Kayori
2025-06-22 13:01:34 +02:00
commit ab0128af32
105 changed files with 8333 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**
+428
View File
@@ -0,0 +1,428 @@
# Created by https://www.toptal.com/developers/gitignore/api/dotnetcore,visualstudiocode,visualstudio
# Edit at https://www.toptal.com/developers/gitignore?templates=dotnetcore,visualstudiocode,visualstudio
### DotnetCore ###
# .NET Core build folders
bin/
obj/
# Common node modules locations
/node_modules
/wwwroot/node_modules
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
### VisualStudio ###
## 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/main/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
*.tlog
*.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 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
# 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/
# Visual Studio History (VSHistory) files
.vshistory/
# 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
# VS Code files for those working on multiple tools
*.code-workspace
# Local History for Visual Studio Code
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
### VisualStudio Patch ###
# Additional files built by Visual Studio
# End of https://www.toptal.com/developers/gitignore/api/dotnetcore,visualstudiocode,visualstudio
/Core/Server/Medusa.db
+13
View File
@@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/projectSettingsUpdater.xml
/modules.xml
/.idea.Medusa.iml
/contentModel.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+1
View File
@@ -0,0 +1 @@
Medusa
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+9
View File
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Abstractions.Handlers;
public abstract class Handler
{
public string HandlerModule { get; set; } = "";
public string HandlerMethod { get; set; } = "";
public string? HandlerGameCode { get; set; }
public string? HandlerMinVer { get; set; }
public string? HandlerMaxVer { get; set; }
public virtual void Configure() { throw new NotImplementedException("Configure method not implemented."); }
protected void Module(string module)
{
this.HandlerModule = module;
}
protected void Method(string method)
{
this.HandlerMethod = method;
}
protected void GameCode(string gameCode)
{
this.HandlerGameCode = gameCode;
}
protected void MinVer(string minVer)
{
this.HandlerMinVer = minVer;
}
protected void MaxVer(string maxVer)
{
this.HandlerMaxVer = maxVer;
}
public virtual XDocument Handle(string model) { throw new NotImplementedException("Handle method not implemented."); }
public virtual Task<XDocument> HandleAsync(string model) { throw new NotImplementedException("Handle method not implemented."); }
}
+10
View File
@@ -0,0 +1,10 @@
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Abstractions.Handlers
{
public interface IHandler
{
Task<XDocument> HandleAsync(string model);
}
}
+14
View File
@@ -0,0 +1,14 @@
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript'
],
parserOptions: {
ecmaVersion: 'latest'
}
}
+30
View File
@@ -0,0 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
+6
View File
@@ -0,0 +1,6 @@
{
"recommendations": [
"Vue.volar",
"dbaeumer.vscode-eslint"
]
}
+11
View File
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.1184077">
<PropertyGroup>
<StartupCommand>npm run dev</StartupCommand>
<JavaScriptTestRoot>.\</JavaScriptTestRoot>
<JavaScriptTestFramework>Jest</JavaScriptTestFramework>
<!-- Allows the build (or compile) script located on package.json to run on Build -->
<ShouldRunBuildScript>false</ShouldRunBuildScript>
<!-- Folder where production build objects will be placed -->
<BuildOutputFolder>$(MSBuildProjectDirectory)\dist</BuildOutputFolder>
</PropertyGroup>
</Project>
+39
View File
@@ -0,0 +1,39 @@
# core2.client
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
## Type Support for `.vue` Imports in TS
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Type-Check, Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<disabledPackageSources>
<clear />
</disabledPackageSources>
</configuration>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<PackageJsonName Condition="$(PackageJsonName) == ''">client</PackageJsonName>
<PackageJsonVersion Condition="$(PackageJsonVersion) == ''">0.0.0</PackageJsonVersion>
<PackageJsonPrivate Condition="$(PackageJsonPrivate) == ''">true</PackageJsonPrivate>
<PackageJsonType Condition="$(PackageJsonType) == ''">module</PackageJsonType>
<PackageJsonScriptsDev Condition="$(PackageJsonScriptsDev) == ''">vite</PackageJsonScriptsDev>
<PackageJsonScriptsBuild Condition="$(PackageJsonScriptsBuild) == ''">run-p type-check "build-only {@}" --</PackageJsonScriptsBuild>
<PackageJsonScriptsPreview Condition="$(PackageJsonScriptsPreview) == ''">vite preview</PackageJsonScriptsPreview>
<PackageJsonScriptsBuildOnly Condition="$(PackageJsonScriptsBuildOnly) == ''">vite build</PackageJsonScriptsBuildOnly>
<PackageJsonScriptsTypeCheck Condition="$(PackageJsonScriptsTypeCheck) == ''">vue-tsc --build --force</PackageJsonScriptsTypeCheck>
<PackageJsonScriptsLint Condition="$(PackageJsonScriptsLint) == ''">eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore</PackageJsonScriptsLint>
<PackageJsonDependenciesVue Condition="$(PackageJsonDependenciesVue) == ''">^3.4.29</PackageJsonDependenciesVue>
<PackageJsonDevdependenciesRushstackEslintPatch Condition="$(PackageJsonDevdependenciesRushstackEslintPatch) == ''">^1.8.0</PackageJsonDevdependenciesRushstackEslintPatch>
<PackageJsonDevdependenciesTsconfigNode20 Condition="$(PackageJsonDevdependenciesTsconfigNode20) == ''">^20.1.4</PackageJsonDevdependenciesTsconfigNode20>
<PackageJsonDevdependenciesTypesNode Condition="$(PackageJsonDevdependenciesTypesNode) == ''">^20.14.5</PackageJsonDevdependenciesTypesNode>
<PackageJsonDevdependenciesVitejsPluginVue Condition="$(PackageJsonDevdependenciesVitejsPluginVue) == ''">^5.0.5</PackageJsonDevdependenciesVitejsPluginVue>
<PackageJsonDevdependenciesVueEslintConfigTypescript Condition="$(PackageJsonDevdependenciesVueEslintConfigTypescript) == ''">^13.0.0</PackageJsonDevdependenciesVueEslintConfigTypescript>
<PackageJsonDevdependenciesVueTsconfig Condition="$(PackageJsonDevdependenciesVueTsconfig) == ''">^0.5.1</PackageJsonDevdependenciesVueTsconfig>
<PackageJsonDevdependenciesEslint Condition="$(PackageJsonDevdependenciesEslint) == ''">^8.57.0</PackageJsonDevdependenciesEslint>
<PackageJsonDevdependenciesEslintPluginVue Condition="$(PackageJsonDevdependenciesEslintPluginVue) == ''">^9.23.0</PackageJsonDevdependenciesEslintPluginVue>
<PackageJsonDevdependenciesNpmRunAll2 Condition="$(PackageJsonDevdependenciesNpmRunAll2) == ''">^6.2.0</PackageJsonDevdependenciesNpmRunAll2>
<PackageJsonDevdependenciesTypescript Condition="$(PackageJsonDevdependenciesTypescript) == ''">~5.4.0</PackageJsonDevdependenciesTypescript>
<PackageJsonDevdependenciesVite Condition="$(PackageJsonDevdependenciesVite) == ''">^5.3.1</PackageJsonDevdependenciesVite>
<PackageJsonDevdependenciesVueTsc Condition="$(PackageJsonDevdependenciesVueTsc) == ''">^2.0.21</PackageJsonDevdependenciesVueTsc>
</PropertyGroup>
</Project>
+3285
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
{
"name": "client",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
},
"dependencies": {
"vue": "^3.4.29"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.8.0",
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.14.5",
"@vitejs/plugin-vue": "^5.0.5",
"@vue/eslint-config-typescript": "^13.0.0",
"@vue/tsconfig": "^0.5.1",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
"npm-run-all2": "^6.2.0",
"typescript": "~5.4.0",
"vite": "^5.3.1",
"vue-tsc": "^2.0.21"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+47
View File
@@ -0,0 +1,47 @@
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>
<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<main>
<TheWelcome />
</main>
</template>
<style scoped>
header {
line-height: 1.5;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
}
</style>
+86
View File
@@ -0,0 +1,86 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

+35
View File
@@ -0,0 +1,35 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}
+99
View File
@@ -0,0 +1,99 @@
<template>
<div class="weather-component">
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
<div v-if="loading" class="loading">
Loading... Please refresh once the ASP.NET backend has started. See <a href="https://aka.ms/jspsintegrationvue">https://aka.ms/jspsintegrationvue</a> for more details.
</div>
<div v-if="post" class="content">
<table>
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
<tr v-for="forecast in post" :key="forecast.date">
<td>{{ forecast.date }}</td>
<td>{{ forecast.temperatureC }}</td>
<td>{{ forecast.temperatureF }}</td>
<td>{{ forecast.summary }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
type Forecasts = {
date: string,
temperatureC: string,
temperatureF: string,
summary: string
}[];
interface Data {
loading: boolean,
post: null | Forecasts
}
export default defineComponent({
data(): Data {
return {
loading: false,
post: null
};
},
created() {
// fetch the data when the view is created and the data is
// already being observed
this.fetchData();
},
watch: {
// call again the method if the route changes
'$route': 'fetchData'
},
methods: {
fetchData(): void {
this.post = null;
this.loading = true;
fetch('weatherforecast')
.then(r => r.json())
.then(json => {
this.post = json as Forecasts;
this.loading = false;
return;
});
}
},
});
</script>
<style scoped>
th {
font-weight: bold;
}
th, td {
padding-left: .5rem;
padding-right: .5rem;
}
.weather-component {
text-align: center;
}
table {
margin-left: auto;
margin-right: auto;
}
</style>
+88
View File
@@ -0,0 +1,88 @@
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
you need to test your components and web pages, check out
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
>Cypress Component Testing</a
>.
<br />
More instructions are available in <code>README.md</code>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
Discord server, or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also subscribe to
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
the official
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
twitter account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>
@@ -0,0 +1,87 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>
@@ -0,0 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>
@@ -0,0 +1,19 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>
+6
View File
@@ -0,0 +1,6 @@
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
+6
View File
@@ -0,0 +1,6 @@
/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
+11
View File
@@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}
+19
View File
@@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}
+59
View File
@@ -0,0 +1,59 @@
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import plugin from '@vitejs/plugin-vue';
import fs from 'fs';
import path from 'path';
import child_process from 'child_process';
import { env } from 'process';
const baseFolder =
env.APPDATA !== undefined && env.APPDATA !== ''
? `${env.APPDATA}/ASP.NET/https`
: `${env.HOME}/.aspnet/https`;
fs.mkdirSync(baseFolder, { recursive: true });
const certificateName = "core2.client";
const certFilePath = path.join(baseFolder, `${certificateName}.pem`);
const keyFilePath = path.join(baseFolder, `${certificateName}.key`);
if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) {
if (0 !== child_process.spawnSync('dotnet', [
'dev-certs',
'https',
'--export-path',
certFilePath,
'--format',
'Pem',
'--no-password',
], { stdio: 'inherit', }).status) {
throw new Error("Could not create certificate.");
}
}
const target = env.ASPNETCORE_HTTPS_PORT ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}` :
env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'https://localhost:7059';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [plugin()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
'^/weatherforecast': {
target,
secure: false
}
},
port: 5173,
https: {
key: fs.readFileSync(keyFilePath),
cert: fs.readFileSync(certFilePath),
}
}
})
+13
View File
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.8",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}
+26
View File
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Server.Entities;
using System;
namespace Server
{
public class AppDbContext(DbContextOptions<AppDbContext> options)
: IdentityDbContext<User, IdentityRole<int>, int>(options)
{
public override DbSet<User> Users { get; set; }
public DbSet<Card> Cards { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<User>()
.HasMany(u => u.Cards)
.WithOne(c => c.User)
.HasForeignKey(c => c.UserId)
.OnDelete(DeleteBehavior.Cascade);
base.OnModelCreating(builder);
}
}
}
@@ -0,0 +1,9 @@
namespace Server.Attributes;
[AttributeUsage(AttributeTargets.Class)]
public class HandlerAttribute(string module, string method) : Attribute
{
public readonly string Module = module;
public readonly string Method = method;
}
+16
View File
@@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Server.Entities
{
public class Card
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public required string KonamiId { get; set; }
public required string RawId { get; set; }
public int UserId { get; set; }
public User User { get; set; } = default!;
}
}
+12
View File
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Identity;
namespace Server.Entities
{
public class User: IdentityUser<int>
{
public int PaseliAmount { get; set; } = 0;
public required int Pin { get; set; }
public List<Card> Cards { get; set; } = default!;
}
}
@@ -0,0 +1,23 @@
using Server.Handlers;
using Server.Services;
using System.Reflection;
using Abstractions.Handlers;
namespace Server.Extensions;
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseHandlers(this IApplicationBuilder app)
{
var handlerService = app.ApplicationServices.GetRequiredService<IHandlerService>();
var assembly = Assembly.GetEntryAssembly() ?? throw new InvalidOperationException("Could not find entry assembly.");
var types = assembly.GetTypes().Where(a => a.GetInterfaces().Contains(typeof(IHandler)) ||
(a.IsSubclassOf(typeof(Handler)) && !a.IsAbstract));
handlerService.Handlers.AddRange(types);
return app;
}
}
@@ -0,0 +1,225 @@
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Server.Services;
using System.Collections.Generic;
using System.Reflection;
namespace Server.Extensions
{
public static class ServiceCollectionExtensions
{
/// <summary>
/// Add handler service to DI
/// </summary>
/// <param name="services">The extended IServiceCollection</param>
/// <returns></returns>
public static IServiceCollection AddHandlers(this IServiceCollection services)
{
services.Add(new ServiceDescriptor(typeof(IHandlerService), typeof(HandlerService), ServiceLifetime.Singleton));
return services;
}
/// <summary>
/// Adds services implementing an interface to the DI container.
/// </summary>
/// <typeparam name="TType">The interface type</typeparam>
/// <param name="serviceCollection">The extended IServiceCollection</param>
/// <param name="lifetime">The lifetime of the services</param>
/// <returns></returns>
public static IServiceCollection AddIServicesWithType<TType>(this IServiceCollection serviceCollection,
ServiceLifetime lifetime = ServiceLifetime.Transient)
{
var assembly = typeof(TType).Assembly;
var types = assembly.DefinedTypes
.Where(x => typeof(TType).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.ToArray();
if(types.Length == 0) return serviceCollection;
foreach(var type in types)
{
serviceCollection.TryAdd(new ServiceDescriptor(typeof(TType), type.AsType(), lifetime));
}
return serviceCollection;
}
/// <summary>
/// Adds services from a namespace that implement an interface to the DI container.
/// </summary>
/// <param name="serviceCollection">The extended IServiceCollection</param>
/// <param name="interfaceNamespace">The namespace of the interfaces</param>
/// <param name="lifetime">The lifetime of the services</param>
/// <returns></returns>
public static IServiceCollection AddServicesInNamespaceWithInterface(this IServiceCollection serviceCollection,
string interfaceNamespace, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var foundAssembly = assemblies.FirstOrDefault(x => x.DefinedTypes.Any(typeInfo => typeInfo.Namespace?.Equals(interfaceNamespace) ?? false));
if(foundAssembly == null)
{
try
{
var loaded = Assembly.Load(interfaceNamespace.Split('.')[0]);
foundAssembly = loaded;
}
catch
{
return serviceCollection;
}
}
var types = foundAssembly.DefinedTypes
.Where(x => x.Namespace == interfaceNamespace && !x.IsInterface && !x.IsAbstract && !x.ContainsGenericParameters && !x.IsNested)
.ToList();
if(types.Count == 0) return serviceCollection;
foreach(var type in types)
{
var interfaceType = type.GetInterfaces().FirstOrDefault();
if(interfaceType != null)
{
serviceCollection.TryAdd(new ServiceDescriptor(interfaceType, type.AsType(), lifetime));
}
}
return serviceCollection;
}
/// <summary>
/// Adds services from multiple namespaces to the DI container.
/// </summary>
/// <param name="serviceCollection">The extended IServiceCollection</param>
/// <param name="namespaces">List of namespaces</param>
/// <param name="lifetime">The lifetime of the services</param>
/// <returns></returns>
public static IServiceCollection AddServicesInNamespaces(this IServiceCollection serviceCollection,
string[] namespaces, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var foundAssemblies = assemblies
.Where(a => a.DefinedTypes.Any(t => namespaces.Contains(t.Namespace)))
.ToList();
var notFoundNamespaces = namespaces.Except(foundAssemblies.SelectMany(a => a.GetTypes().Select(t => t.Namespace))).ToList();
var types = new List<TypeInfo>();
foreach(var assembly in foundAssemblies)
{
var foundTypes = assembly.DefinedTypes
.Where(x => namespaces.Contains(x.Namespace) && !x.IsInterface && !x.IsAbstract && !x.IsNested)
.ToArray();
types.AddRange(foundTypes);
}
foreach(var notFound in notFoundNamespaces)
{
try
{
var assemblyName = notFound?.Split('.')[0];
var assembly = Assembly.Load(assemblyName ?? "");
var foundTypes = assembly.DefinedTypes
.Where(x => x.Namespace == notFound && !x.IsInterface && !x.IsAbstract && !x.IsNested)
.ToArray();
types.AddRange(foundTypes);
}
catch { }
}
if(types.Count == 0) return serviceCollection;
foreach(var type in types)
{
serviceCollection.TryAdd(new ServiceDescriptor(type.AsType(), type.AsType(), lifetime));
}
return serviceCollection;
}
/// <summary>
/// Adds services from a single namespace to the DI container.
/// </summary>
/// <param name="serviceCollection">The extended IServiceCollection</param>
/// <param name="namespace">The namespace to scan</param>
/// <param name="lifetime">The lifetime of the services</param>
/// <returns></returns>
public static IServiceCollection AddServicesInNamespace(this IServiceCollection serviceCollection,
string @namespace, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var assembly = assemblies.FirstOrDefault(x => x.DefinedTypes.Any(typeInfo => typeInfo.Namespace == @namespace));
var types = assembly?.DefinedTypes
.Where(x => x.Namespace == @namespace && !x.IsInterface && !x.IsAbstract && !x.IsNested)
.ToArray();
if(types == null || types.Length == 0) return serviceCollection;
foreach(var type in types)
{
serviceCollection.TryAdd(new ServiceDescriptor(type.AsType(), type.AsType(), lifetime));
}
return serviceCollection;
}
/// <summary>
/// Adds a single class as a service to the DI container.
/// </summary>
/// <param name="serviceCollection">The extended IServiceCollection</param>
/// <param name="serviceClassType">The class type to add</param>
/// <param name="lifetime">The lifetime of the service</param>
/// <returns></returns>
public static IServiceCollection CreateServiceFromClass(this IServiceCollection serviceCollection,
Type serviceClassType, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
serviceCollection.TryAdd(new ServiceDescriptor(serviceClassType, serviceClassType, lifetime));
return serviceCollection;
}
/// <summary>
/// Adds multiple classes as services to the DI container.
/// </summary>
/// <param name="serviceCollection">The extended IServiceCollection</param>
/// <param name="serviceClassTypes">A list of class types</param>
/// <param name="lifetime">The lifetime of the services</param>
/// <returns></returns>
public static IServiceCollection CreateServiceFromClasses(this IServiceCollection serviceCollection,
IEnumerable<Type> serviceClassTypes, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
foreach(var serviceClassType in serviceClassTypes)
{
serviceCollection.TryAdd(new ServiceDescriptor(serviceClassType, serviceClassType, lifetime));
}
return serviceCollection;
}
/// <summary>
/// Adds services based on a type that implements an interface, without adding the interface itself.
/// </summary>
/// <typeparam name="TType">The extended interface</typeparam>
/// <param name="serviceDescriptors">The extended IServiceCollection</param>
/// <param name="lifetime">The lifetime of the services</param>
/// <returns></returns>
public static IServiceCollection AddServicesWithType<TType>(this IServiceCollection serviceDescriptors,
ServiceLifetime lifetime = ServiceLifetime.Transient)
{
var assembly = typeof(TType).Assembly;
var types = assembly.DefinedTypes
.Where(x => typeof(TType).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
.ToArray();
if(types.Length == 0) return serviceDescriptors;
foreach(var type in types)
{
serviceDescriptors.TryAdd(new ServiceDescriptor(type.AsType(), type.AsType(), lifetime));
}
return serviceDescriptors;
}
}
}
@@ -0,0 +1,97 @@
using Microsoft.AspNetCore.Hosting.Server;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.Boot
{
public class GetServicesHandler(IServer server, ILogger<GetServicesHandler> logger, XDocument body) : Handler
{
private readonly IServer _server = server;
private readonly ILogger<GetServicesHandler> _logger = logger;
private readonly XDocument _body = body;
private const string CommonUrl = "http://127.0.0.1:5120/eamuse";
private string listeningAddress = "http://127.0.0.1:5120";
public override void Configure()
{
Module("services");
Method("get");
}
public override Task<XDocument> HandleAsync(string model)
{
var services = CreateCoreServicesElement();
var document = new XDocument(new XElement("response", services));
return Task.FromResult(document);
}
private static XElement CreateCoreServicesElement()
{
var services = new XElement("services",
new XAttribute("expire", "3600"),
new XAttribute("method", "get"),
new XAttribute("mode", "operation"),
new XAttribute("status", "0"));
var coreServices = new string[]{
"cardmng", "facility", "message", "numbering", "package", "pcbevent", "pcbtracker", "pkglist",
"posevent", "userdata", "userid", "eacoin", "dlstatus", "netlog", "info", "reference", "sidmgr",
"local", "local2", "lobby", "slocal", "slocal2", "sglocal", "sglocal2", "lab", "globby",
"slobby", "sglobby", "eacharge"
};
foreach(var service in coreServices)
{
services.Add(new XElement("item",
new XAttribute("name", service),
new XAttribute("url", $"{CommonUrl}/{service}")));
}
services.Add(new XElement("item", new XAttribute("name", "ntp"),
new XAttribute("url", "ntp://pool.ntp.org/")));
services.Add(new XElement("item", new XAttribute("name", "keepalive"),
new XAttribute("url",
"http://127.0.0.1:8083/keepalive?pa=127.0.0.1&ia=127.0.0.1&ga=127.0.0.1&ma=127.0.0.1&t1=2&t2=10")));
return services;
}
private static void AddKfcServices(XElement services)
{
const string sdvxurl = $"{CommonUrl}";
string[] kfcServices = [
"local", "local2", "lobby", "slocal", "slocal2", "sglocal", "sglocal2", "lab", "globby",
"slobby", "sglobby"
];
foreach(string service in kfcServices)
services.Add(new XElement("item", new XAttribute("name", service), new XAttribute("url", sdvxurl)));
}
private static void AddMdxServices(XElement services)
{
const string mdxurl = $"{CommonUrl}";
string[] mdxServices = [
"local", "local2", "lobby", "slocal", "slocal2", "sglocal", "sglocal2", "lab", "globby",
"slobby", "sglobby"
];
foreach(string service in mdxServices)
services.Add(new XElement("item", new XAttribute("name", service), new XAttribute("url", mdxurl)));
}
private static void AddM39Services(XElement services)
{
const string m39url = $"{CommonUrl}";
string[] m39Services = [
"local", "local2", "lobby", "slocal", "slocal2", "sglocal", "sglocal2", "lab", "globby",
"slobby", "sglobby"
];
foreach(string service in m39Services)
services.Add(new XElement("item", new XAttribute("name", service), new XAttribute("url", m39url)));
}
}
}
@@ -0,0 +1,31 @@
using Server.Handlers.Boot;
using Server.Attributes;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.Common
{
[Handler("pcbtracker", "alive")]
public class AlivePcbTrackerHandler(ILogger<GetServicesHandler> logger, XDocument body) : IHandler
{
private readonly ILogger<GetServicesHandler> _logger = logger;
private readonly XDocument _body = body;
public Task<XDocument> HandleAsync(string model)
{
var pcbTracker = new XElement("response",
new XElement("pcbtracker",
new XAttribute("status", "0"),
new XAttribute("expire", "1200"),
new XAttribute("ecenable", "1"),
new XAttribute("eclimit", "0"),
new XAttribute("limit", "0"),
new XAttribute("time", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString())));
var document = new XDocument(new XElement("response", pcbTracker));
return Task.FromResult(document);
}
}
}
@@ -0,0 +1,46 @@
using Server.Services;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.Common.Card
{
public class AuthPassCardManagmentHandker(ICardService cardService, XDocument body) : Handler
{
private readonly XDocument _body = body;
private readonly ICardService _cardService = cardService;
public override void Configure()
{
Module("cardmng");
Method("authpass");
}
public override async Task<XDocument> HandleAsync(string model)
{
var rootCall = _body.Root;
var cardManagement = rootCall?.Element("cardmng")!;
var referanceId = cardManagement.Attribute("refid")?.Value!;
var password = cardManagement.Attribute("pass")?.Value!;
var card = await _cardService.FindByKonamiId(referanceId);
if(card == null)
{
var noCard = new XElement("cardmng", new XAttribute("status", "116"));
return new XDocument(new XElement("response", noCard));
}
if(card.User.Pin != int.Parse(password))
{
var wrongPassword = new XElement("cardmng", new XAttribute("status", "116"));
return new XDocument(new XElement("response", wrongPassword));
}
var authPass = new XElement("cardmng", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", authPass));
return document;
}
}
}
@@ -0,0 +1,21 @@
using System.Xml.Linq;
namespace Server.Handlers.Common.Card
{
public class BindModelCardManagmentHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("cardmng");
Method("bindmodel");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("cardmng")!;
var cardmng = new XElement("cardmng", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", cardmng));
return document;
}
}
}
@@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Identity;
using Server.Entities;
using Server.Services;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.Common.Card
{
public class GetRefIdCardManagmentHandler(ICardService cardService, UserManager<User> userManager, XDocument body) : Handler
{
private readonly XDocument _body = body;
private readonly ICardService _cardService = cardService;
private readonly UserManager<User> _userManager = userManager;
public override void Configure()
{
Module("cardmng");
Method("getrefid");
}
public override async Task<XDocument> HandleAsync(string model)
{
var rootCall = _body.Root;
var cardManagement = rootCall?.Element("cardmng")!;
var cardId = cardManagement.Attribute("cardid")?.Value!;
var password = cardManagement.Attribute("passwd")?.Value!;
var konamiId = _cardService.ConvertUidToKonamiId(cardId);
var card = new Entities.Card { RawId = cardId, KonamiId = konamiId };
var user = new User { Pin = int.Parse(password), UserName = konamiId, Cards = [card] };
user.Cards.Add(card);
await _userManager.CreateAsync(user);
var getRefId = new XElement("cardmng", new XAttribute("status", "0"), new XAttribute("refid", konamiId), new XAttribute("dataid", konamiId));
var document = new XDocument(new XElement("response", getRefId));
return document;
}
}
}
@@ -0,0 +1,52 @@
using Server.Services;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.Common.Card
{
public class InquireCardManagmentHandlerInquireCardManagmentHandler(ICardService cardService, XDocument body) : Handler
{
private readonly XDocument _body = body;
private readonly ICardService _cardService = cardService;
public override void Configure()
{
Module("cardmng");
Method("inquire");
}
public override async Task<XDocument> HandleAsync(string model)
{
var cardId = _body.Root?.Element("cardmng")?.Attribute("cardid")?.Value;
if(string.IsNullOrEmpty(cardId))
{
var noCardId = new XElement("cardmng", new XAttribute("status", "111"));
return new XDocument(new XElement("response", noCardId));
}
var konamiId = _cardService.ConvertUidToKonamiId(cardId);
if(string.IsNullOrEmpty(konamiId))
{
var noKonamiId = new XElement("cardmng", new XAttribute("status", "111"));
return new XDocument(new XElement("response", noKonamiId));
}
var exisitngCard = await _cardService.FindByKonamiId(konamiId);
if(exisitngCard == null)
{
var noCard = new XElement("cardmng", new XAttribute("status", "112"));
return new XDocument(new XElement("response", noCard));
}
var cardManagment = new XElement("cardmng", new XAttribute("binded", "0"), new XAttribute("dataid", konamiId),
new XAttribute("ecflag", "1"), new XAttribute("expired", "0"), new XAttribute("newflag", "1"), new XAttribute("extidflag", "1"),
new XAttribute("refid", konamiId), new XAttribute("status", "0"), new XAttribute("useridflag", "1"));
var document = new XDocument(new XElement("response", cardManagment));
return document;
}
}
}
@@ -0,0 +1,65 @@
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Hosting.Server;
using System.Xml.Linq;
using Abstractions.Handlers;
using Server.Attributes;
namespace Server.Handlers.Common
{
[Handler("facility", "get")]
public class GetFacilityHandler(IServer server, XDocument body) : IHandler
{
private readonly IServer _server = server;
private IServerAddressesFeature? serverAddressesFeature => _server.Features.Get<IServerAddressesFeature>();
private readonly XDocument _body = body;
public Task<XDocument> HandleAsync(string model)
{
var port = "8083";
var facility = new XElement("facility",
new XAttribute("status", "0"));
var location = new XElement("location",
new XElement("id", "00000000"),
new XElement("country", "US"),
new XElement("region", "NA"),
new XElement("name", "Medusa"),
new XElement("type", 0, new XAttribute("__type", "u8")));
var line = new XElement("line",
new XElement("id", "00000000"),
new XElement("class", 0, new XAttribute("__type", "u8")));
var portfw = new XElement("portfw",
new XElement("globalip", "127.0.0.1", new XAttribute("__type", "ip4")),
new XElement("globalport", "5246", new XAttribute("__type", "u16")),
new XElement("privateport", "5246", new XAttribute("__type", "u16")));
var _public = new XElement("public",
new XElement("flag", 1, new XAttribute("__type", "u8")),
new XElement("name", "Medusa"),
new XElement("latitude", "0.0"),
new XElement("longitude", "0.0"));
var share = new XElement("share",
new XElement("eacoin",
new XElement("notchamount", 3000, new XAttribute("__type", "s32")),
new XElement("notchcount", 3, new XAttribute("__type", "s32")),
new XElement("supplylimit", 100000, new XAttribute("__type", "s32"))),
new XElement("url",
new XElement("eapass", "http://eagate.573.jp"),
new XElement("arcadefan", "http://eagate.573.jp"),
new XElement("konaminetdx", "http://eagate.573.jp"),
new XElement("konamiid", "http://eagate.573.jp"),
new XElement("eagate", "http://eagate.573.jp")));
facility.Add(location, line, portfw, _public, share);
var document = new XDocument(new XElement("response", facility));
return Task.FromResult(document);
}
}
}
@@ -0,0 +1,25 @@
using System.Xml.Linq;
using Abstractions.Handlers;
using Server.Attributes;
namespace Server.Handlers.Common
{
[Handler("message", "get")]
public class GetMessageHandler(XDocument body) : IHandler
{
private readonly XDocument _body = body;
public Task<XDocument> HandleAsync(string model)
{
var message = new XElement("message",
new XAttribute("expire", "300"),
new XAttribute("status", "0"));
var document = new XDocument(
new XElement("response", message));
return Task.FromResult(document);
}
}
}
@@ -0,0 +1,22 @@
using System.Xml.Linq;
using Abstractions.Handlers;
using Server.Attributes;
namespace Server.Handlers.Common.OTAUpdates
{
[Handler("package", "list")]
public class ListPackageHandler(XDocument body) : IHandler
{
private readonly XDocument _body = body;
public Task<XDocument> HandleAsync(string model)
{
var package = new XElement("package", new XAttribute("expire", "600"), new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", package));
return Task.FromResult(document);
}
}
}
@@ -0,0 +1,22 @@
using System.Xml.Linq;
using Abstractions.Handlers;
using Server.Attributes;
namespace Server.Handlers.Common.OTAUpdates
{
[Handler("dlstatus", "progress")]
public class ProgressDLStatusHandler(XDocument body) : IHandler
{
private readonly XDocument _body = body;
public Task<XDocument> HandleAsync(string model)
{
var dlStatus = new XElement("dlstatus", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", dlStatus));
return Task.FromResult(document);
}
}
}
@@ -0,0 +1,47 @@
using System.Xml.Linq;
using Abstractions.Handlers;
using Server.Attributes;
namespace Server.Handlers.Common
{
[Handler("pcbevent", "put")]
public class PutPcbEventHandler(ILogger<PutPcbEventHandler> logger, XDocument body) : IHandler
{
private readonly XDocument _body = body;
private readonly ILogger<PutPcbEventHandler> _logger = logger;
public Task<XDocument> HandleAsync(string model)
{
var rootCallElement = _body.Root;
var itemElement = rootCallElement?.Element("pcbevent")?.Element("item");
var timeElement = itemElement?.Element("time");
string time = timeElement is not null ? UnixTimeStampToDateTime(double.Parse(timeElement.Value)).ToString() : "";
_logger.LogInformation("PCBEvent: tag={Tag} srcid={SrcId} model={Model} name={Name} time={Time}",
rootCallElement?.Attribute("tag")?.Value ?? "",
rootCallElement?.Attribute("srcid")?.Value ?? "",
rootCallElement?.Attribute("model")?.Value ?? "",
itemElement?.Element("name")?.Value ?? "",
time
);
var pcbEvent = new XElement("response",
new XElement("pcbevent",
new XAttribute("status", "0")));
var document = new XDocument(new XElement("response", pcbEvent));
return Task.FromResult(document);
}
public static DateTime UnixTimeStampToDateTime(double unixTimeStamp)
{
// Unix timestamp is seconds past epoch
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(unixTimeStamp).ToLocalTime();
return dateTime;
}
}
}
@@ -0,0 +1,45 @@
using Microsoft.AspNetCore.Identity;
using Server.Entities;
using Server.Services;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.LA9
{
public class ChargeEaChargeHandler(ICardService cardService, ISessionService sessionService, UserManager<User> userManager, XDocument body): Handler
{
private readonly XDocument _body = body;
private readonly ICardService _cardService = cardService;
private readonly ISessionService _sessionService = sessionService;
private readonly UserManager<User> _userManager = userManager;
public override void Configure()
{
Module("eacharge");
Method("charge");
}
public override async Task<XDocument> HandleAsync(string model)
{
var rootCall = _body.Root;
var charge = rootCall?.Element("eacharge")!;
var amount = int.Parse(charge.Element("chargenum")?.Value!) * 1000;
var session = charge.Element("sessid")?.Value!;
var foundSession = _sessionService.GetSession(session);
var card = await _cardService.FindByCardId(foundSession.refId)!;
card.User.PaseliAmount += amount;
await _userManager.UpdateAsync(card.User);
var balance = new XElement("balance", card.User.PaseliAmount, new XAttribute("__type", "s32"));
var chargeDailyLimit = new XElement("chargedailylimit", 100000, new XAttribute("__type", "s32"));
var chargeDaily = new XElement("chargedaily", 0, new XAttribute("__type", "s32"));
var chargepoint = new XElement("chargepoint", amount, new XAttribute("__type", "s32"));
var eaCharge = new XElement("charge", balance, chargeDailyLimit, chargeDaily, chargepoint, new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", eaCharge));
return document;
}
}
}
@@ -0,0 +1,40 @@
using Server.Services;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.LA9
{
public class CheckInEaChargeHandler(ISessionService sessionService, ICardService cardService, XDocument body): Handler
{
private readonly XDocument _body = body;
private readonly ISessionService _sessionService = sessionService;
private readonly ICardService _cardService = cardService;
public override void Configure()
{
Module("eacharge");
Method("checkin");
}
public override async Task<XDocument> HandleAsync(string model)
{
var rootCall = _body.Root;
var eaCharge = rootCall?.Element("eacharge")!;
var cardId = eaCharge.Element("cardid")?.Value!;
var sessionId = _sessionService.AddSession(cardId);
var card = await _cardService.FindByCardId(cardId);
var userPaseli = card!.User.PaseliAmount;
var sessseq = new XElement("sessseq", 0, new XAttribute("__type", "s32"));
var acname = new XElement("acname", "DUMMY_NAME", new XAttribute("__type", "str"));
var balance = new XElement("balance", userPaseli, new XAttribute("__type", "s32"));
var buyablep = new XElement("buyablep", 100000, new XAttribute("__type", "s32"));
var sessionIdXElement = new XElement("sessid", sessionId, new XAttribute("__type", "str"));
var eacharge = new XElement("eacharge", sessseq, acname, balance, buyablep, sessionIdXElement, new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", eacharge));
return document;
}
}
}
@@ -0,0 +1,28 @@
using Server.Services;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.LA9
{
public class CheckoutEaCharge(ISessionService sessionService, XDocument body): Handler
{
private readonly ISessionService _sessionService = sessionService;
private readonly XDocument _body = body;
public override void Configure()
{
Module("eacharge");
Method("checkout");
}
public override XDocument Handle(string model)
{
var eaCharge = _body.Root?.Element("eacharge")!;
var sessionId = eaCharge.Element("sessid")!.Value!;
_sessionService.removeSession(sessionId);
var checkoutCharge = new XElement("eacharge", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", checkoutCharge));
return document;
}
}
}
@@ -0,0 +1,95 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.LA9
{
public class GetConfigEaChargeHandler(XDocument body): Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("eacharge");
Method("getconf");
}
public override XDocument Handle(string model)
{
var articlerevision = new XElement("articlerevision", 1, new XAttribute("__type", "str"));
var articletype1 = new XElement("articletype", "DOCMT_0000", new XAttribute("__type", "str"));
var articledoc1 = new XElement("articledoc", new XAttribute("__type", "str"));
var item1 = new XElement("item", articletype1, articledoc1);
var articletype2 = new XElement("articletype", "DOCMT_0001", new XAttribute("__type", "str"));
var articledoc2 = new XElement("articledoc", new XAttribute("__type", "str"));
var item2 = new XElement("item", articletype2, articledoc2);
var articletype3 = new XElement("articletype", "DOCMT_0002", new XAttribute("__type", "str"));
var articledoc3 = new XElement("articledoc", new XAttribute("__type", "str"));
var item3 = new XElement("item", articletype3, articledoc3);
var articletype4 = new XElement("articletype", "DOCMT_0003", new XAttribute("__type", "str"));
var articledoc4 = new XElement("articledoc", new XAttribute("__type", "str"));
var item4 = new XElement("item", articletype4, articledoc4);
var articletype5 = new XElement("articletype", "URL_0001_A", new XAttribute("__type", "str"));
var articledoc5 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item5 = new XElement("item", articletype5, articledoc5);
var articletype6 = new XElement("articletype", "URL_0001_B", new XAttribute("__type", "str"));
var articledoc6 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item6 = new XElement("item", articletype6, articledoc6);
var articletype7 = new XElement("articletype", "URL_0002_A", new XAttribute("__type", "str"));
var articledoc7 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item7 = new XElement("item", articletype7, articledoc7);
var articletype8 = new XElement("articletype", "URL_0002_B", new XAttribute("__type", "str"));
var articledoc8 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item8 = new XElement("item", articletype8, articledoc8);
var articletype9 = new XElement("articletype", "URL_0003_A", new XAttribute("__type", "str"));
var articledoc9 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item9 = new XElement("item", articletype9, articledoc9);
var articletype10 = new XElement("articletype", "URL_0003_B", new XAttribute("__type", "str"));
var articledoc10 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item10 = new XElement("item", articletype10, articledoc10);
var articletype11 = new XElement("articletype", "URL_0004_A", new XAttribute("__type", "str"));
var articledoc11 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item11 = new XElement("item", articletype11, articledoc11);
var articletype12 = new XElement("articletype", "URL_0004_B", new XAttribute("__type", "str"));
var articledoc12 = new XElement("articledoc", "http://eagate.573.jp", new XAttribute("__type", "str"));
var item12 = new XElement("item", articletype12, articledoc12);
var articletype13 = new XElement("articletype", "PASELIUNIT", new XAttribute("__type", "str"));
var articledoc13 = new XElement("articledoc", "P", new XAttribute("__type", "str"));
var item13 = new XElement("item", articletype13, articledoc13);
var articlelist = new XElement("articlelist", item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13);
var chargearticle = new XElement("chargearticle", articlerevision, articlelist);
var chargedaily = new XElement("chargedaily", 0, new XAttribute("__type", "s32"));
var chargedailylimit = new XElement("chargedailylimit", 100000, new XAttribute("__type", "s32"));
var chargedetail = new XElement("chargedetail", chargedaily, chargedailylimit);
var currentcode = new XElement("currentcode", 0, new XAttribute("__type", "s32"));
var lastchargestatus = new XElement("lastchargestatus", 1, new XAttribute("__type", "s32"));
var chargestatus = new XElement("chargestatus", currentcode, lastchargestatus);
var eacharge = new XElement("eacharge",
new XAttribute("status", "0"), new XAttribute("expire", "1200"), chargearticle, chargedetail, chargestatus);
var document = new XDocument(
new XElement("response",
eacharge
)
);
return document;
}
}
}
@@ -0,0 +1,30 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.M39
{
public class BootPCB24Handler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("pcb24");
Method("boot");
}
public override XDocument Handle(string model)
{
var pcb24 = new XElement("pcb24",
new XAttribute("status", "0"));
var document = new XDocument(
new XElement("response",
pcb24
)
);
return document;
}
}
}
@@ -0,0 +1,24 @@
using System.Xml.Linq;
namespace Server.Handlers.MDX._2014032700
{
public class CommonGameHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("game");
Method("common");
GameCode("MDX");
MinVer("2014032700");
MaxVer("2014032700");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("game")!;
var newGame = new XElement("game", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", newGame));
return document;
}
}
}
@@ -0,0 +1,24 @@
using System.Xml.Linq;
namespace Server.Handlers.MDX._2014032700
{
public class LogGameHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("game");
Method("log");
GameCode("MDX");
MinVer("2014032700");
MaxVer("2014032700");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("game")!;
var newGame = new XElement("game", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", newGame));
return document;
}
}
}
@@ -0,0 +1,24 @@
using System.Xml.Linq;
namespace Server.Handlers.MDX._2014032700
{
public class MessageGameHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("game");
Method("message");
GameCode("MDX");
MinVer("2014032700");
MaxVer("2014032700");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("game")!;
var newGame = new XElement("game", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", newGame));
return document;
}
}
}
@@ -0,0 +1,24 @@
using System.Xml.Linq;
namespace Server.Handlers.MDX._2014032700
{
public class NewGameHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("game");
Method("new");
GameCode("MDX");
MinVer("2014032700");
MaxVer("2014032700");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("game")!;
var newGame = new XElement("game", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", newGame));
return document;
}
}
}
@@ -0,0 +1,26 @@
using System.Xml.Linq;
namespace Server.Handlers.MDX._2014032700
{
public class RecorderGameHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("game");
Method("recorder");
GameCode("MDX");
MinVer("2014032700");
MaxVer("2014032700");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("game")!;
var recorderGame = new XElement("game", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", recorderGame));
return document;
}
}
}
@@ -0,0 +1,24 @@
using System.Xml.Linq;
namespace Server.Handlers.MDX._2014032700
{
public class ShopGameHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("game");
Method("shop");
GameCode("MDX");
MinVer("2014032700");
MaxVer("2014032700");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("game")!;
var newGame = new XElement("game", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", newGame));
return document;
}
}
}
@@ -0,0 +1,26 @@
using System.Xml.Linq;
namespace Server.Handlers.MDX._2014032700
{
public class TaxInfoGameHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("game");
Method("tax_info");
GameCode("MDX");
MinVer("2014032700");
MaxVer("2014032700");
}
public override XDocument Handle(string model)
{
var game = _body.Root?.Element("game")!;
var recorderGame = new XElement("game", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", recorderGame));
return document;
}
}
}
@@ -0,0 +1,25 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers
{
public class SubscribeAsGuestUserIdHandler(XDocument body): Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("userid");
Method("subscribeAsGuest");
}
public override XDocument Handle(string model)
{
var subscribeAsGuest = new XElement("userid", new XAttribute("status", "0"));
var document = new XDocument(new XElement("response", subscribeAsGuest));
return document;
}
}
}
@@ -0,0 +1,31 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.TBS
{
public class GetGachaSettingDataTBSHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("tbs");
Method("getgachasettingdata");
}
public override XDocument Handle(string model)
{
var tbs = new XElement("tbs",
new XAttribute("status", "0")
);
var document = new XDocument(
new XElement("response",
tbs
)
);
return document;
}
}
}
@@ -0,0 +1,31 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.TBS
{
public class GetServerMessageDataTBSHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("tbs");
Method("getservermessagedata");
}
public override XDocument Handle(string model)
{
var tbs = new XElement("tbs",
new XAttribute("status", "0")
);
var document = new XDocument(
new XElement("response",
tbs
)
);
return document;
}
}
}
@@ -0,0 +1,31 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.TBS
{
public class ResetMatchDataTBSHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("tbs");
Method("resetmatchdata");
}
public override XDocument Handle(string model)
{
var tbs = new XElement("tbs",
new XAttribute("status", "0")
);
var document = new XDocument(
new XElement("response",
tbs
)
);
return document;
}
}
}
@@ -0,0 +1,30 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.TBS
{
public class SetClientLogDataTBSHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("tbs");
Method("setclientlogdata");
}
public override XDocument Handle(string model)
{
var tbs = new XElement("tbs",
new XAttribute("status", "0"));
var document = new XDocument(
new XElement("response",
tbs
)
);
return document;
}
}
}
@@ -0,0 +1,31 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.TBS
{
public class SetEventLogDataTBSHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("tbs");
Method("seteventlogdata");
}
public override XDocument Handle(string model)
{
var tbs = new XElement("tbs",
new XAttribute("status", "0")
);
var document = new XDocument(
new XElement("response",
tbs
)
);
return document;
}
}
}
@@ -0,0 +1,31 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.VFG
{
public class PutMsgVfglogHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("vfglog");
Method("put_msg");
}
public override XDocument Handle(string model)
{
var vfglog = new XElement("vfglog",
new XAttribute("status", "0")
);
var document = new XDocument(
new XElement("response",
vfglog
)
);
return document;
}
}
}
@@ -0,0 +1,38 @@
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Handlers.VFG
{
public class ServiceListVfgacHandler(XDocument body) : Handler
{
private readonly XDocument _body = body;
public override void Configure()
{
Module("vfgac");
Method("service_list");
}
public override XDocument Handle(string model)
{
var serviceUrl = new XElement("service_url", "http://127.0.0.1:5120/eamuse");
var service1 = new XElement("service", new XAttribute("service", "http://127.0.0.1:5120/eamuse"), new XAttribute("mode", "test"), "http://127.0.0.1:5120/eamuse");
var services = new XElement("services", service1);
var vfgac = new XElement("vfgac",
new XAttribute("status", "0"),
serviceUrl,
services
);
var document = new XDocument(
new XElement("response",
vfgac
)
);
return document;
}
}
}
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,81 @@
using static System.Net.Mime.MediaTypeNames;
using System.Text;
using System.Security.Cryptography;
using System.Xml.Linq;
using Server.Utils;
using KbinXml.Net;
namespace Server.Middlewares;
public class BodyParsingMiddleware(RequestDelegate next)
{
static readonly byte[] Key =
Convert.FromHexString("00000000000069D74627D985EE2187161570D08D93B12455035B6DF0D8205DF5");
private readonly RequestDelegate _next = next;
public async Task Invoke(HttpContext context)
{
if(context.Request.Method == "GET" || !context.Request.Path.ToString().Contains("eamuse", StringComparison.CurrentCultureIgnoreCase))
{
await _next(context);
return;
}
var body = await ParseRequest(context);
context.Items["Encoding"] = body.Declaration?.Encoding;
var bodyString = body.ToString();
if(string.IsNullOrEmpty(bodyString))
{
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(""));
await _next(context);
return;
}
context.Request.Body = new MemoryStream(Encoding.GetEncoding(932).GetBytes(bodyString));
await _next(context);
}
public async Task<XDocument> ParseRequest(HttpContext context)
{
var isCompressed = context.Request.Headers["X-Compress"].ToString().Contains("lz77");
var info = context.Request.Headers["X-Eamuse-Info"].FirstOrDefault();
var contentLength = context.Request.Headers.ContentLength ?? 0;
byte[] data = new byte[(int)contentLength];
await context.Request.Body.ReadAsync(data.AsMemory(0, (int)contentLength));
if(info is not null)
{
string[] infoParts = info.Split('-');
for(int i = 0; i < 6; i++)
{
Key[i] = Convert.ToByte((infoParts[1] + infoParts[2]).Substring(i << 1, 2), 0x10);
}
byte[] rc4Key = MD5.HashData(Key);
data = RC4.Decrypt(rc4Key, data);
}
if(isCompressed)
{
data = LZ77.Decompress(data);
}
var returnData = new XDocument();
try
{
returnData = KbinConverter.ReadXmlLinq(data);
}
catch(Exception e)
{
Console.WriteLine(e);
}
//Data is now xml in konami binary form
return returnData;
}
}
+313
View File
@@ -0,0 +1,313 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Server;
#nullable disable
namespace Server.Migrations
{
[DbContext(typeof(AppDbContext))]
[Migration("20241208214019_Init")]
partial class Init
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.0");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<int>("RoleId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
{
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.Property<int>("RoleId")
.HasColumnType("INTEGER");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
{
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Server.Entities.Card", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("KonamiId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RawId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Cards");
});
modelBuilder.Entity("Server.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<int>("PaseliAmount")
.HasColumnType("INTEGER");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<int>("Pin")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<int>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
{
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
{
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<int>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
{
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Server.Entities.Card", b =>
{
b.HasOne("Server.Entities.User", "User")
.WithMany("Cards")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Server.Entities.User", b =>
{
b.Navigation("Cards");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,255 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Server.Migrations
{
/// <inheritdoc />
public partial class Init : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
PaseliAmount = table.Column<int>(type: "INTEGER", nullable: false),
Pin = table.Column<int>(type: "INTEGER", nullable: false),
UserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
PasswordHash = table.Column<string>(type: "TEXT", nullable: true),
SecurityStamp = table.Column<string>(type: "TEXT", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true),
PhoneNumber = table.Column<string>(type: "TEXT", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
LockoutEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
AccessFailedCount = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
RoleId = table.Column<int>(type: "INTEGER", nullable: false),
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<int>(type: "INTEGER", nullable: false),
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
ProviderKey = table.Column<string>(type: "TEXT", nullable: false),
ProviderDisplayName = table.Column<string>(type: "TEXT", nullable: true),
UserId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<int>(type: "INTEGER", nullable: false),
RoleId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<int>(type: "INTEGER", nullable: false),
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Value = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Cards",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
KonamiId = table.Column<string>(type: "TEXT", nullable: false),
RawId = table.Column<string>(type: "TEXT", nullable: false),
UserId = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Cards", x => x.Id);
table.ForeignKey(
name: "FK_Cards_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Cards_UserId",
table: "Cards",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "Cards");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}
@@ -0,0 +1,310 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Server;
#nullable disable
namespace Server.Migrations
{
[DbContext(typeof(AppDbContext))]
partial class AppDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.0");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<int>("RoleId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
{
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.Property<int>("RoleId")
.HasColumnType("INTEGER");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
{
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Server.Entities.Card", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("KonamiId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("RawId")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Cards", (string)null);
});
modelBuilder.Entity("Server.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<int>("PaseliAmount")
.HasColumnType("INTEGER");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<int>("Pin")
.HasColumnType("INTEGER");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<int>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
{
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
{
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<int>", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
{
b.HasOne("Server.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Server.Entities.Card", b =>
{
b.HasOne("Server.Entities.User", "User")
.WithMany("Cards")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Server.Entities.User", b =>
{
b.Navigation("Cards");
});
#pragma warning restore 612, 618
}
}
}
+209
View File
@@ -0,0 +1,209 @@
using KbinXml.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Server;
using Server.Entities;
using Server.Extensions;
using Server.Middlewares;
using Server.Request;
using Server.Services;
using Server.Utils;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;
var key =
Convert.FromHexString("00000000000069D74627D985EE2187161570D08D93B12455035B6DF0D8205DF5");
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddHandlers();
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlite("Data Source=Medusa.db;"));
builder.Services.AddIdentityCore<User>(config =>
{
config.Password.RequiredLength = 8;
config.SignIn.RequireConfirmedEmail = true;
config.Lockout.AllowedForNewUsers = true;
}).AddEntityFrameworkStores<AppDbContext>();
builder.Services.AddTransient<ICardService, CardService>();
builder.Services.AddSingleton<ISessionService, SessionService>();
var app = builder.Build();
app.UseDefaultFiles();
app.UseStaticFiles();
// Configure the HTTP request pipeline.
//app.UseHttpsRedirection();
app.UseMiddleware<BodyParsingMiddleware>();
app.UseHandlers();
var eamuseGroup = app.MapGroup("eamuse");
eamuseGroup.MapPost("/{model}/{module}/{method}", (string model, string module, string method,
[FromServices] IHttpContextAccessor httpContextAccessor) =>
{
});
eamuseGroup.MapPost("/{m}", async (string m, [FromQuery] string model, [FromQuery] string? module, [FromQuery] string? method, [FromQuery] string? f,
HttpContext httpContext, [FromServices] ILogger<Program> logger, [FromServices] IHandlerService handlerService) =>
{
// Enable buffering to allow multiple reads of the request body
httpContext.Request.EnableBuffering();
// The body is 932 encoded xml
using var reader = new StreamReader(httpContext.Request.Body, Encoding.GetEncoding(932), false, 1024, true);
var body = await reader.ReadToEndAsync();
httpContext.Request.Body.Position = 0;
bool compress = httpContext.Request.Headers["X-Compress"].ToString().Contains("lz77");
bool encrypt = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault() is not null;
var amusementRequest = new AmusementRequest() { Model = model, Module = module ?? "", Method = method ?? "" };
if(!string.IsNullOrEmpty(f))
{
var fParts = f.Split('.');
amusementRequest.Module = fParts[0];
amusementRequest.Method = fParts[1];
}
logger.LogInformation("Handling {Module} {Method}", amusementRequest.Module, amusementRequest.Method);
var document = new XDocument();
if (!string.IsNullOrEmpty(body))
document = XDocument.Parse(body);
var responseXml = await handlerService.Handle(amusementRequest.Model, amusementRequest.Module, amusementRequest.Method, document);
var encoding = httpContext.Items["Encoding"]?.ToString() ?? "ShiftJIS";
switch(encoding)
{
case "shift_jis":
encoding = "ShiftJIS";
break;
case "us-ascii":
encoding = "ASCII";
break;
case "utf-8":
encoding = "UTF8";
break;
case "euc-jp":
encoding = "EUC_JP";
break;
}
byte[] encodedBody = KbinConverter.Write(responseXml, (KnownEncodings)Enum.Parse(typeof(KnownEncodings), encoding, true));
if(compress)
{
encodedBody = LZ77.CompressEmpty(encodedBody);
}
if(encrypt)
{
string[] originalInfo = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault()?.Split('-') ?? [];
byte[] part = Convert.FromHexString((originalInfo[1] + originalInfo[2]));
for(int i = 0; i < 6; i++)
key[i] = part[i];
var rc4Key = MD5.HashData(key);
encodedBody = RC4.Encrypt(rc4Key, encodedBody);
httpContext.Response.Headers.Append("X-Eamuse-Info", string.Join('-', originalInfo));
}
return TypedResults.Bytes(encodedBody, "application/octet-stream");
});
eamuseGroup.MapPost("/", async ([FromQuery] string model, [FromQuery] string? module, [FromQuery] string? method, [FromQuery] string? f,
HttpContext httpContext, [FromServices] ILogger<Program> logger, [FromServices] IHandlerService handlerService) =>
{
// Enable buffering to allow multiple reads of the request body
httpContext.Request.EnableBuffering();
var body = "";
// The body is 932 encoded xml
if(httpContext.Request.Body.Length != 0)
{
using var reader = new StreamReader(httpContext.Request.Body, Encoding.GetEncoding(932), false, 1024, true);
body = await reader.ReadToEndAsync();
}
httpContext.Request.Body.Position = 0;
bool compress = httpContext.Request.Headers["X-Compress"].ToString().Contains("lz77");
bool encrypt = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault() is not null;
var amusementRequest = new AmusementRequest() { Model = model, Module = module ?? "", Method = method ?? ""};
if (!string.IsNullOrEmpty(f))
{
var fParts = f.Split('.');
amusementRequest.Module = fParts[0];
amusementRequest.Method = fParts[1];
}
logger.LogInformation("Handling {Module} {Method}", amusementRequest.Module, amusementRequest.Method);
var document = new XDocument();
if(!string.IsNullOrEmpty(body))
document = XDocument.Parse(body);
var responseXml = await handlerService.Handle(amusementRequest.Model, amusementRequest.Module, amusementRequest.Method, document);
var encoding = httpContext.Items["Encoding"]?.ToString() ?? "ShiftJIS";
switch(encoding)
{
case "shift_jis":
encoding = "ShiftJIS";
break;
case "us-ascii":
encoding = "ASCII";
break;
case "utf-8":
encoding = "UTF8";
break;
case "euc-jp":
encoding = "EUC_JP";
break;
}
var encodedBody = KbinConverter.Write(responseXml, (KnownEncodings)Enum.Parse(typeof(KnownEncodings), encoding, true));
if(compress)
{
encodedBody = LZ77.CompressEmpty(encodedBody);
}
if (!encrypt) return TypedResults.Bytes(encodedBody, "application/octet-stream");
var originalInfo = httpContext.Request.Headers["X-Eamuse-Info"].FirstOrDefault()?.Split('-') ?? [];
var part = Convert.FromHexString((originalInfo[1] + originalInfo[2]));
for(var i = 0; i < 6; i++)
key[i] = part[i];
var rc4Key = MD5.HashData(key);
encodedBody = RC4.Encrypt(rc4Key, encodedBody);
httpContext.Response.Headers.Append("X-Eamuse-Info", string.Join('-', originalInfo));
return TypedResults.Bytes(encodedBody, "application/octet-stream");
});
app.MapFallbackToFile("/index.html");
await using var scope = app.Services.CreateAsyncScope();
var appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await appDbContext.Database.MigrateAsync();
app.Run();
@@ -0,0 +1,45 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:52284",
"sslPort": 44317
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "http://localhost:5120",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "weatherforecast",
"applicationUrl": "https://localhost:7059;http://localhost:5120",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "weatherforecast",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.SpaProxy"
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Server.Request
{
public class AmusementRequest
{
public required string Model { get; init; }
public required string Module { get; set; }
public required string Method { get; set; }
}
}
+38
View File
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<SpaRoot>..\Client</SpaRoot>
<SpaProxyLaunchCommand>npm run dev</SpaProxyLaunchCommand>
<SpaProxyServerUrl>https://localhost:5173</SpaProxyServerUrl>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Abstractions\Abstractions.csproj" />
<ProjectReference Include="..\Client\Client.esproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="KbinXml.Net" Version="1.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.SpaProxy">
<Version>9.0.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
+6
View File
@@ -0,0 +1,6 @@
@Server_HostAddress = http://localhost:5120
GET {{Server_HostAddress}}/weatherforecast/
Accept: application/json
###
+208
View File
@@ -0,0 +1,208 @@
using Microsoft.EntityFrameworkCore;
using Server.Entities;
using Server.Utils;
using System.Text;
namespace Server.Services;
public class CardService : ICardService
{
private const string Alphabet = "0123456789ABCDEFGHJKLMNPRSTUWXYZ";
private readonly byte[] RawKey = Encoding.ASCII.GetBytes("?I'llB2c.YouXXXeMeHaYpy!");
private readonly DesEncryption _desEncryption;
private readonly ILogger<CardService> _logger;
private readonly AppDbContext _context;
public CardService(ILogger<CardService> logger, AppDbContext context)
{
var encryptionKey = RawKey.Select(x => (byte)(x * 2)).ToArray();
_desEncryption = new DesEncryption(encryptionKey);
_logger = logger;
_context = context;
}
public string ConvertKonamiIdToUid(string konamiId)
{
if(konamiId.Length != 16)
{
_logger.LogError("Invalid Konami ID length must be 16 characters");
return string.Empty;
}
int cardType = konamiId[14] == '1' ? 1 : konamiId[14] == '2' ? 2 : -1;
if(cardType == -1)
{
_logger.LogError("Invalid Konami ID");
return string.Empty;
}
var card = konamiId.Select(k => (byte)Alphabet.IndexOf(k)).ToArray();
if(card[11] % 2 != card[12] % 2 || card[13] != (card[12] ^ 1) || card[15] != CalculateChecksum(card))
{
_logger.LogError("Invalid card data");
return string.Empty;
}
for(int i = 13; i > 0; i--)
{
card[i] ^= card[i - 1];
}
card[0] ^= (byte)cardType;
var packed = Pack5(card.Take(13).ToArray()).Take(8).ToArray();
var decrypted = _desEncryption.Decrypt(packed).Reverse().ToArray();
string cardId = BitConverter.ToString(decrypted).Replace("-", "").ToUpper();
if(cardType == 1 && !cardId.StartsWith("E004") || cardType == 2 && cardId[0] != '0')
{
_logger.LogError("Invalid card `type");
return string.Empty;
}
return cardId;
}
public string ConvertUidToKonamiId(string uid)
{
if(uid.Length != 16)
{
_logger.LogError("Invalid UID length must be 16 characters");
return string.Empty;
}
int cardType = uid.StartsWith("E004") ? 1 : uid[0] == '0' ? 2 : -1;
if(cardType == -1)
{
_logger.LogError("Invalid UID prefix");
return string.Empty;
}
var konamiId = Enumerable.Range(0, uid.Length / 2)
.Select(x => Convert.ToByte(uid.Substring(x * 2, 2), 16))
.ToArray();
konamiId = konamiId.Reverse().ToArray();
if(konamiId.Length != 8)
{
_logger.LogError("Invalid UID length. Must be 8 bytes");
return string.Empty;
}
var encryptedId = _desEncryption.Encrypt(konamiId);
if(encryptedId.Length != 8)
{
_logger.LogError("Invalid ID length. Must be 8 bytes");
return string.Empty;
}
var unpackedId = Unpack5(encryptedId);
unpackedId = unpackedId.Take(13).Concat(new byte[] { 0, 0, 0 }).ToArray();
if(unpackedId.Length != 16)
{
_logger.LogError("Invalid unpacked ID length. Must be 16 bytes");
return string.Empty;
}
unpackedId[0] ^= (byte)cardType;
unpackedId[13] = 1;
for(int i = 1; i < 14; i++)
{
unpackedId[i] ^= unpackedId[i - 1];
}
unpackedId[14] = (byte)cardType;
unpackedId[15] = (byte)CalculateChecksum(unpackedId);
return string.Concat(unpackedId.Select(u => Alphabet[u]));
}
private static byte[] Unpack5(byte[] data)
{
// Convert each byte to an 8-bit binary string and concatenate them
var binaryString = new StringBuilder();
foreach(byte b in data)
{
binaryString.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
}
// Ensure the total length is a multiple of 5 by padding with zeroes if necessary
int remainder = binaryString.Length % 5;
if(remainder != 0)
{
binaryString.Append(new string('0', 5 - remainder));
}
// Convert the 5-bit chunks back into bytes
var result = new List<byte>();
for(int i = 0; i < binaryString.Length; i += 5)
{
string chunk = binaryString.ToString(i, 5);
result.Add(Convert.ToByte(chunk, 2));
}
return [.. result];
}
private static byte[] Pack5(byte[] data)
{
var packed = new StringBuilder();
foreach(byte b in data)
{
packed.Append(Convert.ToString(b, 2).PadLeft(5, '0'));
}
// Padding if not a multiple of 8
if(packed.Length % 8 != 0)
{
packed.Append('0', 8 - packed.Length % 8);
}
return Enumerable.Range(0, packed.Length / 8)
.Select(i => Convert.ToByte(packed.ToString().Substring(i * 8, 8), 2))
.ToArray();
}
private static byte CalculateChecksum(byte[] data)
{
int checksum = 0;
for(int i = 0; i < 15; i++)
{
if(i >= data.Length)
{
break;
}
checksum += data[i] * ((i % 3) + 1);
}
// Reduce the checksum to fit within 5 bits
while(checksum > 31)
{
checksum = (checksum >> 5) + (checksum & 31);
}
// Return the checksum as a byte
return (byte)checksum;
}
public Task<Card?> FindByKonamiId(string konamiId)
{
return _context.Cards.Include(x => x.User).FirstOrDefaultAsync(c => c.KonamiId == konamiId);
}
public Task<Card?> FindByCardId(string cardId)
{
return _context.Cards.Include(x => x.User).FirstOrDefaultAsync(c => c.RawId == cardId);
}
}
+140
View File
@@ -0,0 +1,140 @@
using Server.Attributes;
using Server.Handlers;
using System.Xml.Linq;
using Abstractions.Handlers;
namespace Server.Services;
public class HandlerService(IServiceScopeFactory serviceScopeFactory, ILogger<HandlerService> logger) : IHandlerService
{
private readonly IServiceScopeFactory _serviceScopeFactory = serviceScopeFactory;
private readonly ILogger<HandlerService> _logger = logger;
public List<Type> Handlers { get; set; } = [];
public async Task<XDocument> Handle(string model, string module, string method, XDocument body)
{
foreach(var handler in Handlers)
{
//Module and service are on the attribute
var handlerAttribute = handler.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(HandlerAttribute));
if(handlerAttribute is null)
{
var response = await HandleInheritanceClass(handler, model, module, method, body);
if(response is not null)
{
return response;
}
continue;
}
var attributeModule = handlerAttribute.ConstructorArguments[0].Value!.ToString();
var attributeMethod = handlerAttribute.ConstructorArguments[1].Value!.ToString();
if (attributeModule != module || attributeMethod != method) continue;
await using var scope = _serviceScopeFactory.CreateAsyncScope();
// Body param is optional, so check for it
var requiresXDocumentConstructor = handler.GetConstructors()
.Any(c => c.GetParameters().Any(p => p.ParameterType == typeof(XDocument)));
var handlerInstance = requiresXDocumentConstructor
? (IHandler)ActivatorUtilities.CreateInstance(scope.ServiceProvider, handler, body)
: (IHandler)ActivatorUtilities.CreateInstance(scope.ServiceProvider, handler);
return await handlerInstance.HandleAsync(model);
}
//If no handler is found return an empty document
_logger.LogWarning($"No handler found for {model}/{module}/{method}");
return new XDocument();
}
private async Task<XDocument?> HandleInheritanceClass(Type handler, string model, string module, string method, XDocument body)
{
await using var scope = _serviceScopeFactory.CreateAsyncScope();
bool requiredXdocumentConstructor = handler.GetConstructors()
.Any(c => c.GetParameters().Any(p => p.ParameterType == typeof(XDocument)));
Handler handlerInstance = requiredXdocumentConstructor
? (Handler)ActivatorUtilities.CreateInstance(scope.ServiceProvider, handler, body)
: (Handler)ActivatorUtilities.CreateInstance(scope.ServiceProvider, handler);
try
{
var configureMethod = handler.GetMethod("Configure");
configureMethod?.Invoke(handlerInstance, null);
}
catch(NotImplementedException e)
{
_logger.LogError(e, "Configure method not implemented for {Handler}", handler.Name);
return null;
}
var modelParts = model.Split(':');
var handlerModule = handlerInstance.HandlerModule;
var handlerMethod = handlerInstance.HandlerMethod;
var handlerGameCode = handlerInstance.HandlerGameCode;
var handlerMinVer = handlerInstance.HandlerMinVer;
var handlerMaxVer = handlerInstance.HandlerMaxVer;
if(string.IsNullOrEmpty(handlerModule))
{
_logger.LogError("Module not set for {Handler}", handler.Name);
return null;
}
if(string.IsNullOrEmpty(handlerMethod))
{
_logger.LogError("Method not set for {Handler}", handler.Name);
return null;
}
if(!string.IsNullOrEmpty(handlerGameCode))
{
if(handlerGameCode != modelParts[0])
return null;
}
var version = string.Join(string.Empty, modelParts.Skip(4));
if(!string.IsNullOrEmpty(handlerMinVer))
{
var parsedVersion = int.Parse(version);
var parsedMinVer = int.Parse(handlerMinVer);
if(parsedVersion < parsedMinVer)
return null;
}
if(!string.IsNullOrEmpty(handlerMaxVer))
{
var parsedVersion = int.Parse(version);
var parsedMaxVer = int.Parse(handlerMaxVer);
if(parsedVersion > parsedMaxVer)
return null;
}
if(handlerModule != module || handlerMethod != method)
return null;
var handleMethod = handlerInstance.GetType().GetMethod("Handle", [typeof(string)]);
var handleAsyncMethod = handlerInstance.GetType().GetMethod("HandleAsync", [typeof(string)]);
var response = handleMethod?.DeclaringType != typeof(Handler)
? handlerInstance.Handle(model)
: handleAsyncMethod?.DeclaringType != typeof(Handler)
? await handlerInstance.HandleAsync(model)
: null;
if (response is not null) return response;
_logger.LogError("Handle and HandleAsync not implemented for {Handler}", handler.Name);
return null;
}
}
+13
View File
@@ -0,0 +1,13 @@
using Server.Entities;
namespace Server.Services
{
public interface ICardService
{
string ConvertUidToKonamiId(string uid);
string ConvertKonamiIdToUid(string konamiId);
Task<Card?> FindByKonamiId(string konamiId);
Task<Card?> FindByCardId(string cardId);
}
}
+11
View File
@@ -0,0 +1,11 @@
using System.Xml.Linq;
namespace Server.Services
{
public interface IHandlerService
{
public List<Type> Handlers { get; set; }
Task<XDocument> Handle(string model, string module, string method, XDocument body);
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace Server.Services
{
public interface ISessionService
{
public void AddSession(string sessionId, string refId);
string AddSession(string cardid);
public (string sessionId, string refId) GetSession(string sessionId);
public void removeSession(string sessionId);
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Server.Services
{
public interface IUserService
{
}
}
+33
View File
@@ -0,0 +1,33 @@
namespace Server.Services
{
public class SessionService : ISessionService
{
private readonly List<(string sessionId, string refId)> sessions = [];
public void AddSession(string sessionId, string refId)
{
sessions.Add((sessionId, refId));
}
public string AddSession(string cardid)
{
var sessionId = GenerateSessionId();
sessions.Add((sessionId, cardid));
return sessionId;
}
public (string sessionId, string refId) GetSession(string sessionId)
{
return sessions.Find(s => s.sessionId == sessionId);
}
public void removeSession(string sessionId)
{
sessions.Remove(sessions.Find(s => s.sessionId == sessionId));
}
private string GenerateSessionId()
{
return Guid.NewGuid().ToString().Replace("-", "").ToUpper();
}
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Server.Services
{
public class UserService: IUserService
{
}
}
+33
View File
@@ -0,0 +1,33 @@
using System.Security.Cryptography;
namespace Server.Utils;
public class DesEncryption(byte[] key)
{
public byte[] Encrypt(byte[] data)
{
using var tripleDes = TripleDES.Create();
tripleDes.Key = key;
tripleDes.Mode = CipherMode.CBC;
tripleDes.Padding = PaddingMode.None;
tripleDes.IV = new byte[8];
using var encryptor = tripleDes.CreateEncryptor();
return encryptor.TransformFinalBlock(data, 0, data.Length);
}
public byte[] Decrypt(byte[] data)
{
using var tripleDES = TripleDES.Create();
tripleDES.Key = key;
tripleDES.Mode = CipherMode.CBC;
tripleDES.Padding = PaddingMode.None;
tripleDES.IV = new byte[8];
using var decryptor = tripleDES.CreateDecryptor();
return decryptor.TransformFinalBlock(data, 0, data.Length);
}
}
+95
View File
@@ -0,0 +1,95 @@
namespace Server.Utils;
public class LZ77
{
public static byte[] Decompress(byte[] data)
{
// init a buffer, size comes from eamuemu
byte[] res = new byte[0x190000];
// current data location
int p = 0;
// current output location
int r = 0;
// traceback location
int t = 0;
// bitmask location
int b = 8; // read next bitmask byte on start
byte mask = 0;
while(true)
{
// read bitmask on block end
if(b == 8)
{
mask = data[p];
p += 1;
b = 0;
}
// get mask for next byte
if((mask & 1) == 1)
{ // not coded
// copy the byte from data to result
res[r] = data[p];
r += 1;
p += 1;
}
else
{ // coded
// read descriptors
int distance = data[p];
int count = data[p + 1];
// EOF mark
if(distance == 0 && count == 0)
{
break;
}
p += 2;
// shift to correct location
distance <<= 4;
distance |= count >> 4;
count = (count & 0x0F) + 3;
// copy earlier result bytes to the end
t = r - distance; // initialize traceback location
for(int i = 0; i < count; i++)
{
res[r] = t < 0 ? (byte)0x00 : res[t];
r += 1;
t += 1;
}
}
// shift mask
mask >>= 1;
b += 1;
}
// r = result length
byte[] output = new byte[r];
Array.Copy(res, output, r);
return output;
}
public static byte[] CompressEmpty(byte[] data)
{
byte[] res = new byte[data.Length + data.Length / 8 + 3];
int p = 0;
for(int i = 0; i < data.Length; i++)
{
if(i % 8 == 0)
{
if(data.Length - i < 8)
{
res[p] = (byte)(Math.Pow(2, data.Length - i) - 1);
}
else
{
res[p] = 255;
}
p += 1;
}
res[p] = data[i];
p += 1;
}
res[p] = 0;
res[p + 1] = 0;
return res;
}
}
+68
View File
@@ -0,0 +1,68 @@
using System.Text;
namespace Server.Utils
{
public class RC4
{
public static string Encrypt(string key, string data)
{
Encoding unicode = Encoding.Unicode;
return Convert.ToBase64String(Encrypt(unicode.GetBytes(key), unicode.GetBytes(data)));
}
public static string Decrypt(string key, string data)
{
Encoding unicode = Encoding.Unicode;
return unicode.GetString(Encrypt(unicode.GetBytes(key), Convert.FromBase64String(data)));
}
public static byte[] Encrypt(byte[] key, byte[] data)
{
return EncryptOutput(key, data).ToArray();
}
public static byte[] Decrypt(byte[] key, byte[] data)
{
return EncryptOutput(key, data).ToArray();
}
private static byte[] InitializeKeyStream(byte[] key)
{
byte[] keyStream = Enumerable.Range(0, 256)
.Select(i => (byte)i)
.ToArray();
for(int keyStreamIndex = 0, keyIndex = 0; keyStreamIndex < 256; keyStreamIndex++)
{
keyIndex = (keyIndex + key[keyStreamIndex % key.Length] + keyStream[keyStreamIndex]) & 255;
Swap(keyStream, keyStreamIndex, keyIndex);
}
return keyStream;
}
private static IEnumerable<byte> EncryptOutput(byte[] key, IEnumerable<byte> data)
{
byte[] keyStream = InitializeKeyStream(key);
int keyStreamIndex = 0;
int keyIndex = 0;
return data.Select((dataByte) =>
{
keyStreamIndex = (keyStreamIndex + 1) & 255;
keyIndex = (keyIndex + keyStream[keyStreamIndex]) & 255;
Swap(keyStream, keyStreamIndex, keyIndex);
return (byte)(dataByte ^ keyStream[(keyStream[keyStreamIndex] + keyStream[keyIndex]) & 255]);
});
}
private static void Swap(byte[] keyStream, int firstIndex, int secondIndex) => (keyStream[secondIndex], keyStream[firstIndex]) = (keyStream[firstIndex], keyStream[secondIndex]);
}
}

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