mirror of
https://github.com/laochan-eacnet/backend.git
synced 2026-09-22 22:28:11 +03:00
gitadora done
This commit is contained in:
@@ -1,16 +1,20 @@
|
||||
import { singleton } from "tsyringe";
|
||||
import { inject, singleton } from "tsyringe";
|
||||
import { Combine } from "../../utils/combine.js";
|
||||
import { EacGeneric } from "../eac-generic.js";
|
||||
import { User } from "./user.js";
|
||||
import { generic } from "../../decorators/eacnet.js";
|
||||
import { Serializable, v } from "../../utils/kxml-value.js";
|
||||
import { event, musicinfo } from "../../datas/gitadora.js";
|
||||
import { UserService } from "../../services/gitadora/user.js";
|
||||
|
||||
@singleton()
|
||||
export default class extends Combine(EacGeneric, User) {
|
||||
constructor() {
|
||||
constructor(
|
||||
@inject(UserService) private readonly userService: UserService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this.userService;
|
||||
}
|
||||
|
||||
@generic()
|
||||
|
||||
@@ -1,8 +1,224 @@
|
||||
import { UserService } from "../../services/gitadora/user.js";
|
||||
import { Context } from "../../types.js";
|
||||
import { Serializable } from "../../utils/kxml-value.js";
|
||||
import { Player } from "../../types/gitadora/data.js";
|
||||
import { Serializable, v } from "../../utils/kxml-value.js";
|
||||
import { tokenToHash } from "../../utils/laochan-id.js";
|
||||
|
||||
export class User {
|
||||
async knst_cardutil_check(ctx: Context): Promise<Serializable> {
|
||||
userService: UserService;
|
||||
|
||||
knst_cardutil_check(): 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,
|
||||
player: {
|
||||
$no: 1,
|
||||
$state: 2,
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
getGameType(ctx: Context): 'gf' | 'dm' {
|
||||
if (typeof ctx.body.gametype !== 'string') {
|
||||
throw new Error('invaild request');
|
||||
}
|
||||
|
||||
if (ctx.body.gametype == 'U32:J:A') {
|
||||
return 'gf';
|
||||
} else if (ctx.body.gametype == 'U32:J:B') {
|
||||
return 'dm';
|
||||
}
|
||||
|
||||
throw new Error('unknown game type');
|
||||
}
|
||||
|
||||
async knst_gametop_get(ctx: Context): Promise<Serializable> {
|
||||
const type = this.getGameType(ctx);
|
||||
const [data, musics] = await Promise.all([
|
||||
this.userService.findPlayer(ctx.token),
|
||||
this.userService.findMusicDatas(ctx.token, type),
|
||||
]);
|
||||
|
||||
const save = data.gameSave[type];
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error_code: v.s32(0),
|
||||
xrpc_status_code: v.s32(0),
|
||||
xrpc_fault_code: v.s32(0),
|
||||
response: {
|
||||
knst_gametop: {
|
||||
$status: 0,
|
||||
player: {
|
||||
now_date: v.u64(Date.now()),
|
||||
rq_key: v.str('dummy_rq_key'),
|
||||
player_info: {
|
||||
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),
|
||||
},
|
||||
playinfo: Object.fromEntries(
|
||||
Object.entries(save).map(kv => [kv[0], v.s32(kv[1])])
|
||||
),
|
||||
reward: {
|
||||
status: v.s32(save.rewardStatus),
|
||||
},
|
||||
customdata: {
|
||||
playstyle: v.s32(save.customData.playStyle),
|
||||
custom: v.s32(save.customData.custom),
|
||||
},
|
||||
skilldata: {
|
||||
skill: v.s32(save.skill),
|
||||
all_skill: v.s32(data.gameSave.gf.skill + data.gameSave.dm.skill),
|
||||
old_skill: v.s32(-1),
|
||||
old_all_skill: v.s32(-1),
|
||||
},
|
||||
ranking: {
|
||||
skill: {
|
||||
rank: v.s32(1),
|
||||
total_nr: v.s32(1),
|
||||
},
|
||||
all_skill: {
|
||||
rank: v.s32(1),
|
||||
total_nr: v.s32(1),
|
||||
}
|
||||
},
|
||||
secretmusic: {},
|
||||
favoritemusic: {
|
||||
list_1: v.s32(save.favoriteMusic.list1),
|
||||
list_2: v.s32(save.favoriteMusic.list2),
|
||||
list_3: v.s32(save.favoriteMusic.list3),
|
||||
},
|
||||
information: {
|
||||
info: v.u32(save.information),
|
||||
},
|
||||
record: {
|
||||
gf: {
|
||||
max_record: Object.fromEntries(
|
||||
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])])
|
||||
),
|
||||
},
|
||||
dm: {
|
||||
max_record: Object.fromEntries(
|
||||
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])])
|
||||
),
|
||||
},
|
||||
},
|
||||
stage_result: {},
|
||||
rockwave: {},
|
||||
mp_list: {},
|
||||
musiclist: {
|
||||
$nr: musics.length,
|
||||
musicdata: musics.map(m => {
|
||||
let bestSeq = -1;
|
||||
let bestScore = -1;
|
||||
const mdata = [
|
||||
-1,
|
||||
-2, -2, -2, -2, -2, -2, -2, -2,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
];
|
||||
const meter = new Array(8).fill(0);
|
||||
const meterProg = new Array(8).fill(0);
|
||||
|
||||
for (const seq in m.seqs) {
|
||||
const rec = m.seqs[seq];
|
||||
if (rec.score > bestScore) {
|
||||
bestScore = rec.score;
|
||||
bestSeq = +seq;
|
||||
}
|
||||
|
||||
mdata[+seq] = rec.clear ? rec.perc : -1;
|
||||
mdata[8 + seq] = rec.rank;
|
||||
meter[+seq] = rec.meter;
|
||||
meterProg[+seq] = rec.meterProgress;
|
||||
}
|
||||
|
||||
return {
|
||||
$musicid: m.musicId,
|
||||
flag: v.u16([0, 0, 0, 0]),
|
||||
sdata: v.s16([bestSeq, bestScore]),
|
||||
mdata: v.s16(mdata),
|
||||
meter: v.u64(meter),
|
||||
meter_prog: v.s16(meterProg),
|
||||
}
|
||||
}),
|
||||
},
|
||||
finish: v.bool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async knst_gameend_regist(ctx: Context): Promise<Serializable> {
|
||||
const type = this.getGameType(ctx);
|
||||
const player = ctx.body.player as Player;
|
||||
|
||||
await Promise.all([
|
||||
this.userService.updateMusicData(ctx.token, ctx.body.player.stage, type),
|
||||
this.userService.upsertPlayer(ctx.token, {
|
||||
[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,
|
||||
},
|
||||
})
|
||||
]);
|
||||
|
||||
return {
|
||||
status: v.s32(0),
|
||||
error_code: v.s32(0),
|
||||
xrpc_status_code: v.s32(0),
|
||||
xrpc_fault_code: v.s32(0),
|
||||
response: {
|
||||
knst_gameend: {
|
||||
$status: 0,
|
||||
gamemode: {
|
||||
$mode: 'game_mode',
|
||||
},
|
||||
player: {
|
||||
$no: 1,
|
||||
skill: {
|
||||
rank: v.s32(1),
|
||||
total_nr: v.s32(1),
|
||||
},
|
||||
all_skill: {
|
||||
rank: v.s32(1),
|
||||
total_nr: v.s32(1),
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
import { Db, UpdateResult } from "mongodb";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
import { GameSave, MusicData, PlayerData, PlayRecord, Stage } from "../../types/gitadora/index.js";
|
||||
|
||||
@injectable()
|
||||
export class UserService {
|
||||
constructor(
|
||||
@inject(Db)
|
||||
private readonly db: Db,
|
||||
) { }
|
||||
|
||||
get playerDataCol() {
|
||||
return this.db.collection<PlayerData>('gitadora_play_data');
|
||||
}
|
||||
|
||||
musicDataCol(type: 'gf' | 'dm') {
|
||||
return this.db.collection<MusicData>('gitadora_' + type + '_music_data');
|
||||
}
|
||||
|
||||
playRecordCol(type: 'gf' | 'dm') {
|
||||
return this.db.collection<PlayRecord>('gitadora_' + type + '_play_record');
|
||||
}
|
||||
|
||||
private emptyGameSave(): GameSave {
|
||||
return {
|
||||
playInfo: {
|
||||
cabid: 0,
|
||||
play: 0,
|
||||
playtime: 0,
|
||||
playterm: 0,
|
||||
session_cnt: 0,
|
||||
matching_num: 0,
|
||||
extra_stage: 0,
|
||||
extra_play: 0,
|
||||
extra_clear: 0,
|
||||
encore_play: 0,
|
||||
encore_clear: 0,
|
||||
pencore_play: 0,
|
||||
pencore_clear: 0,
|
||||
max_clear_diff: 0,
|
||||
max_full_diff: 0,
|
||||
max_exce_diff: 0,
|
||||
clear_num: 0,
|
||||
full_num: 0,
|
||||
exce_num: 0,
|
||||
no_num: 0,
|
||||
e_num: 0,
|
||||
d_num: 0,
|
||||
c_num: 0,
|
||||
b_num: 0,
|
||||
a_num: 0,
|
||||
s_num: 0,
|
||||
ss_num: 0,
|
||||
last_category: 0,
|
||||
last_musicid: -1,
|
||||
last_seq: 0,
|
||||
disp_level: 0,
|
||||
},
|
||||
rewardStatus: new Array(50).fill(0),
|
||||
customData: {
|
||||
playStyle: new Array(50).fill(0),
|
||||
custom: new Array(50).fill(0),
|
||||
},
|
||||
skill: 0,
|
||||
favoriteMusic: {
|
||||
list1: new Array(100).fill(-1),
|
||||
list2: new Array(100).fill(-1),
|
||||
list3: new Array(100).fill(-1),
|
||||
},
|
||||
information: new Array(50).fill(0),
|
||||
maxRecord: {
|
||||
skill: 0,
|
||||
all_skill: 0,
|
||||
clear_diff: 0,
|
||||
full_diff: 0,
|
||||
exce_diff: 0,
|
||||
clear_music_num: 0,
|
||||
full_music_num: 0,
|
||||
exce_music_num: 0,
|
||||
clear_seq_num: 0,
|
||||
classic_all_skill: 0,
|
||||
},
|
||||
diffRecord: {
|
||||
diff_100_nr: 0,
|
||||
diff_150_nr: 0,
|
||||
diff_200_nr: 0,
|
||||
diff_250_nr: 0,
|
||||
diff_300_nr: 0,
|
||||
diff_350_nr: 0,
|
||||
diff_400_nr: 0,
|
||||
diff_450_nr: 0,
|
||||
diff_500_nr: 0,
|
||||
diff_550_nr: 0,
|
||||
diff_600_nr: 0,
|
||||
diff_650_nr: 0,
|
||||
diff_700_nr: 0,
|
||||
diff_750_nr: 0,
|
||||
diff_800_nr: 0,
|
||||
diff_850_nr: 0,
|
||||
diff_900_nr: 0,
|
||||
diff_950_nr: 0,
|
||||
diff_100_clear: new Array(7).fill(0),
|
||||
diff_150_clear: new Array(7).fill(0),
|
||||
diff_200_clear: new Array(7).fill(0),
|
||||
diff_250_clear: new Array(7).fill(0),
|
||||
diff_300_clear: new Array(7).fill(0),
|
||||
diff_350_clear: new Array(7).fill(0),
|
||||
diff_400_clear: new Array(7).fill(0),
|
||||
diff_450_clear: new Array(7).fill(0),
|
||||
diff_500_clear: new Array(7).fill(0),
|
||||
diff_550_clear: new Array(7).fill(0),
|
||||
diff_600_clear: new Array(7).fill(0),
|
||||
diff_650_clear: new Array(7).fill(0),
|
||||
diff_700_clear: new Array(7).fill(0),
|
||||
diff_750_clear: new Array(7).fill(0),
|
||||
diff_800_clear: new Array(7).fill(0),
|
||||
diff_850_clear: new Array(7).fill(0),
|
||||
diff_900_clear: new Array(7).fill(0),
|
||||
diff_950_clear: new Array(7).fill(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async findPlayer(token: string): Promise<PlayerData> {
|
||||
const player = await this.playerDataCol.findOne({ _id: token }) ?? {
|
||||
_id: token,
|
||||
gameSave: {}
|
||||
} as PlayerData;
|
||||
|
||||
if (!player.gameSave.gf) {
|
||||
player.gameSave.gf = this.emptyGameSave();
|
||||
}
|
||||
|
||||
if (!player.gameSave.dm) {
|
||||
player.gameSave.dm = this.emptyGameSave();
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
upsertPlayer(token: string, data: Partial<PlayerData>): Promise<UpdateResult<PlayerData>> {
|
||||
return this.playerDataCol.updateOne({ _id: token }, {
|
||||
$set: data,
|
||||
}, { upsert: true });
|
||||
}
|
||||
|
||||
findMusicDatas(token: string, type: 'gf' | 'dm'): Promise<MusicData[]> {
|
||||
return this.musicDataCol(type).find({ player: token }, { useBigInt64: true }).toArray();
|
||||
}
|
||||
|
||||
async updateMusicData(token: string, stage: Stage, type: 'gf' | 'dm'): Promise<void> {
|
||||
// make sure meter is bigint
|
||||
stage.meter = BigInt(stage.meter);
|
||||
|
||||
const [data, result] = await Promise.all([
|
||||
(async () => await this.musicDataCol(type).findOne({
|
||||
player: token,
|
||||
musicId: stage.musicid,
|
||||
}, { useBigInt64: true }) ?? {
|
||||
player: token,
|
||||
musicId: stage.musicid,
|
||||
} as MusicData)(),
|
||||
this.playRecordCol(type).insertOne({
|
||||
player: token,
|
||||
...stage
|
||||
}, { useBigInt64: true }),
|
||||
]);
|
||||
|
||||
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;
|
||||
seqData.excellent = seqData.excellent || stage.excellent;
|
||||
seqData.clear = seqData.clear || stage.clear;
|
||||
|
||||
if (stage.perc > seqData.perc) {
|
||||
seqData.perc = stage.perc;
|
||||
seqData.bestRecord = result.insertedId;
|
||||
}
|
||||
|
||||
seqData.rank = Math.max(seqData.rank, stage.rank);
|
||||
seqData.score = Math.max(seqData.score, stage.score);
|
||||
|
||||
if (stage.meter_prog > seqData.meterProgress) {
|
||||
seqData.meterProgress = stage.meter_prog;
|
||||
seqData.meter = stage.meter;
|
||||
}
|
||||
}
|
||||
|
||||
await this.musicDataCol(type).updateOne({ player: token, musicId: stage.musicid }, {
|
||||
$set: data,
|
||||
}, { useBigInt64: true });
|
||||
}
|
||||
}
|
||||
@@ -74,3 +74,302 @@ export interface MdbData {
|
||||
ifs_ver: number;
|
||||
knst_course: number;
|
||||
}
|
||||
|
||||
export interface Stage {
|
||||
date_ms: number;
|
||||
stage_no: number;
|
||||
musicid: number;
|
||||
seq: number;
|
||||
skill: number;
|
||||
new_skill: number;
|
||||
clear: boolean;
|
||||
auto_clear: boolean;
|
||||
fullcombo: boolean;
|
||||
excellent: boolean;
|
||||
medal: number;
|
||||
perc: number;
|
||||
new_perc: number;
|
||||
rank: number;
|
||||
score: number;
|
||||
combo: number;
|
||||
max_combo_perc: number;
|
||||
flags: number;
|
||||
phrase_combo_perc: number;
|
||||
perfect: number;
|
||||
great: number;
|
||||
good: number;
|
||||
ok: number;
|
||||
miss: number;
|
||||
perfect_perc: number;
|
||||
great_perc: number;
|
||||
good_perc: number;
|
||||
ok_perc: number;
|
||||
miss_perc: number;
|
||||
meter: number | bigint;
|
||||
meter_prog: number;
|
||||
before_meter: number;
|
||||
before_meter_prog: number;
|
||||
is_new_meter: boolean;
|
||||
phrase_data_num: number;
|
||||
phrase_addr: number[];
|
||||
phrase_type: number[];
|
||||
phrase_status: number[];
|
||||
phrase_end_addr: number;
|
||||
session: Record<'total' | 'gf1' | 'gf2' | 'dm', {
|
||||
perc: number;
|
||||
perfect: number;
|
||||
combo: number;
|
||||
synchro_meter: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
play_mode: number;
|
||||
rq_key: string;
|
||||
refid: string;
|
||||
charaid: number;
|
||||
used_app_point: number;
|
||||
card_no: string;
|
||||
customdata: Customdata;
|
||||
playinfo: { [key: string]: number };
|
||||
title_parts: TitleParts;
|
||||
secretmusic: Secretmusic;
|
||||
tutorial: Tutorial;
|
||||
information: Information;
|
||||
reward: Reward;
|
||||
skilldata: Skilldata;
|
||||
groove: Groove;
|
||||
record: Rec;
|
||||
battledata: Battledata;
|
||||
sessiondata: Sessiondata;
|
||||
favoritemusic: Favoritemusic;
|
||||
recommend_musicid_list: number[];
|
||||
thanks_medal: ThanksMedal;
|
||||
skindata: Skindata;
|
||||
monthly_skill: MonthlySkill;
|
||||
event_skill: EventSkill;
|
||||
phrase_combo_challenge: PhraseComboChallenge;
|
||||
phrase_combo_challenge_2: PhraseComboChallenge;
|
||||
phrase_combo_challenge_3: PhraseComboChallenge;
|
||||
phrase_combo_challenge_4: PhraseComboChallenge;
|
||||
phrase_combo_challenge_5: PhraseComboChallenge;
|
||||
phrase_combo_challenge_6: PhraseComboChallenge;
|
||||
phrase_combo_challenge_7: PhraseComboChallenge;
|
||||
phrase_combo_challenge_8: PhraseComboChallenge;
|
||||
phrase_combo_challenge_9: PhraseComboChallenge;
|
||||
phrase_combo_challenge_10: PhraseComboChallenge;
|
||||
phrase_combo_challenge_11: PhraseComboChallenge;
|
||||
phrase_combo_challenge_12: PhraseComboChallenge;
|
||||
phrase_combo_challenge_13: PhraseComboChallenge;
|
||||
phrase_combo_challenge_14: PhraseComboChallenge;
|
||||
phrase_combo_challenge_15: PhraseComboChallenge;
|
||||
phrase_combo_challenge_16: PhraseComboChallenge;
|
||||
phrase_combo_challenge_17: PhraseComboChallenge;
|
||||
phrase_combo_challenge_18: PhraseComboChallenge;
|
||||
phrase_combo_challenge_19: PhraseComboChallenge;
|
||||
phrase_combo_challenge_20: PhraseComboChallenge;
|
||||
rockwave: Rockwave;
|
||||
stage: Stage;
|
||||
card: string;
|
||||
no: string;
|
||||
}
|
||||
|
||||
export interface Battledata {
|
||||
info: Info;
|
||||
setting: Setting;
|
||||
score: BattledataScore;
|
||||
}
|
||||
|
||||
export interface Info {
|
||||
orb: number;
|
||||
get_gb_point: number;
|
||||
send_gb_point: number;
|
||||
}
|
||||
|
||||
export interface BattledataScore {
|
||||
battle_class: number;
|
||||
max_battle_class: number;
|
||||
battle_point: number;
|
||||
win: number;
|
||||
lose: number;
|
||||
draw: number;
|
||||
consecutive_win: number;
|
||||
max_consecutive_win: number;
|
||||
glorious_win: number;
|
||||
max_defeat_skill: number;
|
||||
latest_result: number;
|
||||
}
|
||||
|
||||
export interface Setting {
|
||||
matching: number;
|
||||
info_level: number;
|
||||
}
|
||||
|
||||
export interface Customdata {
|
||||
playstyle: number[];
|
||||
custom: number[];
|
||||
}
|
||||
|
||||
export interface EventSkill {
|
||||
skill: number;
|
||||
target_skill_eventid: number[];
|
||||
}
|
||||
|
||||
export interface Favoritemusic {
|
||||
music_list_1: number[];
|
||||
music_list_2: number[];
|
||||
music_list_3: number[];
|
||||
}
|
||||
|
||||
export interface Groove {
|
||||
extra_gauge: number;
|
||||
encore_gauge: number;
|
||||
encore_cnt: number;
|
||||
encore_success: number;
|
||||
unlock_point: number;
|
||||
}
|
||||
|
||||
export interface Information {
|
||||
info: number[];
|
||||
}
|
||||
|
||||
export interface MonthlySkill {
|
||||
term: number;
|
||||
total_skill: number;
|
||||
target_score: TargetScore;
|
||||
}
|
||||
|
||||
export interface TargetScore {
|
||||
music: TargetScoreMusic[];
|
||||
}
|
||||
|
||||
export interface TargetScoreMusic {
|
||||
seq: number;
|
||||
perc: number;
|
||||
skill: number;
|
||||
}
|
||||
|
||||
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[];
|
||||
}
|
||||
|
||||
export interface Rockwave {
|
||||
score_list: ScoreList;
|
||||
}
|
||||
|
||||
export interface ScoreList {
|
||||
score: ScoreElement[];
|
||||
}
|
||||
|
||||
export interface ScoreElement {
|
||||
data_id: number;
|
||||
point: number[];
|
||||
mtime: number;
|
||||
play_cnt: number;
|
||||
is_clear: boolean;
|
||||
}
|
||||
|
||||
export interface Secretmusic {
|
||||
music: SecretmusicMusic[];
|
||||
}
|
||||
|
||||
export interface SecretmusicMusic {
|
||||
musicid: number;
|
||||
seq: number;
|
||||
kind: number;
|
||||
}
|
||||
|
||||
export interface Sessiondata {
|
||||
date_ms: number;
|
||||
member: { [key: string]: Member };
|
||||
}
|
||||
|
||||
export interface Member {
|
||||
did: number;
|
||||
}
|
||||
|
||||
export interface Skilldata {
|
||||
skill: number;
|
||||
all_skill: number;
|
||||
exist: number[];
|
||||
new: number[];
|
||||
}
|
||||
|
||||
export interface Skindata {
|
||||
skin: number[];
|
||||
}
|
||||
|
||||
export interface ThanksMedal {
|
||||
medal: number;
|
||||
granted_total_medal: number;
|
||||
}
|
||||
|
||||
export interface TitleParts {
|
||||
}
|
||||
|
||||
export interface Tutorial {
|
||||
progress: number;
|
||||
disp_state: number;
|
||||
}
|
||||
|
||||
|
||||
+37
-52
@@ -1,62 +1,47 @@
|
||||
import { Long } from "mongodb";
|
||||
import { ObjectId } from "mongodb";
|
||||
import { Stage } from "./data.js";
|
||||
|
||||
export interface GameSave {
|
||||
// all s32
|
||||
playInfo: Record<string, number>;
|
||||
rewardStatus: number[];
|
||||
customData: {
|
||||
playStyle: number[];
|
||||
custom: number[];
|
||||
};
|
||||
skill: number;
|
||||
favoriteMusic: {
|
||||
list1: number[];
|
||||
list2: number[];
|
||||
list3: number[];
|
||||
};
|
||||
information: number[];
|
||||
maxRecord: Record<string, number>;
|
||||
diffRecord: Record<string, number | number[]>;
|
||||
}
|
||||
|
||||
export interface PlayerData {
|
||||
_id: string;
|
||||
gameSave: Record<'gf' | 'dm', {
|
||||
playCount: number;
|
||||
playTime: number;
|
||||
playTerm: number;
|
||||
// all s32
|
||||
playInfo: Record<string, number>;
|
||||
tutorial: {
|
||||
progress: number;
|
||||
displayState: number;
|
||||
};
|
||||
rewardStatus: number[];
|
||||
customData: {
|
||||
playStyle: number[];
|
||||
custom: number[];
|
||||
};
|
||||
skill: number;
|
||||
favoriteMusic: {
|
||||
list1: number[];
|
||||
list2: number[];
|
||||
list3: number[];
|
||||
};
|
||||
information: number[];
|
||||
maxRecord: Record<string, number>;
|
||||
diffRecord: Record<string, number>;
|
||||
}>;
|
||||
gameSave: Record<'gf' | 'dm', GameSave>;
|
||||
}
|
||||
|
||||
export interface MusicData {
|
||||
type: 'gf' | 'dm';
|
||||
player: string;
|
||||
musicId: number;
|
||||
|
||||
clearStatus: number;
|
||||
rank: number;
|
||||
|
||||
perfect: number;
|
||||
great: number;
|
||||
good: number;
|
||||
ok: number;
|
||||
miss: number;
|
||||
|
||||
maxCombo: number;
|
||||
score: number;
|
||||
flags: number;
|
||||
point: number;
|
||||
percent: number;
|
||||
|
||||
meter: Long;
|
||||
|
||||
phrase: {
|
||||
count: number;
|
||||
addr: number[];
|
||||
type: number[];
|
||||
status: number[];
|
||||
endAddr: number;
|
||||
comboAerc: number;
|
||||
}
|
||||
seqs: Record<number, {
|
||||
clear: boolean;
|
||||
fullcombo: boolean;
|
||||
excellent: boolean;
|
||||
score: number;
|
||||
perc: number;
|
||||
rank: number;
|
||||
meter: bigint;
|
||||
meterProgress: number;
|
||||
bestRecord: ObjectId;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface PlayRecord extends Stage {
|
||||
player: string;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export const parser = new XMLParser({
|
||||
});
|
||||
|
||||
// TODO: maybe have a better way to workaround this.
|
||||
const ALWAYS_BIGINT_FOR_64BIT_NUMBER = true;
|
||||
const ALWAYS_BIGINT_FOR_64BIT_NUMBER = false;
|
||||
function process64BitInteger(v: string) {
|
||||
const bi = BigInt(v);
|
||||
if (ALWAYS_BIGINT_FOR_64BIT_NUMBER || bi < Number.MIN_SAFE_INTEGER || bi > Number.MAX_SAFE_INTEGER) {
|
||||
@@ -83,7 +83,7 @@ function parseValue(node: { $__type: string; $__count?: unknown }): any {
|
||||
}
|
||||
|
||||
if (node.$__type == 'time') {
|
||||
return new Date(parseInt(node['__value']) * 1000);
|
||||
return new Date(parseInt(node['__value']));
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user