mirror of
https://github.com/laochan-eacnet/backend.git
synced 2026-10-04 20:47:58 +03:00
gitadora working now
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { generic } from "../../decorators/eacnet.js";
|
||||
import { UserService } from "../../services/gitadora/user.js";
|
||||
import { Context } from "../../types.js";
|
||||
import { Player } from "../../types/gitadora/data.js";
|
||||
@@ -7,7 +8,11 @@ import { tokenToHash } from "../../utils/laochan-id.js";
|
||||
export class User {
|
||||
userService: UserService;
|
||||
|
||||
knst_cardutil_check(): Serializable {
|
||||
@generic()
|
||||
async knst_cardutil_check(ctx: Context): Promise<Serializable> {
|
||||
const player = await this.userService.findPlayer(ctx.token);
|
||||
const type = this.getGameType(ctx);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error_code: v.s32(0),
|
||||
@@ -19,12 +24,43 @@ export class User {
|
||||
player: {
|
||||
$no: 1,
|
||||
$state: 2,
|
||||
refid: v.str(tokenToHash(ctx.token)),
|
||||
did: v.s32(1),
|
||||
cardnumber: v.str(''),
|
||||
usercode: v.str(tokenToHash(ctx.token)),
|
||||
player_type: v.s8(0),
|
||||
name: v.str(''),
|
||||
title: v.str(''),
|
||||
charaid: v.s32(0),
|
||||
skilldata: {
|
||||
skill: v.s32(player.gameSave[type].skill),
|
||||
all_skill: v.s32(player.gameSave.gf.skill + player.gameSave.dm.skill),
|
||||
old_skill: v.s32(-1),
|
||||
old_all_skill: v.s32(-1),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@generic()
|
||||
knst_cardutil_regist(): Serializable {
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error_code: v.s32(0),
|
||||
xrpc_status_code: v.s32(0),
|
||||
xrpc_fault_code: v.s32(0),
|
||||
response: {
|
||||
knst_cardutil: {
|
||||
$status: 0,
|
||||
is_succession: v.bool(false),
|
||||
did: v.s32(1),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getGameType(ctx: Context): 'gf' | 'dm' {
|
||||
if (typeof ctx.body.gametype !== 'string') {
|
||||
throw new Error('invaild request');
|
||||
@@ -39,6 +75,7 @@ export class User {
|
||||
throw new Error('unknown game type');
|
||||
}
|
||||
|
||||
@generic()
|
||||
async knst_gametop_get(ctx: Context): Promise<Serializable> {
|
||||
const type = this.getGameType(ctx);
|
||||
const [data, musics] = await Promise.all([
|
||||
@@ -58,22 +95,22 @@ export class User {
|
||||
$status: 0,
|
||||
player: {
|
||||
now_date: v.u64(Date.now()),
|
||||
rq_key: v.str('dummy_rq_key'),
|
||||
rq_key: v.str('dummy-req-key'),
|
||||
player_info: {
|
||||
refid: v.str(tokenToHash(ctx.token)),
|
||||
did: v.s32(1),
|
||||
cardnumber: v.str(''),
|
||||
usercode: v.str(tokenToHash(ctx.token)),
|
||||
usercode: v.str(tokenToHash(ctx.token).slice(0, 10)),
|
||||
player_type: v.s8(0),
|
||||
name: v.str(''),
|
||||
title: v.str(''),
|
||||
charaid: v.s32(0),
|
||||
},
|
||||
playinfo: Object.fromEntries(
|
||||
Object.entries(save).map(kv => [kv[0], v.s32(kv[1])])
|
||||
Object.entries(save.playInfo).map(kv => [kv[0], v.s32(kv[1])])
|
||||
),
|
||||
reward: {
|
||||
status: v.s32(save.rewardStatus),
|
||||
status: v.u32(save.rewardStatus),
|
||||
},
|
||||
customdata: {
|
||||
playstyle: v.s32(save.customData.playStyle),
|
||||
@@ -95,7 +132,13 @@ export class User {
|
||||
total_nr: v.s32(1),
|
||||
}
|
||||
},
|
||||
secretmusic: {},
|
||||
secretmusic: {
|
||||
music: data.secretMusics.map(m => ({
|
||||
musicid: v.s32(m),
|
||||
seq: v.u16(255),
|
||||
kind: v.s32(1),
|
||||
})),
|
||||
},
|
||||
favoritemusic: {
|
||||
list_1: v.s32(save.favoriteMusic.list1),
|
||||
list_2: v.s32(save.favoriteMusic.list2),
|
||||
@@ -110,7 +153,7 @@ export class User {
|
||||
Object.entries(data.gameSave.gf.maxRecord).map(kv => [kv[0], v.s32(kv[1])])
|
||||
),
|
||||
diff_record: Object.fromEntries(
|
||||
Object.entries(data.gameSave.gf.maxRecord).map(kv => [kv[0], v.s32(kv[1])])
|
||||
Object.entries(data.gameSave.gf.diffRecord).map(kv => [kv[0], v.s32(kv[1])])
|
||||
),
|
||||
},
|
||||
dm: {
|
||||
@@ -118,7 +161,7 @@ export class User {
|
||||
Object.entries(data.gameSave.dm.maxRecord).map(kv => [kv[0], v.s32(kv[1])])
|
||||
),
|
||||
diff_record: Object.fromEntries(
|
||||
Object.entries(data.gameSave.dm.maxRecord).map(kv => [kv[0], v.s32(kv[1])])
|
||||
Object.entries(data.gameSave.dm.diffRecord).map(kv => [kv[0], v.s32(kv[1])])
|
||||
),
|
||||
},
|
||||
},
|
||||
@@ -140,13 +183,13 @@ export class User {
|
||||
|
||||
for (const seq in m.seqs) {
|
||||
const rec = m.seqs[seq];
|
||||
if (rec.score > bestScore) {
|
||||
bestScore = rec.score;
|
||||
if (rec.skill > bestScore) {
|
||||
bestScore = rec.skill;
|
||||
bestSeq = +seq;
|
||||
}
|
||||
|
||||
mdata[+seq] = rec.clear ? rec.perc : -1;
|
||||
mdata[8 + seq] = rec.rank;
|
||||
mdata[8 + (+seq)] = rec.rank;
|
||||
meter[+seq] = rec.meter;
|
||||
meterProg[+seq] = rec.meterProgress;
|
||||
}
|
||||
@@ -168,29 +211,38 @@ export class User {
|
||||
};
|
||||
}
|
||||
|
||||
@generic()
|
||||
async knst_gameend_regist(ctx: Context): Promise<Serializable> {
|
||||
const type = this.getGameType(ctx);
|
||||
const player = ctx.body.player as Player;
|
||||
|
||||
const stages = ctx.body.player.stage ? (
|
||||
ctx.body.player.stage instanceof Array ?
|
||||
ctx.body.player.stage : [ctx.body.player.stage]
|
||||
) : [];
|
||||
|
||||
await Promise.all([
|
||||
this.userService.updateMusicData(ctx.token, ctx.body.player.stage, type),
|
||||
stages.map(m => this.userService.updateMusicData(ctx.token, m, type)),
|
||||
this.userService.upsertPlayer(ctx.token, {
|
||||
[type]: {
|
||||
playInfo: player.playinfo,
|
||||
rewardStatus: player.reward.status,
|
||||
customData: {
|
||||
playStyle: player.customdata.playstyle,
|
||||
custom: player.customdata.custom,
|
||||
secretMusics: player.secretmusic.music.map(v => v.musicid),
|
||||
gameSave: {
|
||||
[type]: {
|
||||
playInfo: player.playinfo,
|
||||
rewardStatus: player.reward.status,
|
||||
customData: {
|
||||
playStyle: player.customdata.playstyle,
|
||||
custom: player.customdata.custom,
|
||||
},
|
||||
skill: player.skilldata.skill,
|
||||
favoriteMusic: {
|
||||
list1: player.favoritemusic.music_list_1,
|
||||
list2: player.favoritemusic.music_list_2,
|
||||
list3: player.favoritemusic.music_list_3,
|
||||
},
|
||||
information: player.information.info,
|
||||
maxRecord: player.record.max,
|
||||
diffRecord: player.record.diff,
|
||||
},
|
||||
skill: player.skilldata.skill,
|
||||
favoriteMusic: {
|
||||
list1: player.favoritemusic.music_list_1,
|
||||
list2: player.favoritemusic.music_list_2,
|
||||
list3: player.favoritemusic.music_list_3,
|
||||
},
|
||||
information: player.information.info,
|
||||
maxRecord: player.record.max,
|
||||
diffRecord: player.record.diff,
|
||||
},
|
||||
})
|
||||
]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LZ77 } from '../utils/lz77.js';
|
||||
import { toKBinXml } from '../utils/kbinxml.js';
|
||||
import config from '../utils/config.js';
|
||||
|
||||
export function eacnet(protocol_name: string, topName: string, dataOffset: number) {
|
||||
return (encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8') => {
|
||||
@@ -8,7 +9,7 @@ export function eacnet(protocol_name: string, topName: string, dataOffset: numbe
|
||||
descriptor.value = async function (...args: any[]) {
|
||||
const obj = await orig.apply(this, args);
|
||||
|
||||
const kxml = toKBinXml(topName, obj, encoding);
|
||||
const kxml = toKBinXml(topName, obj, encoding, config.isDev);
|
||||
const compressed = LZ77.compress(kxml.data);
|
||||
const result = Buffer.allocUnsafe(compressed.length + dataOffset);
|
||||
|
||||
|
||||
@@ -118,6 +118,8 @@ async function main(): Promise<void> {
|
||||
return '<ITEM>';
|
||||
} else if (key == 'param') {
|
||||
return '<PARAM>';
|
||||
} else if (typeof value === 'bigint') {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
@@ -124,7 +124,8 @@ export class UserService {
|
||||
async findPlayer(token: string): Promise<PlayerData> {
|
||||
const player = await this.playerDataCol.findOne({ _id: token }) ?? {
|
||||
_id: token,
|
||||
gameSave: {}
|
||||
gameSave: {},
|
||||
secretMusics: [],
|
||||
} as PlayerData;
|
||||
|
||||
if (!player.gameSave.gf) {
|
||||
@@ -159,6 +160,7 @@ export class UserService {
|
||||
}, { useBigInt64: true }) ?? {
|
||||
player: token,
|
||||
musicId: stage.musicid,
|
||||
seqs: {},
|
||||
} as MusicData)(),
|
||||
this.playRecordCol(type).insertOne({
|
||||
player: token,
|
||||
@@ -167,18 +169,6 @@ export class UserService {
|
||||
]);
|
||||
|
||||
if (stage.seq in data.seqs) {
|
||||
data.seqs[stage.seq] = {
|
||||
fullcombo: stage.fullcombo,
|
||||
excellent: stage.excellent,
|
||||
clear: stage.clear,
|
||||
score: stage.score,
|
||||
perc: stage.perc,
|
||||
rank: stage.rank,
|
||||
meter: stage.meter,
|
||||
meterProgress: stage.meter_prog,
|
||||
bestRecord: result.insertedId,
|
||||
}
|
||||
} else {
|
||||
const seqData = data.seqs[stage.seq];
|
||||
|
||||
seqData.fullcombo = seqData.fullcombo || stage.fullcombo;
|
||||
@@ -191,16 +181,28 @@ export class UserService {
|
||||
}
|
||||
|
||||
seqData.rank = Math.max(seqData.rank, stage.rank);
|
||||
seqData.score = Math.max(seqData.score, stage.score);
|
||||
seqData.skill = Math.max(seqData.skill, stage.score);
|
||||
|
||||
if (stage.meter_prog > seqData.meterProgress) {
|
||||
seqData.meterProgress = stage.meter_prog;
|
||||
seqData.meter = stage.meter;
|
||||
}
|
||||
} else {
|
||||
data.seqs[stage.seq] = {
|
||||
fullcombo: stage.fullcombo,
|
||||
excellent: stage.excellent,
|
||||
clear: stage.clear,
|
||||
skill: stage.skill,
|
||||
perc: stage.perc,
|
||||
rank: stage.rank,
|
||||
meter: stage.meter,
|
||||
meterProgress: stage.meter_prog,
|
||||
bestRecord: result.insertedId,
|
||||
}
|
||||
}
|
||||
|
||||
await this.musicDataCol(type).updateOne({ player: token, musicId: stage.musicid }, {
|
||||
$set: data,
|
||||
}, { useBigInt64: true });
|
||||
}, { useBigInt64: true, upsert: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,10 @@ export interface Player {
|
||||
reward: Reward;
|
||||
skilldata: Skilldata;
|
||||
groove: Groove;
|
||||
record: Rec;
|
||||
record: {
|
||||
max: Record<string, number>;
|
||||
diff: Record<string, number | number[]>;
|
||||
};
|
||||
battledata: Battledata;
|
||||
sessiondata: Sessiondata;
|
||||
favoritemusic: Favoritemusic;
|
||||
@@ -253,63 +256,6 @@ export interface PhraseComboChallenge {
|
||||
point: number;
|
||||
}
|
||||
|
||||
export interface Rec {
|
||||
max: Max;
|
||||
diff: Diff;
|
||||
}
|
||||
|
||||
export interface Diff {
|
||||
diff_100_nr: number;
|
||||
diff_150_nr: number;
|
||||
diff_200_nr: number;
|
||||
diff_250_nr: number;
|
||||
diff_300_nr: number;
|
||||
diff_350_nr: number;
|
||||
diff_400_nr: number;
|
||||
diff_450_nr: number;
|
||||
diff_500_nr: number;
|
||||
diff_550_nr: number;
|
||||
diff_600_nr: number;
|
||||
diff_650_nr: number;
|
||||
diff_700_nr: number;
|
||||
diff_750_nr: number;
|
||||
diff_800_nr: number;
|
||||
diff_850_nr: number;
|
||||
diff_900_nr: number;
|
||||
diff_950_nr: number;
|
||||
diff_100_clear: number[];
|
||||
diff_150_clear: number[];
|
||||
diff_200_clear: number[];
|
||||
diff_250_clear: number[];
|
||||
diff_300_clear: number[];
|
||||
diff_350_clear: number[];
|
||||
diff_400_clear: number[];
|
||||
diff_450_clear: number[];
|
||||
diff_500_clear: number[];
|
||||
diff_550_clear: number[];
|
||||
diff_600_clear: number[];
|
||||
diff_650_clear: number[];
|
||||
diff_700_clear: number[];
|
||||
diff_750_clear: number[];
|
||||
diff_800_clear: number[];
|
||||
diff_850_clear: number[];
|
||||
diff_900_clear: number[];
|
||||
diff_950_clear: number[];
|
||||
}
|
||||
|
||||
export interface Max {
|
||||
skill: number;
|
||||
all_skill: number;
|
||||
clear_diff: number;
|
||||
full_diff: number;
|
||||
exce_diff: number;
|
||||
clear_music_num: number;
|
||||
full_music_num: number;
|
||||
exce_music_num: number;
|
||||
clear_seq_num: number;
|
||||
classic_all_skill: number;
|
||||
}
|
||||
|
||||
export interface Reward {
|
||||
status: number[];
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@ export interface GameSave {
|
||||
|
||||
export interface PlayerData {
|
||||
_id: string;
|
||||
gameSave: Record<'gf' | 'dm', GameSave>;
|
||||
gameSave: {
|
||||
[type: string]: GameSave
|
||||
};
|
||||
secretMusics: number[];
|
||||
}
|
||||
|
||||
export interface MusicData {
|
||||
@@ -33,7 +36,7 @@ export interface MusicData {
|
||||
clear: boolean;
|
||||
fullcombo: boolean;
|
||||
excellent: boolean;
|
||||
score: number;
|
||||
skill: number;
|
||||
perc: number;
|
||||
rank: number;
|
||||
meter: bigint;
|
||||
|
||||
+16
-8
@@ -2,6 +2,7 @@ import { to_bin, to_xml } from "@geekidos/kbinxml";
|
||||
import { XMLParser } from "fast-xml-parser";
|
||||
import _ from "lodash";
|
||||
import { Serializable } from "./kxml-value.js";
|
||||
import { writeFileSync } from "fs";
|
||||
|
||||
export const parser = new XMLParser({
|
||||
ignoreAttributes: false,
|
||||
@@ -215,7 +216,7 @@ function serializeObject(obj: Serializable, name: string, linePrefix: string = '
|
||||
}
|
||||
|
||||
// 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));
|
||||
// attrs.sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
|
||||
|
||||
for (const attr of attrs) {
|
||||
if (attr[1] === undefined)
|
||||
@@ -224,9 +225,9 @@ function serializeObject(obj: Serializable, name: string, linePrefix: string = '
|
||||
output += ` ${attr[0]}="${_.escape(attr[1])}"`;
|
||||
}
|
||||
|
||||
output += '>';
|
||||
|
||||
if (value) {
|
||||
output += '>';
|
||||
|
||||
if (value[1] === undefined) {
|
||||
return '';
|
||||
}
|
||||
@@ -243,11 +244,11 @@ function serializeObject(obj: Serializable, name: string, linePrefix: string = '
|
||||
.join(' ');
|
||||
} else {
|
||||
const elements = entries
|
||||
.filter(v => !v[0].startsWith('$'))
|
||||
.sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
|
||||
.filter(v => !v[0].startsWith('$'));
|
||||
// .sort((a, b) => a[0] < b[0] ? 1 : (a[0] > b[0] ? -1 : 0));
|
||||
|
||||
if (elements.length) {
|
||||
output += '\n';
|
||||
output += '>\n';
|
||||
|
||||
for (const element of elements) {
|
||||
if (element[1] instanceof Array) {
|
||||
@@ -261,6 +262,8 @@ function serializeObject(obj: Serializable, name: string, linePrefix: string = '
|
||||
}
|
||||
|
||||
output += linePrefix;
|
||||
} else {
|
||||
return output + ' />\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,11 +271,16 @@ function serializeObject(obj: Serializable, name: string, linePrefix: string = '
|
||||
}
|
||||
|
||||
export function toKBinXml(topName: string, obj: Serializable, encoding: 'UTF-8' | 'SHIFT_JIS' = 'UTF-8', dumpXml: boolean = false) {
|
||||
const xml = `<?xml version="1.0" encoding="${encoding}"?>` + serializeObject(obj, topName);
|
||||
const xml = `<?xml version="1.0" encoding="${encoding}"?>\n` + serializeObject(obj, topName);
|
||||
|
||||
if (dumpXml) {
|
||||
console.log(xml);
|
||||
}
|
||||
|
||||
return to_bin(xml);
|
||||
const bin = to_bin(xml);
|
||||
if (dumpXml) {
|
||||
writeFileSync('dump.bin', Buffer.from(bin.data));
|
||||
}
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user