Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87661cf871 | ||
|
|
d6d98aa561 | ||
|
|
2c78e6646a | ||
|
|
0f8010ae8d | ||
|
|
cb5db5e55f | ||
|
|
a99503a86d | ||
|
|
37660a382a | ||
|
|
e43faeff10 | ||
|
|
2189132753 | ||
|
|
85aad3377e | ||
|
|
2f5a63858f | ||
|
|
dbffb15779 | ||
|
|
1f544ddb7e | ||
|
|
7f47315130 | ||
|
|
afe9f27c25 | ||
|
|
f8ca0bdcd3 | ||
|
|
98caa9248b | ||
|
|
0236d44466 | ||
|
|
4c0e933ea4 | ||
|
|
8b284107b1 | ||
|
|
2c31bd0c47 | ||
|
|
7b329bc37c | ||
|
|
0937c0fadf | ||
|
|
ea92e4e981 | ||
|
|
1cfd577f56 | ||
|
|
b7187fe0ed | ||
|
|
60c089a810 | ||
|
|
377096a077 | ||
|
|
87de79ef12 | ||
|
|
f1386f8e3d | ||
|
|
6d581c114d | ||
|
|
9570349565 | ||
|
|
206d12742a | ||
|
|
f2434cef7e | ||
|
|
2474879c89 | ||
|
|
4c36e0abf0 | ||
|
|
ccd5fd72c8 | ||
|
|
46499a5425 | ||
|
|
8562da15b6 | ||
|
|
8195e9f937 | ||
|
|
9f57ff1342 | ||
|
|
a7b7a01685 | ||
|
|
8ada783f7c | ||
|
|
f79c4a4370 | ||
|
|
f3be39823a | ||
|
|
03d0e87b55 | ||
|
|
efcc1caa76 | ||
|
|
2a546c0d9d | ||
|
|
63627eaf78 | ||
|
|
ed7c461fe1 | ||
|
|
75ac6e03f2 | ||
|
|
2f2dc82b5a | ||
|
|
198256e6ff | ||
|
|
eb276128c2 | ||
|
|
6090a60467 | ||
|
|
2099622b0f | ||
|
|
9494f3026b | ||
|
|
f7ceb6bcda | ||
|
|
04dac7279e | ||
|
|
8c164b6130 | ||
|
|
27b229e1c2 | ||
|
|
c877d9d214 | ||
|
|
636789b8bf | ||
|
|
48aeb1c9d2 | ||
|
|
5f09c00eb4 | ||
|
|
bf28710000 | ||
|
|
8c61bda865 | ||
|
|
1fa268e6ba | ||
|
|
4fe69077fc | ||
|
|
4f5eb97309 | ||
|
|
122c397b59 | ||
|
|
d3984dcd5d | ||
|
|
4abc04e9f6 | ||
|
|
c01dbb50f7 | ||
|
|
fa5ed284e1 | ||
|
|
76583f17ce | ||
|
|
f959993197 | ||
|
|
6dceb7c48a | ||
|
|
1869c37949 | ||
|
|
1d8fd582c9 | ||
|
|
9b22318934 | ||
|
|
71a7cd7eed | ||
|
|
6c22c4287e | ||
|
|
a8539b475e | ||
|
|
abcccfb9a0 | ||
|
|
b2288b9b8f | ||
|
|
e61b103221 | ||
|
|
94ed27503a | ||
|
|
ad1b48c210 | ||
|
|
75b35e09e2 | ||
|
|
834804b2ea | ||
|
|
ac9cf50d28 | ||
|
|
cb4743f86f | ||
|
|
cef7e99e46 | ||
|
|
4b007b446d | ||
|
|
4c5122d163 | ||
|
|
01198d546a | ||
|
|
bc5f26d768 | ||
|
|
9a652e1ace | ||
|
|
7b21a0bdae | ||
|
|
5311618d1d | ||
|
|
37ce14dda7 | ||
|
|
0d8d5845c9 | ||
|
|
143f534ba1 | ||
|
|
4db3d94ee8 | ||
|
|
e195cf77cd | ||
|
|
ac9622f1bd | ||
|
|
e8535b1a80 | ||
|
|
d81b763d9b | ||
|
|
1c5726be4c | ||
|
|
6da5abcefc | ||
|
|
c5e39f104f | ||
|
|
60d2f7cfd3 | ||
|
|
0ac8b44ce0 | ||
|
|
59fec74d08 | ||
|
|
52f142a2b9 | ||
|
|
3a7132d276 | ||
|
|
4f89894fd1 | ||
|
|
c50a0093fd | ||
|
|
1b41b82745 | ||
|
|
d1d63e1ef9 | ||
|
|
beb9144e9b | ||
|
|
d58f4bed8f | ||
|
|
125e4d8ca7 | ||
|
|
b096a79f95 | ||
|
|
04b0ccfda3 | ||
|
|
82bd9ad7bb | ||
|
|
6a4ac795b2 | ||
|
|
3a71f5054c | ||
|
|
650e05310d | ||
|
|
3aee0f611c | ||
|
|
fa45169b96 | ||
|
|
07222c9ae9 | ||
|
|
38298a2840 | ||
|
|
2440bedf4a | ||
|
|
9acc375d99 | ||
|
|
799ace06d7 | ||
|
|
6d91659b24 | ||
|
|
151dd41e64 | ||
|
|
13e234a68c | ||
|
|
839debca97 | ||
|
|
abda3d5279 | ||
|
|
44d8c0a824 | ||
|
|
f348feff62 | ||
|
|
6603b6aaa6 | ||
|
|
a96924c24f | ||
|
|
aa53fd8146 | ||
|
|
bbb5a3b408 | ||
|
|
072d70b42a | ||
|
|
f9970fa81c | ||
|
|
30d42bd832 | ||
|
|
3e6b4f4798 | ||
|
|
2ccd9dc382 | ||
|
|
691841187b | ||
|
|
a71a73e324 | ||
|
|
078cb84b35 | ||
|
|
775584a645 | ||
|
|
384212ce2e | ||
|
|
1567b689b9 | ||
|
|
254478e20c | ||
|
|
4dd3f39472 | ||
|
|
49802602bf | ||
|
|
6ab2112ea9 | ||
|
|
6be23145d8 | ||
|
|
cc0cd06c92 | ||
|
|
9697b4b06e | ||
|
|
db260d5f2f | ||
|
|
3f62f1683b | ||
|
|
7118a72b3b | ||
|
|
a5256c97d3 | ||
|
|
84023e2eb8 | ||
|
|
39849483c8 | ||
|
|
490264b35a | ||
|
|
4298810728 | ||
|
|
3fea2dae56 | ||
|
|
4a2b3f51a4 |
@@ -9,6 +9,9 @@ insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 79
|
||||
|
||||
[*.md]
|
||||
indent_size = 2
|
||||
|
||||
[*.sql]
|
||||
indent_size = 4
|
||||
|
||||
|
||||
+1
-3
@@ -1,6 +1,4 @@
|
||||
bin/
|
||||
data/
|
||||
node_modules/
|
||||
pki/
|
||||
state/
|
||||
.env
|
||||
hostname.txt
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
image: node:12-buster
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
do_build:
|
||||
stage: build
|
||||
script:
|
||||
- npm install
|
||||
- npm run build
|
||||
- npm test
|
||||
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"EditorConfig.EditorConfig",
|
||||
"esbenp.prettier-vscode",
|
||||
"mikestead.dotenv"
|
||||
]
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [79],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v016
|
||||
|
||||
- IDZ: Fix SQL syntax error (Tau)
|
||||
|
||||
## v015
|
||||
|
||||
- IDZ: Team fixes (BemaniWitch)
|
||||
|
||||
## v014
|
||||
|
||||
- IDZ: Add support for Initial D v2.1 (BemaniWitch)
|
||||
|
||||
## v013
|
||||
|
||||
- IDZ: Miscellaneous refactoring (Tau)
|
||||
- Chunithm: Add stubs to support Amazon Plus (Felix)
|
||||
|
||||
## v012
|
||||
|
||||
- Chunithm: Fix large data loads (NeumPhis)
|
||||
- Chunithm: Add some support for older versions (NeumPhis)
|
||||
- Chunithm: Fix large data loads (Felix)
|
||||
- Chunithm: Enable all events (Felix)
|
||||
- Chunithm: Remove server maintenance period (Felix)
|
||||
- Chunithm: Fix data loss (esterTion)
|
||||
- Chunithm: Add some support for older versions (esterTion)
|
||||
- IDZ: Team-related fixes (Emi Midnight)
|
||||
- Add support for newer AIME card services (cxm)
|
||||
- Chunithm: Enable ticket system (Rob)
|
||||
- Chunithm: Implement Recent Rating feature (seika1)
|
||||
- Chunithm: Fix User Activity feature (seika1)
|
||||
|
||||
## v011
|
||||
|
||||
- IDZ: Fix loading of >10 cars in garage (Tau)
|
||||
|
||||
## v010
|
||||
|
||||
- Chunithm: Bug fix: Fix new database creation and repair affected databases. Again. (Tau)
|
||||
Switch to a different SQLite native code extension. C compiler is no longer required. (Tau)
|
||||
|
||||
## v009
|
||||
|
||||
- IDZ: Display team banners in attract loop rankings (Tau)
|
||||
- Chunithm: Bug fix: Fix new database creation and repair affected databases (Tau)
|
||||
|
||||
## v008
|
||||
|
||||
- Implement auras in IDZ (BemaniWitch)
|
||||
- Fix user ratings (NeumPhis)
|
||||
- Load and save course results (NeumPhis)
|
||||
- Load and save user duel list (Felix)
|
||||
|
||||
## v007
|
||||
|
||||
- Fix IDZ team creation (Tau)
|
||||
- Fix startup script working directory (Tau)
|
||||
|
||||
## v006
|
||||
|
||||
- Fix Chunithm HTTP POST size limit (Tau)
|
||||
|
||||
## v005
|
||||
|
||||
- Initial public release (Tau)
|
||||
- Chunithm v1.30.00 Amazon
|
||||
- Initial D Zero v1.21.00
|
||||
- Initial D Zero v1.31.00
|
||||
@@ -0,0 +1,3 @@
|
||||
# MiniMe
|
||||
|
||||
A simple self-hosted ALLNET/AiMe server written in TypeScript, running on Node.js, that stores its data in an SQLite database.
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This shell script documents the process that was used to generate our fake
|
||||
# P-Ras PKI. It should not need to be run again under normal circumstances.
|
||||
|
||||
set -e
|
||||
|
||||
D=`dirname $0`
|
||||
|
||||
Generated
+7796
File diff suppressed because it is too large
Load Diff
+21
-16
@@ -1,49 +1,54 @@
|
||||
{
|
||||
"name": "knuckles",
|
||||
"name": "minime",
|
||||
"version": "0.1.0",
|
||||
"description": "Test server",
|
||||
"main": "src/index.js",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@decafcode/sqlite": "^2.0.0",
|
||||
"compression": "^1.7.3",
|
||||
"date-fns": "^1.30.1",
|
||||
"debug": "^4.1.1",
|
||||
"dotenv": "^7.0.0",
|
||||
"express": "^4.16.4",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"morgan": "^1.9.1",
|
||||
"multiparty": "^4.2.1",
|
||||
"pg": "^7.10.0",
|
||||
"raw-body": "^2.3.3",
|
||||
"sql-bricks-postgres": "^0.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "jest",
|
||||
"start": "ts-node src/index.ts"
|
||||
"snake-case": "^2.1.0",
|
||||
"sql-bricks-postgres": "^0.5.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/compression": "^0.0.36",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/dotenv": "^6.1.1",
|
||||
"@types/express": "^4.16.1",
|
||||
"@types/jest": "^24.0.11",
|
||||
"@types/multiparty": "^0.0.32",
|
||||
"@types/node": "^11.11.3",
|
||||
"@types/pg": "^7.4.14",
|
||||
"@types/sql-bricks": "^2.0.1",
|
||||
"jest": "^24.5.0",
|
||||
"jest-haste-map": "^24.5.0",
|
||||
"@types/node": "^12.11.7",
|
||||
"jest": "^26.4.2",
|
||||
"jest-haste-map": "^26.3.0",
|
||||
"jest-resolve": "^24.5.0",
|
||||
"prettier": "^1.16.4",
|
||||
"ts-jest": "^24.0.0",
|
||||
"ts-node": "^8.0.3",
|
||||
"typescript": "^3.3.3333",
|
||||
"ts-jest": "^26.3.0",
|
||||
"typescript": "^3.8.3",
|
||||
"utility-types": "^3.6.1"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepare": "tsc",
|
||||
"test": "jest",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "ts-jest",
|
||||
"testEnvironment": "node"
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAML2GPUuzv2N4bYC
|
||||
xtc5bZSzolHFWdCUbP+whjr3K98FOLnYeoi7mtUSUUYOW8wIqy6WM3c4c0Bp7FcQ
|
||||
LnZ0zWMm1TfLGHZzZmk5n7Iv6HDPr3ehDgbWLnOpRrVqZDxpAGD2vQb4p2DW4I2x
|
||||
GUqnqDa++C8dH/0lXqE6cqwGXNGtAgMBAAECgYEAizgPhG4Dk55QkpeTBDfXH3vT
|
||||
Ko9B3qdO2ptkjxDX/C8PXe7POXq2SvcEoIE6Xg3Gp8LMR5NBAbth8J32f9JSov3P
|
||||
SiGCGno4k2i2s3jRuVg76FGLDsZH/N1dt4h78VnW0VlInwaM6bQv3zp0u8rXVk/P
|
||||
wpYh9AGmquBJS3VYUcECQQD0PDRe28SrhollygGZSO321rYbYhoTIstDXZWyQ/y/
|
||||
PWKNwNHcYTHIVGmTrJx2AJUyr1tJhwjiOwlsI5Y1Q4/9AkEAzFpFPcs1r/xgSFxB
|
||||
eYrcNseWYbVajtVxG9t57sayaEQbH2UMNA2vqSYK/nU6oJhj5eLRIsPHlA5ZbIiZ
|
||||
rvc/cQJAKS0RQ0DX+ncXKQMSm+4wuGHgl+NFNB60mCnp+AEAVpmZyP5OI1J7myOo
|
||||
HQ6H3lkgzkfEIzRR6ho773BcfaRjXQJAfS4nEE11G9ML4AezjBLGB0CIHF6NlMWn
|
||||
PhtaPCy3iSt/OeIacaCYpJNLVMjXGx1+xIoG9rbbgRSxLs0W55lJ4QJBALOUVcNw
|
||||
GKEJdxhIkA8iuUlEyGpKluAgHUNOOKvC3ogRoB0OyH+If/9o8wWDfxgexgM0zGBc
|
||||
u178W9XDW+IijDA=
|
||||
-----END PRIVATE KEY-----
|
||||
Binary file not shown.
BIN
Binary file not shown.
+28
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeHEJ2L+FXrcdW
|
||||
G8LNZ/WknTzE9lsB4I1Gb7LcrXi/Yq+rEnF8iru2h4j1icmDSuwLBS//qENO2LfE
|
||||
+iU1zmtlhjAfGv8SYSXg1ssAbB6Sv4vvz0U0pNCAvnJ/K8UN7kAXHB1rw5dRvO47
|
||||
A+M1NO8S6pYTL5jJhK+BaNVmP6w9eOQ4c+0ZvKFOEX0E/uy1qcUuS386ZcPFRxRF
|
||||
vErUo8WjyMAwV9HZoVa3h3tAywKfDrYjF8DtdNG93G3igzpv4Iro4AuESrPE3t4x
|
||||
5uiO4UxwUObolkVsmOAzo7g1QiEi0wlHTQX1ZH3dXgaRAF5vv/V02ZAnmI5keXfP
|
||||
DDIqURf3AgMBAAECggEAHQD3tNM/y+FHfHkXkRcYPqzBuL6q163pBN+lLagBcoyC
|
||||
gAZih27eYFGGkvmxNNHdzPqab/oa//rQ1IoNvd78qz9AnW87C71f4uJpk96Kh4M3
|
||||
1NLuKJe1GnrEHNMsYktQVzQ2q1HZOrU/LrmtO89zwLadblfyza3j9TQpWbbK2SPn
|
||||
zVFlBZazX5s5APHgI9PymjPkwC9jsUbSrpe816W9KT8n8cG/CdS7relOXic5PWVL
|
||||
zgWQdCFMhY/aHFbLWM7RIkQTdX0GopJWgleLQqryKHIVu6Vekyrp/eOpl22wzjku
|
||||
DMWEK9ZWLHYejbCZJJIRKxSsxRlwAX2D/9kQ9vs2YQKBgQD/UYGD7ovAjTclJzyx
|
||||
d6dj/C0fHwyFnF+Lknll7AhA1hBYzQ2uaqBSUc2oV19dsxfZfy/tD0gP2ZVSFl1f
|
||||
iF3ON0xVbbb4r3Drck17c5iQ2+z5PHfOHSD3+AwF+stkKwLiJxIudJJppm1Cn6wc
|
||||
WiysnqeEhsaklOe0PvdA8BIkDwKBgQDetA7g2m7FKfODHb9Tx7qDtiRjsbCYD1fF
|
||||
UpF2yQ+twXJAO9AgkxYmmWMQ9/h+yxpsTjE4oDOO5K907x6gGzJFfFrDn0R0zIfw
|
||||
0EobfwJ4l28rqUYvQzgYyc/UW2PiuLIr2otXQNmtOnYmPogLIMlgQSvEnseXCs//
|
||||
0+TRKKbFmQKBgGVJYVcEeF6P5xOPKE3DCR3qOcBB4gbTTTgiiJR9eZy3D08bMdVC
|
||||
qY1etHaXCtcyKED5avrheBYJnGovQyWWBJi9aUPuvYqUlvhgpQpXhmvZQ35wlZqo
|
||||
BZ85wRNSNgPr6D3tgBH73uWYx3mJvI6W22gznIM/sGg0RmNEI3SYiKnTAoGAJ1mx
|
||||
d1GhWP08peJPuEIGVteMPoFbLsMnQxp/0XldZ9pSkb7/24Gh3FgmBQ2LvvusQ47d
|
||||
a6AC/DC0P/kwdCHaFCUQ9JfjxK+PJaHoNkuO6Df50MEsQZWjB95A9sjfMWRpNw56
|
||||
qIQw8kbuMXvDFhRJANUDIs7bfXjPn+iU+dAxB6ECgYEAusdd0y30VXE9Mj6zJ2Ee
|
||||
Cm1L9THijgcPH/Q7rD9Vk5yPSXuKO9Q2atgY0COOeQxwmwjf8UHYldDxLjor1nz0
|
||||
CdwQihCexJSXuoQ9LA/XeTLDm/F/M1Eze+MgOE23dT9qEUOwHrjDsyuYwfev7ir9
|
||||
a11MWJKcdkeKS/lGXeX3MbE=
|
||||
-----END PRIVATE KEY-----
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDLTCCAhWgAwIBAgIUQ67cF8bq3m9pcWq6zJeZRc8N60EwDQYJKoZIhvcNAQEL
|
||||
BQAwJTEQMA4GA1UEAwwHRHVtbXlDQTERMA8GA1UECgwIRHVtbXlQS0kwIBcNMTgx
|
||||
MDE1MTkzMDUxWhgPMjExODEwMTUxOTMwNTFaMCUxEDAOBgNVBAMMB0R1bW15Q0Ex
|
||||
ETAPBgNVBAoMCER1bW15UEtJMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
||||
AQEA3hxCdi/hV63HVhvCzWf1pJ08xPZbAeCNRm+y3K14v2KvqxJxfIq7toeI9YnJ
|
||||
g0rsCwUv/6hDTti3xPolNc5rZYYwHxr/EmEl4NbLAGwekr+L789FNKTQgL5yfyvF
|
||||
De5AFxwda8OXUbzuOwPjNTTvEuqWEy+YyYSvgWjVZj+sPXjkOHPtGbyhThF9BP7s
|
||||
tanFLkt/OmXDxUcURbxK1KPFo8jAMFfR2aFWt4d7QMsCnw62IxfA7XTRvdxt4oM6
|
||||
b+CK6OALhEqzxN7eMebojuFMcFDm6JZFbJjgM6O4NUIhItMJR00F9WR93V4GkQBe
|
||||
b7/1dNmQJ5iOZHl3zwwyKlEX9wIDAQABo1MwUTAdBgNVHQ4EFgQUxHdbXQ7dqFqw
|
||||
jzzl02L+UefEwNgwHwYDVR0jBBgwFoAUxHdbXQ7dqFqwjzzl02L+UefEwNgwDwYD
|
||||
VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEApcWGs+A06Aaqr9VAxhDp
|
||||
i4vSV0K++912IJWRv6rRRy7Hz4b7Ov5UOZ7AHshnQNWQvd9Qp+ehhNoLjL5joWk9
|
||||
BmwMPGAPhBAvidDh8QULUAIDJWDGF8CtDDaW8oHYjsBTBSVjKu1Ma/OVMc9vF0Ej
|
||||
MUb3LtlKZkNUqmUCjlE/V2bYYjqVLNTogrXO4KxXyqfBFdUzTCB/qX7V+HNnszDq
|
||||
IFHF+wyTklMSOwTUIrvW4R6zojXL0wjRdvtBqXSvPFq5HWJPdg0dfVyyVDZu7yPO
|
||||
3gMJLqkDkHsaYLs2IKHUXqJCbHCJ/whpMWNYYozDFGrhetNSvo3sKht9wvQK08mc
|
||||
yg==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDg1xvj8JLt4nDq
|
||||
rRQOGzCys0bD2VdGRnQ/TYC3ezg0whYbQRD7bUdkNQm1KeNOlj8yr8kjz30swIY6
|
||||
6Ufw82qGH0QjsmbSOTLr13B9K1ogd11aMxErvVnxtNBOkuwtzKL+Zb1D99jvxX0u
|
||||
e0dIsBcuojemyoz6iNbU7WSNtbIFeQFr+MlZujHIv0Hj76MYyZAqNq2F3Ou1SduV
|
||||
kw+Acol8KzTfiII38ibZcP+INgK6YvnrJrxbDZ+NXwsXOeg0y/vZ7hc2h1k5wT3E
|
||||
xWoyMD/bvnVrKTneQfwOI9s4s3pB6o1hGeUGRhPvpJ20+FGOE8LIRzBEixDKl82T
|
||||
G63DVoMJAgMBAAECggEBANKVBf0+BA8jZ4iUpFT16G1mdZ/W/uPF9viXGThAAwt+
|
||||
wH+0ODiUSCo1dqsj2U5wcC6D74pHukBg7RdeCFBHW3zU6dfZLm40vlmfRS8mnFoO
|
||||
EfP6IlnqFcTJCdSdzPC0WfCUz2hKSPeA61bOhZwxuPSnYCIqUVIROczhrqz/AQYX
|
||||
ZuuY7ut0h6X11xoddeeTOdfd+rktxPVpHeJDcz9F6Gk/0pg1ezs66TZ5sWt0/ZhP
|
||||
ZWTB40KPec2lomRGwY36AMOT3uFucnbtJgxhTGdqsRv7Kl0xCTo7xW/85Za7rhXK
|
||||
+xRdHrYr9w6xYTKHi9Ap4HEmUcx/fGAtddxr0fzdpIECgYEA8nRyYYHMdSr4WI/E
|
||||
fD/cpIjKLxw9BCdoXhruVSKp1GmhyQTH7Y0zJQ0uvG7GlsEXJ2V9yqTofzWev8iQ
|
||||
vVzX+14yTKaNUAwc7HCEXMN+xqnq61Bjldx6U54CfdInOhTFuoq8CXu80cHobYMs
|
||||
8SjnYuhp5NqABGR+9YTYaHzjBhkCgYEA7Wa9orxkWruXUdROUZrdhlU44ilxvA66
|
||||
r1vVVLQFNVle2hbie3b93ZQCZrZL8iFsGKOIhXSncQ6pl9GQqDLJvMUiffzUci6G
|
||||
A4GgGlzXpkj+RmAkIkGYafmQqTZhMHNws35LMYoIabn0l8cWwG0XL1pO2YSjaoYg
|
||||
5Kj0TjoNonECgYAcAk3Qa+FFy+ACwyEMxYfkzhSlWprF5xOMg4ny9d0ut8FD6rR6
|
||||
Aezdo+c5R4bTlZzqJTRh+6kMQRKEz1PBPH+K/3fKGReMHsocmmcAHGmB49FKu++1
|
||||
OVI8ZK2fAW8cq5eoFCzi35ORm9gRBq1jcrlAWN8a3A8b8swj6uPhNkQ3yQKBgCH+
|
||||
HBk5MIVtZvVomO5GZoHdog+AL7DlywVg+OLwA+7npRVFQZi8KQ2ZK97ZK3a4ImpE
|
||||
wD+bvH4Lw2zhrPzoiMpmz9GKakEPOFE4NlyP/rDossAQ9BuTmOdTvMr95lyxqumI
|
||||
o+usABhjcAprj25uMGuvWqr6uwt9uSgEqTaqSVmBAoGAMs6/59L8EABC/CkBLp0t
|
||||
y3sYzjyjZr6gnMCkIi95b2FWyIVjlK53dwjKYg67BUI8qLNbUW1NPhyotc/9GklP
|
||||
qiNTkUeKcHOlZqOzP0zPquFk2lSfJuuGrMgElZPcEFLsAWRqnjRGp5iP05nk5OQh
|
||||
foY7svzPedFk/t3eFcOyrPo=
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICtDCCAZwCAQAwDQYJKoZIhvcNAQELBQAwJTEQMA4GA1UEAwwHRHVtbXlDQTER
|
||||
MA8GA1UECgwIRHVtbXlQS0kwIBcNMTgxMDE1MTkzMDUxWhgPMjExODEwMTUxOTMw
|
||||
NTFaMBkxFzAVBgNVBAMMDmliLm5hb21pbmV0LmpwMIIBIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAQ8AMIIBCgKCAQEA4Ncb4/CS7eJw6q0UDhswsrNGw9lXRkZ0P02At3s4NMIW
|
||||
G0EQ+21HZDUJtSnjTpY/Mq/JI899LMCGOulH8PNqhh9EI7Jm0jky69dwfStaIHdd
|
||||
WjMRK71Z8bTQTpLsLcyi/mW9Q/fY78V9LntHSLAXLqI3psqM+ojW1O1kjbWyBXkB
|
||||
a/jJWboxyL9B4++jGMmQKjathdzrtUnblZMPgHKJfCs034iCN/Im2XD/iDYCumL5
|
||||
6ya8Ww2fjV8LFznoNMv72e4XNodZOcE9xMVqMjA/2751ayk53kH8DiPbOLN6QeqN
|
||||
YRnlBkYT76SdtPhRjhPCyEcwRIsQypfNkxutw1aDCQIDAQABMA0GCSqGSIb3DQEB
|
||||
CwUAA4IBAQANOqFqjnGf80vvwYEkUsfOWp8rNVat+8rdXl0BShGBXiDItzMOU79K
|
||||
YkPObniQ5RLBMhrvqlCsSk0Np+ZgvV12J4Wtmf/znLa5ZKyeI4N1FCefU9cl4xpB
|
||||
08Fv8YWbYV7SMNr54ZkURdho4FVR1pAnpuittpAEjMT4R4ubbOH8UEbMTbVgxXdn
|
||||
086IAlfsYn0gnOlf76RkJFLe4UlWaZB75SaaXnNavBPN9iFnqXLckg6tsFUJnNMC
|
||||
esq5aHQ9sXWs4oKpJi8SXxt/zNRmgTnQK2ONM38NeZpLmlWPkyNxzsRNlYWo+kWP
|
||||
C98jVFe1+3K88ISk0DSN4XOQQnrIvn68
|
||||
-----END CERTIFICATE-----
|
||||
+5
-41
@@ -1,45 +1,9 @@
|
||||
create schema "aime";
|
||||
|
||||
create type "aime"."region" as enum (
|
||||
'JPN',
|
||||
'HKG',
|
||||
'SGP',
|
||||
'KOR',
|
||||
'USA'
|
||||
);
|
||||
|
||||
create table "aime"."shop" (
|
||||
"id" bigint primary key not null,
|
||||
create table "aime_player" (
|
||||
"id" integer primary key not null,
|
||||
"ext_id" integer not null,
|
||||
"name" text not null,
|
||||
"region" "aime"."region" not null,
|
||||
constraint "shop_uq" unique ("ext_id")
|
||||
);
|
||||
|
||||
create table "aime"."machine" (
|
||||
"id" bigint primary key not null,
|
||||
"shop_id" bigint not null
|
||||
references "aime"."shop"("id"),
|
||||
"pcb_id" text not null,
|
||||
"keychip_id" text not null,
|
||||
constraint "machine_pcb_id_uq" unique ("pcb_id"),
|
||||
constraint "machine_keychip_id_uq" unique ("keychip_id")
|
||||
);
|
||||
|
||||
create table "aime"."player" (
|
||||
"id" bigint primary key not null,
|
||||
"ext_id" integer not null,
|
||||
"register_time" timestamp not null,
|
||||
constraint "player_uq" unique ("ext_id")
|
||||
);
|
||||
|
||||
create table "aime"."card" (
|
||||
"id" bigint primary key not null,
|
||||
"player_id" bigint not null
|
||||
references "aime"."player"("id")
|
||||
on delete cascade,
|
||||
"nfc_id" text not null,
|
||||
"luid" text not null,
|
||||
"register_time" timestamp not null,
|
||||
"access_time" timestamp not null,
|
||||
constraint "card_uq" unique ("nfc_id")
|
||||
constraint "aime_player_ext_id_uq" unique ("ext_id"),
|
||||
constraint "aime_player_luid_uq" unique ("luid")
|
||||
);
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
-- SQLite's ALTER TABLE statement is almost useless, and who knows how much of
|
||||
-- this stuff is present in older versions. So while ideally we'd mark a lot
|
||||
-- of these columns NOT NULL that might come back to bite us as we extend
|
||||
-- support downward from Chunithm 4 to Chunithm 1.
|
||||
|
||||
create table "cm_user_data" (
|
||||
"id" integer primary key not null,
|
||||
-- Could in theory do a shared primary key with aime_player, since this
|
||||
-- is going to be a 1:1 relation for the foreseeable future, but again
|
||||
-- the SQLite strait-jacket forces us to design defensively.
|
||||
"player_id" integer not null
|
||||
references "aime_player"("id")
|
||||
on delete cascade,
|
||||
"access_code" text,
|
||||
"user_name" text,
|
||||
"is_web_join" text,
|
||||
"web_limit_date" text,
|
||||
"level" integer,
|
||||
"reincarnation_num" integer,
|
||||
"exp" integer,
|
||||
"point" integer,
|
||||
"total_point" integer,
|
||||
"play_count" integer,
|
||||
"multi_play_count" integer,
|
||||
"multi_win_count" integer,
|
||||
"request_res_count" integer,
|
||||
"accept_res_count" integer,
|
||||
"success_res_count" integer,
|
||||
"player_rating" integer,
|
||||
"highest_rating" integer,
|
||||
"nameplate_id" integer,
|
||||
"frame_id" integer,
|
||||
"character_id" integer,
|
||||
"trophy_id" integer,
|
||||
"played_tutorial_bit" integer,
|
||||
"first_tutorial_cancel_num" integer,
|
||||
"master_tutorial_cancel_num" integer,
|
||||
"total_repertoire_count" integer,
|
||||
"total_map_num" integer,
|
||||
"total_hi_score" integer,
|
||||
"total_basic_high_score" integer,
|
||||
"total_advanced_high_score" integer,
|
||||
"total_expert_high_score" integer,
|
||||
"total_master_high_score" integer,
|
||||
"event_watched_date" text,
|
||||
"friend_count" integer,
|
||||
"is_maimai" text,
|
||||
"first_game_id" text,
|
||||
"first_rom_version" text,
|
||||
"first_data_version" text,
|
||||
"first_play_date" text,
|
||||
"last_game_id" text,
|
||||
"last_rom_version" text,
|
||||
"last_data_version" text,
|
||||
"last_play_date" text,
|
||||
"last_place_id" integer,
|
||||
"last_place_name" text,
|
||||
"last_region_id" text,
|
||||
"last_region_name" text,
|
||||
"last_all_net_id" text,
|
||||
"last_client_id" text
|
||||
);
|
||||
|
||||
create table "cm_user_activity" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"kind" integer not null,
|
||||
-- This is just called "id" on the wire but, well, we already have an "id"
|
||||
-- column up there ^
|
||||
"activity_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_activity_uq" unique (
|
||||
"profile_id",
|
||||
"kind",
|
||||
"activity_id"
|
||||
)
|
||||
);
|
||||
|
||||
create table "cm_user_charge" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"charge_id" integer not null,
|
||||
"stock" integer not null,
|
||||
"purchase_date" text not null,
|
||||
"valid_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param_date" text not null,
|
||||
constraint "cm_user_charge_uq" unique ("profile_id", "charge_id")
|
||||
);
|
||||
|
||||
create table "cm_user_course" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"course_id" integer not null,
|
||||
"class_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"score_rank" integer not null,
|
||||
"event_id" integer not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
"is_clear" text not null,
|
||||
constraint "cm_user_course_uq" unique ("profile_id", "course_id")
|
||||
);
|
||||
|
||||
create table "cm_user_data_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"compatible_cm_version" text not null,
|
||||
"medal" integer not null,
|
||||
"map_icon_id" integer not null,
|
||||
"voice_id" integer not null,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null,
|
||||
"ext_str1" text not null,
|
||||
"ext_str2" text not null,
|
||||
"ext_str3" text not null,
|
||||
"ext_str4" text not null,
|
||||
"ext_str5" text not null,
|
||||
"ext_long1" integer not null,
|
||||
"ext_long2" integer not null,
|
||||
"ext_long3" integer not null,
|
||||
"ext_long4" integer not null,
|
||||
"ext_long5" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_duel_list" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"duel_id" integer not null,
|
||||
"progress" integer not null,
|
||||
"point" integer not null,
|
||||
"is_clear" boolean not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_duel_list_uq" unique ("profile_id", "duel_id")
|
||||
);
|
||||
|
||||
create table "cm_user_character" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"character_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"level" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"friendship_exp" integer not null,
|
||||
"is_valid" integer not null,
|
||||
"is_new_mark" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
constraint "cm_user_character_uq" unique ("profile_id", "character_id")
|
||||
);
|
||||
|
||||
create table "cm_user_game_option" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"bg_info" integer not null,
|
||||
"field_color" integer not null,
|
||||
"guide_sound" integer not null,
|
||||
"sound_effect" integer not null,
|
||||
"guide_line" integer not null,
|
||||
"speed" integer not null,
|
||||
"option_set" integer not null,
|
||||
"matching" integer not null,
|
||||
"judge_pos" integer not null,
|
||||
"rating" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"headphone" integer not null,
|
||||
"player_level" integer not null,
|
||||
"success_tap" integer not null,
|
||||
"success_ex_tap" integer not null,
|
||||
"success_slide_hold" integer not null,
|
||||
"success_air" integer not null,
|
||||
"success_flick" integer not null,
|
||||
"success_skill" integer not null,
|
||||
"success_tap_timbre" integer not null,
|
||||
"privacy" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_game_option_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_item" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"item_kind" integer not null,
|
||||
"item_id" integer not null,
|
||||
"stock" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_item_uq" unique ("profile_id", "item_kind", "item_id")
|
||||
);
|
||||
|
||||
create table "cm_user_map" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"map_id" integer not null,
|
||||
"position" integer not null,
|
||||
"is_clear" text not null,
|
||||
"area_id" integer not null,
|
||||
"route_number" integer not null,
|
||||
"event_id" integer not null,
|
||||
"rate" integer not null,
|
||||
"status_count" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_map_uq" unique ("profile_id", "map_id")
|
||||
);
|
||||
|
||||
create table "cm_user_music" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"res_request_count" integer not null,
|
||||
"res_accept_count" integer not null,
|
||||
"res_success_count" integer not null,
|
||||
"miss_count" integer not null,
|
||||
"max_combo_count" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"full_chain" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"score_rank" integer not null,
|
||||
"is_lock" text not null,
|
||||
constraint "cm_user_music_uq" unique ("profile_id", "music_id", "level")
|
||||
);
|
||||
|
||||
create table "cm_user_playlog" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"order_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"place_id" integer not null,
|
||||
"play_date" text not null,
|
||||
"user_play_date" text not null,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"custom_id" integer not null,
|
||||
"played_user_id1" integer not null,
|
||||
"played_user_id2" integer not null,
|
||||
"played_user_id3" integer not null,
|
||||
"played_user_name1" text not null,
|
||||
"played_user_name2" text not null,
|
||||
"played_user_name3" text not null,
|
||||
"played_music_level1" integer not null,
|
||||
"played_music_level2" integer not null,
|
||||
"played_music_level3" integer not null,
|
||||
"played_custom1" integer not null,
|
||||
"played_custom2" integer not null,
|
||||
"played_custom3" integer not null,
|
||||
"track" integer not null,
|
||||
"score" integer not null,
|
||||
"rank" integer not null,
|
||||
"max_combo" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"rate_tap" integer not null,
|
||||
"rate_hold" integer not null,
|
||||
"rate_slide" integer not null,
|
||||
"rate_air" integer not null,
|
||||
"rate_flick" integer not null,
|
||||
"judge_guilty" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_critical" integer not null,
|
||||
"event_id" integer not null,
|
||||
"player_rating" integer not null,
|
||||
"is_new_record" text not null,
|
||||
"is_full_combo" text not null,
|
||||
"full_chain_kind" integer not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_continue" text not null,
|
||||
"is_free_to_play" text not null,
|
||||
"character_id" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"play_kind" integer not null,
|
||||
"is_clear" text not null,
|
||||
"skill_level" integer not null,
|
||||
"skill_effect" integer not null,
|
||||
"place_name" text not null,
|
||||
"is_maimai" text not null
|
||||
);
|
||||
|
||||
create table "cm_user_recent_rating" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"sort_order" integer not null,
|
||||
"music_id" integer not null,
|
||||
"difficult_id" integer not null,
|
||||
"rom_version_code" integer not null,
|
||||
"score" integer not null,
|
||||
constraint "cm_user_recent_rating_uq" unique ("profile_id", "sort_order")
|
||||
);
|
||||
+180
-134
@@ -2,129 +2,159 @@
|
||||
-- A "course" contains an uphill and downhill "route"
|
||||
-- (or inbound/outbound etc)
|
||||
|
||||
create schema "idz";
|
||||
|
||||
create type "idz"."gender" as enum ('male', 'female');
|
||||
|
||||
create table "idz"."profile" (
|
||||
"id" bigint primary key not null,
|
||||
"player_id" bigint not null
|
||||
references "aime"."player"("id")
|
||||
create table "idz_profile" (
|
||||
"id" integer primary key not null,
|
||||
"player_id" integer not null
|
||||
references "aime_player"("id")
|
||||
on delete cascade,
|
||||
-- Major version of Initial D Zero, either 1 or 2.
|
||||
-- The two major versions are incompatible with each other and do not
|
||||
-- permit the player to carry progress over from one to the other.
|
||||
"version" integer not null,
|
||||
-- TODO shop_id
|
||||
"name" text not null,
|
||||
"lv" smallint not null,
|
||||
"lv" integer not null,
|
||||
"exp" integer not null,
|
||||
"fame" integer not null,
|
||||
"dpoint" integer not null,
|
||||
"mileage" integer not null,
|
||||
"register_time" timestamp not null,
|
||||
"access_time" timestamp not null,
|
||||
constraint "profile_player_uq" unique ("player_id")
|
||||
constraint "idz_profile_player_uq" unique ("player_id", "version")
|
||||
);
|
||||
|
||||
create table "idz"."chara" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_chara" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"gender" "idz"."gender" not null,
|
||||
"gender" text not null,
|
||||
"field_02" integer not null,
|
||||
"field_04" integer not null,
|
||||
"field_06" integer not null,
|
||||
"field_08" integer not null,
|
||||
"field_0A" integer not null,
|
||||
"field_0C" integer not null,
|
||||
"field_0E" integer not null,
|
||||
"field_0a" integer not null,
|
||||
"field_0c" integer not null,
|
||||
"field_0e" integer not null,
|
||||
"title" integer not null,
|
||||
"background" integer not null
|
||||
);
|
||||
|
||||
create table "idz"."course_plays" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_course_plays" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"course_no" smallint not null,
|
||||
"course_no" integer not null,
|
||||
"count" integer not null,
|
||||
constraint "course_plays_uq" unique ("profile_id", "course_no")
|
||||
constraint "idz_course_plays_uq" unique ("profile_id", "course_no")
|
||||
);
|
||||
|
||||
create table "idz"."car" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_car" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"selector" integer not null,
|
||||
"field_00" integer not null,
|
||||
"field_02" integer not null,
|
||||
"field_04" integer[] not null,
|
||||
"field_04" text not null,
|
||||
"field_46" integer not null,
|
||||
"field_48" integer not null,
|
||||
"field_4A" integer not null,
|
||||
"field_4C" integer not null,
|
||||
"field_4a" integer not null,
|
||||
"field_4c" integer not null,
|
||||
"field_50_lo" integer not null,
|
||||
"field_50_hi" integer not null,
|
||||
"field_58" integer not null,
|
||||
"field_5A" integer not null,
|
||||
"field_5B" integer not null,
|
||||
"field_5C" integer not null,
|
||||
"field_5E" integer not null,
|
||||
constraint "car_uq" unique ("profile_id", "selector")
|
||||
"field_5a" integer not null,
|
||||
"field_5b" integer not null,
|
||||
"field_5c" integer not null,
|
||||
"field_5e" integer not null,
|
||||
constraint "idz_car_uq" unique ("profile_id", "selector")
|
||||
);
|
||||
|
||||
create table "idz"."car_selection" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_car_selection" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"car_id" bigint not null
|
||||
references "idz"."car"("id")
|
||||
"car_id" integer not null
|
||||
references "idz_car"("id")
|
||||
on delete cascade
|
||||
);
|
||||
|
||||
create table "idz"."solo_mission_state" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_solo_mission_state" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"grid_no" smallint not null,
|
||||
"cell_no" smallint not null,
|
||||
"value" smallint not null,
|
||||
constraint "solo_mission_state_uq" unique ("profile_id", "grid_no", "cell_no")
|
||||
"grid_no" integer not null,
|
||||
"cell_no" integer not null,
|
||||
"value" integer not null,
|
||||
constraint "idz_solo_mission_state_uq" unique (
|
||||
"profile_id",
|
||||
"grid_no",
|
||||
"cell_no"
|
||||
)
|
||||
);
|
||||
|
||||
create table "idz"."settings" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_settings" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"music" smallint not null,
|
||||
"pack" bigint not null,
|
||||
"paper_cup" smallint not null, -- Not a boolean, oddly enough
|
||||
"gauges" integer not null
|
||||
"music" integer not null,
|
||||
"pack" integer not null,
|
||||
"aura" integer not null,
|
||||
"paper_cup" integer not null, -- Not a boolean, oddly enough
|
||||
"gauges" integer not null,
|
||||
"driving_style" integer not null
|
||||
);
|
||||
|
||||
create table "idz"."story_state" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_stamp_selections" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"x" smallint not null,
|
||||
"y" smallint not null
|
||||
"stamp_01" integer not null,
|
||||
"stamp_02" integer not null,
|
||||
"stamp_03" integer not null,
|
||||
"stamp_04" integer not null
|
||||
);
|
||||
|
||||
create table "idz"."story_cell_state" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_stamp_unlock" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"stamp_no",
|
||||
constraint "idz_stamp_unlock_uq" unique ("profile_id", "stamp_no")
|
||||
);
|
||||
|
||||
create table "idz_story_state" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"x" integer not null,
|
||||
"y" integer not null
|
||||
);
|
||||
|
||||
create table "idz_story_cell_state" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"row_no" integer not null,
|
||||
"col_no" integer not null,
|
||||
"a" smallint not null,
|
||||
"b" smallint not null,
|
||||
constraint story_cell_state_uq unique ("profile_id", "row_no", "col_no")
|
||||
"a" integer not null,
|
||||
"b" integer not null,
|
||||
"c" integer not null,
|
||||
constraint "idz_story_cell_state_uq" unique (
|
||||
"profile_id",
|
||||
"row_no",
|
||||
"col_no"
|
||||
)
|
||||
);
|
||||
|
||||
create table "idz"."free_car" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_free_car" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"valid_from" timestamp not null
|
||||
-- Expiry cannot be controlled by the server
|
||||
@@ -132,112 +162,128 @@ create table "idz"."free_car" (
|
||||
|
||||
-- Times are stored as floating-point seconds
|
||||
|
||||
create table "idz"."ta_result" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_ta_result" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"route_no" smallint not null,
|
||||
"route_no" integer not null,
|
||||
"total_time" float not null,
|
||||
"section_times" float[] not null,
|
||||
"flags" smallint not null,
|
||||
"grade" smallint not null,
|
||||
"section_times" text not null,
|
||||
"flags" integer not null,
|
||||
"grade" integer not null,
|
||||
"car_selector" integer not null,
|
||||
"timestamp" timestamp not null
|
||||
);
|
||||
|
||||
create table "idz"."ta_best" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_ta_best" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"route_no" smallint not null,
|
||||
"route_no" integer not null,
|
||||
"total_time" float not null,
|
||||
"section_times" float[] not null,
|
||||
"flags" smallint not null,
|
||||
"grade" smallint not null, -- TODO enum
|
||||
"section_times" text not null,
|
||||
"flags" integer not null,
|
||||
"grade" integer not null, -- TODO enum
|
||||
"car_selector" integer not null,
|
||||
"timestamp" timestamp not null,
|
||||
constraint "ta_best_uq" unique ("profile_id", "route_no")
|
||||
constraint "idz_ta_best_uq" unique ("profile_id", "route_no")
|
||||
);
|
||||
|
||||
-- TODO ta_section_best
|
||||
|
||||
create table "idz"."background_unlock" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_background_unlock" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"background_no" smallint not null,
|
||||
constraint "background_unlock_uq" unique ("profile_id", "background_no")
|
||||
"background_no" integer not null,
|
||||
constraint "idz_background_unlock_uq" unique (
|
||||
"profile_id",
|
||||
"background_no"
|
||||
)
|
||||
);
|
||||
|
||||
create table "idz"."title_unlock" (
|
||||
"id" bigint primary key not null,
|
||||
"profile_id" bigint not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_title_unlock" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"title_no" smallint not null,
|
||||
constraint "title_unlock_uq" unique ("profile_id", "title_no")
|
||||
"title_no" integer not null,
|
||||
constraint "idz_title_unlock_uq" unique ("profile_id", "title_no")
|
||||
);
|
||||
|
||||
create table "idz"."unlocks" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_unlocks" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"cup" smallint not null,
|
||||
"auras" integer not null,
|
||||
"cup" integer not null,
|
||||
"gauges" integer not null,
|
||||
"music" integer not null,
|
||||
"last_mileage_reward" integer not null
|
||||
);
|
||||
|
||||
create table "idz"."team" (
|
||||
"id" bigint primary key not null,
|
||||
create table "idz_team" (
|
||||
"id" integer primary key not null,
|
||||
"version" integer not null,
|
||||
"ext_id" integer not null,
|
||||
"name" text not null,
|
||||
"name_bg" smallint not null,
|
||||
"name_fx" smallint not null,
|
||||
"name_bg" integer not null,
|
||||
"name_fx" integer not null,
|
||||
"register_time" timestamp not null,
|
||||
constraint "team_uq" unique ("ext_id")
|
||||
constraint "idz_team_uq" unique ("version", "ext_id")
|
||||
);
|
||||
|
||||
create table "idz"."team" (
|
||||
"id" bigint primary key not null,
|
||||
"ext_id" integer not null,
|
||||
"name" text not null,
|
||||
"name_bg" smallint not null,
|
||||
"name_fx" smallint not null,
|
||||
"register_time" timestamp not null,
|
||||
constraint "team_uq" unique ("ext_id")
|
||||
);
|
||||
|
||||
create table "idz"."team_auto" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."team"("id")
|
||||
create table "idz_team_auto" (
|
||||
"id" integer primary key not null
|
||||
references "idz_team"("id")
|
||||
on delete cascade,
|
||||
"serial_no" smallint not null,
|
||||
"name_idx" smallint not null,
|
||||
constraint "team_auto_uq" unique ("serial_no", "name_idx")
|
||||
"serial_no" integer not null,
|
||||
"name_idx" integer not null
|
||||
);
|
||||
|
||||
create table "idz"."team_member" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
create table "idz_team_member" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"team_id" bigint not null
|
||||
references "idz"."team"("id")
|
||||
"team_id" integer not null
|
||||
references "idz_team"("id")
|
||||
on delete cascade,
|
||||
"join_time" timestamp not null,
|
||||
"leader" boolean not null
|
||||
);
|
||||
|
||||
create table "idz"."team_reservation" (
|
||||
"id" bigint primary key not null
|
||||
references "aime"."player"("id")
|
||||
create table "idz_team_reservation" (
|
||||
"id" integer primary key not null
|
||||
references "aime_player"("id")
|
||||
on delete cascade,
|
||||
"team_id" bigint not null
|
||||
references "idz"."team"("id")
|
||||
"team_id" integer not null
|
||||
references "idz_team"("id")
|
||||
on delete cascade,
|
||||
"join_time" timestamp not null,
|
||||
"leader" boolean not null
|
||||
);
|
||||
|
||||
create table "idz_weekly_missions" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"weekly_reset" timestamp not null,
|
||||
"mission_left" integer not null,
|
||||
"progress_left" integer not null,
|
||||
"params_left" integer not null,
|
||||
"mission_right" integer not null,
|
||||
"progress_right" integer not null,
|
||||
"params_right" integer not null
|
||||
);
|
||||
|
||||
create table "idz_my_chara" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"my_chara_no" integer not null,
|
||||
constraint "idz_my_chara_uq" unique ("profile_id", "my_chara_no")
|
||||
);
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
create table "meta" ("schemaver" integer not null);
|
||||
insert into "meta" values (3);
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
-- Helper functions which might be useful in one-off queries.
|
||||
-- These are not utilised by any services; those have their own ID generators.
|
||||
|
||||
create or replace function idnum(
|
||||
str text)
|
||||
returns bigint as $$
|
||||
declare
|
||||
num bigint;
|
||||
x integer;
|
||||
begin
|
||||
num := 0;
|
||||
|
||||
for i in 1..11 loop
|
||||
x := ascii(substr(str, i, 1));
|
||||
num := num << 6;
|
||||
|
||||
if x >= 65 and x <= 90 then
|
||||
-- A to Z
|
||||
num = num | (x - 65);
|
||||
elsif x >= 97 and x <= 122 then
|
||||
-- a to z
|
||||
num = num | (x - 71);
|
||||
elsif x >= 48 and x <= 58 then
|
||||
-- 0 to 9
|
||||
num = num | (x + 4);
|
||||
elsif x = 45 then
|
||||
-- Dash
|
||||
num = num | 62;
|
||||
elsif x = 95 then
|
||||
-- Underscore
|
||||
num = num | 63;
|
||||
else
|
||||
raise 'Bad input';
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
return num;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create or replace function idstr(
|
||||
num bigint)
|
||||
returns text as $$
|
||||
declare
|
||||
a text;
|
||||
pos integer;
|
||||
str text;
|
||||
begin
|
||||
a := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
|
||||
str := '';
|
||||
|
||||
for i in 1..11 loop
|
||||
pos := (num & 63);
|
||||
str := substr(a, pos + 1, 1) || str;
|
||||
num := num >> 6;
|
||||
end loop;
|
||||
|
||||
return str;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create or replace function newid()
|
||||
returns bigint as $$
|
||||
declare
|
||||
bytes bytea;
|
||||
result bigint;
|
||||
begin
|
||||
bytes := gen_random_bytes(8);
|
||||
result := 0;
|
||||
|
||||
for i in 0..7 loop
|
||||
result := (result << 8) | get_byte(bytes, i);
|
||||
end loop;
|
||||
|
||||
-- Truncate high bit to ensure result is positive
|
||||
-- This value is just the decimal representation of 0x7FFFFFFF`FFFFFFFF.
|
||||
|
||||
return result & 9223372036854775807;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
alter table "idz"."profile" drop column "ext_id";
|
||||
|
||||
update "meta" set "schemaver" = 1;
|
||||
@@ -1,42 +0,0 @@
|
||||
create table "idz"."team" (
|
||||
"id" bigint primary key not null,
|
||||
"ext_id" integer not null,
|
||||
"name" text not null,
|
||||
"name_bg" smallint not null,
|
||||
"name_fx" smallint not null,
|
||||
"register_time" timestamp not null,
|
||||
constraint "team_uq" unique ("ext_id")
|
||||
);
|
||||
|
||||
create table "idz"."team_auto" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."team"("id")
|
||||
on delete cascade,
|
||||
"serial_no" smallint not null,
|
||||
"name_idx" smallint not null,
|
||||
constraint "team_auto_uq" unique ("serial_no", "name_idx")
|
||||
);
|
||||
|
||||
create table "idz"."team_member" (
|
||||
"id" bigint primary key not null
|
||||
references "idz"."profile"("id")
|
||||
on delete cascade,
|
||||
"team_id" bigint not null
|
||||
references "idz"."team"("id")
|
||||
on delete cascade,
|
||||
"join_time" timestamp not null,
|
||||
"leader" boolean not null
|
||||
);
|
||||
|
||||
create table "idz"."team_reservation" (
|
||||
"id" bigint primary key not null
|
||||
references "aime"."player"("id")
|
||||
on delete cascade,
|
||||
"team_id" bigint not null
|
||||
references "idz"."team"("id")
|
||||
on delete cascade,
|
||||
"join_time" timestamp not null,
|
||||
"leader" boolean not null
|
||||
);
|
||||
|
||||
update "meta" set "schemaver" = 2;
|
||||
@@ -0,0 +1,27 @@
|
||||
-- Can't add NOT NULL or UNIQUE constraints as we'd like to. Fortunately we
|
||||
-- haven't released anything yet so there aren't really any DBs with live
|
||||
-- data out there in the wild.
|
||||
|
||||
-- (Can't drop and recreate the table either btw because "aime_player" is
|
||||
-- transitively referenced by _literally everything_).
|
||||
|
||||
alter table "aime_player" add column "luid" text;
|
||||
alter table "aime_player" add column "access_time" text;
|
||||
|
||||
update "aime_player" as "p"
|
||||
set "luid" = (
|
||||
select "c"."nfc_id"
|
||||
from "aime_card" as "c"
|
||||
where "c"."player_id" = "p"."id"
|
||||
),
|
||||
"access_time" = (
|
||||
select "c"."access_time"
|
||||
from "aime_card" as "c"
|
||||
where "c"."player_id" = "p"."id"
|
||||
);
|
||||
|
||||
drop table "aime_card";
|
||||
|
||||
-- These were never used. Re-create them if they become necessary.
|
||||
drop table "aime_machine";
|
||||
drop table "aime_shop";
|
||||
@@ -0,0 +1,304 @@
|
||||
-- SQLite's ALTER TABLE statement is almost useless, and who knows how much of
|
||||
-- this stuff is present in older versions. So while ideally we'd mark a lot
|
||||
-- of these columns NOT NULL that might come back to bite us as we extend
|
||||
-- support downward from Chunithm 4 to Chunithm 1.
|
||||
|
||||
create table "cm_user_data" (
|
||||
"id" integer primary key not null,
|
||||
-- Could in theory do a shared primary key with aime_player, since this
|
||||
-- is going to be a 1:1 relation for the foreseeable future, but again
|
||||
-- the SQLite strait-jacket forces us to design defensively.
|
||||
"player_id" integer not null
|
||||
references "aime_player"("id")
|
||||
on delete cascade,
|
||||
"access_code" text,
|
||||
"user_name" text,
|
||||
"is_web_join" text,
|
||||
"web_limit_date" text,
|
||||
"level" integer,
|
||||
"reincarnation_num" integer,
|
||||
"exp" integer,
|
||||
"point" integer,
|
||||
"total_point" integer,
|
||||
"play_count" integer,
|
||||
"multi_play_count" integer,
|
||||
"multi_win_count" integer,
|
||||
"request_res_count" integer,
|
||||
"accept_res_count" integer,
|
||||
"success_res_count" integer,
|
||||
"player_rating" integer,
|
||||
"highest_rating" integer,
|
||||
"nameplate_id" integer,
|
||||
"frame_id" integer,
|
||||
"character_id" integer,
|
||||
"trophy_id" integer,
|
||||
"played_tutorial_bit" integer,
|
||||
"first_tutorial_cancel_num" integer,
|
||||
"master_tutorial_cancel_num" integer,
|
||||
"total_repertoire_count" integer,
|
||||
"total_map_num" integer,
|
||||
"total_hi_score" integer,
|
||||
"total_basic_high_score" integer,
|
||||
"total_advanced_high_score" integer,
|
||||
"total_expert_high_score" integer,
|
||||
"total_master_high_score" integer,
|
||||
"event_watched_date" text,
|
||||
"friend_count" integer,
|
||||
"is_maimai" text,
|
||||
"first_game_id" text,
|
||||
"first_rom_version" text,
|
||||
"first_data_version" text,
|
||||
"first_play_date" text,
|
||||
"last_game_id" text,
|
||||
"last_rom_version" text,
|
||||
"last_data_version" text,
|
||||
"last_play_date" text,
|
||||
"last_place_id" integer,
|
||||
"last_place_name" text,
|
||||
"last_region_id" text,
|
||||
"last_region_name" text,
|
||||
"last_all_net_id" text,
|
||||
"last_client_id" text
|
||||
);
|
||||
|
||||
create table "cm_user_activity" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"kind" integer not null,
|
||||
-- This is just called "id" on the wire but, well, we already have an "id"
|
||||
-- column up there ^
|
||||
"activity_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_activity_uq" unique (
|
||||
"profile_id",
|
||||
"kind",
|
||||
"activity_id"
|
||||
)
|
||||
);
|
||||
|
||||
create table "cm_user_data_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"compatible_cm_version" text not null,
|
||||
"medal" integer not null,
|
||||
"map_icon_id" integer not null,
|
||||
"voice_id" integer not null,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null,
|
||||
"ext_str1" text not null,
|
||||
"ext_str2" text not null,
|
||||
"ext_str3" text not null,
|
||||
"ext_str4" text not null,
|
||||
"ext_str5" text not null,
|
||||
"ext_long1" integer not null,
|
||||
"ext_long2" integer not null,
|
||||
"ext_long3" integer not null,
|
||||
"ext_long4" integer not null,
|
||||
"ext_long5" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_character" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"character_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"level" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"friendship_exp" integer not null,
|
||||
"is_valid" integer not null,
|
||||
"is_new_mark" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
constraint "cm_user_character_uq" unique ("profile_id", "character_id")
|
||||
);
|
||||
|
||||
create table "cm_user_game_option" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"bg_info" integer not null,
|
||||
"field_color" integer not null,
|
||||
"guide_sound" integer not null,
|
||||
"sound_effect" integer not null,
|
||||
"guide_line" integer not null,
|
||||
"speed" integer not null,
|
||||
"option_set" integer not null,
|
||||
"matching" integer not null,
|
||||
"judge_pos" integer not null,
|
||||
"rating" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"headphone" integer not null,
|
||||
"player_level" integer not null,
|
||||
"success_tap" integer not null,
|
||||
"success_ex_tap" integer not null,
|
||||
"success_slide_hold" integer not null,
|
||||
"success_air" integer not null,
|
||||
"success_flick" integer not null,
|
||||
"success_skill" integer not null,
|
||||
"success_tap_timbre" integer not null,
|
||||
"privacy" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_game_option_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_item" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"item_kind" integer not null,
|
||||
"item_id" integer not null,
|
||||
"stock" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_item_uq" unique ("profile_id", "item_kind", "item_id")
|
||||
);
|
||||
|
||||
create table "cm_user_map" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"map_id" integer not null,
|
||||
"position" integer not null,
|
||||
"is_clear" text not null,
|
||||
"area_id" integer not null,
|
||||
"route_number" integer not null,
|
||||
"event_id" integer not null,
|
||||
"rate" integer not null,
|
||||
"status_count" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_map_uq" unique ("profile_id", "map_id")
|
||||
);
|
||||
|
||||
create table "cm_user_music" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"res_request_count" integer not null,
|
||||
"res_accept_count" integer not null,
|
||||
"res_success_count" integer not null,
|
||||
"miss_count" integer not null,
|
||||
"max_combo_count" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"full_chain" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"score_rank" integer not null,
|
||||
"is_lock" text not null,
|
||||
constraint "cm_user_music_uq" unique ("profile_id", "music_id", "level")
|
||||
);
|
||||
|
||||
create table "cm_user_playlog" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"order_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"place_id" integer not null,
|
||||
"play_date" text not null,
|
||||
"user_play_date" text not null,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"custom_id" integer not null,
|
||||
"played_user_id1" integer not null,
|
||||
"played_user_id2" integer not null,
|
||||
"played_user_id3" integer not null,
|
||||
"played_user_name1" text not null,
|
||||
"played_user_name2" text not null,
|
||||
"played_user_name3" text not null,
|
||||
"played_music_level1" integer not null,
|
||||
"played_music_level2" integer not null,
|
||||
"played_music_level3" integer not null,
|
||||
"played_custom1" integer not null,
|
||||
"played_custom2" integer not null,
|
||||
"played_custom3" integer not null,
|
||||
"track" integer not null,
|
||||
"score" integer not null,
|
||||
"rank" integer not null,
|
||||
"max_combo" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"rate_tap" integer not null,
|
||||
"rate_hold" integer not null,
|
||||
"rate_slide" integer not null,
|
||||
"rate_air" integer not null,
|
||||
"rate_flick" integer not null,
|
||||
"judge_guilty" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_critical" integer not null,
|
||||
"event_id" integer not null,
|
||||
"player_rating" integer not null,
|
||||
"is_new_record" text not null,
|
||||
"is_full_combo" text not null,
|
||||
"full_chain_kind" integer not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_continue" text not null,
|
||||
"is_free_to_play" text not null,
|
||||
"character_id" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"play_kind" integer not null,
|
||||
"is_clear" text not null,
|
||||
"skill_level" integer not null,
|
||||
"skill_effect" integer not null,
|
||||
"place_name" text not null,
|
||||
"is_maimai" text not null
|
||||
);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- This adds the required columns to save player aura settings and unlocks
|
||||
-- Default values are set according to what the game expects
|
||||
|
||||
alter table "idz_settings" add column "aura" integer NOT NULL DEFAULT '0';
|
||||
alter table "idz_unlocks" add column "auras" integer NOT NULL DEFAULT '1';
|
||||
@@ -0,0 +1,16 @@
|
||||
create table "cm_user_duel_list" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"duel_id" integer not null,
|
||||
"progress" integer not null,
|
||||
"point" integer not null,
|
||||
"is_clear" boolean not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_duel_list_uq" unique ("profile_id", "duel_id")
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
create table "cm_user_course" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"course_id" integer not null,
|
||||
"class_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"score_rank" integer not null,
|
||||
"event_id" integer not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
"is_clear" text not null,
|
||||
constraint "cm_user_course_uq" unique ("profile_id", "course_id")
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
-- This was not present in new DBs initialized to schema version 11.
|
||||
create table if not exists "cm_user_course" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"course_id" integer not null,
|
||||
"class_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"score_rank" integer not null,
|
||||
"event_id" integer not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
"is_clear" text not null,
|
||||
constraint "cm_user_course_uq" unique ("profile_id", "course_id")
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
-- This was also not present in the db init scripts...
|
||||
create table if not exists "cm_user_duel_list" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"duel_id" integer not null,
|
||||
"progress" integer not null,
|
||||
"point" integer not null,
|
||||
"is_clear" boolean not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_duel_list_uq" unique ("profile_id", "duel_id")
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
create table if not exists "cm_user_charge" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"charge_id" integer not null,
|
||||
"stock" integer not null,
|
||||
"purchase_date" text not null,
|
||||
"valid_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param_date" text not null,
|
||||
constraint "cm_user_charge_uq" unique ("profile_id", "charge_id")
|
||||
);
|
||||
@@ -0,0 +1,31 @@
|
||||
create table if not exists "cm_user_recent_rating" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"sort_order" integer not null,
|
||||
"music_id" integer not null,
|
||||
"difficult_id" integer not null,
|
||||
"rom_version_code" integer not null,
|
||||
"score" integer not null,
|
||||
constraint "cm_user_recent_rating_uq" unique ("profile_id", "sort_order")
|
||||
);
|
||||
|
||||
-- Prepopulate this table by backfilling the most recent 30 scores per user.
|
||||
-- This isn't exactly correct since not every recent score should go in here, but it's probably close enough.
|
||||
INSERT INTO cm_user_recent_rating (profile_id, sort_order, music_id, difficult_id, rom_version_code, score)
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
profile_id,
|
||||
row_number()
|
||||
OVER (PARTITION BY profile_id ORDER BY user_play_date DESC) AS sort_order,
|
||||
music_id,
|
||||
level AS difficult_id,
|
||||
'1030000' AS rom_version_code,
|
||||
score
|
||||
FROM cm_user_playlog
|
||||
WHERE
|
||||
difficult_id < 4 -- skip world's end
|
||||
ORDER BY profile_id ASC, user_play_date DESC
|
||||
)
|
||||
WHERE sort_order <= 30;
|
||||
@@ -0,0 +1,77 @@
|
||||
create table "new_idz_profile" (
|
||||
"id" integer primary key not null,
|
||||
"player_id" integer not null
|
||||
references "aime_player"("id")
|
||||
on delete cascade,
|
||||
"version" integer not null,
|
||||
"name" text not null,
|
||||
"lv" integer not null,
|
||||
"exp" integer not null,
|
||||
"fame" integer not null,
|
||||
"dpoint" integer not null,
|
||||
"mileage" integer not null,
|
||||
"register_time" timestamp not null,
|
||||
"access_time" timestamp not null,
|
||||
constraint "idz_profile_player_uq" unique ("player_id", "version")
|
||||
);
|
||||
|
||||
create table "new_idz_team" (
|
||||
"id" integer primary key not null,
|
||||
"version" integer not null,
|
||||
"ext_id" integer not null,
|
||||
"name" text not null,
|
||||
"name_bg" integer not null,
|
||||
"name_fx" integer not null,
|
||||
"register_time" timestamp not null,
|
||||
constraint "idz_team_uq" unique ("version", "ext_id")
|
||||
);
|
||||
|
||||
insert into "new_idz_profile" (
|
||||
"id",
|
||||
"player_id",
|
||||
"version",
|
||||
"name",
|
||||
"lv",
|
||||
"exp",
|
||||
"fame",
|
||||
"dpoint",
|
||||
"mileage",
|
||||
"register_time",
|
||||
"access_time"
|
||||
) select
|
||||
x."id",
|
||||
x."player_id",
|
||||
1,
|
||||
x."name",
|
||||
x."lv",
|
||||
x."exp",
|
||||
x."fame",
|
||||
x."dpoint",
|
||||
x."mileage",
|
||||
x."register_time",
|
||||
x."access_time"
|
||||
from "idz_profile" as "x";
|
||||
|
||||
insert into "new_idz_team" (
|
||||
"id",
|
||||
"version",
|
||||
"ext_id",
|
||||
"name",
|
||||
"name_bg",
|
||||
"name_fx",
|
||||
"register_time"
|
||||
) select
|
||||
x."id",
|
||||
1,
|
||||
x."ext_id",
|
||||
x."name",
|
||||
x."name_bg",
|
||||
x."name_fx",
|
||||
x."register_time"
|
||||
from "idz_team" as "x";
|
||||
|
||||
drop table "idz_profile";
|
||||
drop table "idz_team";
|
||||
|
||||
alter table "new_idz_profile" rename to "idz_profile";
|
||||
alter table "new_idz_team" rename to "idz_team";
|
||||
@@ -0,0 +1,111 @@
|
||||
create table "idz_my_chara" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"my_chara_no" integer not null,
|
||||
constraint "idz_my_chara_uq" unique ("profile_id", "my_chara_no")
|
||||
);
|
||||
|
||||
create table "new_idz_settings" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"music" integer not null,
|
||||
"pack" integer not null,
|
||||
"aura" integer not null,
|
||||
"paper_cup" integer not null, -- Not a boolean, oddly enough
|
||||
"gauges" integer not null,
|
||||
"driving_style" integer not null
|
||||
);
|
||||
|
||||
create table "idz_stamp_selections" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"stamp_01" integer not null,
|
||||
"stamp_02" integer not null,
|
||||
"stamp_03" integer not null,
|
||||
"stamp_04" integer not null
|
||||
);
|
||||
|
||||
create table "idz_stamp_unlock" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"stamp_no",
|
||||
constraint "idz_stamp_unlock_uq" unique ("profile_id", "stamp_no")
|
||||
);
|
||||
|
||||
create table "new_idz_story_cell_state" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"row_no" integer not null,
|
||||
"col_no" integer not null,
|
||||
"a" integer not null,
|
||||
"b" integer not null,
|
||||
"c" integer not null,
|
||||
constraint "idz_story_cell_state_uq" unique (
|
||||
"profile_id",
|
||||
"row_no",
|
||||
"col_no"
|
||||
)
|
||||
);
|
||||
|
||||
create table "idz_weekly_missions" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"weekly_reset" timestamp not null,
|
||||
"mission_left" integer not null,
|
||||
"progress_left" integer not null,
|
||||
"params_left" integer not null,
|
||||
"mission_right" integer not null,
|
||||
"progress_right" integer not null,
|
||||
"params_right" integer not null
|
||||
);
|
||||
|
||||
insert into "new_idz_settings" (
|
||||
"id",
|
||||
"music",
|
||||
"pack",
|
||||
"aura",
|
||||
"paper_cup",
|
||||
"gauges",
|
||||
"driving_style"
|
||||
) select
|
||||
x."id",
|
||||
x."music",
|
||||
x."pack",
|
||||
x."aura",
|
||||
x."paper_cup",
|
||||
x."gauges",
|
||||
0
|
||||
from "idz_settings" as x;
|
||||
|
||||
insert into "new_idz_story_cell_state" (
|
||||
"id",
|
||||
"profile_id",
|
||||
"row_no",
|
||||
"col_no",
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
) select
|
||||
x."id",
|
||||
x."profile_id",
|
||||
x."row_no",
|
||||
x."col_no",
|
||||
x."a",
|
||||
x."b",
|
||||
0
|
||||
from "idz_story_cell_state" as x;
|
||||
|
||||
drop table "idz_settings";
|
||||
drop table "idz_story_cell_state";
|
||||
|
||||
alter table "new_idz_settings" rename to "idz_settings";
|
||||
alter table "new_idz_story_cell_state" rename to "idz_story_cell_state";
|
||||
@@ -0,0 +1,13 @@
|
||||
create table "new_idz_team_auto" (
|
||||
"id" integer primary key not null
|
||||
references "idz_team"("id")
|
||||
on delete cascade,
|
||||
"serial_no" integer not null,
|
||||
"name_idx" integer not null
|
||||
);
|
||||
|
||||
insert into "new_idz_team_auto"
|
||||
select "id", "serial_no", "name_idx" from "idz_team_auto";
|
||||
|
||||
drop table "idz_team_auto";
|
||||
alter table "new_idz_team_auto" rename to "idz_team_auto";
|
||||
@@ -1,94 +0,0 @@
|
||||
import { PoolClient } from "pg";
|
||||
import * as sql from "sql-bricks";
|
||||
|
||||
import { CardRepository, Transaction } from "./repo";
|
||||
import { connect, generateId, generateExtId } from "../db";
|
||||
import { AimeId } from "../model";
|
||||
|
||||
class CardRepositoryImpl implements CardRepository {
|
||||
constructor(private readonly _conn: PoolClient) {}
|
||||
|
||||
async lookup(luid: string, now: Date): Promise<AimeId | undefined> {
|
||||
const fetchSql = sql
|
||||
.select("c.id", "p.ext_id")
|
||||
.from("aime.card c")
|
||||
.join("aime.player p", { "c.player_id": "p.id" })
|
||||
.where("c.nfc_id", luid)
|
||||
.toParams();
|
||||
|
||||
const { rows } = await this._conn.query(fetchSql);
|
||||
|
||||
if (rows.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const id = rows[0].id;
|
||||
const extId = rows[0].ext_id;
|
||||
|
||||
const touchSql = sql
|
||||
.update("aime.card")
|
||||
.set({ access_time: now })
|
||||
.where("id", id)
|
||||
.toParams();
|
||||
|
||||
await this._conn.query(touchSql);
|
||||
|
||||
return extId;
|
||||
}
|
||||
|
||||
async register(luid: string, now: Date): Promise<AimeId> {
|
||||
const playerId = generateId();
|
||||
const cardId = generateId();
|
||||
const aimeId = generateExtId() as AimeId;
|
||||
|
||||
const playerSql = sql
|
||||
.insert("aime.player", {
|
||||
id: playerId,
|
||||
ext_id: aimeId,
|
||||
register_time: now,
|
||||
})
|
||||
.toParams();
|
||||
|
||||
await this._conn.query(playerSql);
|
||||
|
||||
const cardSql = sql
|
||||
.insert("aime.card", {
|
||||
id: cardId,
|
||||
player_id: playerId,
|
||||
nfc_id: luid,
|
||||
register_time: now,
|
||||
access_time: now,
|
||||
})
|
||||
.toParams();
|
||||
|
||||
await this._conn.query(cardSql);
|
||||
|
||||
return aimeId;
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionImpl implements Transaction {
|
||||
constructor(private readonly _conn: PoolClient) {}
|
||||
|
||||
cards(): CardRepository {
|
||||
return new CardRepositoryImpl(this._conn);
|
||||
}
|
||||
|
||||
async commit(): Promise<void> {
|
||||
await this._conn.query("commit");
|
||||
await this._conn.release();
|
||||
}
|
||||
|
||||
async rollback(): Promise<void> {
|
||||
await this._conn.query("rollback");
|
||||
await this._conn.release();
|
||||
}
|
||||
}
|
||||
|
||||
export async function beginDbSession(): Promise<Transaction> {
|
||||
const conn = await connect();
|
||||
|
||||
await conn.query("begin");
|
||||
|
||||
return new TransactionImpl(conn);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ test("decode lookup", () => {
|
||||
|
||||
const obj = decode(req);
|
||||
|
||||
expect(obj.type).toBe("lookup");
|
||||
expect(obj.type).toBe("lookup2");
|
||||
expect(obj.gameId).toBe("SDBT");
|
||||
expect(obj.keychipId).toBe("A69E01A9999");
|
||||
expect(obj.luid).toBe("01036495850523030676");
|
||||
|
||||
+18
-1
@@ -1,7 +1,10 @@
|
||||
import logger from "debug";
|
||||
import { Transform } from "stream";
|
||||
|
||||
import * as Request from "./request";
|
||||
|
||||
const debug = logger("app:aimedb:decoder");
|
||||
|
||||
function begin(msg: Buffer): Request.AimeRequestBase {
|
||||
const gameId = msg.toString("ascii", 0x000a, 0x000e);
|
||||
const keychipId = msg.toString("ascii", 0x0014, 0x001f);
|
||||
@@ -28,6 +31,15 @@ function readFeliCaLookupRequest(msg: Buffer): Request.FeliCaLookupRequest {
|
||||
};
|
||||
}
|
||||
|
||||
function readFeliCaLookupRequest2(msg: Buffer): Request.FeliCaLookup2Request {
|
||||
return {
|
||||
...begin(msg),
|
||||
type: "felica_lookup2",
|
||||
idm: msg.slice(0x0030, 0x0038).toString("hex"),
|
||||
pmm: msg.slice(0x0038, 0x0040).toString("hex"),
|
||||
};
|
||||
}
|
||||
|
||||
function readLogRequest(msg: Buffer): Request.LogRequest {
|
||||
// idk what any of this stuff means yet
|
||||
// field20 and field28 appear to be an aime id but that is all.
|
||||
@@ -95,6 +107,7 @@ readers.set(0x0009, readLogRequest);
|
||||
readers.set(0x000b, readCampaignRequest);
|
||||
readers.set(0x000d, readRegisterRequest);
|
||||
readers.set(0x000f, readLookupRequest2);
|
||||
readers.set(0x0011, readFeliCaLookupRequest2);
|
||||
readers.set(0x0064, readHelloRequest);
|
||||
readers.set(0x0066, readGoodbyeRequest);
|
||||
|
||||
@@ -116,6 +129,10 @@ export class Decoder extends Transform {
|
||||
);
|
||||
}
|
||||
|
||||
return callback(null, reader(msg));
|
||||
const obj = reader(msg);
|
||||
|
||||
debug("Decode %j", obj);
|
||||
|
||||
return callback(null, obj);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-3
@@ -1,7 +1,10 @@
|
||||
import logger from "debug";
|
||||
import { Transform } from "stream";
|
||||
|
||||
import { AimeResponse, RegisterLevel } from "./response";
|
||||
|
||||
const debug = logger("app:aimedb:encoder");
|
||||
|
||||
const registerLevels = new Map<RegisterLevel, number>();
|
||||
|
||||
registerLevels.set("none", 0);
|
||||
@@ -11,7 +14,7 @@ registerLevels.set("segaid", 2);
|
||||
function begin(length: number) {
|
||||
const buf = Buffer.alloc(length);
|
||||
|
||||
buf.writeUInt16LE(0xa13e, 0x0000); // Magic?
|
||||
buf.writeUInt16LE(0xa13e, 0x0000); // Magic: aime
|
||||
buf.writeUInt16LE(0x3087, 0x0002); // ???
|
||||
buf.writeUInt16LE(length, 0x0006);
|
||||
|
||||
@@ -27,7 +30,7 @@ export class Encoder extends Transform {
|
||||
}
|
||||
|
||||
_transform(msg: AimeResponse, encoding, callback) {
|
||||
console.log("Aimedb: Encode", msg);
|
||||
debug("Encode %j", msg);
|
||||
|
||||
let buf: Buffer;
|
||||
|
||||
@@ -40,6 +43,18 @@ export class Encoder extends Transform {
|
||||
|
||||
break;
|
||||
|
||||
case "felica_lookup2":
|
||||
buf = begin(0x0140);
|
||||
buf.writeUInt16LE(0x0012, 0x0004); // cmd code
|
||||
buf.writeUInt16LE(msg.status, 0x0008);
|
||||
buf.writeInt32LE(msg.aimeId || -1, 0x0020);
|
||||
buf.writeUInt32LE(0xffffffff, 0x0024); // FF
|
||||
buf.writeUInt32LE(0xffffffff, 0x0028); // FF
|
||||
buf.write(msg.accessCode, 0x002c, "hex");
|
||||
buf.writeUInt16LE(0x0001, 0x0037); // 00 01
|
||||
|
||||
break;
|
||||
|
||||
case "hello":
|
||||
buf = begin(0x0020);
|
||||
buf.writeUInt16LE(0x0065, 0x0004); // cmd code
|
||||
@@ -105,7 +120,9 @@ export class Encoder extends Transform {
|
||||
return callback(new Error("Unimplemented response type"));
|
||||
}
|
||||
|
||||
console.log("Aimedb: Send", buf.toString("hex"));
|
||||
if (debug.enabled) {
|
||||
debug("Send %s", buf.toString("hex"));
|
||||
}
|
||||
|
||||
return callback(null, buf);
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,5 +1,8 @@
|
||||
import logger from "debug";
|
||||
import { Transform } from "stream";
|
||||
|
||||
const debug = logger("app:aimedb:frame");
|
||||
|
||||
export class Deframer extends Transform {
|
||||
private state: Buffer;
|
||||
|
||||
@@ -33,7 +36,9 @@ export class Deframer extends Transform {
|
||||
|
||||
const frame = this.state.slice(0, len);
|
||||
|
||||
console.log("Aimedb: Recv", frame.toString("hex"));
|
||||
if (debug.enabled) {
|
||||
debug("Recv %s", frame.toString("hex"));
|
||||
}
|
||||
|
||||
this.state = this.state.slice(len);
|
||||
|
||||
|
||||
+37
-8
@@ -1,13 +1,17 @@
|
||||
import logger from "debug";
|
||||
|
||||
import { Repositories } from "./repo";
|
||||
import * as Req from "./request";
|
||||
import * as Res from "./response";
|
||||
|
||||
const debug = logger("app:aimedb:ops");
|
||||
|
||||
function hello(
|
||||
rep: Repositories,
|
||||
req: Req.HelloRequest,
|
||||
now: Date
|
||||
): Res.HelloResponse {
|
||||
console.log("Aimedb: Hello");
|
||||
debug("Hello");
|
||||
|
||||
return { type: req.type, status: 1 };
|
||||
}
|
||||
@@ -17,7 +21,7 @@ function campaign(
|
||||
req: Req.CampaignRequest,
|
||||
now: Date
|
||||
): Res.CampaignResponse {
|
||||
console.log("Aimedb: Campaign stuff");
|
||||
debug("Campaign stuff");
|
||||
|
||||
return { type: req.type, status: 1 };
|
||||
}
|
||||
@@ -27,7 +31,7 @@ function feliCaLookup(
|
||||
req: Req.FeliCaLookupRequest,
|
||||
now: Date
|
||||
): Res.FeliCaLookupResponse {
|
||||
console.log("Aimedb: FeliCa access code lookup");
|
||||
debug("FeliCa access code lookup");
|
||||
|
||||
// Well, this access code transformation is the million dollar question eh
|
||||
// Return a decimal representation for now.
|
||||
@@ -42,12 +46,34 @@ function feliCaLookup(
|
||||
return { type: req.type, status: 1, accessCode };
|
||||
}
|
||||
|
||||
async function feliCaLookup2(
|
||||
rep: Repositories,
|
||||
req: Req.FeliCaLookup2Request,
|
||||
now: Date
|
||||
): Promise<Res.FeliCaLookup2Response> {
|
||||
debug("FeliCa access code lookup");
|
||||
|
||||
const num = BigInt("0x" + req.idm);
|
||||
let accessCode = num.toString();
|
||||
|
||||
while (accessCode.length < 20) {
|
||||
accessCode = "0" + accessCode;
|
||||
}
|
||||
|
||||
return {
|
||||
type: req.type,
|
||||
status: 1,
|
||||
accessCode,
|
||||
aimeId: await rep.cards().lookup(accessCode, now),
|
||||
};
|
||||
}
|
||||
|
||||
async function lookup(
|
||||
rep: Repositories,
|
||||
req: Req.LookupRequest,
|
||||
now: Date
|
||||
): Promise<Res.LookupResponse> {
|
||||
console.log("Aimedb: Mifare lookup v1", req.luid);
|
||||
debug("Mifare lookup: luid=%s", req.luid);
|
||||
|
||||
return {
|
||||
type: req.type,
|
||||
@@ -62,7 +88,7 @@ async function lookup2(
|
||||
req: Req.LookupRequest2,
|
||||
now: Date
|
||||
): Promise<Res.LookupResponse2> {
|
||||
console.log("Aimedb: Mifare lookup v2", req.luid);
|
||||
debug("FeliCa lookup: luid=%s", req.luid);
|
||||
|
||||
return {
|
||||
type: req.type,
|
||||
@@ -77,7 +103,7 @@ async function register(
|
||||
req: Req.RegisterRequest,
|
||||
now: Date
|
||||
): Promise<Res.RegisterResponse> {
|
||||
console.log("Aimedb: Mifare register", req.luid);
|
||||
debug("User register: luid=%s", req.luid);
|
||||
|
||||
return {
|
||||
type: req.type,
|
||||
@@ -91,7 +117,7 @@ function log(
|
||||
req: Req.LogRequest,
|
||||
now: Date
|
||||
): Res.LogResponse {
|
||||
console.log("Aimedb: Log message");
|
||||
debug("Log message");
|
||||
|
||||
return { type: req.type, status: 1 };
|
||||
}
|
||||
@@ -111,6 +137,9 @@ export async function dispatch(
|
||||
case "felica_lookup":
|
||||
return feliCaLookup(rep, req, now);
|
||||
|
||||
case "felica_lookup2":
|
||||
return feliCaLookup2(rep, req, now);
|
||||
|
||||
case "lookup":
|
||||
return lookup(rep, req, now);
|
||||
|
||||
@@ -124,7 +153,7 @@ export async function dispatch(
|
||||
return log(rep, req, now);
|
||||
|
||||
case "goodbye":
|
||||
console.log("Aimedb: Goodbye");
|
||||
debug("Goodbye");
|
||||
|
||||
return undefined;
|
||||
|
||||
|
||||
+29
-20
@@ -1,35 +1,44 @@
|
||||
import logger from "debug";
|
||||
import { Socket } from "net";
|
||||
|
||||
import { dispatch } from "./handler";
|
||||
import { AimeRequest } from "./request";
|
||||
import { setup } from "./pipeline";
|
||||
import { beginDbSession } from "./db";
|
||||
import { DataSource } from "../sql/api";
|
||||
import { SqlRepositories } from "./sql";
|
||||
|
||||
export default async function aimedb(socket: Socket) {
|
||||
console.log("Aimedb: Connection opened");
|
||||
const debug = logger("app:aimedb:session");
|
||||
|
||||
const { input, output } = setup(socket);
|
||||
const txn = await beginDbSession();
|
||||
export default function aimedb(db: DataSource) {
|
||||
return async function(socket: Socket) {
|
||||
debug("Connection opened");
|
||||
|
||||
const { input, output } = setup(socket);
|
||||
|
||||
try {
|
||||
for await (const obj of input) {
|
||||
console.log("Aimedb: Decode", obj);
|
||||
try {
|
||||
const now = new Date();
|
||||
const req = obj;
|
||||
const res = await db.transaction(txn =>
|
||||
dispatch(new SqlRepositories(txn), req, now)
|
||||
);
|
||||
|
||||
const now = new Date();
|
||||
const req = obj as AimeRequest;
|
||||
const res = await dispatch(txn, req, now);
|
||||
if (res === undefined) {
|
||||
debug("Closing connection");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (res !== undefined) {
|
||||
output.write(res);
|
||||
} catch (e) {
|
||||
if (debug.enabled) {
|
||||
debug("Connection error: %s", e.stack);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await txn.commit();
|
||||
} catch (e) {
|
||||
console.log("Aimedb: Connection error:\n", e);
|
||||
await txn.rollback();
|
||||
}
|
||||
|
||||
console.log("Aimedb: Connection closed\n");
|
||||
socket.end();
|
||||
debug("Connection closed");
|
||||
socket.end();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as crypto from "crypto";
|
||||
import { createCipheriv, createDecipheriv } from "crypto";
|
||||
import { Socket } from "net";
|
||||
import * as stream from "stream";
|
||||
import { promisify } from "util";
|
||||
@@ -26,7 +26,7 @@ export function setup(socket: Socket): Session {
|
||||
|
||||
pipeline(
|
||||
socket,
|
||||
crypto.createDecipheriv("aes-128-ecb", K, null).setAutoPadding(false),
|
||||
createDecipheriv("aes-128-ecb", K, null).setAutoPadding(false),
|
||||
new Deframer({}),
|
||||
input
|
||||
).catch(doNothing);
|
||||
@@ -35,7 +35,7 @@ export function setup(socket: Socket): Session {
|
||||
|
||||
pipeline(
|
||||
output,
|
||||
crypto.createCipheriv("aes-128-ecb", K, null).setAutoPadding(false),
|
||||
createCipheriv("aes-128-ecb", K, null).setAutoPadding(false),
|
||||
socket
|
||||
).catch(doNothing);
|
||||
|
||||
|
||||
@@ -9,9 +9,3 @@ export interface CardRepository {
|
||||
export interface Repositories {
|
||||
cards(): CardRepository;
|
||||
}
|
||||
|
||||
export interface Transaction extends Repositories {
|
||||
commit(): Promise<void>;
|
||||
|
||||
rollback(): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ export interface FeliCaLookupRequest extends AimeRequestBase {
|
||||
pmm: string;
|
||||
}
|
||||
|
||||
export interface FeliCaLookup2Request extends AimeRequestBase {
|
||||
type: "felica_lookup2";
|
||||
idm: string;
|
||||
pmm: string;
|
||||
}
|
||||
|
||||
export interface RegisterRequest extends AimeRequestBase {
|
||||
type: "register";
|
||||
luid: string;
|
||||
@@ -50,6 +56,7 @@ export interface GoodbyeRequest {
|
||||
|
||||
export type AimeRequest =
|
||||
| FeliCaLookupRequest
|
||||
| FeliCaLookup2Request
|
||||
| CampaignRequest
|
||||
| GoodbyeRequest
|
||||
| HelloRequest
|
||||
|
||||
@@ -11,6 +11,12 @@ export interface FeliCaLookupResponse extends AimeResponseBase {
|
||||
accessCode: string;
|
||||
}
|
||||
|
||||
export interface FeliCaLookup2Response extends AimeResponseBase {
|
||||
type: "felica_lookup2";
|
||||
accessCode: string;
|
||||
aimeId?: AimeId;
|
||||
}
|
||||
|
||||
export interface CampaignResponse extends AimeResponseBase {
|
||||
type: "campaign";
|
||||
}
|
||||
@@ -44,6 +50,7 @@ export interface RegisterResponse extends AimeResponseBase {
|
||||
|
||||
export type AimeResponse =
|
||||
| FeliCaLookupResponse
|
||||
| FeliCaLookup2Response
|
||||
| CampaignResponse
|
||||
| HelloResponse
|
||||
| LogResponse
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import sql from "sql-bricks-postgres";
|
||||
|
||||
import { CardRepository, Repositories } from "./repo";
|
||||
import { AimeId, generateExtId } from "../model";
|
||||
import { Transaction } from "../sql";
|
||||
|
||||
class CardRepositoryImpl implements CardRepository {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
|
||||
async lookup(luid: string, now: Date): Promise<AimeId | undefined> {
|
||||
const fetchSql = sql
|
||||
.select("p.id", "p.ext_id")
|
||||
.from("aime_player p")
|
||||
.where("p.luid", luid);
|
||||
|
||||
const row = await this._txn.fetchRow(fetchSql);
|
||||
|
||||
if (row === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const id = row.id!;
|
||||
const extId = row.ext_id!;
|
||||
|
||||
const touchSql = sql
|
||||
.update("aime_player")
|
||||
.set({ access_time: now })
|
||||
.where("id", id);
|
||||
|
||||
await this._txn.modify(touchSql);
|
||||
|
||||
return parseInt(extId, 10) as AimeId;
|
||||
}
|
||||
|
||||
async register(luid: string, now: Date): Promise<AimeId> {
|
||||
const id = this._txn.generateId();
|
||||
const aimeId = generateExtId() as AimeId;
|
||||
|
||||
const insertSql = sql.insert("aime_player", {
|
||||
id: id,
|
||||
ext_id: aimeId,
|
||||
luid: luid,
|
||||
register_time: now,
|
||||
access_time: now,
|
||||
});
|
||||
|
||||
await this._txn.modify(insertSql);
|
||||
|
||||
return aimeId;
|
||||
}
|
||||
}
|
||||
|
||||
export class SqlRepositories implements Repositories {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
|
||||
cards(): CardRepository {
|
||||
return new CardRepositoryImpl(this._txn);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import addHours = require("date-fns/add_hours");
|
||||
import express = require("express");
|
||||
import iconv = require("iconv-lite");
|
||||
import read = require("raw-body");
|
||||
|
||||
import addHours from "date-fns/add_hours";
|
||||
import express from "express";
|
||||
import iconv from "iconv-lite";
|
||||
import logger from "debug";
|
||||
import read from "raw-body";
|
||||
import { unzipSync } from "zlib";
|
||||
|
||||
import { startupHost, startupUri } from "./switchboard";
|
||||
|
||||
const debug = logger("app:allnet");
|
||||
const hourDelta = parseInt(process.env.HOUR_DELTA || "0");
|
||||
const app = express();
|
||||
|
||||
@@ -14,7 +15,7 @@ const app = express();
|
||||
// So in the absence of any exotic Transfer-Encoding headers this Content-Type
|
||||
// is incorrect and we have to override Express' built-in handling.
|
||||
|
||||
app.use(async function(req, res, next) {
|
||||
app.use("/sys/servlet/PowerOn", async function(req, res, next) {
|
||||
if (req.method !== "POST") {
|
||||
return res.status(405).end();
|
||||
}
|
||||
@@ -58,8 +59,8 @@ app.use(async function(req, res, next) {
|
||||
return next();
|
||||
});
|
||||
|
||||
app.post("/sys/servlet/PowerOn", function(req, resp) {
|
||||
console.log("--- Startup Request ---\n\n", req.body);
|
||||
app.post("/sys/servlet/PowerOn", function(req, res) {
|
||||
debug("Startup request: %j", req.body);
|
||||
|
||||
// Cut milliseconds out of ISO timestamp
|
||||
|
||||
@@ -73,14 +74,14 @@ app.post("/sys/servlet/PowerOn", function(req, resp) {
|
||||
uri: startupUri(req.body.game_id),
|
||||
host: startupHost(req.body.game_id),
|
||||
place_id: "123",
|
||||
name: process.env.SHOP_NAME,
|
||||
nickname: process.env.SHOP_NAME,
|
||||
name: process.env.SHOP_NAME || "",
|
||||
nickname: process.env.SHOP_NAME || "",
|
||||
region0: "1",
|
||||
region_name0: "W",
|
||||
region_name1: "X",
|
||||
region_name2: "Y",
|
||||
region_name3: "Z",
|
||||
country: process.env.SHOP_REGION,
|
||||
country: process.env.SHOP_REGION || "JPN",
|
||||
allnet_id: "456",
|
||||
client_timezone: "+0900",
|
||||
|
||||
@@ -90,10 +91,9 @@ app.post("/sys/servlet/PowerOn", function(req, resp) {
|
||||
token: req.body.token,
|
||||
};
|
||||
|
||||
console.log("\n--- Startup Response ---\n\n", resParams);
|
||||
console.log("");
|
||||
debug("Startup response: %j", resParams);
|
||||
|
||||
resp.send(resParams);
|
||||
res.send(resParams);
|
||||
});
|
||||
|
||||
export default app;
|
||||
+6
-5
@@ -1,5 +1,6 @@
|
||||
import read = require("raw-body");
|
||||
import express = require("express");
|
||||
import express from "express";
|
||||
import logger from "debug";
|
||||
import read from "raw-body";
|
||||
|
||||
import { createSign } from "crypto";
|
||||
import { inflateRawSync } from "zlib";
|
||||
@@ -9,6 +10,7 @@ interface Kvps {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
const debug = logger("app:billing");
|
||||
const billingKeyPair = readFileSync("pki/billing.key");
|
||||
|
||||
// nearfull: high 16 bits is billing mode, low 16 bits is actual nearfull val.
|
||||
@@ -60,7 +62,7 @@ app.use(async function(req, res, next) {
|
||||
});
|
||||
|
||||
app.post("/request/", function(req, res) {
|
||||
console.log("--- Billing Request ---\n\n", req.body);
|
||||
debug("Billing request: %j", req.body);
|
||||
|
||||
const first = req.body[0];
|
||||
|
||||
@@ -133,8 +135,7 @@ app.post("/request/", function(req, res) {
|
||||
playhistory: "000000/0:000000/0:000000/0",
|
||||
});
|
||||
|
||||
console.log("\n--- Billing Response ---\n\n", resItems);
|
||||
console.log("");
|
||||
debug("Billing response: %j", resItems);
|
||||
|
||||
res.set("content-type", "text/plain");
|
||||
res.send(resItems);
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
import { readdirSync, readFileSync } from "fs";
|
||||
import logger from "debug";
|
||||
import { resolve } from "path";
|
||||
import sql from "sql-bricks-postgres";
|
||||
|
||||
import { DataSource, Transaction } from "./sql";
|
||||
|
||||
const scripts = [
|
||||
// Order matters! Later scripts reference schema instantiated earlier.
|
||||
"meta.sql",
|
||||
"aime.sql",
|
||||
"cm.sql",
|
||||
"idz.sql",
|
||||
];
|
||||
|
||||
const debug = logger("app:checkdb");
|
||||
const initPath = "./schema/init";
|
||||
const migratePath = "./schema/migrate";
|
||||
const migrateRx = new RegExp("^M([0-9]{4})-.+\\.sql$");
|
||||
|
||||
async function initdb(txn: Transaction): Promise<void> {
|
||||
// Get latest schemaver from migration script filenames
|
||||
|
||||
const migrations = readdirSync(migratePath);
|
||||
let schemaver = 0;
|
||||
|
||||
for (const filename of migrations) {
|
||||
const captures = filename.match(migrateRx);
|
||||
|
||||
if (captures === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const migrationNo = parseInt(captures[1]);
|
||||
|
||||
if (schemaver < migrationNo) {
|
||||
schemaver = migrationNo;
|
||||
}
|
||||
}
|
||||
|
||||
// Run init scripts
|
||||
|
||||
debug("Initializing database");
|
||||
|
||||
for (const script of scripts) {
|
||||
debug("Executing %s", script);
|
||||
|
||||
const scriptPath = resolve(initPath, script);
|
||||
const scriptSql = readFileSync(scriptPath, "utf-8");
|
||||
|
||||
await txn.raw(scriptSql);
|
||||
}
|
||||
|
||||
// Set up metadata table
|
||||
|
||||
const metaInsert = sql.insert("meta", { schemaver });
|
||||
|
||||
await txn.modify(metaInsert);
|
||||
|
||||
debug("Initialized new database to schema version %s", schemaver);
|
||||
}
|
||||
|
||||
async function migratedb(
|
||||
txn: Transaction,
|
||||
schemaver: number
|
||||
): Promise<number | undefined> {
|
||||
const filenames = readdirSync(migratePath);
|
||||
let lastMigrationNo: number | undefined;
|
||||
|
||||
filenames.sort();
|
||||
|
||||
for (const filename of filenames) {
|
||||
const captures = filename.match(migrateRx);
|
||||
|
||||
if (captures === null) {
|
||||
debug("Warning: Unexpected file %s in SQL migrations dir", filename);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const migrationNo = parseInt(captures[1]);
|
||||
|
||||
if (migrationNo <= schemaver) {
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Executing database upgrade: %s", filename);
|
||||
|
||||
const scriptPath = resolve(migratePath, filename);
|
||||
const scriptSql = readFileSync(scriptPath, "utf-8");
|
||||
|
||||
await txn.raw(scriptSql);
|
||||
|
||||
lastMigrationNo = migrationNo;
|
||||
}
|
||||
|
||||
if (lastMigrationNo !== undefined) {
|
||||
const metaUpdate = sql.update("meta", { schemaver: lastMigrationNo });
|
||||
|
||||
await txn.modify(metaUpdate);
|
||||
}
|
||||
|
||||
return lastMigrationNo;
|
||||
}
|
||||
|
||||
export default async function checkdb(db: DataSource): Promise<void> {
|
||||
let schemaver: number | undefined;
|
||||
|
||||
await db.maintenance(async txn => {
|
||||
const stmt = sql.select("schemaver").from("meta");
|
||||
|
||||
try {
|
||||
const row = await txn.fetchRow(stmt);
|
||||
|
||||
if (row !== undefined) {
|
||||
schemaver = parseInt(row.schemaver!);
|
||||
}
|
||||
} catch (e) {
|
||||
return await initdb(txn);
|
||||
}
|
||||
|
||||
if (schemaver === undefined) {
|
||||
throw new Error(
|
||||
"Database corrupted: `meta` table singleton row is missing"
|
||||
);
|
||||
}
|
||||
|
||||
schemaver = await migratedb(txn, schemaver);
|
||||
});
|
||||
|
||||
if (schemaver !== undefined) {
|
||||
debug("Upgraded database to version %s", schemaver);
|
||||
|
||||
await db.vacuum();
|
||||
|
||||
debug("Post-upgrade housekeeping complete");
|
||||
}
|
||||
}
|
||||
-188
@@ -1,188 +0,0 @@
|
||||
import compression = require("compression");
|
||||
import express = require("express");
|
||||
|
||||
const app = express();
|
||||
|
||||
// Thankfully we can use standard middleware for JSON I/O. We have to use a
|
||||
// zlib middleware as well because compression is non-optional: the client will
|
||||
// attempt to inflate whatever response we send to it whether there's a
|
||||
// Transfer-Encoding header or not.
|
||||
|
||||
app.use(compression());
|
||||
app.use(express.json());
|
||||
|
||||
// Trace requests and responses
|
||||
|
||||
app.use(function(req, resp, next) {
|
||||
console.log("\n--- Chunithm %s ---\n", req.url);
|
||||
console.log("Request:", req.body);
|
||||
|
||||
const prevJson = resp.json;
|
||||
|
||||
resp.json = function(obj) {
|
||||
console.log("Response:", obj);
|
||||
|
||||
resp.json = prevJson;
|
||||
resp.json.apply(this, arguments);
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameSettingApi", function(req, resp) {
|
||||
resp.json({
|
||||
gameSetting: {
|
||||
dataVersion: 1,
|
||||
isMaintenance: false,
|
||||
requestInterval: 10,
|
||||
rebootStartTime: 0,
|
||||
rebootEndTime: 0,
|
||||
isBackgroundDistribute: false,
|
||||
maxCountCharacter: 999,
|
||||
maxCountItem: 999,
|
||||
maxCountMusic: 999,
|
||||
},
|
||||
isDumpUpload: false,
|
||||
isAou: false,
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/UpsertClientSettingApi", function(req, resp) {
|
||||
resp.json({
|
||||
returnCode: 1,
|
||||
apiName: "UpsertClientSettingApi",
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/UpsertClientBookkeepingApi", function(req, resp) {
|
||||
resp.json({
|
||||
returnCode: 1,
|
||||
apiName: "UpsertClientBookkeepingApi",
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/UpsertClientTestmodeApi", function(req, resp) {
|
||||
resp.json({
|
||||
returnCode: 1,
|
||||
apiName: "UpsertClientTestmodeApi",
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/UpsertClientErrorApi", function(req, resp) {
|
||||
resp.json({
|
||||
returnCode: 1,
|
||||
apiName: "UpsertClientErrorApi",
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/UpsertClientDevelopApi", function(req, resp) {
|
||||
resp.json({
|
||||
returnCode: 1,
|
||||
apiName: "UpsertClientDevelopApi",
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameMessageApi", function(req, resp) {
|
||||
resp.json({
|
||||
type: 1,
|
||||
length: 0,
|
||||
gameMessageList: [
|
||||
/* {
|
||||
type: 2,
|
||||
id: 1,
|
||||
message: "true",
|
||||
startDate: "0",
|
||||
endDate: "0"
|
||||
},*/
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameIdlistApi", function(req, resp) {
|
||||
const { type } = req.body;
|
||||
|
||||
resp.json({
|
||||
type,
|
||||
length: 0,
|
||||
gameIdlistList: [],
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameEventApi", function(req, resp) {
|
||||
resp.json({
|
||||
type: 1,
|
||||
length: 0,
|
||||
gameEventList: [
|
||||
/*
|
||||
{
|
||||
type: 1,
|
||||
id: 1102, // data/A000/event/event00001102
|
||||
startDate: 'STRINGIDK',
|
||||
endDate: 'STRINGIDK',
|
||||
},
|
||||
*/
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameRankingApi", function(req, resp) {
|
||||
const { type } = req.body;
|
||||
|
||||
resp.json({
|
||||
type,
|
||||
gameRankingList: [
|
||||
/*
|
||||
// QWORD fields maybe?
|
||||
{
|
||||
id: 1,
|
||||
point: 1,
|
||||
}
|
||||
*/
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameSaleApi", function(req, resp) {
|
||||
const { type } = req.body;
|
||||
|
||||
resp.json({
|
||||
type,
|
||||
length: 0,
|
||||
gameSaleList: [
|
||||
/*
|
||||
{
|
||||
orderId: 1234,
|
||||
type,
|
||||
id: 4321,
|
||||
rate: 5678,
|
||||
startDate: 'STRINGIDK',
|
||||
endDate: 'STRINGIDK',
|
||||
},
|
||||
*/
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameChargeApi", function(req, resp) {
|
||||
resp.json({
|
||||
length: 0,
|
||||
gameChargeList: [
|
||||
/*
|
||||
{
|
||||
orderId: 1,
|
||||
chargeId: 1,
|
||||
price: 1,
|
||||
startDate: 'STRINGIDK',
|
||||
endDate: 'STRINGIDK',
|
||||
salePrice:
|
||||
saleStartDate: 'STRINGIDK',
|
||||
saleEndDate: 'STRINGIDK',
|
||||
},
|
||||
*/
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
export default app;
|
||||
@@ -0,0 +1,13 @@
|
||||
// Make the names explicit so that we don't get the order mixed up
|
||||
interface Params {
|
||||
maxCount: number;
|
||||
nextIndex: number;
|
||||
}
|
||||
|
||||
export function paginationCookie<T>(items: T[], params: Params) {
|
||||
if (items.length < params.maxCount) {
|
||||
return "-1";
|
||||
} else {
|
||||
return (params.nextIndex + params.maxCount).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GameLoginRequest } from "../request/gameLogin";
|
||||
import { GameLoginResponse } from "../response/gameLogin";
|
||||
|
||||
export default async function gameLogin(
|
||||
rep: Repositories,
|
||||
req: GameLoginRequest
|
||||
): Promise<GameLoginResponse> {
|
||||
return { returnCode: "1" };
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GameLogoutRequest } from "../request/gameLogout";
|
||||
import { GameLogoutResponse } from "../response/gameLogout";
|
||||
|
||||
export default async function gameLogout(
|
||||
rep: Repositories,
|
||||
req: GameLogoutRequest
|
||||
): Promise<GameLogoutResponse> {
|
||||
return { returnCode: "1" };
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { GameChargeJson } from "../proto/gameCharge";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameChargeRequest } from "../request/getGameCharge";
|
||||
import { GetGameChargeResponse } from "../response/getGameCharge";
|
||||
import { CHARGE_IDS } from "../static/charge";
|
||||
|
||||
export default async function getGameCharge(
|
||||
rep: Repositories,
|
||||
req: GetGameChargeRequest
|
||||
): Promise<GetGameChargeResponse> {
|
||||
const gameChargeList: GameChargeJson[] = [];
|
||||
|
||||
for (const [i, charge] of CHARGE_IDS.entries()) {
|
||||
gameChargeList.push({
|
||||
chargeId: charge.id.toString(),
|
||||
orderId: (i+1).toString(),
|
||||
price: charge.price.toString(),
|
||||
salePrice: charge.salePrice.toString(),
|
||||
startDate: "2017-12-05 07:00:00.0",
|
||||
endDate: "2029-12-31 23:59:59.0",
|
||||
saleStartDate: "2017-12-05 07:00:00.0",
|
||||
saleEndDate: "2030-12-31 23:59:59.0"
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
length: gameChargeList.length.toString(),
|
||||
gameChargeList,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { GameEventJson } from "../proto/gameEvent";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameEventRequest } from "../request/getGameEvent";
|
||||
import { GetGameEventResponse } from "../response/getGameEvent";
|
||||
import { EVENT_IDS } from "../static/event";
|
||||
|
||||
export default async function getGameEvent(
|
||||
rep: Repositories,
|
||||
req: GetGameEventRequest
|
||||
): Promise<GetGameEventResponse> {
|
||||
const gameEventList: GameEventJson[] = [];
|
||||
|
||||
for (const id of EVENT_IDS) {
|
||||
gameEventList.push({
|
||||
type: req.type,
|
||||
id: id.toString(),
|
||||
startDate: "2017-12-05 07:00:00.0",
|
||||
endDate: "2099-12-31 00:00:00.0",
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
type: req.type,
|
||||
length: gameEventList.length.toString(),
|
||||
gameEventList,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameIdlistRequest } from "../request/getGameIdlist";
|
||||
import { GetGameIdlistResponse } from "../response/getGameIdlist";
|
||||
|
||||
export default async function getGameIdlist(
|
||||
rep: Repositories,
|
||||
req: GetGameIdlistRequest
|
||||
): Promise<GetGameIdlistResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
length: "0",
|
||||
gameIdlistList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameMessageRequest } from "../request/getGameMessage";
|
||||
import { GetGameMessageResponse } from "../response/getGameMessage";
|
||||
|
||||
export default async function getGameMessage(
|
||||
rep: Repositories,
|
||||
req: GetGameMessageRequest
|
||||
): Promise<GetGameMessageResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
length: "0",
|
||||
gameMessageList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameRankingRequest } from "../request/getGameRanking";
|
||||
import { GetGameRankingResponse } from "../response/getGameRanking";
|
||||
|
||||
export default async function getGameRanking(
|
||||
rep: Repositories,
|
||||
req: GetGameRankingRequest
|
||||
): Promise<GetGameRankingResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
gameRankingList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameSaleRequest } from "../request/getGameSale";
|
||||
import { GetGameSaleResponse } from "../response/getGameSale";
|
||||
|
||||
export default async function getGameSale(
|
||||
rep: Repositories,
|
||||
req: GetGameSaleRequest
|
||||
): Promise<GetGameSaleResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
length: "0",
|
||||
gameSaleList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { writeDate } from "../proto/base";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameSettingRequest } from "../request/getGameSetting";
|
||||
import { GetGameSettingResponse } from "../response/getGameSetting";
|
||||
|
||||
export default async function getGameSetting(
|
||||
rep: Repositories,
|
||||
req: GetGameSettingRequest
|
||||
): Promise<GetGameSettingResponse> {
|
||||
const rebootStartTime = new Date();
|
||||
rebootStartTime.setHours(rebootStartTime.getHours() - 3);
|
||||
|
||||
const rebootEndTime = new Date();
|
||||
rebootEndTime.setHours(rebootEndTime.getHours() - 2);
|
||||
|
||||
return {
|
||||
gameSetting: {
|
||||
dataVersion: "1",
|
||||
isMaintenance: "false",
|
||||
requestInterval: "10",
|
||||
rebootStartTime: writeDate(rebootStartTime),
|
||||
rebootEndTime: writeDate(rebootEndTime),
|
||||
isBackgroundDistribute: "false",
|
||||
maxCountCharacter: "300",
|
||||
maxCountItem: "300",
|
||||
maxCountMusic: "100",
|
||||
},
|
||||
isDumpUpload: "false",
|
||||
isAou: "true",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserActivity } from "../proto/userActivity";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserActivityRequest } from "../request/getUserActivity";
|
||||
import { GetUserActivityResponse } from "../response/getUserActivity";
|
||||
|
||||
export default async function getUserActivity(
|
||||
rep: Repositories,
|
||||
req: GetUserActivityRequest
|
||||
): Promise<GetUserActivityResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const kind = parseInt(req.kind);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep.userActivity().load(profileId, kind);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
kind: kind.toString(),
|
||||
userActivityList: items.map(writeUserActivity),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { paginationCookie } from "./_util";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserCharacter } from "../proto/userCharacter";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserCharacterRequest } from "../request/getUserCharacter";
|
||||
import { GetUserCharacterResponse } from "../response/getUserCharacter";
|
||||
|
||||
export default async function getUserCharacter(
|
||||
rep: Repositories,
|
||||
req: GetUserCharacterRequest
|
||||
): Promise<GetUserCharacterResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const nextIndex = parseInt(req.nextIndex);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep
|
||||
.userCharacter()
|
||||
.load(profileId, { limit: maxCount, offset: nextIndex });
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
nextIndex: paginationCookie(items, { maxCount, nextIndex }),
|
||||
userCharacterList: items.map(writeUserCharacter),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserCharge } from "../proto/userCharge";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserChargeRequest } from "../request/getUserCharge";
|
||||
import { GetUserChargeResponse } from "../response/getUserCharge";
|
||||
|
||||
export default async function getUserCharge(
|
||||
rep: Repositories,
|
||||
req: GetUserChargeRequest
|
||||
): Promise<GetUserChargeResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep.userCharge().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
userChargeList: items.map(writeUserCharge),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserCourseRequest } from "../request/getUserCourse";
|
||||
import { GetUserCourseResponse } from "../response/getUserCourse";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { paginationCookie } from "./_util";
|
||||
import { writeUserCourse } from "../proto/userCourse";
|
||||
|
||||
export default async function getUserCourse(
|
||||
rep: Repositories,
|
||||
req: GetUserCourseRequest
|
||||
): Promise<GetUserCourseResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const nextIndex = parseInt(req.nextIndex);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep
|
||||
.userCourse()
|
||||
.load(profileId, { limit: maxCount, offset: nextIndex });
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
nextIndex: paginationCookie(items, { maxCount, nextIndex }),
|
||||
userCourseList: items.map(writeUserCourse)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserData } from "../proto/userData";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserDataRequest } from "../request/getUserData";
|
||||
import { GetUserDataResponse } from "../response/getUserData";
|
||||
|
||||
export default async function getUserData(
|
||||
rep: Repositories,
|
||||
req: GetUserDataRequest
|
||||
): Promise<GetUserDataResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userData = await rep.userData().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userData: writeUserData(userData),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserDataEx } from "../proto/userDataEx";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserDataExRequest } from "../request/getUserDataEx";
|
||||
import { GetUserDataExResponse } from "../response/getUserDataEx";
|
||||
|
||||
export default async function getUserDataEx(
|
||||
rep: Repositories,
|
||||
req: GetUserDataExRequest
|
||||
): Promise<GetUserDataExResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userDataEx = await rep.userDataEx().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userDataEx: writeUserDataEx(userDataEx),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserDuelRequest } from "../request/getUserDuel";
|
||||
import { GetUserDuelResponse } from "../response/getUserDuel";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserDuelList } from "../proto/userDuelList";
|
||||
|
||||
export default async function getUserDuel(
|
||||
rep: Repositories,
|
||||
req: GetUserDuelRequest
|
||||
): Promise<GetUserDuelResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
// Get all duel entities if the client specifies to.
|
||||
const duelId = req.isAllDuel ? undefined : parseInt(req.duelId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep.userDuelList().load(profileId, duelId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
userDuelList: items.map(writeUserDuelList),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserFavoriteMusicRequest } from "../request/getUserFavoriteMusic";
|
||||
import { GetUserFavoriteMusicResponse } from "../response/getUserFavoriteMusic";
|
||||
import { readAimeId } from "../proto/base";
|
||||
|
||||
export default async function getUserFavoriteMusic(
|
||||
rep: Repositories,
|
||||
req: GetUserFavoriteMusicRequest
|
||||
): Promise<GetUserFavoriteMusicResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
|
||||
/*
|
||||
* `Chunithm Amazon Plus` does not appear to save a favorites list and there
|
||||
* is no user-accessible favorites function from what I can tell.
|
||||
*/
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userFavoriteMusicList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserItem } from "../proto/userItem";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserItemRequest } from "../request/getUserItem";
|
||||
import { GetUserItemResponse } from "../response/getUserItem";
|
||||
import { UserItemItem } from "../model/userItem";
|
||||
|
||||
// I'm not sure why they went to all this trouble just to avoid passing an
|
||||
// explicit `itemKind` parameter in the request...
|
||||
|
||||
const itemKindMul = 10000000000n;
|
||||
|
||||
export default async function getUserItem(
|
||||
rep: Repositories,
|
||||
req: GetUserItemRequest
|
||||
): Promise<GetUserItemResponse> {
|
||||
// Split the "nextIndex" parameter
|
||||
|
||||
const compoundIn = BigInt(req.nextIndex);
|
||||
const itemKindN = compoundIn / itemKindMul;
|
||||
const nextIndexN = compoundIn % itemKindMul;
|
||||
|
||||
// Now unpack the entire request
|
||||
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const itemKind = parseInt(itemKindN as any);
|
||||
const nextIndex = parseInt(nextIndexN as any);
|
||||
|
||||
// Hit DB
|
||||
// This gets called for not-yet-created profiles for some reason (probably
|
||||
// so that the server can force some bonus items into a new profile's
|
||||
// inventory)? So we need to gracefully handle that condition.
|
||||
|
||||
// (maybe some sort of anchor row is created on first GameLogin...)
|
||||
|
||||
const profileId = await rep.userData().tryLookup(aimeId);
|
||||
let items: UserItemItem[];
|
||||
|
||||
if (profileId !== undefined) {
|
||||
items = await rep
|
||||
.userItem()
|
||||
.load(profileId, itemKind, { limit: maxCount, offset: nextIndex });
|
||||
} else {
|
||||
items = [];
|
||||
}
|
||||
|
||||
// Pack the next pagination cookie into bigint compound form
|
||||
|
||||
const xout = itemKindN * itemKindMul + nextIndexN + BigInt(items.length);
|
||||
|
||||
// Done:
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
nextIndex: items.length < maxCount ? "-1" : xout.toString(),
|
||||
itemKind: itemKind.toString(),
|
||||
userItemList: items.map(writeUserItem),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserMap } from "../proto/userMap";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserMapRequest } from "../request/getUserMap";
|
||||
import { GetUserMapResponse } from "../response/getUserMap";
|
||||
|
||||
export default async function getUserMap(
|
||||
rep: Repositories,
|
||||
req: GetUserMapRequest
|
||||
): Promise<GetUserMapResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep.userMap().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
userMapList: items.map(writeUserMap),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { writeUserMusicDetail, UserMusicJson } from "../proto/userMusic";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserMusicRequest } from "../request/getUserMusic";
|
||||
import { GetUserMusicResponse } from "../response/getUserMusic";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { UserMusicDetailItem } from "../model/userMusic";
|
||||
import { paginationCookie } from "./_util";
|
||||
|
||||
export default async function getUserMusic(
|
||||
rep: Repositories,
|
||||
req: GetUserMusicRequest
|
||||
): Promise<GetUserMusicResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const nextIndex = parseInt(req.nextIndex);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const flat = await rep
|
||||
.userMusic()
|
||||
.load(profileId, { limit: maxCount, offset: nextIndex });
|
||||
|
||||
const groupIdx = new Map<number, UserMusicDetailItem[]>();
|
||||
|
||||
for (const item of flat) {
|
||||
const { musicId } = item;
|
||||
const existing = groupIdx.get(musicId);
|
||||
|
||||
if (existing !== undefined) {
|
||||
existing.push(item);
|
||||
} else {
|
||||
groupIdx.set(musicId, [item]);
|
||||
}
|
||||
}
|
||||
|
||||
const userMusicList = new Array<UserMusicJson>();
|
||||
|
||||
for (const group of groupIdx.values()) {
|
||||
userMusicList.push({
|
||||
length: group.length.toString(),
|
||||
userMusicDetailList: group.map(writeUserMusicDetail),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: userMusicList.length.toString(),
|
||||
nextIndex: paginationCookie(flat, { maxCount, nextIndex }),
|
||||
userMusicList,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserGameOption } from "../proto/userGameOption";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserOptionRequest } from "../request/getUserOption";
|
||||
import { GetUserOptionResponse } from "../response/getUserOption";
|
||||
|
||||
export default async function getUserOption(
|
||||
rep: Repositories,
|
||||
req: GetUserOptionRequest
|
||||
): Promise<GetUserOptionResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userGameOption = await rep.userGameOption().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userGameOption: writeUserGameOption(userGameOption),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserGameOptionEx } from "../proto/userGameOptionEx";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserOptionExRequest } from "../request/getUserOptionEx";
|
||||
import { GetUserOptionExResponse } from "../response/getUserOptionEx";
|
||||
|
||||
export default async function getUserOptionEx(
|
||||
rep: Repositories,
|
||||
req: GetUserOptionExRequest
|
||||
): Promise<GetUserOptionExResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userGameOptionEx = await rep.userGameOptionEx().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userGameOptionEx: writeUserGameOptionEx(userGameOptionEx),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { readAimeId, writeDate } from "../proto/base";
|
||||
import { writeUserCharacter } from "../proto/userCharacter";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserPreviewRequest } from "../request/getUserPreview";
|
||||
import { GetUserPreviewResponse } from "../response/getUserPreview";
|
||||
|
||||
export default async function getUserPreview(
|
||||
rep: Repositories,
|
||||
req: GetUserPreviewRequest
|
||||
): Promise<GetUserPreviewResponse | null> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const profileId = await rep.userData().tryLookup(aimeId);
|
||||
|
||||
// Return an empty JSON object if the player lacks a profile; this will
|
||||
// start the new profile registration flow in the client.
|
||||
|
||||
if (profileId === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const userData = await rep.userData().load(profileId);
|
||||
const userCharacter = await rep
|
||||
.userCharacter()
|
||||
.loadOne(profileId, userData.characterId);
|
||||
const userGameOption = await rep.userGameOption().load(profileId);
|
||||
|
||||
return {
|
||||
userId: "1",
|
||||
|
||||
// Current login (i.e. profile lock) status
|
||||
|
||||
isLogin: "false",
|
||||
lastLoginDate: "1970-01-01 09:00:00",
|
||||
|
||||
// UserData:
|
||||
|
||||
userName: userData.userName,
|
||||
reincarnationNum: userData.reincarnationNum.toString(),
|
||||
level: userData.level.toString(),
|
||||
exp: userData.exp.toString(),
|
||||
playerRating: userData.playerRating.toString(),
|
||||
lastGameId: userData.lastGameId,
|
||||
lastRomVersion: userData.lastRomVersion,
|
||||
lastDataVersion: userData.lastDataVersion,
|
||||
lastPlayDate: writeDate(userData.lastPlayDate),
|
||||
trophyId: userData.trophyId.toString(),
|
||||
|
||||
// Currently selected UserCharacter:
|
||||
|
||||
userCharacter: writeUserCharacter(userCharacter),
|
||||
|
||||
// UserGameOption:
|
||||
|
||||
playerLevel: userGameOption.playerLevel.toString(),
|
||||
rating: userGameOption.rating.toString(),
|
||||
headphone: userGameOption.headphone.toString(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserRecentRatingRequest } from "../request/getUserRecentRating";
|
||||
import { GetUserRecentRatingResponse } from "../response/getUserRecentRating";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserRecentRating } from "../proto/userRecentRating";
|
||||
|
||||
export default async function getUserRecentRating(
|
||||
rep: Repositories,
|
||||
req: GetUserRecentRatingRequest
|
||||
): Promise<GetUserRecentRatingResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
// Return recent 30 plays to calculate rating
|
||||
const items = await rep.userRecentRating().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
userRecentRatingList: items.map(writeUserRecentRating),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserRegionRequest } from "../request/getUserRegion";
|
||||
import { GetUserRegionResponse } from "../response/getUserRegion";
|
||||
|
||||
export default async function getUserRegion(
|
||||
rep: Repositories,
|
||||
req: GetUserRegionRequest
|
||||
): Promise<GetUserRegionResponse> {
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userRegionList: [
|
||||
// Doesn't get saved
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import compression from "compression";
|
||||
import express, { NextFunction, Request, Response } from "express";
|
||||
import logger from "debug";
|
||||
|
||||
import gameLogin from "./gameLogin";
|
||||
import gameLogout from "./gameLogout";
|
||||
import getGameCharge from "./getGameCharge";
|
||||
import getGameEvent from "./getGameEvent";
|
||||
import getGameIdlist from "./getGameIdlist";
|
||||
import getGameMessage from "./getGameMessage";
|
||||
import getGameRanking from "./getGameRanking";
|
||||
import getGameSale from "./getGameSale";
|
||||
import getGameSetting from "./getGameSetting";
|
||||
import getUserActivity from "./getUserActivity";
|
||||
import getUserCharacter from "./getUserCharacter";
|
||||
import getUserCharge from "./getUserCharge";
|
||||
import getUserCourse from "./getUserCourse";
|
||||
import getUserData from "./getUserData";
|
||||
import getUserDataEx from "./getUserDataEx";
|
||||
import getUserDuel from "./getUserDuel";
|
||||
import getUserFavoriteMusic from "./getUserFavoriteMusic";
|
||||
import getUserItem from "./getUserItem";
|
||||
import getUserMap from "./getUserMap";
|
||||
import getUserMusic from "./getUserMusic";
|
||||
import getUserOption from "./getUserOption";
|
||||
import getUserOptionEx from "./getUserOptionEx";
|
||||
import getUserPreview from "./getUserPreview";
|
||||
import getUserRecentRating from "./getUserRecentRating";
|
||||
import getUserRegion from "./getUserRegion";
|
||||
import upsertClientBookkeeping from "./upsertClientBookkeeping";
|
||||
import upsertClientDevelop from "./upsertClientDevelop";
|
||||
import upsertClientError from "./upsertClientError";
|
||||
import upsertClientSetting from "./upsertClientSetting";
|
||||
import upsertClientTestmode from "./upsertClientTestmode";
|
||||
import upsertUserAll from "./upsertUserAll";
|
||||
import upsertUserChargelogApi from "./upsertUserChargelogApi";
|
||||
import createSqlWrapper from "../sql";
|
||||
import { DataSource } from "../../sql";
|
||||
|
||||
const debug = logger("app:chuni:io");
|
||||
|
||||
// Thankfully we can use standard middleware for JSON I/O. We have to use a
|
||||
// zlib middleware as well because compression is non-optional: the client will
|
||||
// attempt to inflate whatever response we send to it whether there's a
|
||||
// Transfer-Encoding header or not.
|
||||
|
||||
function quirks(req: Request, res: Response, next: NextFunction) {
|
||||
// Chunithm client does not follow the HTTP spec: user agents must indicate
|
||||
// that they are willing to accept a particular Transfer-Encoding for that
|
||||
// T-E to be used (unless the T-E in question is "chunked" but w/e), and this
|
||||
// indication is advisory anyway, the server is permitted to ignore it.
|
||||
//
|
||||
// Force this header into the request, since it is not present normally.
|
||||
|
||||
// ... unless, that is, the client is actually somebody debugging with curl
|
||||
// and not the real game client, in which case this forced deflate can be
|
||||
// annoying. Pass an "X-Debug" request header to suppress forced compression.
|
||||
|
||||
if (req.headers["x-debug"] === undefined) {
|
||||
req.headers["accept-encoding"] = "deflate";
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
function trace(req: Request, res: Response, next: NextFunction) {
|
||||
debug("\n--- Chunithm %s ---\n", req.url);
|
||||
debug("Request: %j", req.body);
|
||||
|
||||
const prevJson = res.json;
|
||||
|
||||
res.json = function(obj) {
|
||||
debug("Response: %j", obj);
|
||||
|
||||
res.json = prevJson;
|
||||
res.json.apply(this, arguments);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
export default function chunithm(db: DataSource) {
|
||||
const wrapper = createSqlWrapper(db);
|
||||
|
||||
wrapper.rpc("/GameLoginApi", gameLogin);
|
||||
wrapper.rpc("/GameLogoutApi", gameLogout);
|
||||
wrapper.rpc("/GetGameChargeApi", getGameCharge);
|
||||
wrapper.rpc("/GetGameEventApi", getGameEvent);
|
||||
wrapper.rpc("/GetGameIdlistApi", getGameIdlist);
|
||||
wrapper.rpc("/GetGameMessageApi", getGameMessage);
|
||||
wrapper.rpc("/GetGameRankingApi", getGameRanking);
|
||||
wrapper.rpc("/GetGameSaleApi", getGameSale);
|
||||
wrapper.rpc("/GetGameSettingApi", getGameSetting);
|
||||
wrapper.rpc("/GetUserActivityApi", getUserActivity);
|
||||
wrapper.rpc("/GetUserCharacterApi", getUserCharacter);
|
||||
wrapper.rpc("/GetUserChargeApi", getUserCharge);
|
||||
wrapper.rpc("/GetUserCourseAPi", getUserCourse);
|
||||
wrapper.rpc("/GetUserDataApi", getUserData);
|
||||
wrapper.rpc("/GetUserDataExApi", getUserDataEx);
|
||||
wrapper.rpc("/GetUserDuelApi", getUserDuel);
|
||||
wrapper.rpc("/GetUserFavoriteMusicApi", getUserFavoriteMusic);
|
||||
wrapper.rpc("/GetUserItemApi", getUserItem);
|
||||
wrapper.rpc("/GetUserMapApi", getUserMap);
|
||||
wrapper.rpc("/GetUserMusicApi", getUserMusic);
|
||||
wrapper.rpc("/GetUserOptionApi", getUserOption);
|
||||
wrapper.rpc("/GetUserOptionExApi", getUserOptionEx);
|
||||
wrapper.rpc("/GetUserPreviewApi", getUserPreview);
|
||||
wrapper.rpc("/GetUserRecentPlayerApi", getUserRecentRating);
|
||||
wrapper.rpc("/GetUserRecentRatingApi", getUserRecentRating);
|
||||
wrapper.rpc("/GetUserRegionApi", getUserRegion);
|
||||
wrapper.rpc("/UpsertClientBookkeepingApi", upsertClientBookkeeping);
|
||||
wrapper.rpc("/UpsertClientDevelopApi", upsertClientDevelop);
|
||||
wrapper.rpc("/UpsertClientErrorApi", upsertClientError);
|
||||
wrapper.rpc("/UpsertClientSettingApi", upsertClientSetting);
|
||||
wrapper.rpc("/UpsertClientTestmodeApi", upsertClientTestmode);
|
||||
wrapper.rpc("/UpsertUserAllApi", upsertUserAll);
|
||||
wrapper.rpc("/UpsertUserChargelogApi", upsertUserChargelogApi)
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(quirks);
|
||||
app.use(compression({ threshold: 0 }));
|
||||
app.use(express.json({ limit: "50mb" })); // that ought to be enough
|
||||
app.use(trace);
|
||||
|
||||
app.use("/ChuniServlet", wrapper);
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientBookkeepingRequest } from "../request/upsertClientBookkeeping";
|
||||
import { UpsertClientBookkeepingResponse } from "../response/upsertClientBookkeeping";
|
||||
|
||||
export default async function upsertClientBookkeeping(
|
||||
rep: Repositories,
|
||||
req: UpsertClientBookkeepingRequest
|
||||
): Promise<UpsertClientBookkeepingResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientDevelopRequest } from "../request/upsertClientDevelop";
|
||||
import { UpsertClientDevelopResponse } from "../response/upsertClientDevelop";
|
||||
|
||||
export default async function upsertClientDevelop(
|
||||
rep: Repositories,
|
||||
req: UpsertClientDevelopRequest
|
||||
): Promise<UpsertClientDevelopResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientErrorRequest } from "../request/upsertClientError";
|
||||
import { UpsertClientErrorResponse } from "../response/upsertClientError";
|
||||
|
||||
export default async function upsertClientError(
|
||||
rep: Repositories,
|
||||
req: UpsertClientErrorRequest
|
||||
): Promise<UpsertClientErrorResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientSettingRequest } from "../request/upsertClientSetting";
|
||||
import { UpsertClientSettingResponse } from "../response/upsertClientSetting";
|
||||
|
||||
export default async function upsertClientSetting(
|
||||
rep: Repositories,
|
||||
req: UpsertClientSettingRequest
|
||||
): Promise<UpsertClientSettingResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientTestmodeRequest } from "../request/upsertClientTestmode";
|
||||
import { UpsertClientTestmodeResponse } from "../response/upsertClientTestmode";
|
||||
|
||||
export default async function upsertClientTestmode(
|
||||
rep: Repositories,
|
||||
req: UpsertClientTestmodeRequest
|
||||
): Promise<UpsertClientTestmodeResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { readUserCharacter } from "../proto/userCharacter";
|
||||
import { readUserData } from "../proto/userData";
|
||||
import { readUserGameOption } from "../proto/userGameOption";
|
||||
import { UpsertUserAllRequest } from "../request/upsertUserAll";
|
||||
import { UpsertUserAllResponse } from "../response/upsertUserAll";
|
||||
import { readUserGameOptionEx } from "../proto/userGameOptionEx";
|
||||
import { readUserMap } from "../proto/userMap";
|
||||
import { readUserItem } from "../proto/userItem";
|
||||
import { readUserMusicDetail } from "../proto/userMusic";
|
||||
import { readUserActivity } from "../proto/userActivity";
|
||||
import { readUserDataEx } from "../proto/userDataEx";
|
||||
import { readUserDuelList } from "../proto/userDuelList";
|
||||
import { readUserPlaylog } from "../proto/userPlaylog";
|
||||
import { readUserCharge } from "../proto/userCharge";
|
||||
import { readUserRecentRating } from "../proto/userRecentRating";
|
||||
import { readUserCourse } from "../proto/userCourse";
|
||||
|
||||
// It shouldn't need to be said really, but seeing as this message (A) requires
|
||||
// fairly lengthy processing and (B) is only sent right at the end of a credit,
|
||||
// you should probably copy the JSON from the server's debug log and then
|
||||
// validate this implementation using curl if you're planning to make any
|
||||
// modifications to how things get saved.
|
||||
|
||||
export default async function upsertUserAll(
|
||||
rep: Repositories,
|
||||
req: UpsertUserAllRequest
|
||||
): Promise<UpsertUserAllResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const payload = req.upsertUserAll;
|
||||
const profile = readUserData(req.upsertUserAll.userData[0]);
|
||||
|
||||
const profileId = await rep.userData().save(aimeId, profile);
|
||||
|
||||
// Empty lists don't get sent at all. Not sure which are potentially empty
|
||||
// so let's just assume the answer is "all of them". Hence the use of || []
|
||||
// everywhere.
|
||||
|
||||
for (const item of payload.userGameOption || []) {
|
||||
await rep.userGameOption().save(profileId, readUserGameOption(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userGameOptionEx || []) {
|
||||
await rep.userGameOptionEx().save(profileId, readUserGameOptionEx(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userMapList || []) {
|
||||
await rep.userMap().save(profileId, readUserMap(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userCharacterList || []) {
|
||||
await rep.userCharacter().save(profileId, readUserCharacter(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userItemList || []) {
|
||||
await rep.userItem().save(profileId, readUserItem(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userMusicDetailList || []) {
|
||||
await rep.userMusic().save(profileId, readUserMusicDetail(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userActivityList || []) {
|
||||
await rep.userActivity().save(profileId, readUserActivity(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userPlaylogList || []) {
|
||||
await rep.userPlaylog().save(profileId, readUserPlaylog(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userChargeList || []) {
|
||||
await rep.userCharge().save(profileId, readUserCharge(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userCourseList || []) {
|
||||
await rep.userCourse().save(profileId, readUserCourse(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userDataEx || []) {
|
||||
await rep.userDataEx().save(profileId, readUserDataEx(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userDuelList || []) {
|
||||
await rep.userDuelList().save(profileId, readUserDuelList(item));
|
||||
}
|
||||
|
||||
await rep
|
||||
.userRecentRating()
|
||||
.save(
|
||||
profileId,
|
||||
(payload.userRecentRatingList || []).map(readUserRecentRating)
|
||||
);
|
||||
|
||||
return { returnCode: "1" };
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertUserChargelogApiRequest } from "../request/upsertUserChargelogApi";
|
||||
import { UpsertUserChargelogApiResponse } from "../response/upsertUserChargelogApi";
|
||||
|
||||
export default async function UpsertUserChargelogApi(
|
||||
rep: Repositories,
|
||||
req: UpsertUserChargelogApiRequest
|
||||
): Promise<UpsertUserChargelogApiResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
import chunithm from "./handler";
|
||||
export default chunithm;
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface GameChargeItem {
|
||||
orderId: number;
|
||||
chargeId: number;
|
||||
price: number;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
salePrice: number;
|
||||
saleStartDate: Date;
|
||||
saleEndDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface GameEventItem {
|
||||
type: number;
|
||||
id: number;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface GameMessageItem {
|
||||
type: number;
|
||||
id: number;
|
||||
message: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GameRankingItem {
|
||||
id: bigint;
|
||||
point: bigint;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface GameSaleItem {
|
||||
orderId: number;
|
||||
type: number;
|
||||
id: number;
|
||||
rate: number;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface UserActivityItem {
|
||||
kind: number;
|
||||
activityId: number; // This is just called "id" in the JSON!
|
||||
sortNumber: number;
|
||||
param1: number;
|
||||
param2: number;
|
||||
param3: number;
|
||||
param4: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface UserCharacterItem {
|
||||
characterId: number;
|
||||
playCount: number;
|
||||
level: number;
|
||||
skillId: number;
|
||||
friendshipExp: number;
|
||||
isValid: boolean;
|
||||
isNewMark: boolean;
|
||||
param1: number;
|
||||
param2: number;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface UserChargeItem {
|
||||
chargeId: number;
|
||||
stock: number;
|
||||
purchaseDate: Date;
|
||||
validDate: Date;
|
||||
param1: number;
|
||||
param2: number;
|
||||
paramDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface UserCourseItem {
|
||||
courseId: number;
|
||||
classId: number;
|
||||
playCount: number;
|
||||
scoreMax: number;
|
||||
isFullCombo: boolean;
|
||||
isAllJustice: boolean;
|
||||
isSuccess: boolean;
|
||||
scoreRank: number;
|
||||
eventId: number;
|
||||
lastPlayDate: Date;
|
||||
param1: number;
|
||||
param2: number;
|
||||
param3: number;
|
||||
param4: number;
|
||||
isClear: boolean;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
export interface UserDataItem {
|
||||
/** NFC LUID */
|
||||
accessCode: string;
|
||||
|
||||
/** Uses wide latin chars */
|
||||
userName: string;
|
||||
|
||||
isWebJoin: boolean;
|
||||
webLimitDate: Date;
|
||||
level: number;
|
||||
reincarnationNum: number;
|
||||
exp: number;
|
||||
point: number;
|
||||
totalPoint: bigint;
|
||||
playCount: number;
|
||||
multiPlayCount: number;
|
||||
multiWinCount: number;
|
||||
requestResCount: number;
|
||||
acceptResCount: number;
|
||||
successResCount: number;
|
||||
playerRating: number;
|
||||
highestRating: number;
|
||||
nameplateId: number;
|
||||
frameId: number;
|
||||
characterId: number;
|
||||
trophyId: number;
|
||||
playedTutorialBit: number;
|
||||
firstTutorialCancelNum: number;
|
||||
masterTutorialCancelNum: number;
|
||||
totalRepertoireCount: number;
|
||||
totalMapNum: number;
|
||||
totalHiScore: bigint;
|
||||
totalBasicHighScore: bigint;
|
||||
totalAdvancedHighScore: bigint;
|
||||
totalExpertHighScore: bigint;
|
||||
totalMasterHighScore: bigint;
|
||||
eventWatchedDate: Date;
|
||||
friendCount: number;
|
||||
isMaimai: boolean;
|
||||
firstGameId: string;
|
||||
firstRomVersion: string;
|
||||
firstDataVersion: string;
|
||||
firstPlayDate: Date;
|
||||
lastGameId: string;
|
||||
lastRomVersion: string;
|
||||
lastDataVersion: string;
|
||||
lastPlayDate: Date;
|
||||
|
||||
/** ALLNet place ID */
|
||||
lastPlaceId: number;
|
||||
|
||||
/** ALLNet place name */
|
||||
lastPlaceName: string;
|
||||
|
||||
/** ALLNet "region0" */
|
||||
lastRegionId: string;
|
||||
|
||||
/** ALLNet "region_name0" */
|
||||
lastRegionName: string;
|
||||
|
||||
/** ALLNet "allnet_id" string */
|
||||
lastAllNetId: string;
|
||||
|
||||
/** Keychip ID */
|
||||
lastClientId: string;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
export interface UserDataExItem {
|
||||
compatibleCmVersion: string;
|
||||
medal: number;
|
||||
mapIconId: number;
|
||||
voiceId: number;
|
||||
ext1: number;
|
||||
ext2: number;
|
||||
ext3: number;
|
||||
ext4: number;
|
||||
ext5: number;
|
||||
ext6: number;
|
||||
ext7: number;
|
||||
ext8: number;
|
||||
ext9: number;
|
||||
ext10: number;
|
||||
ext11: number;
|
||||
ext12: number;
|
||||
ext13: number;
|
||||
ext14: number;
|
||||
ext15: number;
|
||||
ext16: number;
|
||||
ext17: number;
|
||||
ext18: number;
|
||||
ext19: number;
|
||||
ext20: number;
|
||||
extStr1: string;
|
||||
extStr2: string;
|
||||
extStr3: string;
|
||||
extStr4: string;
|
||||
extStr5: string;
|
||||
extLong1: bigint;
|
||||
extLong2: bigint;
|
||||
extLong3: bigint;
|
||||
extLong4: bigint;
|
||||
extLong5: bigint;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface UserDuelListItem {
|
||||
duelId: number;
|
||||
progress: number;
|
||||
point: number;
|
||||
isClear: boolean;
|
||||
lastPlayDate: Date;
|
||||
param1: number;
|
||||
param2: number;
|
||||
param3: number;
|
||||
param4: number;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
export interface UserGameOptionItem {
|
||||
bgInfo: number;
|
||||
fieldColor: number;
|
||||
guideSound: number;
|
||||
soundEffect: number;
|
||||
guideLine: number;
|
||||
speed: number;
|
||||
optionSet: number;
|
||||
matching: number;
|
||||
judgePos: number;
|
||||
rating: number;
|
||||
judgeJustice: number;
|
||||
judgeAttack: number;
|
||||
headphone: number;
|
||||
playerLevel: number;
|
||||
successTap: number;
|
||||
successExTap: number;
|
||||
successSlideHold: number;
|
||||
successAir: number;
|
||||
successFlick: number;
|
||||
successSkill: number;
|
||||
successTapTimbre: number;
|
||||
privacy: number;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user