mirror of
https://github.com/laochan-eacnet/backend.git
synced 2026-09-29 02:08:05 +03:00
add api stuff
This commit is contained in:
Generated
+84
@@ -11,6 +11,8 @@
|
||||
"dependencies": {
|
||||
"@cordisjs/logger": "^0.1.3",
|
||||
"@kamyu/kbinxml": "^2.0.2",
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@koa/router": "^12.0.1",
|
||||
"arc4": "^2.1.4",
|
||||
"dedent-js": "^1.0.1",
|
||||
"dotenv": "^16.4.5",
|
||||
@@ -27,6 +29,8 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "~29.5",
|
||||
"@types/koa": "^2.13.12",
|
||||
"@types/koa__cors": "^5.0.0",
|
||||
"@types/koa__router": "^12.0.4",
|
||||
"@types/koa-bodyparser": "^4.3.12",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "~20",
|
||||
@@ -1744,6 +1748,55 @@
|
||||
"resolved": "https://registry.npmmirror.com/@kamyu/kbinxml/-/kbinxml-2.0.2.tgz",
|
||||
"integrity": "sha512-41ZiqAunpiwF0yu4LtWcYyF4BIhRGrOPMRVe4eAXWQ1KRL1cZI0+XGqnQIIxlhTcfBMDMLUnrmqpXRAh1sxhPQ=="
|
||||
},
|
||||
"node_modules/@koa/cors": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@koa/cors/-/cors-5.0.0.tgz",
|
||||
"integrity": "sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==",
|
||||
"dependencies": {
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@koa/router": {
|
||||
"version": "12.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/@koa/router/-/router-12.0.1.tgz",
|
||||
"integrity": "sha512-ribfPYfHb+Uw3b27Eiw6NPqjhIhTpVFzEWLwyc/1Xp+DCdwRRyIlAUODX+9bPARF6aQtUu1+/PHzdNvRzcs/+Q==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4",
|
||||
"http-errors": "^2.0.0",
|
||||
"koa-compose": "^4.1.0",
|
||||
"methods": "^1.1.2",
|
||||
"path-to-regexp": "^6.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/@koa/router/node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@koa/router/node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz",
|
||||
@@ -2047,6 +2100,24 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/koa__cors": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/koa__cors/-/koa__cors-5.0.0.tgz",
|
||||
"integrity": "sha512-LCk/n25Obq5qlernGOK/2LUwa/2YJb2lxHUkkvYFDOpLXlVI6tKcdfCHRBQnOY4LwH6el5WOLs6PD/a8Uzau6g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/koa": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/koa__router": {
|
||||
"version": "12.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/koa__router/-/koa__router-12.0.4.tgz",
|
||||
"integrity": "sha512-Y7YBbSmfXZpa/m5UGGzb7XadJIRBRnwNY9cdAojZGp65Cpe5MAP3mOZE7e3bImt8dfKS4UFcR16SLH8L/z7PBw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/koa": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/koa-bodyparser": {
|
||||
"version": "4.3.12",
|
||||
"resolved": "https://registry.npmmirror.com/@types/koa-bodyparser/-/koa-bodyparser-4.3.12.tgz",
|
||||
@@ -5332,6 +5403,14 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||
@@ -5704,6 +5783,11 @@
|
||||
"node": "14 || >=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz",
|
||||
"integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw=="
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "~29.5",
|
||||
"@types/koa": "^2.13.12",
|
||||
"@types/koa__cors": "^5.0.0",
|
||||
"@types/koa__router": "^12.0.4",
|
||||
"@types/koa-bodyparser": "^4.3.12",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/node": "~20",
|
||||
@@ -45,6 +47,8 @@
|
||||
"dependencies": {
|
||||
"@cordisjs/logger": "^0.1.3",
|
||||
"@kamyu/kbinxml": "^2.0.2",
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@koa/router": "^12.0.1",
|
||||
"arc4": "^2.1.4",
|
||||
"dedent-js": "^1.0.1",
|
||||
"dotenv": "^16.4.5",
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import Router from "@koa/router";
|
||||
import { p2dRoutes } from "./p2d/index.js";
|
||||
import { DefaultContext, DefaultState, ParameterizedContext } from "koa";
|
||||
import { ILaochanContext } from "../types.js";
|
||||
|
||||
const router = new Router({
|
||||
prefix: '/api',
|
||||
})
|
||||
.use(p2dRoutes);
|
||||
|
||||
export const apiRoutes = router.routes();
|
||||
export type Context = ParameterizedContext<DefaultState, ILaochanContext & Router.RouterParamContext<DefaultState, DefaultContext>, any>;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Logger } from "@cordisjs/logger";
|
||||
import Router from "@koa/router";
|
||||
import { Context } from "../index.js";
|
||||
import { UserService } from "../../services/p2d/user.js";
|
||||
import { PlayerCustomizeSetting } from "../../database/index.js";
|
||||
|
||||
const logger = new Logger('p2d-api');
|
||||
const router = new Router({
|
||||
prefix: '/p2d',
|
||||
})
|
||||
|
||||
.get('/pdata/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
|
||||
if (!token) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('request pdata %s', token);
|
||||
const pdata = await ctx.resolve(UserService).getPDataDecoded(token);
|
||||
|
||||
ctx.status = pdata ? 200 : 404;
|
||||
ctx.body = pdata ? pdata : {};
|
||||
})
|
||||
|
||||
.get('/customize/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
|
||||
if (!token) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('request customize %s', token);
|
||||
const customize = await ctx.resolve(UserService).getCustomizeSetting(token);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = {
|
||||
customize,
|
||||
};
|
||||
})
|
||||
|
||||
.put('/customize/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
|
||||
if (!token) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('set customize %s', token);
|
||||
const customize = ctx.request.body as PlayerCustomizeSetting;
|
||||
|
||||
if (!customize) {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
customize._id = token;
|
||||
await ctx.resolve(UserService).upsertCustomizeSetting(customize);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = { success: true };
|
||||
});
|
||||
|
||||
|
||||
export const p2dRoutes = router.routes();
|
||||
@@ -1,555 +1,316 @@
|
||||
{
|
||||
"customize": {
|
||||
"2": [
|
||||
"I1100000",
|
||||
"I1100001",
|
||||
"I1100002",
|
||||
"I1100003",
|
||||
"I1100004",
|
||||
"I1100005",
|
||||
"I1100006",
|
||||
"I1100007",
|
||||
"I1100008",
|
||||
"I1100009",
|
||||
"I1100010",
|
||||
"I1100011",
|
||||
"I1100012",
|
||||
"I1100013",
|
||||
"I1100014",
|
||||
"I1100015",
|
||||
"I1100016",
|
||||
"I1100017",
|
||||
"I1100018",
|
||||
"I1100019",
|
||||
"I1100020"
|
||||
],
|
||||
"3": [
|
||||
"I1200000",
|
||||
"I1200001",
|
||||
"I1200002",
|
||||
"I1200003",
|
||||
"I1200004",
|
||||
"I1200005",
|
||||
"I1200006",
|
||||
"I1200007",
|
||||
"I1200008",
|
||||
"I1200009",
|
||||
"I1200010",
|
||||
"I1200011",
|
||||
"I1200012",
|
||||
"I1200013",
|
||||
"I1200014",
|
||||
"I1200015",
|
||||
"I1200016",
|
||||
"I1200017",
|
||||
"I1200018",
|
||||
"I1200019",
|
||||
"I1200020",
|
||||
"I1200021"
|
||||
],
|
||||
"4": [
|
||||
"I1300000",
|
||||
"I1300001",
|
||||
"I1300002",
|
||||
"I1300003",
|
||||
"I1300004",
|
||||
"I1300005",
|
||||
"I1300006",
|
||||
"I1300007",
|
||||
"I1300008",
|
||||
"I1300009",
|
||||
"I1300010",
|
||||
"I1300011",
|
||||
"I1300012",
|
||||
"I1300013",
|
||||
"I1300014",
|
||||
"I1300015",
|
||||
"I1300016",
|
||||
"I1300017",
|
||||
"I1300018",
|
||||
"I1300019",
|
||||
"I1300020",
|
||||
"I1300021",
|
||||
"I1300022",
|
||||
"I1300023",
|
||||
"I1300024",
|
||||
"I1300025",
|
||||
"I1300026",
|
||||
"I1300027",
|
||||
"I1300028",
|
||||
"I1300029",
|
||||
"I1300030",
|
||||
"I1300031",
|
||||
"I1300032",
|
||||
"I1300033",
|
||||
"I1300034",
|
||||
"I1300035",
|
||||
"I1300036",
|
||||
"I1300037",
|
||||
"I1300038",
|
||||
"I1300039",
|
||||
"I1300040",
|
||||
"I1300041",
|
||||
"I1300042",
|
||||
"I1300043",
|
||||
"I1300044",
|
||||
"I1300045",
|
||||
"I1300046",
|
||||
"I1300047",
|
||||
"I1300048",
|
||||
"I1300049",
|
||||
"I1300050",
|
||||
"I1300051",
|
||||
"I1300052",
|
||||
"I1300053",
|
||||
"I1300054",
|
||||
"I1300055",
|
||||
"I1300056",
|
||||
"I1300057",
|
||||
"I1300058",
|
||||
"I1300059",
|
||||
"I1300060",
|
||||
"I1300061",
|
||||
"I1300062",
|
||||
"I1300063",
|
||||
"I1300064",
|
||||
"I1300065",
|
||||
"I1300066",
|
||||
"I1300067",
|
||||
"I1300068",
|
||||
"I1300069",
|
||||
"I1300070",
|
||||
"I1300071",
|
||||
"I1300072",
|
||||
"I1300073",
|
||||
"I1300074",
|
||||
"I1300075",
|
||||
"I1300076",
|
||||
"I1300077",
|
||||
"I1300078",
|
||||
"I1300079",
|
||||
"I1300080",
|
||||
"I1300081",
|
||||
"I1300082",
|
||||
"I1300083",
|
||||
"I1300084",
|
||||
"I1300085",
|
||||
"I1300086",
|
||||
"I1300087",
|
||||
"I1300088",
|
||||
"I1300089",
|
||||
"I1300090",
|
||||
"I1300091",
|
||||
"I1300092",
|
||||
"I1300093",
|
||||
"I1300094",
|
||||
"I1300095",
|
||||
"I1300096",
|
||||
"I1300097",
|
||||
"I1300098",
|
||||
"I1300099",
|
||||
"I1300100",
|
||||
"I1300101",
|
||||
"I1300102",
|
||||
"I1300103",
|
||||
"I1300104",
|
||||
"I1300105",
|
||||
"I1300106",
|
||||
"I1300107",
|
||||
"I1300108",
|
||||
"I1300109",
|
||||
"I1300110",
|
||||
"I1300111",
|
||||
"I1300112",
|
||||
"I1300113",
|
||||
"I1300114",
|
||||
"I1300115",
|
||||
"I1300116",
|
||||
"I1300117",
|
||||
"I1300118",
|
||||
"I1300119",
|
||||
"I1300120",
|
||||
"I1300121",
|
||||
"I1300122",
|
||||
"I1300123",
|
||||
"I1300124",
|
||||
"I1300125",
|
||||
"I1300126",
|
||||
"I1300127",
|
||||
"I1300128",
|
||||
"I1300129",
|
||||
"I1300130",
|
||||
"I1300131",
|
||||
"I1300132",
|
||||
"I1300133",
|
||||
"I1300134",
|
||||
"I1300135",
|
||||
"I1300136",
|
||||
"I1300137",
|
||||
"I1300138",
|
||||
"I1300139",
|
||||
"I1300140",
|
||||
"I1300141",
|
||||
"I1300142",
|
||||
"I1300143",
|
||||
"I1300144",
|
||||
"I1300145",
|
||||
"I1300146",
|
||||
"I1300147",
|
||||
"I1300148",
|
||||
"I1300149",
|
||||
"I1300150",
|
||||
"I1300151",
|
||||
"I1300152",
|
||||
"I1300153",
|
||||
"I1300154",
|
||||
"I1300155",
|
||||
"I1300156",
|
||||
"I1300157",
|
||||
"I1300158",
|
||||
"I1300159",
|
||||
"I1300160",
|
||||
"I1300161",
|
||||
"I1300162",
|
||||
"I1300163",
|
||||
"I1300164",
|
||||
"I1300165",
|
||||
"I1300166",
|
||||
"I1300167",
|
||||
"I1300168",
|
||||
"I1300169",
|
||||
"I1300170",
|
||||
"I1300171",
|
||||
"I1300172",
|
||||
"I1300173",
|
||||
"I1300174",
|
||||
"I1300175",
|
||||
"I1300176",
|
||||
"I1300177",
|
||||
"I1300178",
|
||||
"I1300179",
|
||||
"I1300180",
|
||||
"I1300181",
|
||||
"I1300182",
|
||||
"I1300183",
|
||||
"I1300184",
|
||||
"I1300185",
|
||||
"I1300186"
|
||||
],
|
||||
"5": [
|
||||
"I1400000",
|
||||
"I1400001",
|
||||
"I1400002",
|
||||
"I1400003",
|
||||
"I1400004",
|
||||
"I1400005",
|
||||
"I1400006",
|
||||
"I1400007",
|
||||
"I1400008",
|
||||
"I1400009",
|
||||
"I1400010",
|
||||
"I1400011",
|
||||
"I1400012",
|
||||
"I1400013",
|
||||
"I1400014"
|
||||
],
|
||||
"6": [
|
||||
"I1500000",
|
||||
"I1500001",
|
||||
"I1500002",
|
||||
"I1500003",
|
||||
"I1500004",
|
||||
"I1500005",
|
||||
"I1500006",
|
||||
"I1500007",
|
||||
"I1500008",
|
||||
"I1500009",
|
||||
"I1500010",
|
||||
"I1500011"
|
||||
],
|
||||
"7": [
|
||||
"I1600000",
|
||||
"I1600001",
|
||||
"I1600002",
|
||||
"I1600003",
|
||||
"I1600004",
|
||||
"I1600005",
|
||||
"I1600006",
|
||||
"I1600007",
|
||||
"I1600008",
|
||||
"I1600009",
|
||||
"I1600010",
|
||||
"I1600011",
|
||||
"I1600012",
|
||||
"I1600013",
|
||||
"I1600014",
|
||||
"I1600015",
|
||||
"I1600016",
|
||||
"I1600017",
|
||||
"I1600018"
|
||||
],
|
||||
"8": [
|
||||
"I1700000",
|
||||
"I1700001",
|
||||
"I1700002",
|
||||
"I1700003",
|
||||
"I1700004",
|
||||
"I1700005",
|
||||
"I1700006",
|
||||
"I1700007",
|
||||
"I1700008",
|
||||
"I1700009",
|
||||
"I1700010",
|
||||
"I1700011"
|
||||
],
|
||||
"11": [
|
||||
"I1300000",
|
||||
"I1300001",
|
||||
"I1300002",
|
||||
"I1300003",
|
||||
"I1300004",
|
||||
"I1300005",
|
||||
"I1300006",
|
||||
"I1300007",
|
||||
"I1300008",
|
||||
"I1300009",
|
||||
"I1300010",
|
||||
"I1300011",
|
||||
"I1300012",
|
||||
"I1300013",
|
||||
"I1300014",
|
||||
"I1300015",
|
||||
"I1300016",
|
||||
"I1300017",
|
||||
"I1300018",
|
||||
"I1300019",
|
||||
"I1300020",
|
||||
"I1300021",
|
||||
"I1300022",
|
||||
"I1300023",
|
||||
"I1300024",
|
||||
"I1300025",
|
||||
"I1300026",
|
||||
"I1300027",
|
||||
"I1300028",
|
||||
"I1300029",
|
||||
"I1300030",
|
||||
"I1300031",
|
||||
"I1300032",
|
||||
"I1300033",
|
||||
"I1300034",
|
||||
"I1300035",
|
||||
"I1300036",
|
||||
"I1300037",
|
||||
"I1300038",
|
||||
"I1300039",
|
||||
"I1300040",
|
||||
"I1300041",
|
||||
"I1300042",
|
||||
"I1300043",
|
||||
"I1300044",
|
||||
"I1300045",
|
||||
"I1300046",
|
||||
"I1300047",
|
||||
"I1300048",
|
||||
"I1300049",
|
||||
"I1300050",
|
||||
"I1300051",
|
||||
"I1300052",
|
||||
"I1300053",
|
||||
"I1300054",
|
||||
"I1300055",
|
||||
"I1300056",
|
||||
"I1300057",
|
||||
"I1300058",
|
||||
"I1300059",
|
||||
"I1300060",
|
||||
"I1300061",
|
||||
"I1300062",
|
||||
"I1300063",
|
||||
"I1300064",
|
||||
"I1300065",
|
||||
"I1300066",
|
||||
"I1300067",
|
||||
"I1300068",
|
||||
"I1300069",
|
||||
"I1300070",
|
||||
"I1300071",
|
||||
"I1300072",
|
||||
"I1300073",
|
||||
"I1300074",
|
||||
"I1300075",
|
||||
"I1300076",
|
||||
"I1300077",
|
||||
"I1300078",
|
||||
"I1300079",
|
||||
"I1300080",
|
||||
"I1300081",
|
||||
"I1300082",
|
||||
"I1300083",
|
||||
"I1300084",
|
||||
"I1300085",
|
||||
"I1300086",
|
||||
"I1300087",
|
||||
"I1300088",
|
||||
"I1300089",
|
||||
"I1300090",
|
||||
"I1300091",
|
||||
"I1300092",
|
||||
"I1300093",
|
||||
"I1300094",
|
||||
"I1300095",
|
||||
"I1300096",
|
||||
"I1300097",
|
||||
"I1300098",
|
||||
"I1300099",
|
||||
"I1300100",
|
||||
"I1300101",
|
||||
"I1300102",
|
||||
"I1300103",
|
||||
"I1300104",
|
||||
"I1300105",
|
||||
"I1300106",
|
||||
"I1300107",
|
||||
"I1300108",
|
||||
"I1300109",
|
||||
"I1300110",
|
||||
"I1300111",
|
||||
"I1300112",
|
||||
"I1300113",
|
||||
"I1300114",
|
||||
"I1300115",
|
||||
"I1300116",
|
||||
"I1300117",
|
||||
"I1300118",
|
||||
"I1300119",
|
||||
"I1300120",
|
||||
"I1300121",
|
||||
"I1300122",
|
||||
"I1300123",
|
||||
"I1300124",
|
||||
"I1300125",
|
||||
"I1300126",
|
||||
"I1300127",
|
||||
"I1300128",
|
||||
"I1300129",
|
||||
"I1300130",
|
||||
"I1300131",
|
||||
"I1300132",
|
||||
"I1300133",
|
||||
"I1300134",
|
||||
"I1300135",
|
||||
"I1300136",
|
||||
"I1300137",
|
||||
"I1300138",
|
||||
"I1300139",
|
||||
"I1300140",
|
||||
"I1300141",
|
||||
"I1300142",
|
||||
"I1300143",
|
||||
"I1300144",
|
||||
"I1300145",
|
||||
"I1300146",
|
||||
"I1300147",
|
||||
"I1300148",
|
||||
"I1300149",
|
||||
"I1300150",
|
||||
"I1300151",
|
||||
"I1300152",
|
||||
"I1300153",
|
||||
"I1300154",
|
||||
"I1300155",
|
||||
"I1300156",
|
||||
"I1300157",
|
||||
"I1300158",
|
||||
"I1300159",
|
||||
"I1300160",
|
||||
"I1300161",
|
||||
"I1300162",
|
||||
"I1300163",
|
||||
"I1300164",
|
||||
"I1300165",
|
||||
"I1300166",
|
||||
"I1300167",
|
||||
"I1300168",
|
||||
"I1300169",
|
||||
"I1300170",
|
||||
"I1300171",
|
||||
"I1300172",
|
||||
"I1300173",
|
||||
"I1300174",
|
||||
"I1300175",
|
||||
"I1300176",
|
||||
"I1300177",
|
||||
"I1300178",
|
||||
"I1300179",
|
||||
"I1300180",
|
||||
"I1300181",
|
||||
"I1300182",
|
||||
"I1300183",
|
||||
"I1300184",
|
||||
"I1300185",
|
||||
"I1300186"
|
||||
],
|
||||
"10": [
|
||||
"I1900000",
|
||||
"I1900001",
|
||||
"I1900002",
|
||||
"I1900003",
|
||||
"I1900004",
|
||||
"I1900005",
|
||||
"I1900006",
|
||||
"I1900007",
|
||||
"I1900008",
|
||||
"I1900009",
|
||||
"I1900010",
|
||||
"I1900011",
|
||||
"I1900012",
|
||||
"I1900013",
|
||||
"I1900014",
|
||||
"I1900015",
|
||||
"I1900016",
|
||||
"I1900017",
|
||||
"I1900018",
|
||||
"I1900019",
|
||||
"I1900020",
|
||||
"I1900021",
|
||||
"I1900022"
|
||||
]
|
||||
},
|
||||
"other_customize": {
|
||||
"1": [
|
||||
"C1000000",
|
||||
"C1000001",
|
||||
"C1000002"
|
||||
],
|
||||
"2": [
|
||||
"C1100000",
|
||||
"C1100001",
|
||||
"C1100002",
|
||||
"C1100003",
|
||||
"C1100004",
|
||||
"C1100005",
|
||||
"C1100006"
|
||||
],
|
||||
"3": [
|
||||
"C1200000",
|
||||
"C1200001"
|
||||
],
|
||||
"4": [
|
||||
"C1300000",
|
||||
"C1300001",
|
||||
"C1300002",
|
||||
"C1300003"
|
||||
],
|
||||
"5": [
|
||||
"C1400000",
|
||||
"C1400001",
|
||||
"C1400002",
|
||||
"C1400003"
|
||||
]
|
||||
},
|
||||
"items": [
|
||||
"I1700000",
|
||||
"I1700001",
|
||||
"I1700002",
|
||||
"I1700003",
|
||||
"I1700004",
|
||||
"I1700005",
|
||||
"I1700006",
|
||||
"I1700007",
|
||||
"I1700008",
|
||||
"I1700009",
|
||||
"I1700010",
|
||||
"I1700011",
|
||||
"I1500000",
|
||||
"I1500001",
|
||||
"I1500002",
|
||||
"I1500003",
|
||||
"I1500004",
|
||||
"I1500005",
|
||||
"I1500006",
|
||||
"I1500007",
|
||||
"I1500008",
|
||||
"I1500009",
|
||||
"I1500010",
|
||||
"I1500011",
|
||||
"I1400000",
|
||||
"I1400001",
|
||||
"I1400002",
|
||||
"I1400003",
|
||||
"I1400004",
|
||||
"I1400005",
|
||||
"I1400006",
|
||||
"I1400007",
|
||||
"I1400008",
|
||||
"I1400009",
|
||||
"I1400010",
|
||||
"I1400011",
|
||||
"I1400012",
|
||||
"I1400013",
|
||||
"I1400014",
|
||||
"I1300000",
|
||||
"I1300001",
|
||||
"I1300002",
|
||||
"I1300003",
|
||||
"I1300004",
|
||||
"I1300005",
|
||||
"I1300006",
|
||||
"I1300007",
|
||||
"I1300008",
|
||||
"I1300009",
|
||||
"I1300010",
|
||||
"I1300011",
|
||||
"I1300012",
|
||||
"I1300013",
|
||||
"I1300014",
|
||||
"I1300015",
|
||||
"I1300016",
|
||||
"I1300017",
|
||||
"I1300018",
|
||||
"I1300019",
|
||||
"I1300020",
|
||||
"I1300021",
|
||||
"I1300022",
|
||||
"I1300023",
|
||||
"I1300024",
|
||||
"I1300025",
|
||||
"I1300026",
|
||||
"I1300027",
|
||||
"I1300028",
|
||||
"I1300029",
|
||||
"I1300030",
|
||||
"I1300031",
|
||||
"I1300032",
|
||||
"I1300033",
|
||||
"I1300034",
|
||||
"I1300035",
|
||||
"I1300036",
|
||||
"I1300037",
|
||||
"I1300038",
|
||||
"I1300039",
|
||||
"I1300040",
|
||||
"I1300041",
|
||||
"I1300042",
|
||||
"I1300043",
|
||||
"I1300044",
|
||||
"I1300045",
|
||||
"I1300046",
|
||||
"I1300047",
|
||||
"I1300048",
|
||||
"I1300049",
|
||||
"I1300050",
|
||||
"I1300051",
|
||||
"I1300052",
|
||||
"I1300053",
|
||||
"I1300054",
|
||||
"I1300055",
|
||||
"I1300056",
|
||||
"I1300057",
|
||||
"I1300058",
|
||||
"I1300059",
|
||||
"I1300060",
|
||||
"I1300061",
|
||||
"I1300062",
|
||||
"I1300063",
|
||||
"I1300064",
|
||||
"I1300065",
|
||||
"I1300066",
|
||||
"I1300067",
|
||||
"I1300068",
|
||||
"I1300069",
|
||||
"I1300070",
|
||||
"I1300071",
|
||||
"I1300072",
|
||||
"I1300073",
|
||||
"I1300074",
|
||||
"I1300075",
|
||||
"I1300076",
|
||||
"I1300077",
|
||||
"I1300078",
|
||||
"I1300079",
|
||||
"I1300080",
|
||||
"I1300081",
|
||||
"I1300082",
|
||||
"I1300083",
|
||||
"I1300084",
|
||||
"I1300085",
|
||||
"I1300086",
|
||||
"I1300087",
|
||||
"I1300088",
|
||||
"I1300089",
|
||||
"I1300090",
|
||||
"I1300091",
|
||||
"I1300092",
|
||||
"I1300093",
|
||||
"I1300094",
|
||||
"I1300095",
|
||||
"I1300096",
|
||||
"I1300097",
|
||||
"I1300098",
|
||||
"I1300099",
|
||||
"I1300100",
|
||||
"I1300101",
|
||||
"I1300102",
|
||||
"I1300103",
|
||||
"I1300104",
|
||||
"I1300105",
|
||||
"I1300106",
|
||||
"I1300107",
|
||||
"I1300108",
|
||||
"I1300109",
|
||||
"I1300110",
|
||||
"I1300111",
|
||||
"I1300112",
|
||||
"I1300113",
|
||||
"I1300114",
|
||||
"I1300115",
|
||||
"I1300116",
|
||||
"I1300117",
|
||||
"I1300118",
|
||||
"I1300119",
|
||||
"I1300120",
|
||||
"I1300121",
|
||||
"I1300122",
|
||||
"I1300123",
|
||||
"I1300124",
|
||||
"I1300125",
|
||||
"I1300126",
|
||||
"I1300127",
|
||||
"I1300128",
|
||||
"I1300129",
|
||||
"I1300130",
|
||||
"I1300131",
|
||||
"I1300132",
|
||||
"I1300133",
|
||||
"I1300134",
|
||||
"I1300135",
|
||||
"I1300136",
|
||||
"I1300137",
|
||||
"I1300138",
|
||||
"I1300139",
|
||||
"I1300140",
|
||||
"I1300141",
|
||||
"I1300142",
|
||||
"I1300143",
|
||||
"I1300144",
|
||||
"I1300145",
|
||||
"I1300146",
|
||||
"I1300147",
|
||||
"I1300148",
|
||||
"I1300149",
|
||||
"I1300150",
|
||||
"I1300151",
|
||||
"I1300152",
|
||||
"I1300153",
|
||||
"I1300154",
|
||||
"I1300155",
|
||||
"I1300156",
|
||||
"I1300157",
|
||||
"I1300158",
|
||||
"I1300159",
|
||||
"I1300160",
|
||||
"I1300161",
|
||||
"I1300162",
|
||||
"I1300163",
|
||||
"I1300164",
|
||||
"I1300165",
|
||||
"I1300166",
|
||||
"I1300167",
|
||||
"I1300168",
|
||||
"I1300169",
|
||||
"I1300170",
|
||||
"I1300171",
|
||||
"I1300172",
|
||||
"I1300173",
|
||||
"I1300174",
|
||||
"I1300175",
|
||||
"I1300176",
|
||||
"I1300177",
|
||||
"I1300178",
|
||||
"I1300179",
|
||||
"I1300180",
|
||||
"I1300181",
|
||||
"I1300182",
|
||||
"I1300183",
|
||||
"I1300184",
|
||||
"I1300185",
|
||||
"I1300186",
|
||||
"I1200000",
|
||||
"I1200001",
|
||||
"I1200002",
|
||||
"I1200003",
|
||||
"I1200004",
|
||||
"I1200005",
|
||||
"I1200006",
|
||||
"I1200007",
|
||||
"I1200008",
|
||||
"I1200009",
|
||||
"I1200010",
|
||||
"I1200011",
|
||||
"I1200012",
|
||||
"I1200013",
|
||||
"I1200014",
|
||||
"I1200015",
|
||||
"I1200016",
|
||||
"I1200017",
|
||||
"I1200018",
|
||||
"I1200019",
|
||||
"I1200020",
|
||||
"I1200021",
|
||||
"I1100000",
|
||||
"I1100001",
|
||||
"I1100002",
|
||||
"I1100003",
|
||||
"I1100004",
|
||||
"I1100005",
|
||||
"I1100006",
|
||||
"I1100007",
|
||||
"I1100008",
|
||||
"I1100009",
|
||||
"I1100010",
|
||||
"I1100011",
|
||||
"I1100012",
|
||||
"I1100013",
|
||||
"I1100014",
|
||||
"I1100015",
|
||||
"I1100016",
|
||||
"I1100017",
|
||||
"I1100018",
|
||||
"I1100019",
|
||||
"I1100020",
|
||||
"I1900000",
|
||||
"I1900001",
|
||||
"I1900002",
|
||||
"I1900003",
|
||||
"I1900004",
|
||||
"I1900005",
|
||||
"I1900006",
|
||||
"I1900007",
|
||||
"I1900008",
|
||||
"I1900009",
|
||||
"I1900010",
|
||||
"I1900011",
|
||||
"I1900012",
|
||||
"I1900013",
|
||||
"I1900014",
|
||||
"I1900015",
|
||||
"I1900016",
|
||||
"I1900017",
|
||||
"I1900018",
|
||||
"I1900019",
|
||||
"I1900020",
|
||||
"I1900021",
|
||||
"I1900022",
|
||||
"I1600000",
|
||||
"I1600001",
|
||||
"I1600002",
|
||||
"I1600003",
|
||||
"I1600004",
|
||||
"I1600005",
|
||||
"I1600006",
|
||||
"I1600007",
|
||||
"I1600008",
|
||||
"I1600009",
|
||||
"I1600010",
|
||||
"I1600011",
|
||||
"I1600012",
|
||||
"I1600013",
|
||||
"I1600014",
|
||||
"I1600015",
|
||||
"I1600016",
|
||||
"I1600017",
|
||||
"I1600018",
|
||||
"I1000000",
|
||||
"I1000001",
|
||||
"I1800001",
|
||||
@@ -4,7 +4,7 @@ import { Service } from "./service.js";
|
||||
import { User } from "./user.js";
|
||||
import data from './data.json' with { type: "json" };
|
||||
import { inject, singleton } from "tsyringe";
|
||||
import { Db } from "mongodb";
|
||||
import { UserService } from "../../services/p2d/user.js";
|
||||
|
||||
function Combine(...modules: ({ new(): any })[]) {
|
||||
class Modules {
|
||||
@@ -31,11 +31,11 @@ function Combine(...modules: ({ new(): any })[]) {
|
||||
@singleton()
|
||||
export default class extends Combine(Service, User, Game) {
|
||||
constructor(
|
||||
@inject(Db) private readonly db: Db,
|
||||
@inject(UserService) private readonly userService: UserService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.db;
|
||||
this.userService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
import { eacnet } from "../../decorators/eacnet.js";
|
||||
import { Context } from "../../types.js";
|
||||
import { v } from "../../utils/kxml-value.js";
|
||||
import { ITEM_LIST, MUSIC_LIST } from "./index.js";
|
||||
import { PlayerPlayLog } from "../../database/index.js";
|
||||
import { fromToken } from "../../utils/laochan-id.js";
|
||||
import { UserService } from "../../services/p2d/user.js";
|
||||
|
||||
export class User {
|
||||
userService: UserService;
|
||||
|
||||
|
||||
@eacnet('p2d')
|
||||
async sendGradeCertificationLog(ctx: Context) {
|
||||
await this.userService.addCourseLog({
|
||||
player: ctx.token,
|
||||
...ctx.body,
|
||||
});
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async consumeItem() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
free_count: v.s32(1000),
|
||||
not_free_count: v.s32(1000),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async reserveConsumeItem() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getCustomizeSetting(ctx: Context) {
|
||||
const { customize, other_customize } = await this.userService.getCustomizeSetting(ctx.token);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
customize: customize.map(c => ({
|
||||
item_category: v.s32(c.item_category),
|
||||
item_id: v.str(c.item_id),
|
||||
})),
|
||||
other_customize: other_customize.map(c => ({
|
||||
item_category: v.s32(c.item_category),
|
||||
item_id: v.str(c.item_id),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async gameEnd() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPointList() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
point_count: v.s32(1),
|
||||
point: [{
|
||||
point_id: v.str('P0100000'),
|
||||
point_num: v.u32(114514),
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPrivilegeClient() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
list_num: v.s32(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPrivilegeServer() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
list_num: v.s32(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async reportMusicResult(ctx: Context) {
|
||||
const result = ctx.body as PlayerPlayLog;
|
||||
result.player = ctx.token;
|
||||
|
||||
let updateTask = Promise.resolve();
|
||||
|
||||
const play_style = Math.floor(result.note_id / 5);
|
||||
const diff = result.note_id % 5;
|
||||
|
||||
updateTask = (async () => {
|
||||
const musicData = await this.userService.getMusicData(result.player, result.music_id, play_style);
|
||||
|
||||
if (result.clear_flag > musicData.clear_flag[diff])
|
||||
musicData.clear_flag[diff] = result.clear_flag;
|
||||
|
||||
if (result.miss_count < musicData.miss_count[diff])
|
||||
musicData.miss_count[diff] = result.miss_count;
|
||||
|
||||
if (result.score > musicData.score[diff])
|
||||
musicData.score[diff] = result.score;
|
||||
|
||||
if (result.clear_flag >= 2)
|
||||
musicData.clear_num[diff]++;
|
||||
|
||||
musicData.play_num[diff]++;
|
||||
|
||||
this.userService.upsertMusicData(musicData);
|
||||
})();
|
||||
|
||||
await Promise.all([
|
||||
this.userService.addPlayLog(result),
|
||||
updateTask,
|
||||
])
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async savePlayData(ctx: Context) {
|
||||
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
|
||||
|
||||
await this.userService.upsertPDataBinary(ctx.token, pdata, check_sum);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPlayData(ctx: Context) {
|
||||
const result = await this.userService.getPDataBinary(ctx.token);
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
status: v.s32(1),
|
||||
error: v.s32(404),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
size: v.s32(result.pdata.length),
|
||||
rest_size: v.s32(0),
|
||||
check_sum: v.str(result.check_sum),
|
||||
pdata: v.bin(result.pdata),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async registPlayer(ctx: Context) {
|
||||
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
|
||||
|
||||
await this.userService.upsertPDataBinary(ctx.token, pdata, check_sum);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async checkPlayData(ctx: Context) {
|
||||
const { check_sum } = ctx.body as { check_sum: string };
|
||||
|
||||
const checksum = await this.userService.getPDataChecksum(ctx.token);
|
||||
const id = fromToken(ctx.token);
|
||||
|
||||
if (!checksum) {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
infinitas_id: v.str(id),
|
||||
valid: v.bool(false),
|
||||
is_exist: v.bool(false),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (checksum !== check_sum) {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
infinitas_id: v.str(id),
|
||||
valid: v.bool(false),
|
||||
is_exist: v.bool(true),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
infinitas_id: v.str(id),
|
||||
valid: v.bool(true),
|
||||
is_exist: v.bool(true),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getRivalInfo() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {},
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getMusicList() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
status: v.s32(0),
|
||||
check_sum: v.str('2e49eba0b6ca8b6898f5d684ea0c3bc1ed6f1d572a4d308cf32727d64069a588'),
|
||||
music_list: {
|
||||
music_num: v.s32(MUSIC_LIST.length),
|
||||
music: MUSIC_LIST,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getCompeScoreData() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {},
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getMusicData(ctx: Context) {
|
||||
const { play_style } = ctx.body as { play_style: number };
|
||||
|
||||
const musicDatas = await this.userService.getMusicDatas(ctx.token, play_style);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
play_style: v.s32(play_style),
|
||||
result: {
|
||||
music: musicDatas.map(data => ({
|
||||
music_id: v.s32(data.music_id),
|
||||
score: v.s32(data.score),
|
||||
clear_flag: v.s32(data.clear_flag),
|
||||
miss_count: v.s32(data.miss_count),
|
||||
play_num: v.s32(data.play_num),
|
||||
clear_num: v.s32(data.clear_num),
|
||||
}))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getItemList() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
item_list: {
|
||||
item_num: v.s32(ITEM_LIST.length),
|
||||
item: ITEM_LIST.map(id => ({
|
||||
item_id: v.str(id),
|
||||
not_free_count: v.s32(1000),
|
||||
free_count: v.s32(1000),
|
||||
}))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -76,3 +76,15 @@ export interface CourseResult {
|
||||
clear_rate: number;
|
||||
groove_gage: number;
|
||||
}
|
||||
|
||||
export interface PlayerCustomizeSetting {
|
||||
_id: string;
|
||||
customize: {
|
||||
item_id: string,
|
||||
item_category: number,
|
||||
}[];
|
||||
other_customize: {
|
||||
item_id: string,
|
||||
item_category: number,
|
||||
}[];
|
||||
}
|
||||
|
||||
+2
-149
@@ -1,130 +1,12 @@
|
||||
import _ from 'lodash';
|
||||
import { to_bin } from '@kamyu/kbinxml';
|
||||
import { LZ77 } from '../utils/lz77.js';
|
||||
|
||||
function serializeValue(value: any, type: string): string {
|
||||
if (
|
||||
[
|
||||
's8',
|
||||
'u8',
|
||||
's16',
|
||||
'u16',
|
||||
's32',
|
||||
'u32',
|
||||
's64',
|
||||
'u64',
|
||||
'float',
|
||||
'double',
|
||||
].includes(type)
|
||||
) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (['b', 'bool'].includes(type)) {
|
||||
return value ? '1' : '0';
|
||||
}
|
||||
|
||||
if (['bin', 'binary'].includes(type)) {
|
||||
if (!(value instanceof Buffer)) {
|
||||
throw new Error('binary with non-buffer value');
|
||||
}
|
||||
|
||||
return value.toString('hex');
|
||||
}
|
||||
|
||||
if (['ip4', 'str', 'string'].includes(type)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (type == 'time') {
|
||||
if (value instanceof Date) {
|
||||
return Math.floor(value.valueOf() / 1000).toString();
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
throw new Error('time with not date or number');
|
||||
}
|
||||
|
||||
throw new Error(`unsupported type ${type}`);
|
||||
}
|
||||
|
||||
function serializeObject(obj: Object, name: string, linePrefix: string = '') {
|
||||
let output = `${linePrefix}<${name}`;
|
||||
|
||||
const entries = Object.entries(obj);
|
||||
const attrs = entries
|
||||
.filter(v => v[0].startsWith('$'))
|
||||
.map(v => [v[0].substring(1), v[1]]);
|
||||
|
||||
// handle kxml value shit
|
||||
const value = entries.find(v => v[0] == '__value');
|
||||
|
||||
if (value && value[1] instanceof Array) {
|
||||
attrs.push(['__count', value[1].length]);
|
||||
}
|
||||
|
||||
if (value && value[1] instanceof Buffer) {
|
||||
attrs.push(['__size', value[1].length]);
|
||||
}
|
||||
|
||||
// the kbinxml library had issue when serializing non-ordered stuff
|
||||
attrs.sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
|
||||
|
||||
for (const attr of attrs) {
|
||||
output += ` ${attr[0]}="${_.escape(attr[1])}"`;
|
||||
}
|
||||
|
||||
output += '>';
|
||||
|
||||
if (value) {
|
||||
const type = attrs.find(v => v[0] == '__type');
|
||||
|
||||
if (!type) {
|
||||
// or normal xml innerXml?
|
||||
throw new Error(`value with no type in ${name}`);
|
||||
}
|
||||
|
||||
output += (value[1] instanceof Array ? value[1] : [value[1]])
|
||||
.map(v => serializeValue(v, type[1]))
|
||||
.join(' ');
|
||||
} else {
|
||||
const elements = entries
|
||||
.filter(v => !v[0].startsWith('$'))
|
||||
.sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
|
||||
|
||||
if (elements.length) {
|
||||
output += '\n';
|
||||
|
||||
for (const element of elements) {
|
||||
if (element[1] instanceof Array) {
|
||||
for (const item of element[1])
|
||||
output += serializeObject(item, element[0], linePrefix + ' ');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
output += serializeObject(element[1], element[0], linePrefix + ' ');
|
||||
}
|
||||
|
||||
output += linePrefix;
|
||||
}
|
||||
}
|
||||
|
||||
return output + `</${name}>\n`;
|
||||
}
|
||||
import { toKBinXml } from '../utils/kbinxml.js';
|
||||
|
||||
export function eacnet(topName: string, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8'): MethodDecorator {
|
||||
return function (_target, _propertyKey, descriptor) {
|
||||
const orig = descriptor.value as Function;
|
||||
descriptor.value = async function (...args: any[]) {
|
||||
const obj = await orig.apply(this, args);
|
||||
const xml =
|
||||
`<?xml version="1.0" encoding="${encoding}"?>` + serializeObject(obj, topName);
|
||||
|
||||
const kxml = to_bin(xml);
|
||||
const kxml = toKBinXml(topName, obj, encoding);
|
||||
const compressed = LZ77.compress(kxml.data);
|
||||
const result = Buffer.allocUnsafe(compressed.length + 0x2E);
|
||||
|
||||
@@ -135,32 +17,3 @@ export function eacnet(topName: string, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8
|
||||
} as unknown as any;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(serializeObject({
|
||||
// $attr: 'attr1',
|
||||
// sb: {
|
||||
// $attr: 'attr2',
|
||||
// $__type: 'str',
|
||||
// __value: 'sba',
|
||||
// },
|
||||
// sb1: {
|
||||
// $attr: 'attr3',
|
||||
// nested: {
|
||||
// $attr: 'attr4',
|
||||
// $__type: 's32',
|
||||
// __value: [1, 1, 4, 5, 1, 4],
|
||||
// }
|
||||
// },
|
||||
// array: [{
|
||||
// $attr: 'test'
|
||||
// }, {
|
||||
// $attr: 'test2'
|
||||
// }, {
|
||||
// $attr: 'test3'
|
||||
// }, {
|
||||
// $attr: 'test4'
|
||||
// }, {
|
||||
// $attr: 'test5'
|
||||
// }]
|
||||
// }, 'test'))
|
||||
|
||||
|
||||
+24
-17
@@ -1,15 +1,17 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import cors from '@koa/cors';
|
||||
import Koa, { DefaultState } from 'koa';
|
||||
import bodyParser from 'koa-bodyparser';
|
||||
import { InjectionToken, Provider, container } from 'tsyringe';
|
||||
|
||||
import { ILaochanContext } from './types.js';
|
||||
import { DefaultService } from './services/default.js';
|
||||
import { DefaultService } from './controllers/default.js';
|
||||
import { Logger } from '@cordisjs/logger';
|
||||
import { eacnet } from './middlewares/eacnet.js';
|
||||
import config from './utils/config.js';
|
||||
import { MongoClient, Db } from 'mongodb';
|
||||
import { apiRoutes } from './apis/index.js';
|
||||
|
||||
function tryResolve<T>(token: InjectionToken<T>): T | undefined {
|
||||
if (!container.isRegistered(token)) return undefined;
|
||||
@@ -57,21 +59,22 @@ async function main(): Promise<void> {
|
||||
ctx.body = `${e.stack ?? 'Internal Error'}`;
|
||||
}
|
||||
})
|
||||
.use(cors())
|
||||
.use(bodyParser({
|
||||
formLimit: '1mb',
|
||||
}))
|
||||
.use(eacnet)
|
||||
.use(async (ctx, next) => {
|
||||
if (!ctx.service) {
|
||||
return await next();
|
||||
}
|
||||
|
||||
ctx.logger = ctx.service
|
||||
? register(`logger:${ctx.service.name}:${ctx.service.method}`, {
|
||||
useValue: new Logger(`${ctx.service.name}:${ctx.service.method}`),
|
||||
})
|
||||
: logger;
|
||||
|
||||
if (!ctx.service) {
|
||||
return await next();
|
||||
}
|
||||
|
||||
let service: object | undefined = tryResolve(ctx.service.name);
|
||||
|
||||
if (service === undefined) {
|
||||
@@ -80,17 +83,17 @@ async function main(): Promise<void> {
|
||||
|
||||
try {
|
||||
module = await import(
|
||||
`./services/${ctx.service.name}/index.js`
|
||||
`./controllers/${ctx.service.name}/index.js`
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.code != 'ERR_MODULE_NOT_FOUND') {
|
||||
logger.error(
|
||||
ctx.logger.error(
|
||||
`load service error %s`,
|
||||
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
|
||||
);
|
||||
} else {
|
||||
module = await import(
|
||||
`./services/${ctx.service.name}.js`
|
||||
`./controllers/${ctx.service.name}.js`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -100,19 +103,17 @@ async function main(): Promise<void> {
|
||||
});
|
||||
} catch (e) {
|
||||
if (e.code != 'ERR_MODULE_NOT_FOUND') {
|
||||
logger.error(
|
||||
`load service error %s`,
|
||||
ctx.logger.error(
|
||||
`load controller error %s`,
|
||||
JSON.stringify(e, Object.getOwnPropertyNames(e), 2),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
'%s %s.%s [%s]: request = %s',
|
||||
ctx.logger.info(
|
||||
'%s [%s]: request = %s',
|
||||
ctx.url,
|
||||
ctx.service.name,
|
||||
ctx.service.method,
|
||||
ctx.token,
|
||||
JSON.stringify(ctx.body, (key, value) => {
|
||||
if (key === 'pdata') {
|
||||
@@ -126,7 +127,7 @@ async function main(): Promise<void> {
|
||||
let method = ctx.service.method;
|
||||
|
||||
if (!service || !(service[method])?.bind) {
|
||||
logger.warn(`unimplemented method ${method} in ${ctx.service.name}`);
|
||||
ctx.logger.warn(`unimplemented method.`);
|
||||
|
||||
service = container.resolve(DefaultService);
|
||||
method = 'default';
|
||||
@@ -135,13 +136,19 @@ async function main(): Promise<void> {
|
||||
ctx.body = await (service[method] as Function).bind(service)(ctx);
|
||||
ctx.status = 200;
|
||||
|
||||
logger.info('%s.%s [server]: status = %d', ctx.service.name, ctx.service.method, ctx.status);
|
||||
ctx.logger.info('[server]: status = %d', ctx.status);
|
||||
})
|
||||
.use(apiRoutes)
|
||||
.use(async (ctx, next) => {
|
||||
ctx.body = 'Laochan-Eacnet is running.';
|
||||
if (!ctx.body) {
|
||||
ctx.body = 'Laochan-Eacnet is running.';
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
app.context.resolve = container.resolve.bind(container);
|
||||
|
||||
logger.info('listening on %d', config.port);
|
||||
app.listen(config.port);
|
||||
}
|
||||
|
||||
+13
-105
@@ -1,103 +1,7 @@
|
||||
import { Next } from 'koa';
|
||||
import { Context } from '../types.js';
|
||||
import { LZ77 } from '../utils/lz77.js';
|
||||
import { to_xml } from '@kamyu/kbinxml';
|
||||
import { XMLParser } from 'fast-xml-parser';
|
||||
|
||||
const parser = new XMLParser({
|
||||
ignoreAttributes: false,
|
||||
parseAttributeValue: false,
|
||||
attributeNamePrefix: '$',
|
||||
removeNSPrefix: true,
|
||||
numberParseOptions: {
|
||||
hex: false,
|
||||
leadingZeros: false,
|
||||
eNotation: false,
|
||||
skipLike: /.*/,
|
||||
},
|
||||
});
|
||||
|
||||
function parseValue(node: { $__type: string; $__count?: unknown }): any {
|
||||
const isArray = '$__count' in node;
|
||||
|
||||
if (
|
||||
[
|
||||
's8',
|
||||
'u8',
|
||||
's16',
|
||||
'u16',
|
||||
's32',
|
||||
'u32',
|
||||
's64',
|
||||
'u64',
|
||||
].includes(node.$__type)
|
||||
) {
|
||||
if (isArray) {
|
||||
return node['#text'].split(' ').map((v) => parseInt(v));
|
||||
}
|
||||
|
||||
return parseInt(node['#text']);
|
||||
}
|
||||
|
||||
if (['float', 'double'].includes(node.$__type)) {
|
||||
if (isArray) {
|
||||
return node['#text'].split(' ').map((v) => parseFloat(v));
|
||||
}
|
||||
|
||||
return parseFloat(node['#text']);
|
||||
}
|
||||
|
||||
if (['b', 'bool'].includes(node.$__type)) {
|
||||
return !!parseInt(node['#text']);
|
||||
}
|
||||
|
||||
if (['bin', 'binary'].includes(node.$__type)) {
|
||||
return Buffer.from(node['#text'], 'hex');
|
||||
}
|
||||
|
||||
if (['ip4', 'str', 'string'].includes(node.$__type)) {
|
||||
return node['#text'];
|
||||
}
|
||||
|
||||
if (node.$__type == 'time') {
|
||||
return new Date(parseInt(node['#text']) * 1000);
|
||||
}
|
||||
|
||||
return {
|
||||
type: node.$__type,
|
||||
value: node['#text'],
|
||||
};
|
||||
}
|
||||
|
||||
function kxmlToObject(node: Record<string, unknown> | unknown[] | string): any {
|
||||
if (node instanceof Array) {
|
||||
return node.map((v) => kxmlToObject(v as Record<string, unknown>));
|
||||
}
|
||||
|
||||
if (typeof node === 'string') {
|
||||
return {};
|
||||
}
|
||||
|
||||
if ('$__type' in node) {
|
||||
return parseValue(node as { $__type: string; $__count?: unknown });
|
||||
}
|
||||
|
||||
const obj: object = {};
|
||||
|
||||
for (const key in node) {
|
||||
if (key == '?xml') continue;
|
||||
const value = node[key];
|
||||
|
||||
if (key.startsWith('$')) {
|
||||
obj[key.substring(1)] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
obj[key] = kxmlToObject(node[key] as Record<string, unknown>);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
import { fromKBinXml } from '../utils/kbinxml.js';
|
||||
|
||||
export async function eacnet(ctx: Context, next: Next): Promise<any> {
|
||||
const body = ctx.request.body as {
|
||||
@@ -110,32 +14,36 @@ export async function eacnet(ctx: Context, next: Next): Promise<any> {
|
||||
}
|
||||
|
||||
const { request, p2d_token: token } = body;
|
||||
|
||||
if (!request || !token) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(request
|
||||
.replaceAll(' ', '+')
|
||||
.replaceAll('-', '+')
|
||||
.replaceAll('_', '/') + '===',
|
||||
'base64'
|
||||
);
|
||||
|
||||
const decoded = LZ77.decompress(buffer);
|
||||
const xml = to_xml(decoded).data;
|
||||
const parsedXml = parser.parse(xml);
|
||||
const xmlResult = kxmlToObject(parsedXml);
|
||||
const result = fromKBinXml(decoded);
|
||||
|
||||
const game = Object.keys(xmlResult)[0];
|
||||
const game = Object.keys(result)[0];
|
||||
|
||||
if (!(game in xmlResult)) {
|
||||
if (!(game in result)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (xmlResult[game].params) {
|
||||
ctx.body = xmlResult.p2d.params;
|
||||
if (result[game].params) {
|
||||
ctx.body = result.p2d.params;
|
||||
} else {
|
||||
ctx.body = {};
|
||||
}
|
||||
|
||||
ctx.service = {
|
||||
name: game,
|
||||
method: xmlResult[game].method,
|
||||
method: result[game].method,
|
||||
}
|
||||
|
||||
ctx.token = token;
|
||||
|
||||
+113
-335
@@ -1,13 +1,18 @@
|
||||
import { Binary, Db } from "mongodb";
|
||||
import { eacnet } from "../../decorators/eacnet.js";
|
||||
import { Context } from "../../types.js";
|
||||
import { v } from "../../utils/kxml-value.js";
|
||||
import { ITEM_LIST, MUSIC_LIST } from "./index.js";
|
||||
import { PlayerCourseLog, PlayerMusicData, PlayerPlayData, PlayerPlayLog } from "../../database/index.js";
|
||||
import { fromToken } from "../../utils/laochan-id.js";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { PlayerPlayData, PlayerMusicData, PlayerPlayLog, PlayerCourseLog, PlayerCustomizeSetting } from "../../database/index.js";
|
||||
import { Pdata } from "../../database/pdata.js";
|
||||
import { fromKBinXml } from "../../utils/kbinxml.js";
|
||||
|
||||
export class User {
|
||||
db: Db;
|
||||
@injectable()
|
||||
export class UserService {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
@inject(Db)
|
||||
private readonly db: Db,
|
||||
) { }
|
||||
|
||||
get playDataCol() {
|
||||
return this.db.collection<PlayerPlayData>('player_play_data');
|
||||
@@ -25,360 +30,133 @@ export class User {
|
||||
return this.db.collection<PlayerCourseLog>('player_course_log');
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async sendGradeCertificationLog(ctx: Context) {
|
||||
await this.courseLogCol.insertOne({
|
||||
player: ctx.token,
|
||||
...ctx.body,
|
||||
});
|
||||
get customizeSettingCol() {
|
||||
return this.db.collection<PlayerCustomizeSetting>('player_customize_setting');
|
||||
}
|
||||
|
||||
addCourseLog(coruseLog: PlayerCourseLog) {
|
||||
return this.courseLogCol.insertOne(coruseLog);
|
||||
}
|
||||
|
||||
addPlayLog(playLog: PlayerPlayLog) {
|
||||
return this.playLogCol.insertOne(playLog);
|
||||
}
|
||||
|
||||
upsertCustomizeSetting(customizeSetting: PlayerCustomizeSetting) {
|
||||
return this.customizeSettingCol.updateOne({ _id: customizeSetting._id }, {
|
||||
$set: customizeSetting
|
||||
}, { upsert: true });
|
||||
}
|
||||
|
||||
async getCustomizeSetting(player: string): Promise<PlayerCustomizeSetting> {
|
||||
const result = await this.customizeSettingCol.findOne({ _id: player });
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
_id: player,
|
||||
customize: [
|
||||
{ item_category: 2, item_id: 'I1100000' },
|
||||
{ item_category: 3, item_id: 'I1200000' },
|
||||
{ item_category: 4, item_id: 'I1300000' },
|
||||
{ item_category: 5, item_id: 'I1400000' },
|
||||
{ item_category: 6, item_id: 'I1500000' },
|
||||
{ item_category: 7, item_id: 'I1600000' },
|
||||
{ item_category: 8, item_id: 'I1700000' },
|
||||
{ item_category: 11, item_id: 'I1300000' },
|
||||
{ item_category: 10, item_id: 'I1900000' },
|
||||
],
|
||||
other_customize: [
|
||||
{ item_category: 1, item_id: 'C1000000' },
|
||||
{ item_category: 2, item_id: 'C1100000' },
|
||||
{ item_category: 3, item_id: 'C1200000' },
|
||||
{ item_category: 4, item_id: 'C1300000' },
|
||||
// change default to very short
|
||||
{ item_category: 5, item_id: 'C1400002' },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async consumeItem() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
free_count: v.s32(1000),
|
||||
not_free_count: v.s32(1000),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async reserveConsumeItem() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getCustomizeSetting() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
customize: [
|
||||
{ item_category: v.s32(2), item_id: v.str('I1100000') },
|
||||
{ item_category: v.s32(3), item_id: v.str('I1200000') },
|
||||
{ item_category: v.s32(4), item_id: v.str('I1300000') },
|
||||
{ item_category: v.s32(5), item_id: v.str('I1400000') },
|
||||
{ item_category: v.s32(6), item_id: v.str('I1500000') },
|
||||
{ item_category: v.s32(7), item_id: v.str('I1600000') },
|
||||
{ item_category: v.s32(8), item_id: v.str('I1700000') },
|
||||
{ item_category: v.s32(11), item_id: v.str('I1300000') },
|
||||
{ item_category: v.s32(10), item_id: v.str('I1900000') }
|
||||
],
|
||||
other_customize: [
|
||||
{ item_category: v.s32(1), item_id: v.str('C1000000') },
|
||||
{ item_category: v.s32(2), item_id: v.str('C1100000') },
|
||||
{ item_category: v.s32(3), item_id: v.str('C1200000') },
|
||||
{ item_category: v.s32(4), item_id: v.str('C1300000') },
|
||||
{ item_category: v.s32(5), item_id: v.str('C1400000') }
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async gameEnd() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPointList() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
point_count: v.s32(1),
|
||||
point: [{
|
||||
point_id: v.str('P0100000'),
|
||||
point_num: v.u32(114514),
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPrivilegeClient() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
list_num: v.s32(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPrivilegeServer() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
list_num: v.s32(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async reportMusicResult(ctx: Context) {
|
||||
const result = ctx.body as PlayerPlayLog;
|
||||
result.player = ctx.token;
|
||||
|
||||
let updateTask = Promise.resolve();
|
||||
|
||||
const play_style = Math.floor(result.note_id / 5);
|
||||
const diff = result.note_id % 5;
|
||||
|
||||
updateTask = (async () => {
|
||||
const musicData = (await this.musicDataCol.findOne({
|
||||
player: result.player,
|
||||
music_id: result.music_id,
|
||||
play_style: play_style
|
||||
})) ?? {
|
||||
player: result.player,
|
||||
music_id: result.music_id,
|
||||
play_style: play_style,
|
||||
|
||||
score: [0, 0, 0, 0, 0],
|
||||
clear_flag: [0, 0, 0, 0, 0],
|
||||
miss_count: [0, 0, 0, 0, 0],
|
||||
play_num: [0, 0, 0, 0, 0],
|
||||
clear_num: [0, 0, 0, 0, 0],
|
||||
};
|
||||
|
||||
if (result.clear_flag > musicData.clear_flag[diff])
|
||||
musicData.clear_flag[diff] = result.clear_flag;
|
||||
|
||||
if (result.miss_count < musicData.miss_count[diff])
|
||||
musicData.miss_count[diff] = result.miss_count;
|
||||
|
||||
if (result.score > musicData.score[diff])
|
||||
musicData.score[diff] = result.score;
|
||||
|
||||
if (result.clear_flag >= 2)
|
||||
musicData.clear_num[diff]++;
|
||||
|
||||
musicData.play_num[diff]++;
|
||||
|
||||
await this.musicDataCol.updateOne({
|
||||
player: result.player,
|
||||
music_id: result.music_id,
|
||||
play_style: play_style
|
||||
}, {
|
||||
$set: musicData
|
||||
}, {
|
||||
upsert: true
|
||||
});
|
||||
})();
|
||||
|
||||
await Promise.all([
|
||||
this.playLogCol.insertOne(result),
|
||||
updateTask,
|
||||
])
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async savePlayData(ctx: Context) {
|
||||
const uid = ctx.token;
|
||||
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
|
||||
|
||||
await this.playDataCol
|
||||
.updateOne({ _id: uid }, {
|
||||
$set: {
|
||||
pdata: new Binary(pdata),
|
||||
check_sum,
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPlayData(ctx: Context) {
|
||||
const uid = ctx.token;
|
||||
|
||||
const result = await this.playDataCol
|
||||
.findOne({ _id: uid });
|
||||
|
||||
if (!result) {
|
||||
return {
|
||||
status: v.s32(1),
|
||||
error: v.s32(404),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
size: v.s32(result.pdata.length()),
|
||||
rest_size: v.s32(0),
|
||||
check_sum: v.str(result.check_sum),
|
||||
pdata: v.bin(result.pdata.buffer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async registPlayer(ctx: Context) {
|
||||
const uid = ctx.token;
|
||||
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
|
||||
|
||||
await this.playDataCol
|
||||
.insertOne({ _id: uid, pdata: new Binary(pdata), check_sum });
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async checkPlayData(ctx: Context) {
|
||||
const { check_sum } = ctx.body as { check_sum: string };
|
||||
|
||||
const data = await this.playDataCol.findOne({ _id: ctx.token }, {
|
||||
async getPDataChecksum(player: string): Promise<string | undefined> {
|
||||
const result = await this.playDataCol.findOne({ _id: player }, {
|
||||
projection: {
|
||||
check_sum: 1,
|
||||
}
|
||||
});
|
||||
|
||||
const id = fromToken(ctx.token);
|
||||
if (result)
|
||||
return result.check_sum;
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
infinitas_id: v.str(id),
|
||||
valid: v.bool(false),
|
||||
is_exist: v.bool(false),
|
||||
}
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (data.check_sum !== check_sum) {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
infinitas_id: v.str(id),
|
||||
valid: v.bool(false),
|
||||
is_exist: v.bool(true),
|
||||
}
|
||||
};
|
||||
}
|
||||
async getPDataBinary(player: string): Promise<{ pdata: Buffer, check_sum: string } | undefined> {
|
||||
const result = await this.playDataCol.findOne({ _id: player });
|
||||
|
||||
if (!result)
|
||||
return undefined;
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
infinitas_id: v.str(id),
|
||||
valid: v.bool(true),
|
||||
is_exist: v.bool(true),
|
||||
}
|
||||
pdata: Buffer.from(result.pdata.buffer),
|
||||
check_sum: result.check_sum,
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getRivalInfo() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
rival_data: [{
|
||||
async getPDataDecoded(player: string): Promise<Pdata | undefined> {
|
||||
const binary = await this.getPDataBinary(player);
|
||||
if (!binary) return undefined;
|
||||
|
||||
}]
|
||||
},
|
||||
}
|
||||
return fromKBinXml(binary.pdata);
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getMusicList() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
status: v.s32(0),
|
||||
check_sum: v.str('2e49eba0b6ca8b6898f5d684ea0c3bc1ed6f1d572a4d308cf32727d64069a588'),
|
||||
music_list: {
|
||||
music_num: v.s32(MUSIC_LIST.length),
|
||||
music: MUSIC_LIST,
|
||||
upsertPDataBinary(player: string, pdata: Buffer, check_sum: string) {
|
||||
return this.playDataCol
|
||||
.updateOne({ _id: player }, {
|
||||
$set: {
|
||||
pdata: new Binary(pdata),
|
||||
check_sum,
|
||||
}
|
||||
},
|
||||
}
|
||||
}, { upsert: true });
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getCompeScoreData() {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {},
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getMusicData(ctx: Context) {
|
||||
const { play_style } = ctx.body as { play_style: number };
|
||||
|
||||
const musicDatas = await this.musicDataCol.find({
|
||||
player: ctx.token,
|
||||
play_style,
|
||||
getMusicDatas(player: string, play_style: number): Promise<PlayerMusicData[]> {
|
||||
return this.musicDataCol.find({
|
||||
player, play_style,
|
||||
}).toArray();
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
play_style: v.s32(play_style),
|
||||
result: {
|
||||
music: musicDatas.map(data => ({
|
||||
music_id: v.s32(data.music_id),
|
||||
score: v.s32(data.score),
|
||||
clear_flag: v.s32(data.clear_flag),
|
||||
miss_count: v.s32(data.miss_count),
|
||||
play_num: v.s32(data.play_num),
|
||||
clear_num: v.s32(data.clear_num),
|
||||
}))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getItemList() {
|
||||
async getMusicData(player: string, music_id: number, play_style: number): Promise<PlayerMusicData> {
|
||||
const result = await this.musicDataCol.findOne({
|
||||
player: player,
|
||||
music_id: music_id,
|
||||
play_style: play_style
|
||||
});
|
||||
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
item_list: {
|
||||
item_num: v.s32(ITEM_LIST.length),
|
||||
item: ITEM_LIST.map(id => ({
|
||||
item_id: v.str(id),
|
||||
not_free_count: v.s32(1000),
|
||||
free_count: v.s32(1000),
|
||||
}))
|
||||
}
|
||||
}
|
||||
player: result.player,
|
||||
music_id: result.music_id,
|
||||
play_style: play_style,
|
||||
|
||||
score: [0, 0, 0, 0, 0],
|
||||
clear_flag: [0, 0, 0, 0, 0],
|
||||
miss_count: [-1, -1, -1, -1, -1],
|
||||
play_num: [0, 0, 0, 0, 0],
|
||||
clear_num: [0, 0, 0, 0, 0],
|
||||
};
|
||||
}
|
||||
|
||||
upsertMusicData(musicData: PlayerMusicData) {
|
||||
return this.musicDataCol.updateOne({
|
||||
player: musicData.player,
|
||||
music_id: musicData.music_id,
|
||||
play_style: musicData.play_style
|
||||
}, {
|
||||
$set: musicData
|
||||
}, {
|
||||
upsert: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Logger } from '@cordisjs/logger';
|
||||
import { DefaultState, ParameterizedContext } from 'koa';
|
||||
import { InjectionToken } from 'tsyringe';
|
||||
|
||||
export interface ILaochanContext {
|
||||
token?: string;
|
||||
@@ -11,6 +12,7 @@ export interface ILaochanContext {
|
||||
|
||||
logger: Logger;
|
||||
body?: unknown;
|
||||
resolve: <T>(resolveToken: InjectionToken<T>) => T;
|
||||
}
|
||||
|
||||
export type Context = ParameterizedContext<DefaultState, ILaochanContext, any>;
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
import { to_bin, to_xml } from "@kamyu/kbinxml";
|
||||
import { XMLParser } from "fast-xml-parser";
|
||||
import _ from "lodash";
|
||||
|
||||
const parser = new XMLParser({
|
||||
ignoreAttributes: false,
|
||||
parseAttributeValue: false,
|
||||
attributeNamePrefix: '$',
|
||||
removeNSPrefix: true,
|
||||
numberParseOptions: {
|
||||
hex: false,
|
||||
leadingZeros: false,
|
||||
eNotation: false,
|
||||
skipLike: /.*/,
|
||||
},
|
||||
});
|
||||
|
||||
function parseValue(node: { $__type: string; $__count?: unknown }): any {
|
||||
const isArray = '$__count' in node;
|
||||
|
||||
if (
|
||||
[
|
||||
's8',
|
||||
'u8',
|
||||
's16',
|
||||
'u16',
|
||||
's32',
|
||||
'u32',
|
||||
's64',
|
||||
'u64',
|
||||
].includes(node.$__type)
|
||||
) {
|
||||
if (isArray) {
|
||||
return node['#text'].split(' ').map((v) => parseInt(v));
|
||||
}
|
||||
|
||||
return parseInt(node['#text']);
|
||||
}
|
||||
|
||||
if (['float', 'double'].includes(node.$__type)) {
|
||||
if (isArray) {
|
||||
return node['#text'].split(' ').map((v) => parseFloat(v));
|
||||
}
|
||||
|
||||
return parseFloat(node['#text']);
|
||||
}
|
||||
|
||||
if (['b', 'bool'].includes(node.$__type)) {
|
||||
return !!parseInt(node['#text']);
|
||||
}
|
||||
|
||||
if (['bin', 'binary'].includes(node.$__type)) {
|
||||
return Buffer.from(node['#text'], 'hex');
|
||||
}
|
||||
|
||||
if (['ip4', 'str', 'string'].includes(node.$__type)) {
|
||||
return node['#text'];
|
||||
}
|
||||
|
||||
if (node.$__type == 'time') {
|
||||
return new Date(parseInt(node['#text']) * 1000);
|
||||
}
|
||||
|
||||
return {
|
||||
type: node.$__type,
|
||||
value: node['#text'],
|
||||
};
|
||||
}
|
||||
|
||||
function toObject(node: Record<string, unknown> | unknown[] | string): any {
|
||||
if (node instanceof Array) {
|
||||
return node.map((v) => toObject(v as Record<string, unknown>));
|
||||
}
|
||||
|
||||
if (typeof node === 'string') {
|
||||
return {};
|
||||
}
|
||||
|
||||
if ('$__type' in node) {
|
||||
return parseValue(node as { $__type: string; $__count?: unknown });
|
||||
}
|
||||
|
||||
const obj: object = {};
|
||||
|
||||
for (const key in node) {
|
||||
if (key == '?xml') continue;
|
||||
const value = node[key];
|
||||
|
||||
if (key.startsWith('$')) {
|
||||
obj[key.substring(1)] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
obj[key] = toObject(node[key] as Record<string, unknown>);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function fromKBinXml(kbinxml: Buffer) {
|
||||
const xml = to_xml(kbinxml).data;
|
||||
const parsedXml = parser.parse(xml);
|
||||
return toObject(parsedXml);
|
||||
}
|
||||
|
||||
function serializeValue(value: any, type: string): string {
|
||||
if (
|
||||
[
|
||||
's8',
|
||||
'u8',
|
||||
's16',
|
||||
'u16',
|
||||
's32',
|
||||
'u32',
|
||||
's64',
|
||||
'u64',
|
||||
'float',
|
||||
'double',
|
||||
].includes(type)
|
||||
) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (['b', 'bool'].includes(type)) {
|
||||
return value ? '1' : '0';
|
||||
}
|
||||
|
||||
if (['bin', 'binary'].includes(type)) {
|
||||
if (!(value instanceof Buffer)) {
|
||||
throw new Error('binary with non-buffer value');
|
||||
}
|
||||
|
||||
return value.toString('hex');
|
||||
}
|
||||
|
||||
if (['ip4', 'str', 'string'].includes(type)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (type == 'time') {
|
||||
if (value instanceof Date) {
|
||||
return Math.floor(value.valueOf() / 1000).toString();
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
throw new Error('time with not date or number');
|
||||
}
|
||||
|
||||
throw new Error(`unsupported type ${type}`);
|
||||
}
|
||||
|
||||
function serializeObject(obj: Object, name: string, linePrefix: string = '') {
|
||||
let output = `${linePrefix}<${name}`;
|
||||
|
||||
const entries = Object.entries(obj);
|
||||
const attrs = entries
|
||||
.filter(v => v[0].startsWith('$'))
|
||||
.map(v => [v[0].substring(1), v[1]]);
|
||||
|
||||
// handle kxml value shit
|
||||
const value = entries.find(v => v[0] == '__value');
|
||||
|
||||
if (value && value[1] instanceof Array) {
|
||||
attrs.push(['__count', value[1].length]);
|
||||
}
|
||||
|
||||
if (value && value[1] instanceof Buffer) {
|
||||
attrs.push(['__size', value[1].length]);
|
||||
}
|
||||
|
||||
// the kbinxml library had issue when serializing non-ordered stuff
|
||||
attrs.sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
|
||||
|
||||
for (const attr of attrs) {
|
||||
output += ` ${attr[0]}="${_.escape(attr[1])}"`;
|
||||
}
|
||||
|
||||
output += '>';
|
||||
|
||||
if (value) {
|
||||
const type = attrs.find(v => v[0] == '__type');
|
||||
|
||||
if (!type) {
|
||||
// or normal xml innerXml?
|
||||
throw new Error(`value with no type in ${name}`);
|
||||
}
|
||||
|
||||
output += (value[1] instanceof Array ? value[1] : [value[1]])
|
||||
.map(v => serializeValue(v, type[1]))
|
||||
.join(' ');
|
||||
} else {
|
||||
const elements = entries
|
||||
.filter(v => !v[0].startsWith('$'))
|
||||
.sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
|
||||
|
||||
if (elements.length) {
|
||||
output += '\n';
|
||||
|
||||
for (const element of elements) {
|
||||
if (element[1] instanceof Array) {
|
||||
for (const item of element[1])
|
||||
output += serializeObject(item, element[0], linePrefix + ' ');
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
output += serializeObject(element[1], element[0], linePrefix + ' ');
|
||||
}
|
||||
|
||||
output += linePrefix;
|
||||
}
|
||||
}
|
||||
|
||||
return output + `</${name}>\n`;
|
||||
}
|
||||
|
||||
export function toKBinXml(topName: string, obj: unknown, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8') {
|
||||
const xml = `<?xml version="1.0" encoding="${encoding}"?>` + serializeObject(obj, topName);
|
||||
return to_bin(xml);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { createHash } from "crypto";
|
||||
|
||||
export function sha256(data: Buffer) {
|
||||
return createHash('sha256')
|
||||
.update(data)
|
||||
.digest('hex');
|
||||
}
|
||||
Reference in New Issue
Block a user