Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# CHANGELOG
|
||||||
|
|
||||||
|
## v012
|
||||||
|
|
||||||
|
* Chunithm: Fix large data loads (NeumPhis)
|
||||||
|
* Chunithm: Add some support for older versions (NeumPhis)
|
||||||
|
* Chunithm: Fix large data loads (Matt Bilker)
|
||||||
|
* Chunithm: Enable all events (Matt Bilker)
|
||||||
|
* Chunithm: Remove server maintenance period (Matt Bilker)
|
||||||
|
* 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
|
||||||
Generated
+28
-121
@@ -192,6 +192,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@decafcode/sqlite": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@decafcode/sqlite/-/sqlite-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-CvDlc1G2ExETrRXVjjHRZmZiDCY56pw3xU3pxWnOzkQ4j4bi+DUAYlQbFKwpb4BzRdFo84e7H+T2kmoEBf90JA==",
|
||||||
|
"requires": {
|
||||||
|
"node-gyp-build": "^4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@jest/console": {
|
"@jest/console": {
|
||||||
"version": "24.9.0",
|
"version": "24.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz",
|
||||||
@@ -401,15 +409,6 @@
|
|||||||
"@babel/types": "^7.3.0"
|
"@babel/types": "^7.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/better-sqlite3": {
|
|
||||||
"version": "5.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-5.4.0.tgz",
|
|
||||||
"integrity": "sha512-nzm7lJ7l3jBmGUbtkL8cdOMhPkN6Pw2IM+b0V7iIKba+YKiLrjkIy7vVLsBIVnd7+lgzBzrHsXZxCaFTcmw5Ow==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/integer": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/body-parser": {
|
"@types/body-parser": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz",
|
||||||
@@ -474,12 +473,6 @@
|
|||||||
"@types/range-parser": "*"
|
"@types/range-parser": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/integer": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/integer/-/integer-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-3viiRKLoSP2Qr78nMoQjkDc0fan4BgmpOyV1+1gKjE8wWXo3QQ78WItO6f9WuBf3qe3ymDYhM65oqHTOZ0rFxw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/istanbul-lib-coverage": {
|
"@types/istanbul-lib-coverage": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz",
|
||||||
@@ -666,12 +659,6 @@
|
|||||||
"normalize-path": "^2.1.1"
|
"normalize-path": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"arg": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"arr-diff": {
|
"arr-diff": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||||
@@ -895,15 +882,6 @@
|
|||||||
"tweetnacl": "^0.14.3"
|
"tweetnacl": "^0.14.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"better-sqlite3": {
|
|
||||||
"version": "5.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-5.4.3.tgz",
|
|
||||||
"integrity": "sha512-fPp+8f363qQIhuhLyjI4bu657J/FfMtgiiHKfaTsj3RWDkHlWC1yT7c6kHZDnBxzQVoAINuzg553qKmZ4F1rEw==",
|
|
||||||
"requires": {
|
|
||||||
"integer": "^2.1.0",
|
|
||||||
"tar": "^4.4.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"body-parser": {
|
"body-parser": {
|
||||||
"version": "1.19.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
@@ -1110,11 +1088,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chownr": {
|
|
||||||
"version": "1.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
|
|
||||||
"integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw=="
|
|
||||||
},
|
|
||||||
"ci-info": {
|
"ci-info": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
|
||||||
@@ -1196,9 +1169,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.20.0",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
@@ -1481,12 +1454,6 @@
|
|||||||
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
|
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"diff": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
|
|
||||||
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"diff-sequences": {
|
"diff-sequences": {
|
||||||
"version": "24.9.0",
|
"version": "24.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
|
||||||
@@ -1995,14 +1962,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
||||||
},
|
},
|
||||||
"fs-minipass": {
|
|
||||||
"version": "1.2.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
|
||||||
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
|
|
||||||
"requires": {
|
|
||||||
"minipass": "^2.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
@@ -2626,9 +2585,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"handlebars": {
|
"handlebars": {
|
||||||
"version": "4.4.3",
|
"version": "4.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
|
||||||
"integrity": "sha512-B0W4A2U1ww3q7VVthTKfh+epHx+q4mCt6iK+zEAzbMBpWQAwxCeKxEGpj/1oQTpzPXDNSOG7hmG14TsISH50yw==",
|
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
@@ -2782,11 +2741,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||||
},
|
},
|
||||||
"integer": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/integer/-/integer-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-vBtiSgrEiNocWvvZX1RVfeOKa2mCHLZQ2p9nkQkQZ/BvEiY+6CcUz0eyjvIiewjJoeNidzg2I+tpPJvpyspL1w=="
|
|
||||||
},
|
|
||||||
"invariant": {
|
"invariant": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||||
@@ -3830,24 +3784,8 @@
|
|||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
},
|
"dev": true
|
||||||
"minipass": {
|
|
||||||
"version": "2.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
|
|
||||||
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "^5.1.2",
|
|
||||||
"yallist": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minizlib": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
|
|
||||||
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
|
|
||||||
"requires": {
|
|
||||||
"minipass": "^2.9.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"mixin-deep": {
|
"mixin-deep": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
@@ -3874,6 +3812,7 @@
|
|||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
@@ -3980,6 +3919,11 @@
|
|||||||
"lower-case": "^1.1.1"
|
"lower-case": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-gyp-build": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-4oiumOLhCDU9Rronz8PZ5S4IvT39H5+JEv/hps9V8s7RSLhsac0TCP78ulnHXOo8X1wdpPiTayGlM1jr4IbnaQ=="
|
||||||
|
},
|
||||||
"node-int64": {
|
"node-int64": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
|
||||||
@@ -4646,7 +4590,8 @@
|
|||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
||||||
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
|
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"safe-regex": {
|
"safe-regex": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
@@ -5203,20 +5148,6 @@
|
|||||||
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tar": {
|
|
||||||
"version": "4.4.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
|
|
||||||
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
|
|
||||||
"requires": {
|
|
||||||
"chownr": "^1.1.1",
|
|
||||||
"fs-minipass": "^1.2.5",
|
|
||||||
"minipass": "^2.8.6",
|
|
||||||
"minizlib": "^1.2.1",
|
|
||||||
"mkdirp": "^0.5.0",
|
|
||||||
"safe-buffer": "^5.1.2",
|
|
||||||
"yallist": "^3.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test-exclude": {
|
"test-exclude": {
|
||||||
"version": "5.2.3",
|
"version": "5.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz",
|
||||||
@@ -5348,19 +5279,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ts-node": {
|
|
||||||
"version": "8.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.4.1.tgz",
|
|
||||||
"integrity": "sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"arg": "^4.1.0",
|
|
||||||
"diff": "^4.0.1",
|
|
||||||
"make-error": "^1.1.1",
|
|
||||||
"source-map-support": "^0.5.6",
|
|
||||||
"yn": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tunnel-agent": {
|
"tunnel-agent": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
@@ -5401,13 +5319,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"uglify-js": {
|
"uglify-js": {
|
||||||
"version": "3.6.1",
|
"version": "3.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz",
|
||||||
"integrity": "sha512-+dSJLJpXBb6oMHP+Yvw8hUgElz4gLTh82XuX68QiJVTXaE5ibl6buzhNkQdYhBlIhozWOC9ge16wyRmjG4TwVQ==",
|
"integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"commander": "2.20.0",
|
"commander": "~2.20.3",
|
||||||
"source-map": "~0.6.1"
|
"source-map": "~0.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5675,11 +5593,6 @@
|
|||||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
|
||||||
},
|
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "13.3.0",
|
"version": "13.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
|
||||||
@@ -5707,12 +5620,6 @@
|
|||||||
"camelcase": "^5.0.0",
|
"camelcase": "^5.0.0",
|
||||||
"decamelize": "^1.2.0"
|
"decamelize": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"yn": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -5,7 +5,7 @@
|
|||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "^5.4.3",
|
"@decafcode/sqlite": "^2.0.0",
|
||||||
"compression": "^1.7.3",
|
"compression": "^1.7.3",
|
||||||
"date-fns": "^1.30.1",
|
"date-fns": "^1.30.1",
|
||||||
"debug": "^4.1.1",
|
"debug": "^4.1.1",
|
||||||
@@ -20,7 +20,6 @@
|
|||||||
"supports-color": "^7.1.0"
|
"supports-color": "^7.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/better-sqlite3": "^5.4.0",
|
|
||||||
"@types/compression": "^0.0.36",
|
"@types/compression": "^0.0.36",
|
||||||
"@types/debug": "^4.1.5",
|
"@types/debug": "^4.1.5",
|
||||||
"@types/dotenv": "^6.1.1",
|
"@types/dotenv": "^6.1.1",
|
||||||
@@ -33,7 +32,6 @@
|
|||||||
"jest-resolve": "^24.5.0",
|
"jest-resolve": "^24.5.0",
|
||||||
"prettier": "^1.16.4",
|
"prettier": "^1.16.4",
|
||||||
"ts-jest": "^24.0.0",
|
"ts-jest": "^24.0.0",
|
||||||
"ts-node": "^8.0.3",
|
|
||||||
"typescript": "^3.6.4",
|
"typescript": "^3.6.4",
|
||||||
"utility-types": "^3.6.1"
|
"utility-types": "^3.6.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -82,6 +82,44 @@ create table "cm_user_activity" (
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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" (
|
create table "cm_user_data_ex" (
|
||||||
"id" integer primary key not null
|
"id" integer primary key not null
|
||||||
references "cm_user_data"("id")
|
references "cm_user_data"("id")
|
||||||
@@ -122,6 +160,23 @@ create table "cm_user_data_ex" (
|
|||||||
"ext_long5" 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" (
|
create table "cm_user_character" (
|
||||||
"id" integer primary key not null,
|
"id" integer primary key not null,
|
||||||
"profile_id" integer not null
|
"profile_id" integer not null
|
||||||
@@ -302,3 +357,16 @@ create table "cm_user_playlog" (
|
|||||||
"place_name" text not null,
|
"place_name" text not null,
|
||||||
"is_maimai" 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")
|
||||||
|
);
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ create table "idz_settings" (
|
|||||||
on delete cascade,
|
on delete cascade,
|
||||||
"music" integer not null,
|
"music" integer not null,
|
||||||
"pack" integer not null,
|
"pack" integer not null,
|
||||||
|
"aura" integer not null,
|
||||||
"paper_cup" integer not null, -- Not a boolean, oddly enough
|
"paper_cup" integer not null, -- Not a boolean, oddly enough
|
||||||
"gauges" integer not null
|
"gauges" integer not null
|
||||||
);
|
);
|
||||||
@@ -192,6 +193,7 @@ create table "idz_unlocks" (
|
|||||||
"id" integer primary key not null
|
"id" integer primary key not null
|
||||||
references "idz_profile"("id")
|
references "idz_profile"("id")
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
|
"auras" integer not null,
|
||||||
"cup" integer not null,
|
"cup" integer not null,
|
||||||
"gauges" integer not null,
|
"gauges" integer not null,
|
||||||
"music" integer not null,
|
"music" integer 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;
|
||||||
@@ -105,7 +105,7 @@ test("decode lookup", () => {
|
|||||||
|
|
||||||
const obj = decode(req);
|
const obj = decode(req);
|
||||||
|
|
||||||
expect(obj.type).toBe("lookup");
|
expect(obj.type).toBe("lookup2");
|
||||||
expect(obj.gameId).toBe("SDBT");
|
expect(obj.gameId).toBe("SDBT");
|
||||||
expect(obj.keychipId).toBe("A69E01A9999");
|
expect(obj.keychipId).toBe("A69E01A9999");
|
||||||
expect(obj.luid).toBe("01036495850523030676");
|
expect(obj.luid).toBe("01036495850523030676");
|
||||||
|
|||||||
@@ -31,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 {
|
function readLogRequest(msg: Buffer): Request.LogRequest {
|
||||||
// idk what any of this stuff means yet
|
// idk what any of this stuff means yet
|
||||||
// field20 and field28 appear to be an aime id but that is all.
|
// field20 and field28 appear to be an aime id but that is all.
|
||||||
@@ -98,6 +107,7 @@ readers.set(0x0009, readLogRequest);
|
|||||||
readers.set(0x000b, readCampaignRequest);
|
readers.set(0x000b, readCampaignRequest);
|
||||||
readers.set(0x000d, readRegisterRequest);
|
readers.set(0x000d, readRegisterRequest);
|
||||||
readers.set(0x000f, readLookupRequest2);
|
readers.set(0x000f, readLookupRequest2);
|
||||||
|
readers.set(0x0011, readFeliCaLookupRequest2);
|
||||||
readers.set(0x0064, readHelloRequest);
|
readers.set(0x0064, readHelloRequest);
|
||||||
readers.set(0x0066, readGoodbyeRequest);
|
readers.set(0x0066, readGoodbyeRequest);
|
||||||
|
|
||||||
|
|||||||
+13
-1
@@ -14,7 +14,7 @@ registerLevels.set("segaid", 2);
|
|||||||
function begin(length: number) {
|
function begin(length: number) {
|
||||||
const buf = Buffer.alloc(length);
|
const buf = Buffer.alloc(length);
|
||||||
|
|
||||||
buf.writeUInt16LE(0xa13e, 0x0000); // Magic?
|
buf.writeUInt16LE(0xa13e, 0x0000); // Magic: aime
|
||||||
buf.writeUInt16LE(0x3087, 0x0002); // ???
|
buf.writeUInt16LE(0x3087, 0x0002); // ???
|
||||||
buf.writeUInt16LE(length, 0x0006);
|
buf.writeUInt16LE(length, 0x0006);
|
||||||
|
|
||||||
@@ -43,6 +43,18 @@ export class Encoder extends Transform {
|
|||||||
|
|
||||||
break;
|
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":
|
case "hello":
|
||||||
buf = begin(0x0020);
|
buf = begin(0x0020);
|
||||||
buf.writeUInt16LE(0x0065, 0x0004); // cmd code
|
buf.writeUInt16LE(0x0065, 0x0004); // cmd code
|
||||||
|
|||||||
@@ -46,6 +46,28 @@ function feliCaLookup(
|
|||||||
return { type: req.type, status: 1, accessCode };
|
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(
|
async function lookup(
|
||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: Req.LookupRequest,
|
req: Req.LookupRequest,
|
||||||
@@ -115,6 +137,9 @@ export async function dispatch(
|
|||||||
case "felica_lookup":
|
case "felica_lookup":
|
||||||
return feliCaLookup(rep, req, now);
|
return feliCaLookup(rep, req, now);
|
||||||
|
|
||||||
|
case "felica_lookup2":
|
||||||
|
return feliCaLookup2(rep, req, now);
|
||||||
|
|
||||||
case "lookup":
|
case "lookup":
|
||||||
return lookup(rep, req, now);
|
return lookup(rep, req, now);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ export interface FeliCaLookupRequest extends AimeRequestBase {
|
|||||||
pmm: string;
|
pmm: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FeliCaLookup2Request extends AimeRequestBase {
|
||||||
|
type: "felica_lookup2";
|
||||||
|
idm: string;
|
||||||
|
pmm: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface RegisterRequest extends AimeRequestBase {
|
export interface RegisterRequest extends AimeRequestBase {
|
||||||
type: "register";
|
type: "register";
|
||||||
luid: string;
|
luid: string;
|
||||||
@@ -50,6 +56,7 @@ export interface GoodbyeRequest {
|
|||||||
|
|
||||||
export type AimeRequest =
|
export type AimeRequest =
|
||||||
| FeliCaLookupRequest
|
| FeliCaLookupRequest
|
||||||
|
| FeliCaLookup2Request
|
||||||
| CampaignRequest
|
| CampaignRequest
|
||||||
| GoodbyeRequest
|
| GoodbyeRequest
|
||||||
| HelloRequest
|
| HelloRequest
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ export interface FeliCaLookupResponse extends AimeResponseBase {
|
|||||||
accessCode: string;
|
accessCode: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FeliCaLookup2Response extends AimeResponseBase {
|
||||||
|
type: "felica_lookup2";
|
||||||
|
accessCode: string;
|
||||||
|
aimeId?: AimeId;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CampaignResponse extends AimeResponseBase {
|
export interface CampaignResponse extends AimeResponseBase {
|
||||||
type: "campaign";
|
type: "campaign";
|
||||||
}
|
}
|
||||||
@@ -44,6 +50,7 @@ export interface RegisterResponse extends AimeResponseBase {
|
|||||||
|
|
||||||
export type AimeResponse =
|
export type AimeResponse =
|
||||||
| FeliCaLookupResponse
|
| FeliCaLookupResponse
|
||||||
|
| FeliCaLookup2Response
|
||||||
| CampaignResponse
|
| CampaignResponse
|
||||||
| HelloResponse
|
| HelloResponse
|
||||||
| LogResponse
|
| LogResponse
|
||||||
|
|||||||
+2
-2
@@ -19,8 +19,8 @@ class CardRepositoryImpl implements CardRepository {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = row.id;
|
const id = row.id!;
|
||||||
const extId = row.ext_id;
|
const extId = row.ext_id!;
|
||||||
|
|
||||||
const touchSql = sql
|
const touchSql = sql
|
||||||
.update("aime_player")
|
.update("aime_player")
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ export default async function checkdb(db: DataSource): Promise<void> {
|
|||||||
const row = await db.transaction(txn => txn.fetchRow(stmt));
|
const row = await db.transaction(txn => txn.fetchRow(stmt));
|
||||||
|
|
||||||
if (row !== undefined) {
|
if (row !== undefined) {
|
||||||
maybe = parseInt(row.schemaver);
|
maybe = parseInt(row.schemaver!);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return db.transaction(initdb);
|
return db.transaction(initdb);
|
||||||
|
|||||||
@@ -1,13 +1,30 @@
|
|||||||
|
import { GameChargeJson } from "../proto/gameCharge";
|
||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { GetGameChargeRequest } from "../request/getGameCharge";
|
import { GetGameChargeRequest } from "../request/getGameCharge";
|
||||||
import { GetGameChargeResponse } from "../response/getGameCharge";
|
import { GetGameChargeResponse } from "../response/getGameCharge";
|
||||||
|
import { CHARGE_IDS } from "../static/charge";
|
||||||
|
|
||||||
export default async function getGameCharge(
|
export default async function getGameCharge(
|
||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: GetGameChargeRequest
|
req: GetGameChargeRequest
|
||||||
): Promise<GetGameChargeResponse> {
|
): 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 {
|
return {
|
||||||
length: "0",
|
length: gameChargeList.length.toString(),
|
||||||
gameChargeList: [],
|
gameChargeList,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,27 @@
|
|||||||
|
import { GameEventJson } from "../proto/gameEvent";
|
||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { GetGameEventRequest } from "../request/getGameEvent";
|
import { GetGameEventRequest } from "../request/getGameEvent";
|
||||||
import { GetGameEventResponse } from "../response/getGameEvent";
|
import { GetGameEventResponse } from "../response/getGameEvent";
|
||||||
|
import { EVENT_IDS } from "../static/event";
|
||||||
|
|
||||||
export default async function getGameEvent(
|
export default async function getGameEvent(
|
||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: GetGameEventRequest
|
req: GetGameEventRequest
|
||||||
): Promise<GetGameEventResponse> {
|
): 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 {
|
return {
|
||||||
type: req.type,
|
type: req.type,
|
||||||
length: "0",
|
length: gameEventList.length.toString(),
|
||||||
gameEventList: [],
|
gameEventList,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { writeDate } from "../proto/base";
|
||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { GetGameSettingRequest } from "../request/getGameSetting";
|
import { GetGameSettingRequest } from "../request/getGameSetting";
|
||||||
import { GetGameSettingResponse } from "../response/getGameSetting";
|
import { GetGameSettingResponse } from "../response/getGameSetting";
|
||||||
@@ -6,19 +7,25 @@ export default async function getGameSetting(
|
|||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: GetGameSettingRequest
|
req: GetGameSettingRequest
|
||||||
): Promise<GetGameSettingResponse> {
|
): Promise<GetGameSettingResponse> {
|
||||||
|
const rebootStartTime = new Date();
|
||||||
|
rebootStartTime.setHours(rebootStartTime.getHours() - 3);
|
||||||
|
|
||||||
|
const rebootEndTime = new Date();
|
||||||
|
rebootEndTime.setHours(rebootEndTime.getHours() - 2);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
gameSetting: {
|
gameSetting: {
|
||||||
dataVersion: "1",
|
dataVersion: "1",
|
||||||
isMaintenance: "false",
|
isMaintenance: "false",
|
||||||
requestInterval: "10",
|
requestInterval: "10",
|
||||||
rebootStartTime: "0",
|
rebootStartTime: writeDate(rebootStartTime),
|
||||||
rebootEndTime: "0",
|
rebootEndTime: writeDate(rebootEndTime),
|
||||||
isBackgroundDistribute: "false",
|
isBackgroundDistribute: "false",
|
||||||
maxCountCharacter: "999",
|
maxCountCharacter: "300",
|
||||||
maxCountItem: "999",
|
maxCountItem: "300",
|
||||||
maxCountMusic: "999",
|
maxCountMusic: "100",
|
||||||
},
|
},
|
||||||
isDumpUpload: "false",
|
isDumpUpload: "false",
|
||||||
isAou: "false",
|
isAou: "true",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { readAimeId } from "../proto/base";
|
||||||
|
import { writeUserCharge } from "../proto/userCharge";
|
||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { GetUserChargeRequest } from "../request/getUserCharge";
|
import { GetUserChargeRequest } from "../request/getUserCharge";
|
||||||
import { GetUserChargeResponse } from "../response/getUserCharge";
|
import { GetUserChargeResponse } from "../response/getUserCharge";
|
||||||
@@ -6,9 +8,14 @@ export default async function getUserCharge(
|
|||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: GetUserChargeRequest
|
req: GetUserChargeRequest
|
||||||
): Promise<GetUserChargeResponse> {
|
): Promise<GetUserChargeResponse> {
|
||||||
|
const aimeId = readAimeId(req.userId);
|
||||||
|
|
||||||
|
const profileId = await rep.userData().lookup(aimeId);
|
||||||
|
const items = await rep.userCharge().load(profileId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userId: req.userId,
|
userId: req.userId,
|
||||||
length: "0",
|
length: items.length.toString(),
|
||||||
userChargeList: [],
|
userChargeList: items.map(writeUserCharge),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { GetUserCourseRequest } from "../request/getUserCourse";
|
import { GetUserCourseRequest } from "../request/getUserCourse";
|
||||||
import { GetUserCourseResponse } from "../response/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(
|
export default async function getUserCourse(
|
||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: GetUserCourseRequest
|
req: GetUserCourseRequest
|
||||||
): Promise<GetUserCourseResponse> {
|
): 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 {
|
return {
|
||||||
userId: req.userId,
|
userId: req.userId,
|
||||||
length: "0",
|
length: items.length.toString(),
|
||||||
nextIndex: "-1",
|
nextIndex: paginationCookie(items, { maxCount, nextIndex }),
|
||||||
userCourseList: [
|
userCourseList: items.map(writeUserCourse)
|
||||||
// This will get saved at some point I think
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,23 @@
|
|||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { GetUserDuelRequest } from "../request/getUserDuel";
|
import { GetUserDuelRequest } from "../request/getUserDuel";
|
||||||
import { GetUserDuelResponse } from "../response/getUserDuel";
|
import { GetUserDuelResponse } from "../response/getUserDuel";
|
||||||
|
import { readAimeId } from "../proto/base";
|
||||||
|
import { writeUserDuelList } from "../proto/userDuelList";
|
||||||
|
|
||||||
export default async function getUserDuel(
|
export default async function getUserDuel(
|
||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: GetUserDuelRequest
|
req: GetUserDuelRequest
|
||||||
): Promise<GetUserDuelResponse> {
|
): 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 {
|
return {
|
||||||
userId: req.userId,
|
userId: req.userId,
|
||||||
length: "0",
|
length: items.length.toString(),
|
||||||
userDuelList: [],
|
userDuelList: items.map(writeUserDuelList),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default async function getUserItem(
|
|||||||
|
|
||||||
// Pack the next pagination cookie into bigint compound form
|
// Pack the next pagination cookie into bigint compound form
|
||||||
|
|
||||||
const xout = itemKindN * itemKindMul + BigInt(maxCount + items.length);
|
const xout = itemKindN * itemKindMul + nextIndexN + BigInt(items.length);
|
||||||
|
|
||||||
// Done:
|
// Done:
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { GetUserRecentRatingRequest } from "../request/getUserRecentRating";
|
import { GetUserRecentRatingRequest } from "../request/getUserRecentRating";
|
||||||
import { GetUserRecentRatingResponse } from "../response/getUserRecentRating";
|
import { GetUserRecentRatingResponse } from "../response/getUserRecentRating";
|
||||||
|
import { readAimeId } from "../proto/base";
|
||||||
|
import { writeUserRecentRating } from "../proto/userRecentRating";
|
||||||
|
|
||||||
export default async function getUserRecentRating(
|
export default async function getUserRecentRating(
|
||||||
rep: Repositories,
|
rep: Repositories,
|
||||||
req: GetUserRecentRatingRequest
|
req: GetUserRecentRatingRequest
|
||||||
): Promise<GetUserRecentRatingResponse> {
|
): Promise<GetUserRecentRatingResponse> {
|
||||||
// I don't really know what subset of data to return here.
|
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 {
|
return {
|
||||||
userId: req.userId,
|
userId: req.userId,
|
||||||
length: "0",
|
length: items.length.toString(),
|
||||||
userRecentRatingList: [],
|
userRecentRatingList: items.map(writeUserRecentRating),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import upsertClientError from "./upsertClientError";
|
|||||||
import upsertClientSetting from "./upsertClientSetting";
|
import upsertClientSetting from "./upsertClientSetting";
|
||||||
import upsertClientTestmode from "./upsertClientTestmode";
|
import upsertClientTestmode from "./upsertClientTestmode";
|
||||||
import upsertUserAll from "./upsertUserAll";
|
import upsertUserAll from "./upsertUserAll";
|
||||||
|
import upsertUserChargelogApi from "./upsertUserChargelogApi";
|
||||||
import createSqlWrapper from "../sql";
|
import createSqlWrapper from "../sql";
|
||||||
import { DataSource } from "../../sql";
|
import { DataSource } from "../../sql";
|
||||||
|
|
||||||
@@ -104,6 +105,7 @@ export default function chunithm(db: DataSource) {
|
|||||||
wrapper.rpc("/GetUserOptionApi", getUserOption);
|
wrapper.rpc("/GetUserOptionApi", getUserOption);
|
||||||
wrapper.rpc("/GetUserOptionExApi", getUserOptionEx);
|
wrapper.rpc("/GetUserOptionExApi", getUserOptionEx);
|
||||||
wrapper.rpc("/GetUserPreviewApi", getUserPreview);
|
wrapper.rpc("/GetUserPreviewApi", getUserPreview);
|
||||||
|
wrapper.rpc("/GetUserRecentPlayerApi", getUserRecentRating);
|
||||||
wrapper.rpc("/GetUserRecentRatingApi", getUserRecentRating);
|
wrapper.rpc("/GetUserRecentRatingApi", getUserRecentRating);
|
||||||
wrapper.rpc("/GetUserRegionApi", getUserRegion);
|
wrapper.rpc("/GetUserRegionApi", getUserRegion);
|
||||||
wrapper.rpc("/UpsertClientBookkeepingApi", upsertClientBookkeeping);
|
wrapper.rpc("/UpsertClientBookkeepingApi", upsertClientBookkeeping);
|
||||||
@@ -112,12 +114,13 @@ export default function chunithm(db: DataSource) {
|
|||||||
wrapper.rpc("/UpsertClientSettingApi", upsertClientSetting);
|
wrapper.rpc("/UpsertClientSettingApi", upsertClientSetting);
|
||||||
wrapper.rpc("/UpsertClientTestmodeApi", upsertClientTestmode);
|
wrapper.rpc("/UpsertClientTestmodeApi", upsertClientTestmode);
|
||||||
wrapper.rpc("/UpsertUserAllApi", upsertUserAll);
|
wrapper.rpc("/UpsertUserAllApi", upsertUserAll);
|
||||||
|
wrapper.rpc("/UpsertUserChargelogApi", upsertUserChargelogApi)
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use(quirks);
|
app.use(quirks);
|
||||||
app.use(compression({ threshold: 0 }));
|
app.use(compression({ threshold: 0 }));
|
||||||
app.use(express.json());
|
app.use(express.json({ limit: "50mb" })); // that ought to be enough
|
||||||
app.use(trace);
|
app.use(trace);
|
||||||
|
|
||||||
app.use("/ChuniServlet", wrapper);
|
app.use("/ChuniServlet", wrapper);
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import { readUserItem } from "../proto/userItem";
|
|||||||
import { readUserMusicDetail } from "../proto/userMusic";
|
import { readUserMusicDetail } from "../proto/userMusic";
|
||||||
import { readUserActivity } from "../proto/userActivity";
|
import { readUserActivity } from "../proto/userActivity";
|
||||||
import { readUserDataEx } from "../proto/userDataEx";
|
import { readUserDataEx } from "../proto/userDataEx";
|
||||||
|
import { readUserDuelList } from "../proto/userDuelList";
|
||||||
import { readUserPlaylog } from "../proto/userPlaylog";
|
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
|
// 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,
|
// fairly lengthy processing and (B) is only sent right at the end of a credit,
|
||||||
@@ -65,9 +69,28 @@ export default async function upsertUserAll(
|
|||||||
await rep.userPlaylog().save(profileId, readUserPlaylog(item));
|
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 || []) {
|
for (const item of payload.userDataEx || []) {
|
||||||
await rep.userDataEx().save(profileId, readUserDataEx(item));
|
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" };
|
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,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,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,20 @@
|
|||||||
|
import { Crush, readDate, writeObject } from "./base";
|
||||||
|
import { UserChargeItem } from "../model/userCharge";
|
||||||
|
|
||||||
|
export type UserChargeJson = Crush<UserChargeItem>;
|
||||||
|
|
||||||
|
export function readUserCharge(json: UserChargeJson): UserChargeItem {
|
||||||
|
return {
|
||||||
|
chargeId: parseInt(json.chargeId),
|
||||||
|
stock: parseInt(json.stock),
|
||||||
|
purchaseDate: readDate(json.purchaseDate),
|
||||||
|
validDate: readDate(json.validDate),
|
||||||
|
param1: parseInt(json.param1),
|
||||||
|
param2: parseInt(json.param2),
|
||||||
|
paramDate: readDate(json.paramDate),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeUserCharge(obj: UserChargeItem): UserChargeJson {
|
||||||
|
return writeObject(obj);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { UserCourseItem } from "../model/userCourse";
|
||||||
|
import { Crush, readBoolean, readDate, writeObject } from "./base";
|
||||||
|
|
||||||
|
export type UserCourseJson = Crush<UserCourseItem>;
|
||||||
|
|
||||||
|
export function readUserCourse(json: UserCourseJson): UserCourseItem {
|
||||||
|
return {
|
||||||
|
classId: parseInt(json.classId),
|
||||||
|
courseId: parseInt(json.courseId),
|
||||||
|
eventId: parseInt(json.eventId),
|
||||||
|
isAllJustice: readBoolean(json.isAllJustice),
|
||||||
|
isClear: readBoolean(json.isClear),
|
||||||
|
isFullCombo: readBoolean(json.isFullCombo),
|
||||||
|
isSuccess: readBoolean(json.isSuccess),
|
||||||
|
lastPlayDate: readDate(json.lastPlayDate),
|
||||||
|
param1: parseInt(json.param1),
|
||||||
|
param2: parseInt(json.param2),
|
||||||
|
param3: parseInt(json.param3),
|
||||||
|
param4: parseInt(json.param4),
|
||||||
|
playCount: parseInt(json.playCount),
|
||||||
|
scoreMax: parseInt(json.scoreMax),
|
||||||
|
scoreRank: parseInt(json.scoreRank)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeUserCourse(obj: UserCourseItem): UserCourseJson {
|
||||||
|
return writeObject(obj);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { Crush, readBoolean, readDate, writeObject } from "./base";
|
||||||
|
import { UserDuelListItem } from "../model/userDuelList";
|
||||||
|
|
||||||
|
export type UserDuelListJson = Crush<UserDuelListItem>;
|
||||||
|
|
||||||
|
export function readUserDuelList(
|
||||||
|
json: UserDuelListJson
|
||||||
|
): UserDuelListItem {
|
||||||
|
return {
|
||||||
|
duelId: parseInt(json.duelId),
|
||||||
|
progress: parseInt(json.progress),
|
||||||
|
point: parseInt(json.point),
|
||||||
|
isClear: readBoolean(json.isClear),
|
||||||
|
lastPlayDate: readDate(json.lastPlayDate),
|
||||||
|
param1: parseInt(json.param1),
|
||||||
|
param2: parseInt(json.param2),
|
||||||
|
param3: parseInt(json.param3),
|
||||||
|
param4: parseInt(json.param4),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeUserDuelList(
|
||||||
|
obj: UserDuelListItem
|
||||||
|
): UserDuelListJson {
|
||||||
|
return writeObject(obj);
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { UserPlaylogItem } from "./../model/userPlaylog";
|
||||||
import { Crush, writeObject } from "./base";
|
import { Crush, writeObject } from "./base";
|
||||||
import { UserRecentRatingItem } from "../model/userRecentRating";
|
import { UserRecentRatingItem } from "../model/userRecentRating";
|
||||||
|
|
||||||
|
|||||||
@@ -2,24 +2,32 @@ export { Page } from "./_defs";
|
|||||||
|
|
||||||
import { UserActivityRepository } from "./userActivity";
|
import { UserActivityRepository } from "./userActivity";
|
||||||
import { UserCharacterRepository } from "./userCharacter";
|
import { UserCharacterRepository } from "./userCharacter";
|
||||||
|
import { UserCourseRepository } from "./userCourse";
|
||||||
import { UserDataRepository } from "./userData";
|
import { UserDataRepository } from "./userData";
|
||||||
import { UserDataExRepository } from "./userDataEx";
|
import { UserDataExRepository } from "./userDataEx";
|
||||||
|
import { UserDuelListRepository } from "./userDuelList";
|
||||||
import { UserGameOptionRepository } from "./userGameOption";
|
import { UserGameOptionRepository } from "./userGameOption";
|
||||||
import { UserGameOptionExRepository } from "./userGameOptionEx";
|
import { UserGameOptionExRepository } from "./userGameOptionEx";
|
||||||
import { UserItemRepository } from "./userItem";
|
import { UserItemRepository } from "./userItem";
|
||||||
import { UserMapRepository } from "./userMap";
|
import { UserMapRepository } from "./userMap";
|
||||||
import { UserMusicRepository } from "./userMusic";
|
import { UserMusicRepository } from "./userMusic";
|
||||||
import { UserPlaylogRepository } from "./userPlaylog";
|
import { UserPlaylogRepository } from "./userPlaylog";
|
||||||
|
import { UserChargeRepository } from "./userCharge";
|
||||||
|
import { UserRecentRatingRepository } from "./userRecentRating";
|
||||||
|
|
||||||
export interface Repositories {
|
export interface Repositories {
|
||||||
userActivity(): UserActivityRepository;
|
userActivity(): UserActivityRepository;
|
||||||
|
|
||||||
userCharacter(): UserCharacterRepository;
|
userCharacter(): UserCharacterRepository;
|
||||||
|
|
||||||
|
userCourse(): UserCourseRepository;
|
||||||
|
|
||||||
userData(): UserDataRepository;
|
userData(): UserDataRepository;
|
||||||
|
|
||||||
userDataEx(): UserDataExRepository;
|
userDataEx(): UserDataExRepository;
|
||||||
|
|
||||||
|
userDuelList(): UserDuelListRepository;
|
||||||
|
|
||||||
userGameOption(): UserGameOptionRepository;
|
userGameOption(): UserGameOptionRepository;
|
||||||
|
|
||||||
userGameOptionEx(): UserGameOptionExRepository;
|
userGameOptionEx(): UserGameOptionExRepository;
|
||||||
@@ -31,4 +39,10 @@ export interface Repositories {
|
|||||||
userMusic(): UserMusicRepository;
|
userMusic(): UserMusicRepository;
|
||||||
|
|
||||||
userPlaylog(): UserPlaylogRepository;
|
userPlaylog(): UserPlaylogRepository;
|
||||||
|
|
||||||
|
userCharge(): UserChargeRepository;
|
||||||
|
|
||||||
|
userCourse(): UserCourseRepository;
|
||||||
|
|
||||||
|
userRecentRating(): UserRecentRatingRepository;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { UserChargeItem } from "../model/userCharge";
|
||||||
|
import { RepositoryN } from "./_defs";
|
||||||
|
|
||||||
|
export type UserChargeRepository = RepositoryN<UserChargeItem>;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { UserCourseItem } from "../model/userCourse";
|
||||||
|
import { RepositoryN } from "./_defs";
|
||||||
|
|
||||||
|
export type UserCourseRepository = RepositoryN<UserCourseItem>;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Page } from "./_defs";
|
||||||
|
import { UserDataItem } from "../model/userData";
|
||||||
|
import { UserDuelListItem } from "../model/userDuelList";
|
||||||
|
import { Id } from "../../model";
|
||||||
|
|
||||||
|
export interface UserDuelListRepository {
|
||||||
|
load(
|
||||||
|
profileId: Id<UserDataItem>,
|
||||||
|
duelId?: number,
|
||||||
|
): Promise<UserDuelListItem[]>;
|
||||||
|
|
||||||
|
save(profileId: Id<UserDataItem>, obj: UserDuelListItem): Promise<void>;
|
||||||
|
}
|
||||||
@@ -7,4 +7,5 @@ export interface UserPlaylogRepository {
|
|||||||
// is nice to have.
|
// is nice to have.
|
||||||
|
|
||||||
save(profileId: Id<UserDataItem>, obj: UserPlaylogItem): Promise<void>;
|
save(profileId: Id<UserDataItem>, obj: UserPlaylogItem): Promise<void>;
|
||||||
|
loadLatest(profileId: Id<UserDataItem>, size: number): Promise<UserPlaylogItem[]>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { UserDataItem } from "../model/userData";
|
||||||
|
import { UserRecentRatingItem } from "../model/userRecentRating";
|
||||||
|
import { Id } from "../../model";
|
||||||
|
|
||||||
|
export interface UserRecentRatingRepository {
|
||||||
|
load(profileId: Id<UserDataItem>): Promise<UserRecentRatingItem[]>;
|
||||||
|
|
||||||
|
save(
|
||||||
|
profileId: Id<UserDataItem>,
|
||||||
|
objs: UserRecentRatingItem[]
|
||||||
|
): Promise<void>;
|
||||||
|
}
|
||||||
@@ -8,7 +8,10 @@ import { UserMusicDetailJson } from "../proto/userMusic";
|
|||||||
import { UserActivityJson } from "../proto/userActivity";
|
import { UserActivityJson } from "../proto/userActivity";
|
||||||
import { UserRecentRatingJson } from "../proto/userRecentRating";
|
import { UserRecentRatingJson } from "../proto/userRecentRating";
|
||||||
import { UserPlaylogJson } from "../proto/userPlaylog";
|
import { UserPlaylogJson } from "../proto/userPlaylog";
|
||||||
|
import { UserChargeJson } from "../proto/userCharge";
|
||||||
|
import { UserCourseJson } from "../proto/userCourse";
|
||||||
import { UserDataExJson } from "../proto/userDataEx";
|
import { UserDataExJson } from "../proto/userDataEx";
|
||||||
|
import { UserDuelListJson } from "../proto/userDuelList";
|
||||||
|
|
||||||
export interface UpsertUserAllRequest {
|
export interface UpsertUserAllRequest {
|
||||||
/** Integer, AiMe ID */
|
/** Integer, AiMe ID */
|
||||||
@@ -25,7 +28,10 @@ export interface UpsertUserAllRequest {
|
|||||||
userActivityList?: UserActivityJson[];
|
userActivityList?: UserActivityJson[];
|
||||||
userRecentRatingList?: UserRecentRatingJson[];
|
userRecentRatingList?: UserRecentRatingJson[];
|
||||||
userPlaylogList?: UserPlaylogJson[];
|
userPlaylogList?: UserPlaylogJson[];
|
||||||
|
userChargeList?: UserChargeJson[];
|
||||||
|
userCourseList?: UserCourseJson[];
|
||||||
userDataEx?: UserDataExJson[];
|
userDataEx?: UserDataExJson[];
|
||||||
|
userDuelList?: UserDuelListJson[];
|
||||||
|
|
||||||
/** String of binary digits */
|
/** String of binary digits */
|
||||||
isNewMapList: string;
|
isNewMapList: string;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
export interface UpsertUserChargelogApiRequest {
|
||||||
|
userId: string;
|
||||||
|
|
||||||
|
userChargelog: {
|
||||||
|
chargeId: string,
|
||||||
|
price: string,
|
||||||
|
purchaseDate: Date,
|
||||||
|
playCount: string,
|
||||||
|
playerRating: string,
|
||||||
|
placeId: string,
|
||||||
|
regionId: string,
|
||||||
|
clientId: string
|
||||||
|
};
|
||||||
|
|
||||||
|
userCharge: {
|
||||||
|
chargeId: string,
|
||||||
|
stock: string,
|
||||||
|
purchaseDate: Date,
|
||||||
|
validDate: Date,
|
||||||
|
param1: string,
|
||||||
|
param2: string,
|
||||||
|
paramDate: Date
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { UserChargeJson } from "../proto/userCharge";
|
||||||
|
|
||||||
export interface GetUserChargeResponse {
|
export interface GetUserChargeResponse {
|
||||||
/** Integer, AiMe ID */
|
/** Integer, AiMe ID */
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -5,6 +7,5 @@ export interface GetUserChargeResponse {
|
|||||||
/** Integer, number of results returned */
|
/** Integer, number of results returned */
|
||||||
length: string;
|
length: string;
|
||||||
|
|
||||||
/** TBD */
|
userChargeList: UserChargeJson[];
|
||||||
userChargeList: [];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { UserCourseJson } from "../proto/userCourse";
|
||||||
|
|
||||||
export interface GetUserCourseResponse {
|
export interface GetUserCourseResponse {
|
||||||
/** Integer, AiMe ID */
|
/** Integer, AiMe ID */
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -8,6 +10,5 @@ export interface GetUserCourseResponse {
|
|||||||
/** Integer, pagination cookie */
|
/** Integer, pagination cookie */
|
||||||
nextIndex: string;
|
nextIndex: string;
|
||||||
|
|
||||||
/** TBD */
|
userCourseList: UserCourseJson[];
|
||||||
userCourseList: [];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { UserDuelListJson } from "../proto/userDuelList";
|
||||||
|
|
||||||
export interface GetUserDuelResponse {
|
export interface GetUserDuelResponse {
|
||||||
/** Integer, AiMe ID */
|
/** Integer, AiMe ID */
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -5,6 +7,9 @@ export interface GetUserDuelResponse {
|
|||||||
/** Integer, number of results returned */
|
/** Integer, number of results returned */
|
||||||
length: string;
|
length: string;
|
||||||
|
|
||||||
/** TBD */
|
/**
|
||||||
userDuelList: [];
|
* List of duel entities, either all duel entities associated with `userId`
|
||||||
|
* or single requested by `duelId` in request.
|
||||||
|
*/
|
||||||
|
userDuelList: UserDuelListJson[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface UpsertUserChargelogApiResponse {
|
||||||
|
returnCode: string;
|
||||||
|
}
|
||||||
@@ -1,24 +1,32 @@
|
|||||||
import { SqlUserActivityRepository } from "./userActivity";
|
import { SqlUserActivityRepository } from "./userActivity";
|
||||||
import { SqlUserCharacterRepository } from "./userCharacter";
|
import { SqlUserCharacterRepository } from "./userCharacter";
|
||||||
|
import { SqlUserChargeRepository } from "./userCharge";
|
||||||
|
import { SqlUserCourseRepository } from "./userCourse";
|
||||||
import { SqlUserDataRepository } from "./userData";
|
import { SqlUserDataRepository } from "./userData";
|
||||||
import { SqlUserDataExRepository } from "./userDataEx";
|
import { SqlUserDataExRepository } from "./userDataEx";
|
||||||
|
import { SqlUserDuelListRepository } from "./userDuelList";
|
||||||
import { SqlUserGameOptionRepository } from "./userGameOption";
|
import { SqlUserGameOptionRepository } from "./userGameOption";
|
||||||
import { SqlUserGameOptionExRepository } from "./userGameOptionEx";
|
import { SqlUserGameOptionExRepository } from "./userGameOptionEx";
|
||||||
import { SqlUserItemRepository } from "./userItem";
|
import { SqlUserItemRepository } from "./userItem";
|
||||||
import { SqlUserMapRepository } from "./userMap";
|
import { SqlUserMapRepository } from "./userMap";
|
||||||
import { SqlUserMusicRepository } from "./userMusic";
|
import { SqlUserMusicRepository } from "./userMusic";
|
||||||
import { SqlUserPlaylogRepository } from "./userPlaylog";
|
import { SqlUserPlaylogRepository } from "./userPlaylog";
|
||||||
|
import { SqlUserRecentRatingRepository } from "./userRecentRating";
|
||||||
import { Repositories } from "../repo";
|
import { Repositories } from "../repo";
|
||||||
import { UserActivityRepository } from "../repo/userActivity";
|
import { UserActivityRepository } from "../repo/userActivity";
|
||||||
import { UserCharacterRepository } from "../repo/userCharacter";
|
import { UserCharacterRepository } from "../repo/userCharacter";
|
||||||
|
import { UserChargeRepository } from "../repo/userCharge";
|
||||||
|
import { UserCourseRepository } from "../repo/userCourse";
|
||||||
import { UserDataRepository } from "../repo/userData";
|
import { UserDataRepository } from "../repo/userData";
|
||||||
import { UserDataExRepository } from "../repo/userDataEx";
|
import { UserDataExRepository } from "../repo/userDataEx";
|
||||||
|
import { UserDuelListRepository } from "../repo/userDuelList";
|
||||||
import { UserGameOptionRepository } from "../repo/userGameOption";
|
import { UserGameOptionRepository } from "../repo/userGameOption";
|
||||||
import { UserGameOptionExRepository } from "../repo/userGameOptionEx";
|
import { UserGameOptionExRepository } from "../repo/userGameOptionEx";
|
||||||
import { UserItemRepository } from "../repo/userItem";
|
import { UserItemRepository } from "../repo/userItem";
|
||||||
import { UserMapRepository } from "../repo/userMap";
|
import { UserMapRepository } from "../repo/userMap";
|
||||||
import { UserMusicRepository } from "../repo/userMusic";
|
import { UserMusicRepository } from "../repo/userMusic";
|
||||||
import { UserPlaylogRepository } from "../repo/userPlaylog";
|
import { UserPlaylogRepository } from "../repo/userPlaylog";
|
||||||
|
import { UserRecentRatingRepository } from "../repo/userRecentRating";
|
||||||
import { Transaction } from "../../sql";
|
import { Transaction } from "../../sql";
|
||||||
|
|
||||||
export class SqlRepositories implements Repositories {
|
export class SqlRepositories implements Repositories {
|
||||||
@@ -32,6 +40,14 @@ export class SqlRepositories implements Repositories {
|
|||||||
return new SqlUserCharacterRepository(this._txn);
|
return new SqlUserCharacterRepository(this._txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userCharge(): UserChargeRepository {
|
||||||
|
return new SqlUserChargeRepository(this._txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
userCourse(): UserCourseRepository {
|
||||||
|
return new SqlUserCourseRepository(this._txn);
|
||||||
|
}
|
||||||
|
|
||||||
userData(): UserDataRepository {
|
userData(): UserDataRepository {
|
||||||
return new SqlUserDataRepository(this._txn);
|
return new SqlUserDataRepository(this._txn);
|
||||||
}
|
}
|
||||||
@@ -40,6 +56,10 @@ export class SqlRepositories implements Repositories {
|
|||||||
return new SqlUserDataExRepository(this._txn);
|
return new SqlUserDataExRepository(this._txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userDuelList(): UserDuelListRepository {
|
||||||
|
return new SqlUserDuelListRepository(this._txn);
|
||||||
|
}
|
||||||
|
|
||||||
userGameOption(): UserGameOptionRepository {
|
userGameOption(): UserGameOptionRepository {
|
||||||
return new SqlUserGameOptionRepository(this._txn);
|
return new SqlUserGameOptionRepository(this._txn);
|
||||||
}
|
}
|
||||||
@@ -63,4 +83,8 @@ export class SqlRepositories implements Repositories {
|
|||||||
userPlaylog(): UserPlaylogRepository {
|
userPlaylog(): UserPlaylogRepository {
|
||||||
return new SqlUserPlaylogRepository(this._txn);
|
return new SqlUserPlaylogRepository(this._txn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userRecentRating(): UserRecentRatingRepository {
|
||||||
|
return new SqlUserRecentRatingRepository(this._txn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ export class SqlUserActivityRepository implements UserActivityRepository {
|
|||||||
.select("*")
|
.select("*")
|
||||||
.from("cm_user_activity")
|
.from("cm_user_activity")
|
||||||
.where("profile_id", profileId)
|
.where("profile_id", profileId)
|
||||||
.where("kind", kind);
|
.where("kind", kind)
|
||||||
|
.orderBy("sort_number DESC");
|
||||||
|
|
||||||
const rows = await this._txn.fetchRows(stmt);
|
const rows = await this._txn.fetchRows(stmt);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import sql from "sql-bricks-postgres";
|
||||||
|
import { createSqlMapper, T } from "../../sql/util";
|
||||||
|
import { UserChargeRepository } from "../repo/userCharge";
|
||||||
|
import { Id } from "../../model";
|
||||||
|
import { Page } from "../repo";
|
||||||
|
import { UserDataItem } from "../model/userData";
|
||||||
|
import { UserChargeItem } from "../model/userCharge";
|
||||||
|
import { Transaction } from "../../sql";
|
||||||
|
|
||||||
|
const { readRow, writeRow, colNames } = createSqlMapper({
|
||||||
|
chargeId: T.number,
|
||||||
|
stock: T.number,
|
||||||
|
purchaseDate: T.Date,
|
||||||
|
validDate: T.Date,
|
||||||
|
param1: T.number,
|
||||||
|
param2: T.number,
|
||||||
|
paramDate: T.Date,
|
||||||
|
});
|
||||||
|
|
||||||
|
export class SqlUserChargeRepository implements UserChargeRepository {
|
||||||
|
constructor(private readonly _txn: Transaction) {}
|
||||||
|
|
||||||
|
async load(profileId: Id<UserDataItem>): Promise<UserChargeItem[]> {
|
||||||
|
const stmt = sql
|
||||||
|
.select("*")
|
||||||
|
.from("cm_user_charge")
|
||||||
|
.where("profile_id", profileId);
|
||||||
|
|
||||||
|
const rows = await this._txn.fetchRows(stmt);
|
||||||
|
|
||||||
|
return rows.map(readRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(profileId: Id<UserDataItem>, obj: UserChargeItem): Promise<void> {
|
||||||
|
const stmt = sql
|
||||||
|
.insert("cm_user_charge", {
|
||||||
|
id: this._txn.generateId(),
|
||||||
|
profile_id: profileId,
|
||||||
|
...writeRow(obj),
|
||||||
|
})
|
||||||
|
.onConflict("profile_id", "charge_id")
|
||||||
|
.doUpdate(colNames);
|
||||||
|
|
||||||
|
return this._txn.modify(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import sql from "sql-bricks-postgres";
|
||||||
|
import { createSqlMapper, T } from "../../sql/util";
|
||||||
|
import { UserCourseRepository } from "../repo/userCourse";
|
||||||
|
import { Id } from "../../model";
|
||||||
|
import { Page } from "../repo";
|
||||||
|
import { UserDataItem } from "../model/userData";
|
||||||
|
import { UserCourseItem } from "../model/userCourse";
|
||||||
|
import { Transaction } from "../../sql";
|
||||||
|
|
||||||
|
const { readRow, writeRow, colNames } = createSqlMapper({
|
||||||
|
courseId: T.number,
|
||||||
|
classId: T.number,
|
||||||
|
playCount: T.number,
|
||||||
|
scoreMax: T.number,
|
||||||
|
isFullCombo: T.boolean,
|
||||||
|
isAllJustice: T.boolean,
|
||||||
|
isSuccess: T.boolean,
|
||||||
|
scoreRank: T.number,
|
||||||
|
eventId: T.number,
|
||||||
|
lastPlayDate: T.Date,
|
||||||
|
param1: T.number,
|
||||||
|
param2: T.number,
|
||||||
|
param3: T.number,
|
||||||
|
param4: T.number,
|
||||||
|
isClear: T.boolean
|
||||||
|
});
|
||||||
|
|
||||||
|
export class SqlUserCourseRepository implements UserCourseRepository {
|
||||||
|
constructor(private readonly _txn: Transaction) {}
|
||||||
|
|
||||||
|
async load(
|
||||||
|
profileId: Id<UserDataItem>,
|
||||||
|
page?: Page
|
||||||
|
): Promise<UserCourseItem[]> {
|
||||||
|
const stmt = sql
|
||||||
|
.select("*")
|
||||||
|
.from("cm_user_course")
|
||||||
|
.where("profile_id", profileId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCourse has no paging before CHUNITHM Amazon
|
||||||
|
*/
|
||||||
|
if (page && !isNaN(page.limit)) {
|
||||||
|
stmt.limit(page.limit).offset(page.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = await this._txn.fetchRows(stmt);
|
||||||
|
|
||||||
|
return rows.map(readRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(profileId: Id<UserDataItem>, obj: UserCourseItem): Promise<void> {
|
||||||
|
const stmt = sql
|
||||||
|
.insert("cm_user_course", {
|
||||||
|
id: this._txn.generateId(),
|
||||||
|
profile_id: profileId,
|
||||||
|
...writeRow(obj),
|
||||||
|
})
|
||||||
|
.onConflict("profile_id", "course_id")
|
||||||
|
.doUpdate(colNames);
|
||||||
|
|
||||||
|
return this._txn.modify(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -56,7 +56,47 @@ export class SqlUserDataExRepository implements UserDataExRepository {
|
|||||||
const row = await this._txn.fetchRow(stmt);
|
const row = await this._txn.fetchRow(stmt);
|
||||||
|
|
||||||
if (row === undefined) {
|
if (row === undefined) {
|
||||||
throw new Error("UserDataEx record not found");
|
/**
|
||||||
|
* When upgrading from CHUNITHM Star Plus & earlier
|
||||||
|
* to Amazon, there is no UserDataEx, so we return
|
||||||
|
* a "default" row.
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
compatibleCmVersion: "",
|
||||||
|
medal: 0,
|
||||||
|
mapIconId: 0,
|
||||||
|
voiceId: 0,
|
||||||
|
ext1: 0,
|
||||||
|
ext2: 0,
|
||||||
|
ext3: 0,
|
||||||
|
ext4: 0,
|
||||||
|
ext5: 0,
|
||||||
|
ext6: 0,
|
||||||
|
ext7: 0,
|
||||||
|
ext8: 0,
|
||||||
|
ext9: 0,
|
||||||
|
ext10: 0,
|
||||||
|
ext11: 0,
|
||||||
|
ext12: 0,
|
||||||
|
ext13: 0,
|
||||||
|
ext14: 0,
|
||||||
|
ext15: 0,
|
||||||
|
ext16: 0,
|
||||||
|
ext17: 0,
|
||||||
|
ext18: 0,
|
||||||
|
ext19: 0,
|
||||||
|
ext20: 0,
|
||||||
|
extStr1: "",
|
||||||
|
extStr2: "",
|
||||||
|
extStr3: "",
|
||||||
|
extStr4: "",
|
||||||
|
extStr5: "",
|
||||||
|
extLong1: 0n,
|
||||||
|
extLong2: 0n,
|
||||||
|
extLong3: 0n,
|
||||||
|
extLong4: 0n,
|
||||||
|
extLong5: 0n,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return readRow(row);
|
return readRow(row);
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import sql from "sql-bricks-postgres";
|
||||||
|
|
||||||
|
import { UserDataItem } from "../model/userData";
|
||||||
|
import { UserDuelListItem } from "../model/userDuelList";
|
||||||
|
import { Page } from "../repo";
|
||||||
|
import { UserDuelListRepository } from "../repo/userDuelList";
|
||||||
|
import { Id } from "../../model";
|
||||||
|
import { Transaction } from "../../sql";
|
||||||
|
import { T, createSqlMapper } from "../../sql/util";
|
||||||
|
|
||||||
|
const { readRow, writeRow, colNames } = createSqlMapper({
|
||||||
|
duelId: T.number,
|
||||||
|
progress: T.number,
|
||||||
|
point: T.number,
|
||||||
|
isClear: T.boolean,
|
||||||
|
lastPlayDate: T.Date,
|
||||||
|
param1: T.number,
|
||||||
|
param2: T.number,
|
||||||
|
param3: T.number,
|
||||||
|
param4: T.number,
|
||||||
|
});
|
||||||
|
|
||||||
|
export class SqlUserDuelListRepository implements UserDuelListRepository {
|
||||||
|
constructor(private readonly _txn: Transaction) {}
|
||||||
|
|
||||||
|
async load(
|
||||||
|
profileId: Id<UserDataItem>,
|
||||||
|
duelId?: number
|
||||||
|
): Promise<UserDuelListItem[]> {
|
||||||
|
const stmt = sql
|
||||||
|
.select("*")
|
||||||
|
.from("cm_user_duel_list")
|
||||||
|
.where("profile_id", profileId);
|
||||||
|
|
||||||
|
if (duelId !== undefined) {
|
||||||
|
stmt.where("duel_id", duelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = await this._txn.fetchRows(stmt);
|
||||||
|
|
||||||
|
return rows.map(readRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(profileId: Id<UserDataItem>, obj: UserDuelListItem): Promise<void> {
|
||||||
|
const stmt = sql
|
||||||
|
.insert("cm_user_duel_list", {
|
||||||
|
id: this._txn.generateId(),
|
||||||
|
profile_id: profileId,
|
||||||
|
...writeRow(obj),
|
||||||
|
})
|
||||||
|
.onConflict("profile_id", "duel_id")
|
||||||
|
.doUpdate(colNames);
|
||||||
|
|
||||||
|
return this._txn.modify(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,14 +34,22 @@ export class SqlUserMusicRepository implements UserMusicRepository {
|
|||||||
profileId: Id<UserDataItem>,
|
profileId: Id<UserDataItem>,
|
||||||
page?: Page
|
page?: Page
|
||||||
): Promise<UserMusicDetailItem[]> {
|
): Promise<UserMusicDetailItem[]> {
|
||||||
|
const preStmt = sql
|
||||||
|
.select("DISTINCT(music_id)")
|
||||||
|
.from("cm_user_music")
|
||||||
|
.where("profile_id", profileId)
|
||||||
|
.orderBy("music_id");
|
||||||
|
if (page) {
|
||||||
|
preStmt.limit(page.limit).offset(page.offset);
|
||||||
|
}
|
||||||
|
const preRows = await this._txn.fetchRows(preStmt);
|
||||||
|
const musicIds = preRows.map(r => r.music_id);
|
||||||
|
|
||||||
const stmt = sql
|
const stmt = sql
|
||||||
.select("*")
|
.select("*")
|
||||||
.from("cm_user_music")
|
.from("cm_user_music")
|
||||||
.where("profile_id", profileId);
|
.where("profile_id", profileId)
|
||||||
|
.and(sql.in('music_id', musicIds));
|
||||||
if (page) {
|
|
||||||
stmt.limit(page.limit).offset(page.offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows = await this._txn.fetchRows(stmt);
|
const rows = await this._txn.fetchRows(stmt);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { UserPlaylogRepository } from "../repo/userPlaylog";
|
|||||||
import { Transaction } from "../../sql";
|
import { Transaction } from "../../sql";
|
||||||
import { T, createSqlMapper } from "../../sql/util";
|
import { T, createSqlMapper } from "../../sql/util";
|
||||||
|
|
||||||
const { writeRow } = createSqlMapper({
|
const { readRow, writeRow } = createSqlMapper({
|
||||||
orderId: T.number,
|
orderId: T.number,
|
||||||
sortNumber: T.number,
|
sortNumber: T.number,
|
||||||
placeId: T.number,
|
placeId: T.number,
|
||||||
@@ -72,4 +72,16 @@ export class SqlUserPlaylogRepository implements UserPlaylogRepository {
|
|||||||
|
|
||||||
return this._txn.modify(stmt);
|
return this._txn.modify(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadLatest(profileId: Id<UserDataItem>, size: number): Promise<UserPlaylogItem[]> {
|
||||||
|
const stmt = sql
|
||||||
|
.select("music_id", "score", "level","user_play_date")
|
||||||
|
.from("cm_user_playlog")
|
||||||
|
.where("profile_id", profileId)
|
||||||
|
.limit(size).order("user_play_date DESC");
|
||||||
|
|
||||||
|
const rows = await this._txn.fetchRows(stmt);
|
||||||
|
|
||||||
|
return rows.map(readRow);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import sql from "sql-bricks-postgres";
|
||||||
|
|
||||||
|
import { Id } from "../../model";
|
||||||
|
import { UserDataItem } from "../model/userData";
|
||||||
|
import { UserRecentRatingItem } from "../model/userRecentRating";
|
||||||
|
import { UserRecentRatingRepository } from "../repo/userRecentRating";
|
||||||
|
import { Transaction } from "../../sql";
|
||||||
|
import { T, createSqlMapper } from "../../sql/util";
|
||||||
|
|
||||||
|
const { readRow, writeRow, colNames } = createSqlMapper({
|
||||||
|
musicId: T.number,
|
||||||
|
difficultId: T.number,
|
||||||
|
romVersionCode: T.number,
|
||||||
|
score: T.number,
|
||||||
|
});
|
||||||
|
|
||||||
|
export class SqlUserRecentRatingRepository
|
||||||
|
implements UserRecentRatingRepository {
|
||||||
|
constructor(private readonly _txn: Transaction) {}
|
||||||
|
|
||||||
|
async load(profileId: Id<UserDataItem>): Promise<UserRecentRatingItem[]> {
|
||||||
|
const stmt = sql
|
||||||
|
.select("music_id", "difficult_id", "rom_version_code", "score")
|
||||||
|
.from("cm_user_recent_rating")
|
||||||
|
.where("profile_id", profileId)
|
||||||
|
.order("sort_order ASC");
|
||||||
|
|
||||||
|
const rows = await this._txn.fetchRows(stmt);
|
||||||
|
|
||||||
|
return rows.map(readRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
save(
|
||||||
|
profileId: Id<UserDataItem>,
|
||||||
|
objs: UserRecentRatingItem[]
|
||||||
|
): Promise<void> {
|
||||||
|
// Don't do anything if there's nothing to save,
|
||||||
|
// since trying to execute an empty insert statement fails.
|
||||||
|
if (objs.length === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const stmt = sql
|
||||||
|
.insert(
|
||||||
|
"cm_user_recent_rating",
|
||||||
|
objs.map((obj, idx) => {
|
||||||
|
return {
|
||||||
|
id: this._txn.generateId(),
|
||||||
|
profile_id: profileId,
|
||||||
|
sort_order: idx + 1,
|
||||||
|
...writeRow(obj),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.onConflict("profile_id", "sort_order")
|
||||||
|
.doUpdate(colNames);
|
||||||
|
|
||||||
|
return this._txn.modify(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const CHARGE_IDS = Object.freeze([
|
||||||
|
{
|
||||||
|
id: 2310, //World's End ticket x5
|
||||||
|
price: 1,
|
||||||
|
salePrice: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2060, //4x bonus ticket
|
||||||
|
price: 1,
|
||||||
|
salePrice: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2230, //6 songs premium ticket
|
||||||
|
price: 2,
|
||||||
|
salePrice: 2,
|
||||||
|
},
|
||||||
|
]);
|
||||||
@@ -0,0 +1,854 @@
|
|||||||
|
export const EVENT_IDS = Object.freeze([
|
||||||
|
// A000
|
||||||
|
0,
|
||||||
|
1506,
|
||||||
|
1604,
|
||||||
|
1702,
|
||||||
|
1953,
|
||||||
|
2206,
|
||||||
|
2353,
|
||||||
|
2553,
|
||||||
|
2804,
|
||||||
|
2999,
|
||||||
|
3000,
|
||||||
|
3001,
|
||||||
|
3002,
|
||||||
|
3003,
|
||||||
|
3006,
|
||||||
|
3007,
|
||||||
|
3008,
|
||||||
|
3009,
|
||||||
|
3010,
|
||||||
|
3011,
|
||||||
|
3012,
|
||||||
|
3013,
|
||||||
|
3014,
|
||||||
|
3015,
|
||||||
|
3016,
|
||||||
|
3017,
|
||||||
|
3018,
|
||||||
|
3019,
|
||||||
|
3020,
|
||||||
|
3021,
|
||||||
|
3026,
|
||||||
|
3027,
|
||||||
|
20003,
|
||||||
|
|
||||||
|
// A014
|
||||||
|
3004,
|
||||||
|
3005,
|
||||||
|
3022,
|
||||||
|
3023,
|
||||||
|
3024,
|
||||||
|
3025,
|
||||||
|
3028,
|
||||||
|
3029,
|
||||||
|
3030,
|
||||||
|
3031,
|
||||||
|
3032,
|
||||||
|
3100,
|
||||||
|
3101,
|
||||||
|
3102,
|
||||||
|
3103,
|
||||||
|
3104,
|
||||||
|
3150,
|
||||||
|
3151,
|
||||||
|
3152,
|
||||||
|
3153,
|
||||||
|
3154,
|
||||||
|
3155,
|
||||||
|
3156,
|
||||||
|
3157,
|
||||||
|
3158,
|
||||||
|
3159,
|
||||||
|
3160,
|
||||||
|
3161,
|
||||||
|
3162,
|
||||||
|
3163,
|
||||||
|
3164,
|
||||||
|
3165,
|
||||||
|
3166,
|
||||||
|
3167,
|
||||||
|
3168,
|
||||||
|
3169,
|
||||||
|
3200,
|
||||||
|
3201,
|
||||||
|
3202,
|
||||||
|
3203,
|
||||||
|
3204,
|
||||||
|
3205,
|
||||||
|
3206,
|
||||||
|
3207,
|
||||||
|
3208,
|
||||||
|
3209,
|
||||||
|
3210,
|
||||||
|
3211,
|
||||||
|
3212,
|
||||||
|
3213,
|
||||||
|
3214,
|
||||||
|
3215,
|
||||||
|
3216,
|
||||||
|
3217,
|
||||||
|
3218,
|
||||||
|
3219,
|
||||||
|
3250,
|
||||||
|
3251,
|
||||||
|
3252,
|
||||||
|
3253,
|
||||||
|
3254,
|
||||||
|
3255,
|
||||||
|
3256,
|
||||||
|
3257,
|
||||||
|
3258,
|
||||||
|
3259,
|
||||||
|
3260,
|
||||||
|
3261,
|
||||||
|
3262,
|
||||||
|
3263,
|
||||||
|
3300,
|
||||||
|
3301,
|
||||||
|
3302,
|
||||||
|
3303,
|
||||||
|
3304,
|
||||||
|
3305,
|
||||||
|
3306,
|
||||||
|
3307,
|
||||||
|
3308,
|
||||||
|
3309,
|
||||||
|
3310,
|
||||||
|
3311,
|
||||||
|
3350,
|
||||||
|
3351,
|
||||||
|
3352,
|
||||||
|
3353,
|
||||||
|
3400,
|
||||||
|
3401,
|
||||||
|
3402,
|
||||||
|
3403,
|
||||||
|
3404,
|
||||||
|
3405,
|
||||||
|
3406,
|
||||||
|
3407,
|
||||||
|
3408,
|
||||||
|
3409,
|
||||||
|
3410,
|
||||||
|
3411,
|
||||||
|
3412,
|
||||||
|
3413,
|
||||||
|
3414,
|
||||||
|
3415,
|
||||||
|
3416,
|
||||||
|
3417,
|
||||||
|
3450,
|
||||||
|
3451,
|
||||||
|
3452,
|
||||||
|
3453,
|
||||||
|
3454,
|
||||||
|
3455,
|
||||||
|
|
||||||
|
// A017
|
||||||
|
3500,
|
||||||
|
3501,
|
||||||
|
3502,
|
||||||
|
3503,
|
||||||
|
3504,
|
||||||
|
3505,
|
||||||
|
3506,
|
||||||
|
3507,
|
||||||
|
3508,
|
||||||
|
3509,
|
||||||
|
3510,
|
||||||
|
3511,
|
||||||
|
3512,
|
||||||
|
3513,
|
||||||
|
3514,
|
||||||
|
3515,
|
||||||
|
3550,
|
||||||
|
3551,
|
||||||
|
3552,
|
||||||
|
3553,
|
||||||
|
3554,
|
||||||
|
3555,
|
||||||
|
3556,
|
||||||
|
3557,
|
||||||
|
3580,
|
||||||
|
3581,
|
||||||
|
|
||||||
|
// Other event IDs for Chunithm Amazon
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
100,
|
||||||
|
101,
|
||||||
|
102,
|
||||||
|
103,
|
||||||
|
200,
|
||||||
|
201,
|
||||||
|
202,
|
||||||
|
203,
|
||||||
|
300,
|
||||||
|
301,
|
||||||
|
400,
|
||||||
|
401,
|
||||||
|
500,
|
||||||
|
501,
|
||||||
|
700,
|
||||||
|
701,
|
||||||
|
810,
|
||||||
|
820,
|
||||||
|
821,
|
||||||
|
830,
|
||||||
|
831,
|
||||||
|
832,
|
||||||
|
834,
|
||||||
|
835,
|
||||||
|
840,
|
||||||
|
841,
|
||||||
|
842,
|
||||||
|
843,
|
||||||
|
850,
|
||||||
|
851,
|
||||||
|
852,
|
||||||
|
853,
|
||||||
|
860,
|
||||||
|
861,
|
||||||
|
862,
|
||||||
|
863,
|
||||||
|
864,
|
||||||
|
865,
|
||||||
|
870,
|
||||||
|
871,
|
||||||
|
872,
|
||||||
|
873,
|
||||||
|
880,
|
||||||
|
890,
|
||||||
|
891,
|
||||||
|
900,
|
||||||
|
901,
|
||||||
|
902,
|
||||||
|
903,
|
||||||
|
904,
|
||||||
|
905,
|
||||||
|
906,
|
||||||
|
907,
|
||||||
|
908,
|
||||||
|
909,
|
||||||
|
910,
|
||||||
|
911,
|
||||||
|
912,
|
||||||
|
913,
|
||||||
|
914,
|
||||||
|
915,
|
||||||
|
916,
|
||||||
|
917,
|
||||||
|
918,
|
||||||
|
930,
|
||||||
|
931,
|
||||||
|
932,
|
||||||
|
933,
|
||||||
|
934,
|
||||||
|
935,
|
||||||
|
936,
|
||||||
|
937,
|
||||||
|
938,
|
||||||
|
939,
|
||||||
|
940,
|
||||||
|
941,
|
||||||
|
942,
|
||||||
|
943,
|
||||||
|
944,
|
||||||
|
945,
|
||||||
|
946,
|
||||||
|
947,
|
||||||
|
960,
|
||||||
|
961,
|
||||||
|
962,
|
||||||
|
963,
|
||||||
|
964,
|
||||||
|
965,
|
||||||
|
966,
|
||||||
|
967,
|
||||||
|
968,
|
||||||
|
969,
|
||||||
|
990,
|
||||||
|
991,
|
||||||
|
992,
|
||||||
|
993,
|
||||||
|
994,
|
||||||
|
995,
|
||||||
|
996,
|
||||||
|
997,
|
||||||
|
998,
|
||||||
|
999,
|
||||||
|
1000,
|
||||||
|
1001,
|
||||||
|
1002,
|
||||||
|
1003,
|
||||||
|
1004,
|
||||||
|
1005,
|
||||||
|
1006,
|
||||||
|
1020,
|
||||||
|
1021,
|
||||||
|
1022,
|
||||||
|
1023,
|
||||||
|
1024,
|
||||||
|
1025,
|
||||||
|
1026,
|
||||||
|
1027,
|
||||||
|
1028,
|
||||||
|
1029,
|
||||||
|
1030,
|
||||||
|
1031,
|
||||||
|
1050,
|
||||||
|
1051,
|
||||||
|
1052,
|
||||||
|
1053,
|
||||||
|
1054,
|
||||||
|
1055,
|
||||||
|
1056,
|
||||||
|
1057,
|
||||||
|
1058,
|
||||||
|
1059,
|
||||||
|
1060,
|
||||||
|
1061,
|
||||||
|
1070,
|
||||||
|
1098,
|
||||||
|
1099,
|
||||||
|
1100,
|
||||||
|
1101,
|
||||||
|
1102,
|
||||||
|
1103,
|
||||||
|
1104,
|
||||||
|
1105,
|
||||||
|
1106,
|
||||||
|
1107,
|
||||||
|
1108,
|
||||||
|
1109,
|
||||||
|
1110,
|
||||||
|
1111,
|
||||||
|
1112,
|
||||||
|
1113,
|
||||||
|
1114,
|
||||||
|
1130,
|
||||||
|
1131,
|
||||||
|
1132,
|
||||||
|
1133,
|
||||||
|
1134,
|
||||||
|
1135,
|
||||||
|
1136,
|
||||||
|
1137,
|
||||||
|
1138,
|
||||||
|
1159,
|
||||||
|
1160,
|
||||||
|
1161,
|
||||||
|
1162,
|
||||||
|
1163,
|
||||||
|
1164,
|
||||||
|
1165,
|
||||||
|
1166,
|
||||||
|
1167,
|
||||||
|
1168,
|
||||||
|
1169,
|
||||||
|
1170,
|
||||||
|
1171,
|
||||||
|
1172,
|
||||||
|
1173,
|
||||||
|
1174,
|
||||||
|
1175,
|
||||||
|
1190,
|
||||||
|
1191,
|
||||||
|
1192,
|
||||||
|
1193,
|
||||||
|
1194,
|
||||||
|
1195,
|
||||||
|
1196,
|
||||||
|
1197,
|
||||||
|
1198,
|
||||||
|
1199,
|
||||||
|
1200,
|
||||||
|
1201,
|
||||||
|
1220,
|
||||||
|
1221,
|
||||||
|
1222,
|
||||||
|
1223,
|
||||||
|
1224,
|
||||||
|
1225,
|
||||||
|
1226,
|
||||||
|
1227,
|
||||||
|
1228,
|
||||||
|
1229,
|
||||||
|
1230,
|
||||||
|
1231,
|
||||||
|
1232,
|
||||||
|
1233,
|
||||||
|
1250,
|
||||||
|
1251,
|
||||||
|
1252,
|
||||||
|
1253,
|
||||||
|
1254,
|
||||||
|
1255,
|
||||||
|
1256,
|
||||||
|
1257,
|
||||||
|
1258,
|
||||||
|
1259,
|
||||||
|
1260,
|
||||||
|
1261,
|
||||||
|
1279,
|
||||||
|
1280,
|
||||||
|
1281,
|
||||||
|
1282,
|
||||||
|
1283,
|
||||||
|
1284,
|
||||||
|
1285,
|
||||||
|
1286,
|
||||||
|
1287,
|
||||||
|
1288,
|
||||||
|
1289,
|
||||||
|
1290,
|
||||||
|
1291,
|
||||||
|
1292,
|
||||||
|
1293,
|
||||||
|
1294,
|
||||||
|
1295,
|
||||||
|
1296,
|
||||||
|
1297,
|
||||||
|
1298,
|
||||||
|
1299,
|
||||||
|
1310,
|
||||||
|
1311,
|
||||||
|
1312,
|
||||||
|
1313,
|
||||||
|
1314,
|
||||||
|
1315,
|
||||||
|
1316,
|
||||||
|
1317,
|
||||||
|
1318,
|
||||||
|
1319,
|
||||||
|
1320,
|
||||||
|
1321,
|
||||||
|
1322,
|
||||||
|
1323,
|
||||||
|
1324,
|
||||||
|
1325,
|
||||||
|
1326,
|
||||||
|
1327,
|
||||||
|
1328,
|
||||||
|
1329,
|
||||||
|
1340,
|
||||||
|
1341,
|
||||||
|
1342,
|
||||||
|
1343,
|
||||||
|
1344,
|
||||||
|
1345,
|
||||||
|
1346,
|
||||||
|
1347,
|
||||||
|
1348,
|
||||||
|
1349,
|
||||||
|
1350,
|
||||||
|
1351,
|
||||||
|
1352,
|
||||||
|
1353,
|
||||||
|
1354,
|
||||||
|
1355,
|
||||||
|
1356,
|
||||||
|
1357,
|
||||||
|
1370,
|
||||||
|
1371,
|
||||||
|
1372,
|
||||||
|
1373,
|
||||||
|
1374,
|
||||||
|
1375,
|
||||||
|
1376,
|
||||||
|
1377,
|
||||||
|
1378,
|
||||||
|
1379,
|
||||||
|
1380,
|
||||||
|
1381,
|
||||||
|
1382,
|
||||||
|
1383,
|
||||||
|
1384,
|
||||||
|
1385,
|
||||||
|
1386,
|
||||||
|
1387,
|
||||||
|
1388,
|
||||||
|
1400,
|
||||||
|
1401,
|
||||||
|
1402,
|
||||||
|
1403,
|
||||||
|
1404,
|
||||||
|
1405,
|
||||||
|
1406,
|
||||||
|
1407,
|
||||||
|
1408,
|
||||||
|
1409,
|
||||||
|
1410,
|
||||||
|
1411,
|
||||||
|
1412,
|
||||||
|
1413,
|
||||||
|
1414,
|
||||||
|
1415,
|
||||||
|
1416,
|
||||||
|
1430,
|
||||||
|
1431,
|
||||||
|
1432,
|
||||||
|
1433,
|
||||||
|
1434,
|
||||||
|
1435,
|
||||||
|
1436,
|
||||||
|
1437,
|
||||||
|
1438,
|
||||||
|
1439,
|
||||||
|
1440,
|
||||||
|
1441,
|
||||||
|
1442,
|
||||||
|
1443,
|
||||||
|
1444,
|
||||||
|
1445,
|
||||||
|
1446,
|
||||||
|
1447,
|
||||||
|
1460,
|
||||||
|
1461,
|
||||||
|
1462,
|
||||||
|
1463,
|
||||||
|
1464,
|
||||||
|
1465,
|
||||||
|
1500,
|
||||||
|
1501,
|
||||||
|
1502,
|
||||||
|
1504,
|
||||||
|
1505,
|
||||||
|
1507,
|
||||||
|
1508,
|
||||||
|
1509,
|
||||||
|
1510,
|
||||||
|
1511,
|
||||||
|
1512,
|
||||||
|
1513,
|
||||||
|
1514,
|
||||||
|
1515,
|
||||||
|
1516,
|
||||||
|
1517,
|
||||||
|
1518,
|
||||||
|
1519,
|
||||||
|
1520,
|
||||||
|
1521,
|
||||||
|
1522,
|
||||||
|
1523,
|
||||||
|
1524,
|
||||||
|
1600,
|
||||||
|
1601,
|
||||||
|
1603,
|
||||||
|
1605,
|
||||||
|
1606,
|
||||||
|
1650,
|
||||||
|
1651,
|
||||||
|
1652,
|
||||||
|
1653,
|
||||||
|
1654,
|
||||||
|
1655,
|
||||||
|
1656,
|
||||||
|
1657,
|
||||||
|
1658,
|
||||||
|
1659,
|
||||||
|
1660,
|
||||||
|
1661,
|
||||||
|
1700,
|
||||||
|
1701,
|
||||||
|
1703,
|
||||||
|
1750,
|
||||||
|
1751,
|
||||||
|
1752,
|
||||||
|
1753,
|
||||||
|
1754,
|
||||||
|
1755,
|
||||||
|
1756,
|
||||||
|
1757,
|
||||||
|
1758,
|
||||||
|
1759,
|
||||||
|
1760,
|
||||||
|
1761,
|
||||||
|
1762,
|
||||||
|
1763,
|
||||||
|
1764,
|
||||||
|
1765,
|
||||||
|
1766,
|
||||||
|
1767,
|
||||||
|
1768,
|
||||||
|
1769,
|
||||||
|
1770,
|
||||||
|
1800,
|
||||||
|
1801,
|
||||||
|
1802,
|
||||||
|
1803,
|
||||||
|
1804,
|
||||||
|
1805,
|
||||||
|
1850,
|
||||||
|
1851,
|
||||||
|
1852,
|
||||||
|
1853,
|
||||||
|
1854,
|
||||||
|
1855,
|
||||||
|
1856,
|
||||||
|
1857,
|
||||||
|
1880,
|
||||||
|
1881,
|
||||||
|
1882,
|
||||||
|
1883,
|
||||||
|
1884,
|
||||||
|
1885,
|
||||||
|
1886,
|
||||||
|
1887,
|
||||||
|
1888,
|
||||||
|
1889,
|
||||||
|
1890,
|
||||||
|
1900,
|
||||||
|
1901,
|
||||||
|
1903,
|
||||||
|
1904,
|
||||||
|
1905,
|
||||||
|
1906,
|
||||||
|
1907,
|
||||||
|
1908,
|
||||||
|
1909,
|
||||||
|
1910,
|
||||||
|
1911,
|
||||||
|
1912,
|
||||||
|
1913,
|
||||||
|
1950,
|
||||||
|
1951,
|
||||||
|
1952,
|
||||||
|
1954,
|
||||||
|
1955,
|
||||||
|
1956,
|
||||||
|
2000,
|
||||||
|
2001,
|
||||||
|
2003,
|
||||||
|
2004,
|
||||||
|
2005,
|
||||||
|
2006,
|
||||||
|
2007,
|
||||||
|
2008,
|
||||||
|
2009,
|
||||||
|
2010,
|
||||||
|
2011,
|
||||||
|
2012,
|
||||||
|
2013,
|
||||||
|
2050,
|
||||||
|
2051,
|
||||||
|
2052,
|
||||||
|
2053,
|
||||||
|
2054,
|
||||||
|
2055,
|
||||||
|
2056,
|
||||||
|
2100,
|
||||||
|
2101,
|
||||||
|
2103,
|
||||||
|
2104,
|
||||||
|
2105,
|
||||||
|
2106,
|
||||||
|
2107,
|
||||||
|
2108,
|
||||||
|
2109,
|
||||||
|
2110,
|
||||||
|
2111,
|
||||||
|
2150,
|
||||||
|
2151,
|
||||||
|
2152,
|
||||||
|
2153,
|
||||||
|
2154,
|
||||||
|
2155,
|
||||||
|
2156,
|
||||||
|
2200,
|
||||||
|
2201,
|
||||||
|
2202,
|
||||||
|
2203,
|
||||||
|
2204,
|
||||||
|
2205,
|
||||||
|
2207,
|
||||||
|
2208,
|
||||||
|
2209,
|
||||||
|
2210,
|
||||||
|
2211,
|
||||||
|
2212,
|
||||||
|
2213,
|
||||||
|
2214,
|
||||||
|
2215,
|
||||||
|
2216,
|
||||||
|
2217,
|
||||||
|
2218,
|
||||||
|
2219,
|
||||||
|
2220,
|
||||||
|
2221,
|
||||||
|
2222,
|
||||||
|
2223,
|
||||||
|
2224,
|
||||||
|
2225,
|
||||||
|
2226,
|
||||||
|
2250,
|
||||||
|
2251,
|
||||||
|
2252,
|
||||||
|
2253,
|
||||||
|
2280,
|
||||||
|
2281,
|
||||||
|
2282,
|
||||||
|
2283,
|
||||||
|
2300,
|
||||||
|
2301,
|
||||||
|
2302,
|
||||||
|
2303,
|
||||||
|
2304,
|
||||||
|
2305,
|
||||||
|
2306,
|
||||||
|
2307,
|
||||||
|
2308,
|
||||||
|
2350,
|
||||||
|
2351,
|
||||||
|
2352,
|
||||||
|
2354,
|
||||||
|
2355,
|
||||||
|
2356,
|
||||||
|
2380,
|
||||||
|
2381,
|
||||||
|
2382,
|
||||||
|
2383,
|
||||||
|
2400,
|
||||||
|
2401,
|
||||||
|
2402,
|
||||||
|
2403,
|
||||||
|
2404,
|
||||||
|
2405,
|
||||||
|
2406,
|
||||||
|
2407,
|
||||||
|
2408,
|
||||||
|
2409,
|
||||||
|
2410,
|
||||||
|
2411,
|
||||||
|
2412,
|
||||||
|
2413,
|
||||||
|
2414,
|
||||||
|
2415,
|
||||||
|
2416,
|
||||||
|
2417,
|
||||||
|
2418,
|
||||||
|
2419,
|
||||||
|
2420,
|
||||||
|
2421,
|
||||||
|
2450,
|
||||||
|
2451,
|
||||||
|
2452,
|
||||||
|
2453,
|
||||||
|
2454,
|
||||||
|
2455,
|
||||||
|
2456,
|
||||||
|
2457,
|
||||||
|
2458,
|
||||||
|
2459,
|
||||||
|
2500,
|
||||||
|
2501,
|
||||||
|
2502,
|
||||||
|
2503,
|
||||||
|
2504,
|
||||||
|
2505,
|
||||||
|
2506,
|
||||||
|
2507,
|
||||||
|
2508,
|
||||||
|
2509,
|
||||||
|
2510,
|
||||||
|
2511,
|
||||||
|
2550,
|
||||||
|
2551,
|
||||||
|
2552,
|
||||||
|
2554,
|
||||||
|
2555,
|
||||||
|
2556,
|
||||||
|
2557,
|
||||||
|
2558,
|
||||||
|
2559,
|
||||||
|
2560,
|
||||||
|
2561,
|
||||||
|
2562,
|
||||||
|
2600,
|
||||||
|
2601,
|
||||||
|
2602,
|
||||||
|
2603,
|
||||||
|
2604,
|
||||||
|
2605,
|
||||||
|
2606,
|
||||||
|
2607,
|
||||||
|
2608,
|
||||||
|
2609,
|
||||||
|
2610,
|
||||||
|
2611,
|
||||||
|
2612,
|
||||||
|
2613,
|
||||||
|
2614,
|
||||||
|
2615,
|
||||||
|
2650,
|
||||||
|
2651,
|
||||||
|
2652,
|
||||||
|
2653,
|
||||||
|
2654,
|
||||||
|
2655,
|
||||||
|
2656,
|
||||||
|
2657,
|
||||||
|
2658,
|
||||||
|
2659,
|
||||||
|
2660,
|
||||||
|
2700,
|
||||||
|
2701,
|
||||||
|
2702,
|
||||||
|
2703,
|
||||||
|
2704,
|
||||||
|
2705,
|
||||||
|
2706,
|
||||||
|
2707,
|
||||||
|
2708,
|
||||||
|
2709,
|
||||||
|
2710,
|
||||||
|
2711,
|
||||||
|
2750,
|
||||||
|
2751,
|
||||||
|
2752,
|
||||||
|
2753,
|
||||||
|
2754,
|
||||||
|
2755,
|
||||||
|
2800,
|
||||||
|
2801,
|
||||||
|
2802,
|
||||||
|
2803,
|
||||||
|
2805,
|
||||||
|
2806,
|
||||||
|
2807,
|
||||||
|
2808,
|
||||||
|
2809,
|
||||||
|
2810,
|
||||||
|
2811,
|
||||||
|
2812,
|
||||||
|
2813,
|
||||||
|
2814,
|
||||||
|
2815,
|
||||||
|
2816,
|
||||||
|
2817,
|
||||||
|
2818,
|
||||||
|
2850,
|
||||||
|
2851,
|
||||||
|
2852,
|
||||||
|
2853,
|
||||||
|
2854,
|
||||||
|
2855,
|
||||||
|
2856,
|
||||||
|
2857,
|
||||||
|
2900,
|
||||||
|
2901,
|
||||||
|
2902,
|
||||||
|
2903,
|
||||||
|
2950,
|
||||||
|
2951,
|
||||||
|
2952,
|
||||||
|
2953,
|
||||||
|
8001,
|
||||||
|
20000,
|
||||||
|
20001,
|
||||||
|
20002,
|
||||||
|
20004,
|
||||||
|
]);
|
||||||
@@ -67,6 +67,7 @@ export function saveProfile2(buf: Buffer): SaveProfileRequest2 {
|
|||||||
rows: storyRows,
|
rows: storyRows,
|
||||||
},
|
},
|
||||||
unlocks: {
|
unlocks: {
|
||||||
|
auras: buf.readUInt16LE(0x010c),
|
||||||
cup: buf.readUInt8(0x0110),
|
cup: buf.readUInt8(0x0110),
|
||||||
gauges: buf.readUInt16LE(0x0114),
|
gauges: buf.readUInt16LE(0x0114),
|
||||||
music: buf.readUInt16LE(0x0140),
|
music: buf.readUInt16LE(0x0140),
|
||||||
@@ -90,6 +91,7 @@ export function saveProfile2(buf: Buffer): SaveProfileRequest2 {
|
|||||||
settings: {
|
settings: {
|
||||||
music: buf.readUInt16LE(0x045a),
|
music: buf.readUInt16LE(0x045a),
|
||||||
pack: buf.readUInt32LE(0x0034),
|
pack: buf.readUInt32LE(0x0034),
|
||||||
|
aura: buf.readUInt8(0x002c),
|
||||||
paperCup: buf.readUInt8(0x00f6),
|
paperCup: buf.readUInt8(0x00f6),
|
||||||
gauges: buf.readUInt8(0x00f7),
|
gauges: buf.readUInt8(0x00f7),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export function saveProfile3(buf: Buffer): SaveProfileRequest2 {
|
|||||||
rows: storyRows,
|
rows: storyRows,
|
||||||
},
|
},
|
||||||
unlocks: {
|
unlocks: {
|
||||||
|
auras: buf.readUInt16LE(0x010c),
|
||||||
cup: buf.readUInt8(0x0110),
|
cup: buf.readUInt8(0x0110),
|
||||||
gauges: buf.readUInt16LE(0x0114),
|
gauges: buf.readUInt16LE(0x0114),
|
||||||
music: buf.readUInt16LE(0x0140),
|
music: buf.readUInt16LE(0x0140),
|
||||||
@@ -92,6 +93,7 @@ export function saveProfile3(buf: Buffer): SaveProfileRequest2 {
|
|||||||
settings: {
|
settings: {
|
||||||
music: buf.readUInt16LE(0x04ee),
|
music: buf.readUInt16LE(0x04ee),
|
||||||
pack: buf.readUInt32LE(0x0034),
|
pack: buf.readUInt32LE(0x0034),
|
||||||
|
aura: buf.readUInt8(0x002c),
|
||||||
paperCup: buf.readUInt8(0x00f6),
|
paperCup: buf.readUInt8(0x00f6),
|
||||||
gauges: buf.readUInt8(0x00f7),
|
gauges: buf.readUInt8(0x00f7),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ export function saveSettings(buf: Buffer): SaveSettingsRequest {
|
|||||||
pack: buf.readUInt32LE(0x000c),
|
pack: buf.readUInt32LE(0x000c),
|
||||||
paperCup: buf.readUInt8(0x0011),
|
paperCup: buf.readUInt8(0x0011),
|
||||||
gauges: buf.readUInt8(0x0012),
|
gauges: buf.readUInt8(0x0012),
|
||||||
|
aura: buf.readUInt8(0x0013),
|
||||||
},
|
},
|
||||||
field_0010: buf.readUInt8(0x0010),
|
field_0010: buf.readUInt8(0x0010),
|
||||||
field_0013: buf.readUInt8(0x0013),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ export function _team(res: CreateAutoTeamResponse | LoadTeamResponse) {
|
|||||||
buf.writeInt16LE(0x0078, 0x0000);
|
buf.writeInt16LE(0x0078, 0x0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const leader = res.members.find(item => item.leader);
|
const leader = res.members.find(item => item.leader === true);
|
||||||
|
|
||||||
buf.writeUInt32LE(res.team.extId, 0x000c);
|
buf.writeUInt32LE(res.team.extId, 0x000c);
|
||||||
|
iconv.encode(leader ? leader.profile.name : "Error\0", "shift_jis").copy(buf, 0x0010);
|
||||||
iconv.encode(res.team.name, "shift_jis").copy(buf, 0x0024);
|
iconv.encode(res.team.name, "shift_jis").copy(buf, 0x0024);
|
||||||
|
iconv.encode(process.env.SHOP_NAME ? process.env.SHOP_NAME : "\0", "shift_jis").copy(buf, 0x0044);
|
||||||
buf.writeUInt32LE(res.team.nameBg, 0x00d8);
|
buf.writeUInt32LE(res.team.nameBg, 0x00d8);
|
||||||
buf.writeUInt32LE(res.team.nameFx, 0x00dc);
|
buf.writeUInt32LE(res.team.nameFx, 0x00dc);
|
||||||
buf.fill(0xff, 0x00e0, 0x00f9); // Bitset: Unlocked BGs probably
|
buf.fill(0xff, 0x00e0, 0x00f9); // Bitset: Unlocked BGs probably
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export function loadProfile2(res: LoadProfileResponse2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf.writeUInt16LE(0x0065, 0x0000);
|
buf.writeUInt16LE(0x0065, 0x0000);
|
||||||
|
buf.writeUInt16LE(res.unlocks.auras, 0x00b0);
|
||||||
buf.writeUInt8(res.unlocks.cup, 0x00b4);
|
buf.writeUInt8(res.unlocks.cup, 0x00b4);
|
||||||
buf.writeUInt16LE(res.unlocks.gauges, 0x00b8);
|
buf.writeUInt16LE(res.unlocks.gauges, 0x00b8);
|
||||||
buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x01e8);
|
buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x01e8);
|
||||||
@@ -92,6 +93,7 @@ export function loadProfile2(res: LoadProfileResponse2) {
|
|||||||
encodeMission(res.missions.solo).copy(buf, 0x06e4);
|
encodeMission(res.missions.solo).copy(buf, 0x06e4);
|
||||||
encodeChara(res.chara).copy(buf, 0x070c);
|
encodeChara(res.chara).copy(buf, 0x070c);
|
||||||
encodeBitmap(res.titles, 0xb4).copy(buf, 0x720);
|
encodeBitmap(res.titles, 0xb4).copy(buf, 0x720);
|
||||||
|
buf.writeUInt8(res.settings.aura, 0x07d6);
|
||||||
buf.writeUInt8(res.settings.paperCup, 0x07d9);
|
buf.writeUInt8(res.settings.paperCup, 0x07d9);
|
||||||
buf.writeUInt8(res.settings.gauges, 0x07da);
|
buf.writeUInt8(res.settings.gauges, 0x07da);
|
||||||
buf.writeUInt32LE(res.teamId || 0xffffffff, 0x07e0);
|
buf.writeUInt32LE(res.teamId || 0xffffffff, 0x07e0);
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ export function loadProfile3(res: LoadProfileResponse3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf.writeUInt16LE(0x012e, 0x0000);
|
buf.writeUInt16LE(0x012e, 0x0000);
|
||||||
|
buf.writeUInt16LE(res.unlocks.auras, 0x00b0);
|
||||||
buf.writeUInt8(res.unlocks.cup, 0x00b4);
|
buf.writeUInt8(res.unlocks.cup, 0x00b4);
|
||||||
buf.writeUInt16LE(res.unlocks.gauges, 0x00b8);
|
buf.writeUInt16LE(res.unlocks.gauges, 0x00b8);
|
||||||
buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x0218);
|
buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x0218);
|
||||||
@@ -92,6 +93,7 @@ export function loadProfile3(res: LoadProfileResponse3) {
|
|||||||
encodeMission(res.missions.solo).copy(buf, 0x0858);
|
encodeMission(res.missions.solo).copy(buf, 0x0858);
|
||||||
encodeChara(res.chara).copy(buf, 0x0880);
|
encodeChara(res.chara).copy(buf, 0x0880);
|
||||||
encodeBitmap(res.titles, 0xb4).copy(buf, 0x0894);
|
encodeBitmap(res.titles, 0xb4).copy(buf, 0x0894);
|
||||||
|
buf.writeUInt8(res.settings.aura, 0x094a);
|
||||||
buf.writeUInt8(res.settings.paperCup, 0x094d);
|
buf.writeUInt8(res.settings.paperCup, 0x094d);
|
||||||
buf.writeUInt8(res.settings.gauges, 0x094e);
|
buf.writeUInt8(res.settings.gauges, 0x094e);
|
||||||
buf.writeUInt32LE(res.teamId || 0xffffffff, 0x0954);
|
buf.writeUInt32LE(res.teamId || 0xffffffff, 0x0954);
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ export function loadTopTen(res: LoadTopTenResponse): Buffer {
|
|||||||
buf.writeUInt8(row.field_0F ? 1 : 0, innerOff + 0x000f); // Boolean
|
buf.writeUInt8(row.field_0F ? 1 : 0, innerOff + 0x000f); // Boolean
|
||||||
buf.writeUInt8(row.field_10, innerOff + 0x0010);
|
buf.writeUInt8(row.field_10, innerOff + 0x0010);
|
||||||
iconv.encode(row.driverName, "shift_jis").copy(buf, innerOff + 0x0014);
|
iconv.encode(row.driverName, "shift_jis").copy(buf, innerOff + 0x0014);
|
||||||
iconv.encode(row.teamName, "shift_jis").copy(buf, innerOff + 0x0028);
|
iconv.encode(row.team.name, "shift_jis").copy(buf, innerOff + 0x0028);
|
||||||
iconv.encode(row.shopName, "shift_jis").copy(buf, innerOff + 0x0048);
|
iconv.encode(row.shopName, "shift_jis").copy(buf, innerOff + 0x0048);
|
||||||
buf.writeUInt32LE(row.field_74, innerOff + 0x0074);
|
buf.writeUInt32LE(row.team.nameBg, innerOff + 0x0074);
|
||||||
buf.writeUInt16LE(row.field_78, innerOff + 0x0078);
|
buf.writeUInt16LE(row.team.nameFx, innerOff + 0x0078);
|
||||||
buf.writeUInt8(row.field_7C, innerOff + 0x007c);
|
buf.writeUInt8(row.field_7C, innerOff + 0x007c);
|
||||||
buf.writeUInt8(row.field_7D, innerOff + 0x007d);
|
buf.writeUInt8(row.field_7D, innerOff + 0x007d);
|
||||||
// 66 bytes of fucking nothing what
|
// 66 bytes of empty space at the end. Maybe a string used to live here?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ export async function createProfile(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const missions: MissionState = { team: [], solo: [] };
|
const missions: MissionState = { team: [], solo: [] };
|
||||||
const settings: Settings = { music: 0, pack: 13640, paperCup: 0, gauges: 5 };
|
const settings: Settings = { music: 0, pack: 13640, aura: 0, paperCup: 0, gauges: 5 };
|
||||||
const story: Story = { x: 0, y: 0, rows: [] };
|
const story: Story = { x: 0, y: 0, rows: [] };
|
||||||
const unlocks: Unlocks = {
|
const unlocks: Unlocks = {
|
||||||
|
auras: 1,
|
||||||
cup: 0,
|
cup: 0,
|
||||||
gauges: 1 << 5,
|
gauges: 1 << 5,
|
||||||
music: 0,
|
music: 0,
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ export async function loadGarage(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: "load_garage_res",
|
type: "load_garage_res",
|
||||||
cars: await w.car().loadAllCars(profileId),
|
cars: await w.car().loadCars(profileId, 10, req.fetchOffset),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export async function loadTopTen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { routeNo, minTimestamp } = selector;
|
const { routeNo, minTimestamp } = selector;
|
||||||
const src = await w.timeAttack().loadTopTen(routeNo, minTimestamp);
|
const src = await w.timeAttack().loadTop(routeNo, minTimestamp, 10);
|
||||||
|
|
||||||
if (src.length === 0) {
|
if (src.length === 0) {
|
||||||
continue;
|
continue;
|
||||||
@@ -33,10 +33,8 @@ export async function loadTopTen(
|
|||||||
field_0F: false,
|
field_0F: false,
|
||||||
field_10: 0,
|
field_10: 0,
|
||||||
driverName: srcItem.driverName,
|
driverName: srcItem.driverName,
|
||||||
teamName: process.env.TEAM_NAME || "",
|
|
||||||
shopName: process.env.SHOP_NAME || "",
|
shopName: process.env.SHOP_NAME || "",
|
||||||
field_74: 0,
|
team: srcItem.team,
|
||||||
field_78: 0,
|
|
||||||
field_7C: 0,
|
field_7C: 0,
|
||||||
field_7D: 0,
|
field_7D: 0,
|
||||||
ta: srcItem.ta,
|
ta: srcItem.ta,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export interface Settings {
|
export interface Settings {
|
||||||
music: number;
|
music: number;
|
||||||
pack: number;
|
pack: number;
|
||||||
|
aura: number;
|
||||||
paperCup: number;
|
paperCup: number;
|
||||||
gauges: number;
|
gauges: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export interface Unlocks {
|
export interface Unlocks {
|
||||||
|
auras: number;
|
||||||
cup: number;
|
cup: number;
|
||||||
gauges: number;
|
gauges: number;
|
||||||
music: number;
|
music: number;
|
||||||
|
|||||||
+9
-3
@@ -11,7 +11,11 @@ export type TeamSpec = Subtract<
|
|||||||
export interface CarRepository {
|
export interface CarRepository {
|
||||||
countCars(profileId: Id<Model.Profile>): Promise<number>;
|
countCars(profileId: Id<Model.Profile>): Promise<number>;
|
||||||
|
|
||||||
loadAllCars(profileId: Id<Model.Profile>): Promise<Model.Car[]>;
|
loadCars(
|
||||||
|
profileId: Id<Model.Profile>,
|
||||||
|
limit: number,
|
||||||
|
offset: number
|
||||||
|
): Promise<Model.Car[]>;
|
||||||
|
|
||||||
loadSelectedCar(profileId: Id<Model.Profile>): Promise<Model.Car>;
|
loadSelectedCar(profileId: Id<Model.Profile>): Promise<Model.Car>;
|
||||||
|
|
||||||
@@ -111,13 +115,15 @@ export interface TeamReservationRepository {
|
|||||||
// TODO extend and factorize
|
// TODO extend and factorize
|
||||||
export interface TopTenResult {
|
export interface TopTenResult {
|
||||||
driverName: string;
|
driverName: string;
|
||||||
|
team: Model.Team;
|
||||||
ta: Model.TimeAttackScore;
|
ta: Model.TimeAttackScore;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TimeAttackRepository {
|
export interface TimeAttackRepository {
|
||||||
loadTopTen(
|
loadTop(
|
||||||
routeNo: Model.RouteNo,
|
routeNo: Model.RouteNo,
|
||||||
minTimestamp: Date
|
minTimestamp: Date,
|
||||||
|
limit: number
|
||||||
): Promise<TopTenResult[]>;
|
): Promise<TopTenResult[]>;
|
||||||
|
|
||||||
loadAll(profileId: Id<Model.Profile>): Promise<Model.TimeAttackScore[]>;
|
loadAll(profileId: Id<Model.Profile>): Promise<Model.TimeAttackScore[]>;
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ export interface SaveSettingsRequest {
|
|||||||
dpoint: number; // ?? why
|
dpoint: number; // ?? why
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
field_0010: number;
|
field_0010: number;
|
||||||
field_0013: number;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ export interface LoadTopTenResponseRow {
|
|||||||
field_0F: boolean;
|
field_0F: boolean;
|
||||||
field_10: number;
|
field_10: number;
|
||||||
driverName: string;
|
driverName: string;
|
||||||
teamName: string;
|
|
||||||
shopName: string;
|
shopName: string;
|
||||||
field_74: number;
|
team: {
|
||||||
field_78: number;
|
name: string;
|
||||||
|
nameBg: number;
|
||||||
|
nameFx: number;
|
||||||
|
};
|
||||||
field_7C: number;
|
field_7C: number;
|
||||||
field_7D: number;
|
field_7D: number;
|
||||||
ta: TimeAttackScore;
|
ta: TimeAttackScore;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class SqlBackgroundsRepository
|
|||||||
const result = new Set<BackgroundCode>();
|
const result = new Set<BackgroundCode>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
result.add(parseInt(row.background_no) as BackgroundCode);
|
result.add(parseInt(row.background_no!) as BackgroundCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+25
-18
@@ -8,21 +8,21 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
function _extractRow(row: Row): Car {
|
function _extractRow(row: Row): Car {
|
||||||
return {
|
return {
|
||||||
selector: parseInt(row.selector) as CarSelector,
|
selector: parseInt(row.selector!) as CarSelector,
|
||||||
field_00: parseInt(row.field_00),
|
field_00: parseInt(row.field_00!),
|
||||||
field_02: parseInt(row.field_02),
|
field_02: parseInt(row.field_02!),
|
||||||
field_04: row.field_04.split(",").map((x: string) => parseInt(x)),
|
field_04: row.field_04!.split(",").map((x: string) => parseInt(x)),
|
||||||
field_46: parseInt(row.field_46),
|
field_46: parseInt(row.field_46!),
|
||||||
field_48: parseInt(row.field_48),
|
field_48: parseInt(row.field_48!),
|
||||||
field_4a: parseInt(row.field_4a),
|
field_4a: parseInt(row.field_4a!),
|
||||||
field_4c: parseInt(row.field_4c),
|
field_4c: parseInt(row.field_4c!),
|
||||||
field_50_lo: parseInt(row.field_50_lo),
|
field_50_lo: parseInt(row.field_50_lo!),
|
||||||
field_50_hi: parseInt(row.field_50_hi),
|
field_50_hi: parseInt(row.field_50_hi!),
|
||||||
field_58: parseInt(row.field_58),
|
field_58: parseInt(row.field_58!),
|
||||||
field_5a: parseInt(row.field_5a),
|
field_5a: parseInt(row.field_5a!),
|
||||||
field_5b: parseInt(row.field_5b),
|
field_5b: parseInt(row.field_5b!),
|
||||||
field_5c: parseInt(row.field_5c),
|
field_5c: parseInt(row.field_5c!),
|
||||||
field_5e: parseInt(row.field_5e),
|
field_5e: parseInt(row.field_5e!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,14 +37,21 @@ export class SqlCarRepository implements CarRepository {
|
|||||||
|
|
||||||
const row = await this._txn.fetchRow(countSql);
|
const row = await this._txn.fetchRow(countSql);
|
||||||
|
|
||||||
return parseInt(row!.result);
|
return parseInt(row!.result!);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadAllCars(profileId: Id<Profile>): Promise<Car[]> {
|
async loadCars(
|
||||||
|
profileId: Id<Profile>,
|
||||||
|
limit: number,
|
||||||
|
offset: number
|
||||||
|
): Promise<Car[]> {
|
||||||
const loadSql = sql
|
const loadSql = sql
|
||||||
.select("c.*")
|
.select("c.*")
|
||||||
.from("idz_car c")
|
.from("idz_car c")
|
||||||
.where("c.profile_id", profileId);
|
.where("c.profile_id", profileId)
|
||||||
|
.orderBy("selector asc")
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset);
|
||||||
|
|
||||||
const rows = await this._txn.fetchRows(loadSql);
|
const rows = await this._txn.fetchRows(loadSql);
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -9,16 +9,16 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
export function _extractChara(row: Row): Chara {
|
export function _extractChara(row: Row): Chara {
|
||||||
return {
|
return {
|
||||||
gender: row.gender as Gender,
|
gender: row.gender! as Gender,
|
||||||
field_02: parseInt(row.field_02),
|
field_02: parseInt(row.field_02!),
|
||||||
field_04: parseInt(row.field_04),
|
field_04: parseInt(row.field_04!),
|
||||||
field_06: parseInt(row.field_06),
|
field_06: parseInt(row.field_06!),
|
||||||
field_08: parseInt(row.field_08),
|
field_08: parseInt(row.field_08!),
|
||||||
field_0a: parseInt(row.field_0a),
|
field_0a: parseInt(row.field_0a!),
|
||||||
field_0c: parseInt(row.field_0c),
|
field_0c: parseInt(row.field_0c!),
|
||||||
field_0e: parseInt(row.field_0e),
|
field_0e: parseInt(row.field_0e!),
|
||||||
title: parseInt(row.title) as TitleCode,
|
title: parseInt(row.title!) as TitleCode,
|
||||||
background: parseInt(row.background) as BackgroundCode,
|
background: parseInt(row.background!) as BackgroundCode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export class SqlCoursePlaysRepository implements CoursePlaysRepository {
|
|||||||
const result = new Map<CourseNo, number>();
|
const result = new Map<CourseNo, number>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const courseNo = parseInt(row.course_no) as CourseNo;
|
const courseNo = parseInt(row.course_no!) as CourseNo;
|
||||||
const count = parseInt(row.count);
|
const count = parseInt(row.count!);
|
||||||
|
|
||||||
result.set(courseNo, count);
|
result.set(courseNo, count);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ export class SqlMissionsRepository implements FacetRepository<MissionState> {
|
|||||||
const rows = await this._txn.fetchRows(loadSoloSql);
|
const rows = await this._txn.fetchRows(loadSoloSql);
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const gridNo = parseInt(row.grid_no);
|
const gridNo = parseInt(row.grid_no!);
|
||||||
const cellNo = parseInt(row.cell_no);
|
const cellNo = parseInt(row.cell_no!);
|
||||||
const value = parseInt(row.value);
|
const value = parseInt(row.value!);
|
||||||
|
|
||||||
result.solo[gridNo].cells[cellNo] = value;
|
result.solo[gridNo].cells[cellNo] = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
export function _extractProfile(row: Row): Profile {
|
export function _extractProfile(row: Row): Profile {
|
||||||
return {
|
return {
|
||||||
aimeId: parseInt(row.aime_id) as AimeId,
|
aimeId: parseInt(row.aime_id!) as AimeId,
|
||||||
name: row.name,
|
name: row.name!,
|
||||||
lv: parseInt(row.lv),
|
lv: parseInt(row.lv!),
|
||||||
exp: parseInt(row.exp),
|
exp: parseInt(row.exp!),
|
||||||
fame: parseInt(row.fame),
|
fame: parseInt(row.fame!),
|
||||||
dpoint: parseInt(row.dpoint),
|
dpoint: parseInt(row.dpoint!),
|
||||||
mileage: parseInt(row.mileage),
|
mileage: parseInt(row.mileage!),
|
||||||
accessTime: new Date(row.access_time),
|
accessTime: new Date(row.access_time!),
|
||||||
registerTime: new Date(row.register_time),
|
registerTime: new Date(row.register_time!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ export class SqlSettingsRepository implements FacetRepository<Settings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
music: parseInt(row.music),
|
music: parseInt(row.music!),
|
||||||
pack: parseInt(row.pack),
|
pack: parseInt(row.pack!),
|
||||||
paperCup: parseInt(row.paper_cup),
|
aura: parseInt(row.aura!),
|
||||||
gauges: parseInt(row.gauges),
|
paperCup: parseInt(row.paper_cup!),
|
||||||
|
gauges: parseInt(row.gauges!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,11 +36,12 @@ export class SqlSettingsRepository implements FacetRepository<Settings> {
|
|||||||
id: profileId,
|
id: profileId,
|
||||||
music: settings.music,
|
music: settings.music,
|
||||||
pack: settings.pack,
|
pack: settings.pack,
|
||||||
|
aura: settings.aura,
|
||||||
paper_cup: settings.paperCup,
|
paper_cup: settings.paperCup,
|
||||||
gauges: settings.gauges,
|
gauges: settings.gauges,
|
||||||
})
|
})
|
||||||
.onConflict("id")
|
.onConflict("id")
|
||||||
.doUpdate(["music", "pack", "paper_cup", "gauges"]);
|
.doUpdate(["music", "pack", "aura", "paper_cup", "gauges"]);
|
||||||
|
|
||||||
await this._txn.modify(saveSql);
|
await this._txn.modify(saveSql);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export class SqlStoryRepository implements FacetRepository<Story> {
|
|||||||
// Must succeed even if nonexistent (required by save method below)
|
// Must succeed even if nonexistent (required by save method below)
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
x: header !== undefined ? parseInt(header.x) : 0,
|
x: header !== undefined ? parseInt(header.x!) : 0,
|
||||||
y: header !== undefined ? parseInt(header.y) : 0,
|
y: header !== undefined ? parseInt(header.y!) : 0,
|
||||||
rows: new Array<StoryRow>(),
|
rows: new Array<StoryRow>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,12 +43,12 @@ export class SqlStoryRepository implements FacetRepository<Story> {
|
|||||||
const rows = await this._txn.fetchRows(loadCellSql);
|
const rows = await this._txn.fetchRows(loadCellSql);
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const rowNo = parseInt(row.row_no);
|
const rowNo = parseInt(row.row_no!);
|
||||||
const colNo = parseInt(row.col_no);
|
const colNo = parseInt(row.col_no!);
|
||||||
const cell = result.rows[rowNo].cells[colNo];
|
const cell = result.rows[rowNo].cells[colNo];
|
||||||
|
|
||||||
cell.a = parseInt(row.a);
|
cell.a = parseInt(row.a!);
|
||||||
cell.b = parseInt(row.b);
|
cell.b = parseInt(row.b!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+5
-5
@@ -37,11 +37,11 @@ export class SqlTeamRepository implements TeamRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
extId: parseInt(row.ext_id) as ExtId<Team>,
|
extId: parseInt(row.ext_id!) as ExtId<Team>,
|
||||||
name: row.name,
|
name: row.name!,
|
||||||
nameBg: parseInt(row.name_bg),
|
nameBg: parseInt(row.name_bg!),
|
||||||
nameFx: parseInt(row.name_fx),
|
nameFx: parseInt(row.name_fx!),
|
||||||
registerTime: new Date(row.register_time),
|
registerTime: new Date(row.register_time!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export class SqlTeamAutoRepository implements TeamAutoRepository {
|
|||||||
return (
|
return (
|
||||||
row && [
|
row && [
|
||||||
{
|
{
|
||||||
serialNo: parseInt(row.serial_no),
|
serialNo: parseInt(row.serial_no!),
|
||||||
nameIdx: parseInt(row.name_idx),
|
nameIdx: parseInt(row.name_idx!),
|
||||||
},
|
},
|
||||||
row.id as Id<Team>,
|
row.id as Id<Team>,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ export class SqlTeamMemberRepository implements TeamMemberRepository {
|
|||||||
return rows.map(row => ({
|
return rows.map(row => ({
|
||||||
profile: _extractProfile(row),
|
profile: _extractProfile(row),
|
||||||
chara: _extractChara(row),
|
chara: _extractChara(row),
|
||||||
leader: !!row.leader,
|
leader: row.leader === "true",
|
||||||
joinTime: new Date(row.join_time),
|
joinTime: new Date(row.join_time!),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository {
|
|||||||
.where("id", teamId)
|
.where("id", teamId)
|
||||||
.forUpdate();
|
.forUpdate();
|
||||||
|
|
||||||
await this._txn.modify(lockSql);
|
await this._txn.fetchRow(lockSql);
|
||||||
|
|
||||||
// Double-check (with lock held) that there is room to join this team.
|
// Double-check (with lock held) that there is room to join this team.
|
||||||
// If this fails then the error will propagate to the client and it will
|
// If this fails then the error will propagate to the client and it will
|
||||||
@@ -92,7 +92,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository {
|
|||||||
|
|
||||||
const row = await this._txn.fetchRow(countSql);
|
const row = await this._txn.fetchRow(countSql);
|
||||||
|
|
||||||
if (parseInt(row!.count) >= 6) {
|
if (parseInt(row!.count!) >= 6) {
|
||||||
throw new Error(`Team ${teamId} is full`);
|
throw new Error(`Team ${teamId} is full`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class SqlTeamReservationRepository
|
|||||||
.where("tm.team_id", teamId);
|
.where("tm.team_id", teamId);
|
||||||
|
|
||||||
const memberRes = await this._txn.fetchRow(memberSql);
|
const memberRes = await this._txn.fetchRow(memberSql);
|
||||||
const memberCount = parseInt(memberRes!.count);
|
const memberCount = parseInt(memberRes!.count!);
|
||||||
|
|
||||||
const reservSql = sql
|
const reservSql = sql
|
||||||
.select("count(*) as count")
|
.select("count(*) as count")
|
||||||
@@ -36,7 +36,7 @@ export class SqlTeamReservationRepository
|
|||||||
.where("tr.team_id", teamId);
|
.where("tr.team_id", teamId);
|
||||||
|
|
||||||
const reservRes = await this._txn.fetchRow(reservSql);
|
const reservRes = await this._txn.fetchRow(reservSql);
|
||||||
const reservCount = parseInt(reservRes!.count);
|
const reservCount = parseInt(reservRes!.count!);
|
||||||
|
|
||||||
return memberCount + reservCount;
|
return memberCount + reservCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class SqlTicketsRepository implements FacetRepository<Tickets> {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
freeCar: row && {
|
freeCar: row && {
|
||||||
validFrom: new Date(row.valid_from),
|
validFrom: new Date(row.valid_from!),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-14
@@ -1,7 +1,8 @@
|
|||||||
import sql from "sql-bricks-postgres";
|
import sql from "sql-bricks-postgres";
|
||||||
|
|
||||||
import { RouteNo } from "../model/base";
|
import { ExtId, RouteNo } from "../model/base";
|
||||||
import { CarSelector } from "../model/car";
|
import { CarSelector } from "../model/car";
|
||||||
|
import { Team } from "../model/team";
|
||||||
import { Profile } from "../model/profile";
|
import { Profile } from "../model/profile";
|
||||||
import { TimeAttackScore } from "../model/timeAttack";
|
import { TimeAttackScore } from "../model/timeAttack";
|
||||||
import { TimeAttackRepository, TopTenResult } from "../repo";
|
import { TimeAttackRepository, TopTenResult } from "../repo";
|
||||||
@@ -10,36 +11,64 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
function _extractRow(row: Row): TimeAttackScore {
|
function _extractRow(row: Row): TimeAttackScore {
|
||||||
return {
|
return {
|
||||||
routeNo: parseInt(row.route_no) as RouteNo,
|
routeNo: parseInt(row.route_no!) as RouteNo,
|
||||||
timestamp: new Date(row.timestamp),
|
timestamp: new Date(row.timestamp!),
|
||||||
flags: parseInt(row.flags),
|
flags: parseInt(row.flags!),
|
||||||
totalTime: parseFloat(row.total_time),
|
totalTime: parseFloat(row.total_time!),
|
||||||
sectionTimes: row.section_times.split(",").map(parseFloat),
|
sectionTimes: row.section_times!.split(",").map(parseFloat),
|
||||||
grade: parseInt(row.grade),
|
grade: parseInt(row.grade!),
|
||||||
carSelector: parseInt(row.car_selector) as CarSelector,
|
carSelector: parseInt(row.car_selector!) as CarSelector,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SqlTimeAttackRepository implements TimeAttackRepository {
|
export class SqlTimeAttackRepository implements TimeAttackRepository {
|
||||||
constructor(private readonly _txn: Transaction) {}
|
constructor(private readonly _txn: Transaction) {}
|
||||||
|
|
||||||
async loadTopTen(
|
async loadTop(
|
||||||
routeNo: RouteNo,
|
routeNo: RouteNo,
|
||||||
minTimestamp: Date
|
minTimestamp: Date,
|
||||||
|
limit: number
|
||||||
): Promise<TopTenResult[]> {
|
): Promise<TopTenResult[]> {
|
||||||
|
// We're not using an ORM here so this join-heavy SQL is unfortunately
|
||||||
|
// going to be a boilerplated mess.
|
||||||
|
|
||||||
const loadSql = sql
|
const loadSql = sql
|
||||||
.select("p.name", "ta.*")
|
.select(
|
||||||
|
// Profile
|
||||||
|
"p.name as profile_name",
|
||||||
|
// Team
|
||||||
|
"t.ext_id as team_ext_id",
|
||||||
|
"t.name as team_name",
|
||||||
|
"t.name_bg as team_name_bg",
|
||||||
|
"t.name_fx as team_name_fx",
|
||||||
|
"t.register_time as team_register_time",
|
||||||
|
// Time Attack
|
||||||
|
"ta.*"
|
||||||
|
)
|
||||||
.from("idz_ta_best ta")
|
.from("idz_ta_best ta")
|
||||||
.join("idz_profile p", { "ta.profile_id": "p.id" })
|
.join("idz_profile p", { "ta.profile_id": "p.id" })
|
||||||
|
// This is an inner join, so it will exclude anybody who is not currently
|
||||||
|
// a member of a team from the leader boards. Since the only way to play
|
||||||
|
// without a team is to be ejected from one we can consider this an edge
|
||||||
|
// case that we probably don't need to worry about.
|
||||||
|
.join("idz_team_member tm", { "ta.profile_id": "tm.id" })
|
||||||
|
.join("idz_team t", { "tm.team_id": "t.id" })
|
||||||
.where("ta.route_no", routeNo)
|
.where("ta.route_no", routeNo)
|
||||||
.where(sql.gt("ta.timestamp", minTimestamp))
|
.where(sql.gt("ta.timestamp", minTimestamp))
|
||||||
.orderBy(["ta.total_time asc", "ta.timestamp asc"])
|
.orderBy(["ta.total_time asc", "ta.timestamp asc"])
|
||||||
.limit(10);
|
.limit(limit);
|
||||||
|
|
||||||
const rows = await this._txn.fetchRows(loadSql);
|
const rows = await this._txn.fetchRows(loadSql);
|
||||||
|
|
||||||
return rows.map(row => ({
|
return rows.map(row => ({
|
||||||
driverName: row.name,
|
driverName: row.profile_name!,
|
||||||
|
team: {
|
||||||
|
extId: parseInt(row.team_ext_id!) as ExtId<Team>,
|
||||||
|
name: row.team_name!,
|
||||||
|
nameBg: parseInt(row.team_name_bg!),
|
||||||
|
nameFx: parseInt(row.team_name_fx!),
|
||||||
|
registerTime: new Date(row.team_register_time!),
|
||||||
|
},
|
||||||
ta: _extractRow(row),
|
ta: _extractRow(row),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -93,7 +122,7 @@ export class SqlTimeAttackRepository implements TimeAttackRepository {
|
|||||||
|
|
||||||
await this._txn.modify(insertSql);
|
await this._txn.modify(insertSql);
|
||||||
} else {
|
} else {
|
||||||
if (score.totalTime < parseFloat(row.total_time)) {
|
if (score.totalTime < parseFloat(row.total_time!)) {
|
||||||
const updateSql = sql
|
const updateSql = sql
|
||||||
.update("idz_ta_best", {
|
.update("idz_ta_best", {
|
||||||
total_time: score.totalTime,
|
total_time: score.totalTime,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class SqlTitlesRepository implements FlagRepository<TitleCode> {
|
|||||||
const result = new Set<TitleCode>();
|
const result = new Set<TitleCode>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
result.add(parseInt(row.title_no) as TitleCode);
|
result.add(parseInt(row.title_no!) as TitleCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ export class SqlUnlocksRepository implements FacetRepository<Unlocks> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cup: parseInt(row.cup),
|
auras: parseInt(row.auras!),
|
||||||
gauges: parseInt(row.gauges),
|
cup: parseInt(row.cup!),
|
||||||
music: parseInt(row.music),
|
gauges: parseInt(row.gauges!),
|
||||||
lastMileageReward: parseInt(row.last_mileage_reward),
|
music: parseInt(row.music!),
|
||||||
|
lastMileageReward: parseInt(row.last_mileage_reward!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,13 +34,14 @@ export class SqlUnlocksRepository implements FacetRepository<Unlocks> {
|
|||||||
const saveSql = sql
|
const saveSql = sql
|
||||||
.insert("idz_unlocks", {
|
.insert("idz_unlocks", {
|
||||||
id: profileId,
|
id: profileId,
|
||||||
|
auras: unlocks.auras,
|
||||||
cup: unlocks.cup,
|
cup: unlocks.cup,
|
||||||
gauges: unlocks.gauges,
|
gauges: unlocks.gauges,
|
||||||
music: unlocks.music,
|
music: unlocks.music,
|
||||||
last_mileage_reward: unlocks.lastMileageReward,
|
last_mileage_reward: unlocks.lastMileageReward,
|
||||||
})
|
})
|
||||||
.onConflict("id")
|
.onConflict("id")
|
||||||
.doUpdate(["cup", "gauges", "music", "last_mileage_reward"]);
|
.doUpdate(["auras", "cup", "gauges", "music", "last_mileage_reward"]);
|
||||||
|
|
||||||
await this._txn.modify(saveSql);
|
await this._txn.modify(saveSql);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import * as sql from "sql-bricks-postgres";
|
|||||||
import { Id } from "../model";
|
import { Id } from "../model";
|
||||||
|
|
||||||
export interface Row {
|
export interface Row {
|
||||||
[key: string]: string;
|
[key: string]: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Transaction {
|
export interface Transaction {
|
||||||
|
|||||||
+38
-21
@@ -1,14 +1,10 @@
|
|||||||
import Database from "better-sqlite3";
|
import Database, { ResultRow } from "@decafcode/sqlite";
|
||||||
import { randomBytes } from "crypto";
|
import { randomBytes } from "crypto";
|
||||||
import * as sql from "sql-bricks-postgres";
|
import * as sql from "sql-bricks-postgres";
|
||||||
|
|
||||||
import { DataSource, Row, Transaction } from "./api";
|
import { DataSource, Row, Transaction } from "./api";
|
||||||
import { Id } from "../model";
|
import { Id } from "../model";
|
||||||
|
|
||||||
type MixedRow = {
|
|
||||||
[key: string]: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
// bless me father for i have sinned
|
// bless me father for i have sinned
|
||||||
const fuFixup = new RegExp(" FOR UPDATE$");
|
const fuFixup = new RegExp(" FOR UPDATE$");
|
||||||
|
|
||||||
@@ -41,7 +37,7 @@ function _preprocess(stmt: sql.Statement) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function _postprocess(obj: MixedRow): Row {
|
function _postprocess(obj: ResultRow): Row {
|
||||||
const result = {};
|
const result = {};
|
||||||
|
|
||||||
for (const [k, v] of Object.entries(obj)) {
|
for (const [k, v] of Object.entries(obj)) {
|
||||||
@@ -58,7 +54,7 @@ function _postprocess(obj: MixedRow): Row {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SqliteTransaction implements Transaction {
|
class SqliteTransaction implements Transaction {
|
||||||
constructor(private readonly _db: Database.Database) {}
|
constructor(private readonly _db: Database) {}
|
||||||
|
|
||||||
generateId<T>(): Id<T> {
|
generateId<T>(): Id<T> {
|
||||||
const buf = randomBytes(8);
|
const buf = randomBytes(8);
|
||||||
@@ -73,26 +69,43 @@ class SqliteTransaction implements Transaction {
|
|||||||
|
|
||||||
modify(stmt: sql.Statement): Promise<void> {
|
modify(stmt: sql.Statement): Promise<void> {
|
||||||
const params = _preprocess(stmt);
|
const params = _preprocess(stmt);
|
||||||
|
const prepared = this._db.prepare(params.text);
|
||||||
|
|
||||||
this._db.prepare(params.text).run(...params.values);
|
try {
|
||||||
|
prepared.run(params.values);
|
||||||
|
} finally {
|
||||||
|
prepared.close();
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchRow(stmt: sql.SelectStatement): Promise<Row | undefined> {
|
fetchRow(stmt: sql.SelectStatement): Promise<Row | undefined> {
|
||||||
const params = _preprocess(stmt);
|
const params = _preprocess(stmt);
|
||||||
const raw = this._db.prepare(params.text).get(...params.values);
|
const prepared = this._db.prepare(params.text);
|
||||||
const result = raw && _postprocess(raw);
|
|
||||||
|
|
||||||
return Promise.resolve(result);
|
try {
|
||||||
|
const raw = prepared.one(params.values);
|
||||||
|
const result = raw && _postprocess(raw);
|
||||||
|
|
||||||
|
return Promise.resolve(result);
|
||||||
|
} finally {
|
||||||
|
prepared.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchRows(stmt: sql.SelectStatement): Promise<Row[]> {
|
fetchRows(stmt: sql.SelectStatement): Promise<Row[]> {
|
||||||
const params = _preprocess(stmt);
|
const params = _preprocess(stmt);
|
||||||
const raw = this._db.prepare(params.text).all(...params.values);
|
const prepared = this._db.prepare(params.text);
|
||||||
const result = raw.map(_postprocess);
|
|
||||||
|
|
||||||
return Promise.resolve(result);
|
try {
|
||||||
|
const raw = prepared.all(params.values);
|
||||||
|
const result = raw.map(_postprocess);
|
||||||
|
|
||||||
|
return Promise.resolve(result);
|
||||||
|
} finally {
|
||||||
|
prepared.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
raw(sql: string): Promise<void> {
|
raw(sql: string): Promise<void> {
|
||||||
@@ -110,28 +123,32 @@ class SqliteDataSource implements DataSource {
|
|||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
const db = new Database(this._path);
|
const db = new Database(this._path);
|
||||||
|
|
||||||
db.defaultSafeIntegers();
|
|
||||||
db.prepare("pragma foreign_keys = on").run();
|
|
||||||
db.prepare("begin").run();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
db.exec("begin");
|
||||||
|
|
||||||
const txn = new SqliteTransaction(db);
|
const txn = new SqliteTransaction(db);
|
||||||
const result = await callback(txn);
|
const result = await callback(txn);
|
||||||
|
|
||||||
db.prepare("commit").run();
|
db.exec("commit");
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
db.prepare("rollback").run();
|
db.exec("rollback");
|
||||||
|
|
||||||
return Promise.reject(e);
|
return Promise.reject(e);
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vacuum(): Promise<void> {
|
vacuum(): Promise<void> {
|
||||||
const db = new Database(this._path);
|
const db = new Database(this._path);
|
||||||
|
|
||||||
db.prepare("vacuum").run();
|
try {
|
||||||
|
db.exec("vacuum");
|
||||||
|
} finally {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-7
@@ -3,15 +3,23 @@ import snakeCase from "snake-case";
|
|||||||
import { Row } from "./api";
|
import { Row } from "./api";
|
||||||
|
|
||||||
interface ColMapper<F> {
|
interface ColMapper<F> {
|
||||||
_read(str: string): F;
|
_read(str: string | null): F;
|
||||||
|
|
||||||
_write(val: F): string;
|
_write(val: F): string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Spec<R> = {
|
type Spec<R> = {
|
||||||
[K in keyof R]: ColMapper<R[K]>;
|
[K in keyof R]: ColMapper<R[K]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function _nn(str: string | null): string {
|
||||||
|
if (str === null) {
|
||||||
|
throw new Error("Unexpected NULL returned from database");
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function objects describing the precise way in which our SQL driver
|
* Function objects describing the precise way in which our SQL driver
|
||||||
* transmits and receives values to the database as strings. Note that we could
|
* transmits and receives values to the database as strings. Note that we could
|
||||||
@@ -20,25 +28,31 @@ type Spec<R> = {
|
|||||||
*/
|
*/
|
||||||
export const T = {
|
export const T = {
|
||||||
bigint: {
|
bigint: {
|
||||||
_read: (str: string) => BigInt(str),
|
_read: (str: string) => BigInt(_nn(str)),
|
||||||
_write: (val: bigint) => val.toString(),
|
_write: (val: bigint) => val.toString(),
|
||||||
},
|
},
|
||||||
boolean: {
|
boolean: {
|
||||||
_read: (str: string) => str === "true",
|
_read: (str: string) => _nn(str) === "true",
|
||||||
_write: (val: boolean) => val.toString(),
|
_write: (val: boolean) => val.toString(),
|
||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
_read: (str: string) => parseInt(str),
|
_read: (str: string) => parseInt(_nn(str)),
|
||||||
_write: (val: number) => val.toString(),
|
_write: (val: number) => val.toString(),
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
_read: (str: string) => str,
|
_read: (str: string) => _nn(str),
|
||||||
_write: (val: string) => val,
|
_write: (val: string) => val,
|
||||||
},
|
},
|
||||||
Date: {
|
Date: {
|
||||||
_read: (str: string) => new Date(str),
|
_read: (str: string) => new Date(_nn(str)),
|
||||||
_write: (val: Date) => val.toISOString(),
|
_write: (val: Date) => val.toISOString(),
|
||||||
},
|
},
|
||||||
|
nullable: <F>(inner: ColMapper<F>) => ({
|
||||||
|
_read: (str: string | null) =>
|
||||||
|
str !== null ? inner._read(str) : undefined,
|
||||||
|
_write: (val: F | undefined) =>
|
||||||
|
val !== undefined ? inner._write(val) : null,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
where /q node
|
where /q node
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo NodeJS does not appear to be installed.
|
echo NodeJS does not appear to be installed.
|
||||||
@@ -20,9 +22,6 @@ if not exist node_modules (
|
|||||||
echo The server's dependencies will now be installed.
|
echo The server's dependencies will now be installed.
|
||||||
echo This process requires an internet connection and may take some time.
|
echo This process requires an internet connection and may take some time.
|
||||||
echo.
|
echo.
|
||||||
echo NOTE: If install fails try running the following command as admin:
|
|
||||||
echo npm install -g windows-build-tools
|
|
||||||
echo.
|
|
||||||
|
|
||||||
pause
|
pause
|
||||||
call npm install
|
call npm install
|
||||||
@@ -40,8 +39,10 @@ if not exist node_modules (
|
|||||||
echo.
|
echo.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rem Project Diva requires TLSv1.0
|
||||||
|
|
||||||
set DEBUG=app:*
|
set DEBUG=app:*
|
||||||
node .\bin\index.js
|
node --tls-min-v1.0 .\bin\index.js
|
||||||
|
|
||||||
:end
|
:end
|
||||||
echo.
|
echo.
|
||||||
|
|||||||
Vendored
+1
-1
@@ -7,7 +7,7 @@ declare module "sql-bricks-postgres" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface LimitClause extends sql.SelectStatement {
|
interface LimitClause extends sql.SelectStatement {
|
||||||
offset(value: number): sql.Statement;
|
offset(value: number): sql.SelectStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PgInsertStatement extends sql.InsertStatement {
|
export interface PgInsertStatement extends sql.InsertStatement {
|
||||||
|
|||||||
Reference in New Issue
Block a user