mirror of
https://github.com/laochan-eacnet/backend.git
synced 2026-09-22 22:28:11 +03:00
Add rival, ghost, customize item counts, bug fix
This commit is contained in:
+3
-1
@@ -26,4 +26,6 @@ build/
|
||||
.eslintcache
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
.env
|
||||
|
||||
+144
-2
@@ -2,7 +2,8 @@ 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";
|
||||
import { PlayerCustomizeSetting, PlayerRivalData } from "../../types/index.js";
|
||||
import { RivalPatch, RivalPostOrDelete } from "../../types/api.js";
|
||||
|
||||
const logger = new Logger('p2d-api');
|
||||
const router = new Router({
|
||||
@@ -21,7 +22,7 @@ const router = new Router({
|
||||
const pdata = await ctx.resolve(UserService).getPDataDecoded(token);
|
||||
|
||||
ctx.status = pdata ? 200 : 404;
|
||||
ctx.body = pdata ? pdata : {};
|
||||
ctx.body = pdata ? { pdata } : {};
|
||||
})
|
||||
|
||||
.get('/customize/:token', async (ctx: Context) => {
|
||||
@@ -41,6 +42,147 @@ const router = new Router({
|
||||
};
|
||||
})
|
||||
|
||||
.get('/playlog/:token', async (ctx: Context) => {
|
||||
let { skip, limit } = ctx.query;
|
||||
const { token } = ctx.params;
|
||||
|
||||
if (skip instanceof Array) {
|
||||
[skip] = skip;
|
||||
}
|
||||
|
||||
if (limit instanceof Array) {
|
||||
[limit] = limit;
|
||||
}
|
||||
|
||||
const result = await ctx.resolve(UserService)
|
||||
.getPlayLogs(token, skip ? parseInt(skip) : undefined, limit ? parseInt(limit) : undefined);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = {
|
||||
playLogs: result,
|
||||
};
|
||||
})
|
||||
|
||||
.get('/rival/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
const result = await ctx.resolve(UserService)
|
||||
.getPlayerRivalData(token);
|
||||
|
||||
if (!result.enabled) {
|
||||
ctx.status = 200;
|
||||
ctx.body = {
|
||||
enabled: false,
|
||||
spRivals: [],
|
||||
dpRivals: [],
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const getRivalPlayer = async (tk: string) => {
|
||||
const pdata = await ctx.resolve(UserService)
|
||||
.getPDataDecoded(tk);
|
||||
|
||||
return pdata.player;
|
||||
};
|
||||
|
||||
const [spRivals, dpRivals] = await Promise.all([
|
||||
await Promise.all(result.sp.map(getRivalPlayer)),
|
||||
await Promise.all(result.dp.map(getRivalPlayer)),
|
||||
]);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = {
|
||||
enabled: true,
|
||||
spRivals, dpRivals,
|
||||
};
|
||||
})
|
||||
|
||||
.patch('/rival/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
const { enabled } = ctx.request.body as RivalPatch;
|
||||
|
||||
await ctx.resolve(UserService)
|
||||
.upsertPlayerRivalData({
|
||||
_id: token,
|
||||
enabled,
|
||||
} as PlayerRivalData);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
})
|
||||
|
||||
.post('/rival/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
const { infinitas_id, type } = ctx.request.body as RivalPostOrDelete;
|
||||
|
||||
if (!infinitas_id) {
|
||||
ctx.status = 400;
|
||||
ctx.body = { error: 'infinitas_id is undefined' };
|
||||
return;
|
||||
}
|
||||
|
||||
const [existing, { player: target }] = await Promise.all([
|
||||
ctx.resolve(UserService).getPlayerRivalData(token),
|
||||
ctx.resolve(UserService).findPlayerByInfasId(infinitas_id),
|
||||
]);
|
||||
|
||||
if (!target) {
|
||||
ctx.status = 404;
|
||||
ctx.body = { error: 'rival target not found' };
|
||||
return;
|
||||
}
|
||||
|
||||
if (target == token) {
|
||||
ctx.status = 400;
|
||||
ctx.body = { error: 'ni rival ni zi ji?' };
|
||||
return;
|
||||
}
|
||||
|
||||
const targetArray = type ? existing.dp : existing.sp;
|
||||
if (targetArray.includes(target)) {
|
||||
ctx.status = 400;
|
||||
ctx.body = { error: 'rival target already exists' };
|
||||
return;
|
||||
}
|
||||
|
||||
targetArray.push(target);
|
||||
|
||||
await ctx.resolve(UserService)
|
||||
.upsertPlayerRivalData(existing);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
})
|
||||
|
||||
.delete('/rival/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
const { infinitas_id, type } = ctx.request.body as RivalPostOrDelete;
|
||||
|
||||
const [existing, { player: target }] = await Promise.all([
|
||||
ctx.resolve(UserService).getPlayerRivalData(token),
|
||||
ctx.resolve(UserService).findPlayerByInfasId(infinitas_id),
|
||||
]);
|
||||
|
||||
if (type) {
|
||||
existing.dp = existing.dp.filter(v => v != target)
|
||||
} else {
|
||||
existing.sp = existing.sp.filter(v => v != target)
|
||||
}
|
||||
|
||||
await ctx.resolve(UserService)
|
||||
.upsertPlayerRivalData(existing);
|
||||
|
||||
ctx.status = 200;
|
||||
ctx.body = {
|
||||
success: true,
|
||||
};
|
||||
})
|
||||
|
||||
.put('/customize/:token', async (ctx: Context) => {
|
||||
const { token } = ctx.params;
|
||||
|
||||
|
||||
@@ -1,13 +1,86 @@
|
||||
import { PlayerPlayLog } from "../../database/index.js";
|
||||
import { PlayerPlayLog } from "../../types/index.js";
|
||||
import { eacnet } from "../../decorators/eacnet.js";
|
||||
import { UserService } from "../../services/p2d/user.js";
|
||||
import { Context } from "../../types.js";
|
||||
import { v } from "../../utils/kxml-value.js";
|
||||
import { MUSIC_LIST } from "./index.js";
|
||||
import { Binary } from "mongodb";
|
||||
|
||||
export class Music {
|
||||
userService: UserService;
|
||||
|
||||
@eacnet('p2d')
|
||||
async getMusicGhost(ctx: Context) {
|
||||
// playstyle is actually chart diff, game is sending wrong request
|
||||
// TODO: check game code and maybe patch it later
|
||||
const data = ctx.body as {
|
||||
music_id: number,
|
||||
note_id: number,
|
||||
playstyle: number,
|
||||
request_ghost: {
|
||||
kind: number,
|
||||
pref_id: number,
|
||||
grade_id: number,
|
||||
infinitas_id?: string,
|
||||
}[] | {
|
||||
kind: number,
|
||||
pref_id: number,
|
||||
grade_id: number,
|
||||
infinitas_id?: string,
|
||||
};
|
||||
};
|
||||
|
||||
const { music_id, note_id } = data;
|
||||
const request_ghost = 'kind' in data.request_ghost ? [data.request_ghost] : data.request_ghost;
|
||||
|
||||
const play_style = Math.floor(note_id / 5);
|
||||
const diff = note_id % 5;
|
||||
|
||||
const ghost = await Promise.all(request_ghost.map(async req => {
|
||||
if (req.kind == 2) {
|
||||
const { player, djname } = await this.userService.findPlayerByInfasId(req.infinitas_id);
|
||||
|
||||
if (!player) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const mdata = await this.userService.getMusicData(player, music_id, play_style);
|
||||
if (mdata.best_score_clock[diff] === -1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const playlog = await this.userService.getPlayLog(player, mdata.best_score_clock[diff]);
|
||||
|
||||
return {
|
||||
kind: v.s32(2),
|
||||
ex_score: v.s32(playlog.score),
|
||||
exist: v.bool(true),
|
||||
ghost_data: v.bin(Buffer.from(playlog.ghost.buffer)),
|
||||
dj_name: v.str(djname),
|
||||
best_option: {
|
||||
valid: v.bool(1),
|
||||
arrange_0: v.s32(playlog.arrange_0),
|
||||
arrange_1: v.s32(playlog.arrange_1),
|
||||
assist: v.s32(playlog.assist),
|
||||
flip: v.s32(playlog.flip),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}));
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
music_id: v.s32(music_id),
|
||||
note_id: v.s32(note_id),
|
||||
ghost: ghost.filter(v => v),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async reportMusicResult(ctx: Context) {
|
||||
const result = ctx.body as PlayerPlayLog;
|
||||
@@ -27,8 +100,10 @@ export class Music {
|
||||
if (result.miss_count < musicData.miss_count[diff] || musicData.miss_count[diff] == -1)
|
||||
musicData.miss_count[diff] = result.miss_count;
|
||||
|
||||
if (result.score > musicData.score[diff])
|
||||
if (result.score > musicData.score[diff]) {
|
||||
musicData.score[diff] = result.score;
|
||||
musicData.best_score_clock[diff] = result.clock;
|
||||
}
|
||||
|
||||
if (result.clear_flag >= 2)
|
||||
musicData.clear_num[diff]++;
|
||||
@@ -38,6 +113,9 @@ export class Music {
|
||||
this.userService.upsertMusicData(musicData);
|
||||
})();
|
||||
|
||||
// @ts-expect-error ghost is Buffer now, convert it to binary
|
||||
result.ghost = new Binary(result.ghost);
|
||||
|
||||
await Promise.all([
|
||||
this.userService.addPlayLog(result),
|
||||
updateTask,
|
||||
|
||||
+122
-15
@@ -1,9 +1,10 @@
|
||||
import { eacnet } from "../../decorators/eacnet.js";
|
||||
import { Context } from "../../types.js";
|
||||
import { v } from "../../utils/kxml-value.js";
|
||||
import { ITEM_LIST } from "./index.js";
|
||||
import { fromToken } from "../../utils/laochan-id.js";
|
||||
import { UserService } from "../../services/p2d/user.js";
|
||||
import { sha256 } from "../../utils/sha256.js";
|
||||
import { toKBinXml } from "../../utils/kbinxml.js";
|
||||
|
||||
export class User {
|
||||
userService: UserService;
|
||||
@@ -38,13 +39,37 @@ export class User {
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async consumeItem() {
|
||||
async consumeItem(ctx: Context) {
|
||||
const { item_id } = ctx.body;
|
||||
const { items_count } = await this.userService.getCustomizeSetting(ctx.token);
|
||||
|
||||
let free_count = 0;
|
||||
let not_free_count = 0;
|
||||
|
||||
if (item_id === 'I1000000') {
|
||||
not_free_count = items_count.infinitas_ticket;
|
||||
free_count = items_count.infinitas_ticket_free;
|
||||
} else if (item_id === 'I1000001') {
|
||||
not_free_count = items_count.ldisc;
|
||||
free_count = 0;
|
||||
}
|
||||
|
||||
free_count--;
|
||||
if (free_count < 0) {
|
||||
free_count = 0;
|
||||
not_free_count--;
|
||||
}
|
||||
|
||||
if (not_free_count < 0) {
|
||||
not_free_count = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {
|
||||
free_count: v.s32(1000),
|
||||
not_free_count: v.s32(1000),
|
||||
free_count: v.s32(free_count),
|
||||
not_free_count: v.s32(not_free_count),
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -86,7 +111,9 @@ export class User {
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getPointList() {
|
||||
async getPointList(ctx: Context) {
|
||||
const customize = await this.userService.getCustomizeSetting(ctx.token);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
@@ -94,7 +121,7 @@ export class User {
|
||||
point_count: v.s32(1),
|
||||
point: [{
|
||||
point_id: v.str('P0100000'),
|
||||
point_num: v.u32(114514),
|
||||
point_num: v.u32(customize.items_count.bit),
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -125,6 +152,11 @@ export class User {
|
||||
@eacnet('p2d')
|
||||
async savePlayData(ctx: Context) {
|
||||
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
|
||||
const localChecksum = sha256(pdata);
|
||||
|
||||
if (localChecksum != check_sum) {
|
||||
ctx.logger.error('pdata checksum unmatch - excepted %s, actual %s', check_sum, localChecksum);
|
||||
}
|
||||
|
||||
await this.userService.upsertPDataBinary(ctx.token, pdata, check_sum);
|
||||
|
||||
@@ -160,6 +192,11 @@ export class User {
|
||||
@eacnet('p2d')
|
||||
async registPlayer(ctx: Context) {
|
||||
const { pdata, check_sum } = ctx.body as { pdata: Buffer, check_sum: string };
|
||||
const localChecksum = sha256(pdata);
|
||||
|
||||
if (localChecksum != check_sum) {
|
||||
ctx.logger.error('pdata checksum unmatch - excepted %s, actual %s', check_sum, localChecksum);
|
||||
}
|
||||
|
||||
await this.userService.upsertPDataBinary(ctx.token, pdata, check_sum);
|
||||
|
||||
@@ -212,11 +249,73 @@ export class User {
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getRivalInfo() {
|
||||
async getRivalInfo(ctx: Context) {
|
||||
const rivalData = await this.userService.getPlayerRivalData(ctx.token);
|
||||
if (!rivalData.enabled) {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {},
|
||||
}
|
||||
}
|
||||
|
||||
const getRivalDetail = async (id: string, playStyle: number) => {
|
||||
const [pdata, musicDatas] = await Promise.all([
|
||||
this.userService.getPDataDecoded(id),
|
||||
this.userService.getMusicDatas(id, playStyle),
|
||||
]);
|
||||
|
||||
return {
|
||||
infinitas_id: v.str(pdata.player.infinitas_id),
|
||||
dj_name: v.str(pdata.player.djname),
|
||||
shop_name: v.str('Laochan Eacnet'),
|
||||
pref_id: v.s32(pdata.player.pref_id),
|
||||
grade_id_sp: v.s32(pdata.player.grade_id_sp),
|
||||
grade_id_dp: v.s32(pdata.player.grade_id_dp),
|
||||
music_data: {
|
||||
music: musicDatas.map(data => ({
|
||||
music_id: v.s32(data.music_id),
|
||||
score: v.s32(data.score),
|
||||
clear_flag: v.s32(data.clear_flag),
|
||||
})),
|
||||
},
|
||||
challenges: {},
|
||||
comment: {
|
||||
status_comment: v.str(''),
|
||||
rival_challenge_sweeping_victory: v.str(''),
|
||||
rival_challenge_narrow_victory: v.str(''),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [spRival, dpRival] = await Promise.all([
|
||||
Promise.all(rivalData.sp.map(id => getRivalDetail(id, 0))),
|
||||
Promise.all(rivalData.dp.map(id => getRivalDetail(id, 1))),
|
||||
]);
|
||||
|
||||
const rivalInfo = {
|
||||
sp: {
|
||||
rival_num: v.s32(rivalData.sp.length),
|
||||
rival: spRival,
|
||||
},
|
||||
dp: {
|
||||
rival_num: v.s32(rivalData.dp.length),
|
||||
rival: dpRival,
|
||||
},
|
||||
};
|
||||
|
||||
const rival_binary = Buffer.from(toKBinXml('rival_info', rivalInfo).data);
|
||||
const rival_checksum = sha256(rival_binary);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error: v.s32(0),
|
||||
result: {},
|
||||
result: {
|
||||
check_sum: v.str(rival_checksum),
|
||||
size: v.s32(rival_binary.length),
|
||||
rival_data: v.bin(rival_binary),
|
||||
rest_size: v.s32(0),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,18 +329,26 @@ export class User {
|
||||
}
|
||||
|
||||
@eacnet('p2d')
|
||||
async getItemList() {
|
||||
async getItemList(ctx: Context) {
|
||||
const { items_count } = await this.userService.getCustomizeSetting(ctx.token);
|
||||
|
||||
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),
|
||||
}))
|
||||
item_num: v.s32(2),
|
||||
item: [{
|
||||
// infinitas ticket
|
||||
item_id: v.str('I1000000'),
|
||||
not_free_count: v.s32(items_count.infinitas_ticket),
|
||||
free_count: v.s32(items_count.infinitas_ticket_free),
|
||||
}, {
|
||||
// ldisc
|
||||
item_id: v.str('I1000001'),
|
||||
not_free_count: v.s32(items_count.ldisc),
|
||||
free_count: v.s32(0),
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Logger } from "@cordisjs/logger";
|
||||
import { MongoClient } from "mongodb";
|
||||
import config from "./utils/config.js";
|
||||
import { PlayerPlayData } from "./types/index.js";
|
||||
import { fromKBinXml } from "./utils/kbinxml.js";
|
||||
import { Pdata } from "./types/pdata.js";
|
||||
|
||||
interface DatabaseMeta {
|
||||
version: number;
|
||||
}
|
||||
|
||||
export async function initMongoDb() {
|
||||
const logger = new Logger('database');
|
||||
|
||||
logger.info('connecting to mongodb...');
|
||||
const client = await MongoClient.connect(config.mongoUrl);
|
||||
const db = client.db(config.dbName);
|
||||
const metaCol = db.collection<DatabaseMeta>('__metadata');
|
||||
let meta: DatabaseMeta = await metaCol.findOne({});
|
||||
meta ??= { version: 0 };
|
||||
|
||||
// version 1: add infas id and djname to playdata collection
|
||||
if (meta.version < 1) {
|
||||
logger.info('upgrading database to ver 1');
|
||||
const playDataCol = db.collection<PlayerPlayData>('player_play_data');
|
||||
|
||||
const cursor = playDataCol.find({});
|
||||
const tasks = [];
|
||||
for await (const player of cursor) {
|
||||
const { pdata } = fromKBinXml(player.pdata.buffer) as { pdata: Pdata };
|
||||
|
||||
tasks.push(playDataCol.updateOne({ _id: player._id }, {
|
||||
$set: {
|
||||
djname: pdata.player.djname,
|
||||
infinitas_id: pdata.player.infinitas_id,
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
await Promise.all(tasks);
|
||||
|
||||
logger.info('upgraded database to ver 1, effected %d', tasks.length);
|
||||
meta.version = 1;
|
||||
}
|
||||
|
||||
await metaCol.updateOne({}, { $set: meta }, { upsert: true });
|
||||
return db;
|
||||
}
|
||||
+5
-9
@@ -10,8 +10,9 @@ 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 { Db } from 'mongodb';
|
||||
import { apiRoutes } from './apis/index.js';
|
||||
import { initMongoDb } from './database.js';
|
||||
|
||||
function tryResolve<T>(token: InjectionToken<T>): T | undefined {
|
||||
if (!container.isRegistered(token)) return undefined;
|
||||
@@ -34,14 +35,9 @@ async function main(): Promise<void> {
|
||||
|
||||
logger.info('initialization...');
|
||||
|
||||
{
|
||||
logger.info('connecting to mongodb...');
|
||||
const client = await MongoClient.connect(config.mongoUrl);
|
||||
|
||||
container.register(Db, {
|
||||
useValue: client.db(config.dbName),
|
||||
});
|
||||
}
|
||||
container.register(Db, {
|
||||
useValue: await initMongoDb(),
|
||||
});
|
||||
|
||||
logger.info('creating koa app...');
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Binary, Db } from "mongodb";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { PlayerPlayData, PlayerMusicData, PlayerPlayLog, PlayerCourseLog, PlayerCustomizeSetting } from "../../database/index.js";
|
||||
import { Pdata } from "../../database/pdata.js";
|
||||
import { PlayerPlayData, PlayerMusicData, PlayerPlayLog, PlayerCourseLog, PlayerCustomizeSetting, PlayerRivalData } from "../../types/index.js";
|
||||
import { Pdata } from "../../types/pdata.js";
|
||||
import { fromKBinXml } from "../../utils/kbinxml.js";
|
||||
|
||||
@injectable()
|
||||
@@ -34,6 +34,10 @@ export class UserService {
|
||||
return this.db.collection<PlayerCustomizeSetting>('player_customize_setting');
|
||||
}
|
||||
|
||||
get rivalDataCol() {
|
||||
return this.db.collection<PlayerRivalData>('player_rival_data');
|
||||
}
|
||||
|
||||
addCourseLog(coruseLog: PlayerCourseLog) {
|
||||
return this.courseLogCol.insertOne(coruseLog);
|
||||
}
|
||||
@@ -42,6 +46,20 @@ export class UserService {
|
||||
return this.playLogCol.insertOne(playLog);
|
||||
}
|
||||
|
||||
getPlayLogs(player: string, skip = 0, limit = 10) {
|
||||
return this.playLogCol.find({ player }, {
|
||||
sort: { player: 1, clock: -1 },
|
||||
skip, limit,
|
||||
}).toArray();
|
||||
}
|
||||
|
||||
getCourseLogs(player: string, skip = 0, limit = 10) {
|
||||
return this.courseLogCol.find({ player }, {
|
||||
sort: { player: 1, _id: -1 },
|
||||
skip, limit,
|
||||
}).toArray();
|
||||
}
|
||||
|
||||
upsertCustomizeSetting(customizeSetting: PlayerCustomizeSetting) {
|
||||
return this.customizeSettingCol.updateOne({ _id: customizeSetting._id }, {
|
||||
$set: customizeSetting
|
||||
@@ -50,11 +68,25 @@ export class UserService {
|
||||
|
||||
async getCustomizeSetting(player: string): Promise<PlayerCustomizeSetting> {
|
||||
const result = await this.customizeSettingCol.findOne({ _id: player });
|
||||
if (result)
|
||||
if (result) {
|
||||
result.items_count ??= {
|
||||
bit: 15000,
|
||||
ldisc: 5,
|
||||
infinitas_ticket: 50,
|
||||
infinitas_ticket_free: 9,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
_id: player,
|
||||
items_count: {
|
||||
bit: 15000,
|
||||
ldisc: 5,
|
||||
infinitas_ticket: 50,
|
||||
infinitas_ticket_free: 9,
|
||||
},
|
||||
customize: [
|
||||
{ item_category: 2, item_id: 'I1100000' },
|
||||
{ item_category: 3, item_id: 'I1200000' },
|
||||
@@ -90,6 +122,19 @@ export class UserService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async findPlayerByInfasId(infinitas_id: string): Promise<{ player: string, djname: string } | undefined> {
|
||||
const result = await this.playDataCol.findOne({ infinitas_id }, {
|
||||
projection: {
|
||||
_id: 1, djname: 1,
|
||||
}
|
||||
});
|
||||
|
||||
if (result)
|
||||
return { player: result._id, djname: result.djname };
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async getPDataBinary(player: string): Promise<{ pdata: Buffer, check_sum: string } | undefined> {
|
||||
const result = await this.playDataCol.findOne({ _id: player });
|
||||
|
||||
@@ -106,13 +151,18 @@ export class UserService {
|
||||
const binary = await this.getPDataBinary(player);
|
||||
if (!binary) return undefined;
|
||||
|
||||
return fromKBinXml(binary.pdata);
|
||||
return fromKBinXml(binary.pdata).pdata;
|
||||
}
|
||||
|
||||
upsertPDataBinary(player: string, pdata: Buffer, check_sum: string) {
|
||||
const unpacked = fromKBinXml(pdata) as { pdata: Pdata };
|
||||
const { djname, infinitas_id } = unpacked.pdata.player;
|
||||
|
||||
return this.playDataCol
|
||||
.updateOne({ _id: player }, {
|
||||
$set: {
|
||||
djname, infinitas_id,
|
||||
|
||||
pdata: new Binary(pdata),
|
||||
check_sum,
|
||||
}
|
||||
@@ -125,6 +175,12 @@ export class UserService {
|
||||
}).toArray();
|
||||
}
|
||||
|
||||
getPlayLog(player: string, clock: number): Promise<PlayerPlayLog> {
|
||||
return this.playLogCol.findOne({
|
||||
player, clock,
|
||||
});
|
||||
}
|
||||
|
||||
async getMusicData(player: string, music_id: number, play_style: number): Promise<PlayerMusicData> {
|
||||
const result = await this.musicDataCol.findOne({
|
||||
player,
|
||||
@@ -132,8 +188,10 @@ export class UserService {
|
||||
play_style,
|
||||
});
|
||||
|
||||
if (result)
|
||||
if (result) {
|
||||
result.best_score_clock ??= [-1, -1, -1, -1, -1];
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
player,
|
||||
@@ -145,6 +203,7 @@ export class UserService {
|
||||
miss_count: [-1, -1, -1, -1, -1],
|
||||
play_num: [0, 0, 0, 0, 0],
|
||||
clear_num: [0, 0, 0, 0, 0],
|
||||
best_score_clock: [-1, -1, -1, -1, -1],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -159,4 +218,32 @@ export class UserService {
|
||||
upsert: true
|
||||
});
|
||||
}
|
||||
|
||||
async getPlayerRivalData(player: string): Promise<PlayerRivalData> {
|
||||
const result = await this.rivalDataCol.findOne({ _id: player });
|
||||
|
||||
if (result) {
|
||||
if (!result.dp) result.dp = [];
|
||||
if (!result.sp) result.sp = [];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
_id: player,
|
||||
enabled: false,
|
||||
sp: [],
|
||||
dp: [],
|
||||
}
|
||||
}
|
||||
|
||||
upsertPlayerRivalData(rivalData: PlayerRivalData) {
|
||||
return this.rivalDataCol.updateOne({
|
||||
_id: rivalData._id
|
||||
}, {
|
||||
$set: rivalData
|
||||
}, {
|
||||
upsert: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface RivalPatch {
|
||||
enabled: boolean,
|
||||
};
|
||||
|
||||
export interface RivalPostOrDelete {
|
||||
infinitas_id: string;
|
||||
type: number;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,16 @@ export interface PlayerPlayData {
|
||||
pdata: Binary;
|
||||
// sha256 to ^
|
||||
check_sum: string;
|
||||
|
||||
djname: string;
|
||||
infinitas_id: string;
|
||||
}
|
||||
|
||||
export interface PlayerRivalData {
|
||||
_id: string;
|
||||
enabled: boolean;
|
||||
sp: string[];
|
||||
dp: string[];
|
||||
}
|
||||
|
||||
export interface PlayerMusicData {
|
||||
@@ -19,6 +29,9 @@ export interface PlayerMusicData {
|
||||
miss_count: number[];
|
||||
play_num: number[];
|
||||
clear_num: number[];
|
||||
|
||||
// for getting ghost data;
|
||||
best_score_clock: number[];
|
||||
}
|
||||
|
||||
export interface PlayerPlayLog {
|
||||
@@ -49,7 +62,7 @@ export interface PlayerPlayLog {
|
||||
arrange_1: number,
|
||||
assist: number,
|
||||
flip: number,
|
||||
ghost: Buffer,
|
||||
ghost: Binary,
|
||||
};
|
||||
|
||||
export interface PlayerCourseLog {
|
||||
@@ -79,6 +92,12 @@ export interface CourseResult {
|
||||
|
||||
export interface PlayerCustomizeSetting {
|
||||
_id: string;
|
||||
items_count: {
|
||||
bit: number;
|
||||
ldisc: number;
|
||||
infinitas_ticket: number;
|
||||
infinitas_ticket_free: number;
|
||||
},
|
||||
customize: {
|
||||
item_id: string,
|
||||
item_category: number,
|
||||
@@ -97,7 +97,7 @@ function toObject(node: Record<string, unknown> | unknown[] | string): any {
|
||||
return obj;
|
||||
}
|
||||
|
||||
export function fromKBinXml(kbinxml: Buffer) {
|
||||
export function fromKBinXml(kbinxml: Uint8Array) {
|
||||
const xml = to_xml(kbinxml).data;
|
||||
const parsedXml = parser.parse(xml);
|
||||
return toObject(parsedXml);
|
||||
|
||||
Reference in New Issue
Block a user