gitadora working now

This commit is contained in:
GEEKiDoS
2025-04-18 17:44:49 +08:00
parent 22820d5040
commit bf47786448
7 changed files with 125 additions and 111 deletions
+79 -27
View File
@@ -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,
},
})
]);
+2 -1
View File
@@ -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);
+2
View File
@@ -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;
+17 -15
View File
@@ -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 });
}
}
+4 -58
View File
@@ -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[];
}
+5 -2
View File
@@ -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
View File
@@ -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;
}