diff --git a/gitadora@asphyxia/README.md b/gitadora@asphyxia/README.md index 662985e..493fa70 100644 --- a/gitadora@asphyxia/README.md +++ b/gitadora@asphyxia/README.md @@ -1,6 +1,6 @@ GITADORA Plugin for Asphyxia-Core ================================= -![Version: v1.2.1](https://img.shields.io/badge/version-v1.2.1-blue) +![Version: v1.2.2](https://img.shields.io/badge/version-v1.2.2-blue) This plugin is based on converted from public-exported Asphyxia's Routes. @@ -26,10 +26,18 @@ Known Issues * ~Information dialog keep showing as plugin doesn't store item data currently.~ (Fixed as of version 1.2.1) * Special Premium Encore on Nextage - Bandage solution is implemented. Try it. + * Friends and Rivals are unimplemented. Release Notes ============= +v1.2.2 +---------------- +* Major improvements to the MDB (song data) loader. MDB files can now be in .json, .xml or .b64 format. This applies to both the per-version defaults and custom MDBs. +* Added several player profile stats to the web UI. +* MDB loader now logs the number of loaded songs available to GF and DM. +* MDB: Fixed "is_secret" field being ignored (always set to false) + v1.2.1 ---------------- * Secret Music (unlocked songs) are now saved and loaded correctly. Partially fixes Github issue #34. Note that all songs are already marked as unlocked by the server - there is no need to unlock them manually. If you would like to lock them, consider using a custom MDB. diff --git a/gitadora@asphyxia/data/mdb/index.ts b/gitadora@asphyxia/data/mdb/index.ts index 5b4d1f9..9faf9f9 100644 --- a/gitadora@asphyxia/data/mdb/index.ts +++ b/gitadora@asphyxia/data/mdb/index.ts @@ -1,18 +1,6 @@ import Logger from "../../utils/logger"; +import { CommonMusicData } from "../../models/commonmusicdata"; -export interface CommonMusicDataField { - id: KITEM<"s32">; - cont_gf: KITEM<"bool">; - cont_dm: KITEM<"bool">; - is_secret: KITEM<"bool">; - is_hot: KITEM<"bool">; - data_ver: KITEM<"s32">; - diff: KARRAY<"u16">; -} - -export interface CommonMusicData { - music: CommonMusicDataField[] -} export enum DATAVersion { HIGHVOLTAGE = "hv", @@ -68,7 +56,9 @@ export async function readMDBFile(path: string, processHandler?: processRawDataH throw `Invalid MDB file type: ${fileType}. Only .json, .xml, .b64 are supported.` } - logger.debugInfo(`Loaded ${result.music.length} songs from MDB file.`) + let gfCount = result.music.filter((e) => e.cont_gf["@content"][0]).length + let dmCount = result.music.filter((e) => e.cont_dm["@content"][0]).length + logger.debugInfo(`Loaded ${result.music.length} songs from MDB file. ${gfCount} songs for GF, ${dmCount} songs for DM.`) return result } @@ -149,7 +139,7 @@ export async function defaultProcessRawXmlData(path: string): Promise { const version = getVersion(info); + let music: CommonMusicDataField[] = []; try { if (U.GetConfig("enable_custom_mdb")) { let customMdb = findMDBFile("custom") - music = (await readMDBFile(customMdb)).music } } catch (e) { @@ -23,7 +23,12 @@ export const playableMusic: EPR = async (info, data, send) => { music = (await loadSongsForGameVersion(version)).music } - await send.object({ + let response = getPlayableMusicResponse(music) + await send.object(response) +}; + +function getPlayableMusicResponse(music) { + return { hot: { major: K.ITEM('s32', 1), minor: K.ITEM('s32', 1), @@ -31,5 +36,5 @@ export const playableMusic: EPR = async (info, data, send) => { musicinfo: K.ATTR({ nr: `${music.length}` }, { music, }), - }); -}; \ No newline at end of file + } +} \ No newline at end of file diff --git a/gitadora@asphyxia/handlers/info.ts b/gitadora@asphyxia/handlers/info.ts index a0624c9..56c2d82 100644 --- a/gitadora@asphyxia/handlers/info.ts +++ b/gitadora@asphyxia/handlers/info.ts @@ -1,5 +1,7 @@ import { getEncoreStageData } from "../data/extrastage"; +import Logger from "../utils/logger"; +const logger = new Logger('info'); export const shopInfoRegist: EPR = async (info, data, send) => { send.object({ data: { @@ -16,64 +18,8 @@ export const shopInfoRegist: EPR = async (info, data, send) => { } export const gameInfoGet: EPR = async (info, data, send) => { - const addition: any = { - monstar_subjugation: { - bonus_musicid: K.ITEM('s32', 0), - }, - bear_fes: {}, - nextadium: {}, - }; - const time = BigInt(31536000); - for (let i = 1; i <= 20; ++i) { - const obj = { - term: K.ITEM('u8', 0), - start_date_ms: K.ITEM('u64', time), - end_date_ms: K.ITEM('u64', time), - }; - if (i == 1) { - addition[`phrase_combo_challenge`] = obj; - addition[`long_otobear_fes_1`] = { - term: K.ITEM('u8', 0), - start_date_ms: K.ITEM('u64', time), - end_date_ms: K.ITEM('u64', time), - bonus_musicid: {}, - }; - addition[`sdvx_stamprally3`] = obj; - addition[`chronicle_1`] = obj; - addition[`paseli_point_lottery`] = obj; - addition['sticker_campaign'] = { - term: K.ITEM('u8', 0), - sticker_list: {}, - }; - addition['thanksgiving'] = { - ...obj, - box_term: { - state: K.ITEM('u8', 0) - } - }; - addition['lotterybox'] = { - ...obj, - box_term: { - state: K.ITEM('u8', 0) - } - }; - } else { - addition[`phrase_combo_challenge_${i}`] = obj; - } - - if (i <= 4) { - addition['monstar_subjugation'][`monstar_subjugation_${i}`] = obj; - addition['bear_fes'][`bear_fes_${i}`] = obj; - } - - if (i <= 3) { - addition[`kouyou_challenge_${i}`] = { - term: K.ITEM('u8', 0), - bonus_musicid: K.ITEM('s32', 0), - }; - } - } - + + const eventData = getEventDataResponse() const extraData = getEncoreStageData(info) await send.object({ @@ -157,6 +103,70 @@ export const gameInfoGet: EPR = async (info, data, send) => { }, }, }, - ...addition, + ...eventData, }); }; + +function getEventDataResponse() { + const addition: any = { + monstar_subjugation: { + bonus_musicid: K.ITEM('s32', 0), + }, + bear_fes: {}, + nextadium: {}, + }; + const time = BigInt(31536000); + + for (let i = 1; i <= 20; ++i) { + const obj = { + term: K.ITEM('u8', 0), + start_date_ms: K.ITEM('u64', time), + end_date_ms: K.ITEM('u64', time), + }; + if (i == 1) { + addition[`phrase_combo_challenge`] = obj; + addition[`long_otobear_fes_1`] = { + term: K.ITEM('u8', 0), + start_date_ms: K.ITEM('u64', time), + end_date_ms: K.ITEM('u64', time), + bonus_musicid: {}, + }; + addition[`sdvx_stamprally3`] = obj; + addition[`chronicle_1`] = obj; + addition[`paseli_point_lottery`] = obj; + addition['sticker_campaign'] = { + term: K.ITEM('u8', 0), + sticker_list: {}, + }; + addition['thanksgiving'] = { + ...obj, + box_term: { + state: K.ITEM('u8', 0) + } + }; + addition['lotterybox'] = { + ...obj, + box_term: { + state: K.ITEM('u8', 0) + } + }; + } else { + + addition[`phrase_combo_challenge_${i}`] = obj; + } + + if (i <= 4) { + addition['monstar_subjugation'][`monstar_subjugation_${i}`] = obj; + addition['bear_fes'][`bear_fes_${i}`] = obj; + } + + if (i <= 3) { + addition[`kouyou_challenge_${i}`] = { + term: K.ITEM('u8', 0), + bonus_musicid: K.ITEM('s32', 0), + }; + } + } + + return addition +} diff --git a/gitadora@asphyxia/handlers/profiles.ts b/gitadora@asphyxia/handlers/profiles.ts index 59d0b8e..e5ca1fa 100644 --- a/gitadora@asphyxia/handlers/profiles.ts +++ b/gitadora@asphyxia/handlers/profiles.ts @@ -5,6 +5,8 @@ import { Record } from "../models/record"; import { Extra } from "../models/extra"; import { getVersion, isDM } from "../utils"; import { Scores } from "../models/scores"; +import { PlayerStickerResponse } from "../models/playerstickerresponse"; +import { SecretMusicResponse } from "../models/secretmusicresponse"; import { PLUGIN_VER } from "../const"; import Logger from "../utils/logger" import { isAsphyxiaDebugMode } from "../Utils/index"; @@ -230,38 +232,7 @@ export const getPlayer: EPR = async (info, data, send) => { })); } - const sticker: any[] = []; - - if (_.isArray(name.card)) { - for (const item of name.card) { - const id = _.get(item, 'id'); - const posX = _.get(item, 'position.0'); - const posY = _.get(item, 'position.1'); - const scaleX = _.get(item, 'scale.0'); - const scaleY = _.get(item, 'scale.1'); - const rotation = _.get(item, 'rotation'); - - if ( - !isFinite(id) || - !isFinite(posX) || - !isFinite(posY) || - !isFinite(scaleX) || - !isFinite(scaleY) || - !isFinite(rotation) - ) { - continue; - } - - sticker.push({ - id: K.ITEM('s32', id), - pos_x: K.ITEM('float', posX), - pos_y: K.ITEM('float', posY), - scale_x: K.ITEM('float', scaleX), - scale_y: K.ITEM('float', scaleY), - rotate: K.ITEM('float', rotation), - }); - } - } + const sticker: PlayerStickerResponse[] = getPlayerStickers(name.card); const playerData: any = { playerboard: { @@ -376,6 +347,7 @@ export const getPlayer: EPR = async (info, data, send) => { } const innerSecretMusic = getSecretMusicResponse(profile) + const innerFriendData = getFriendDataResponse(profile) const response = { player: K.ATTR({ 'no': `${no}` }, { @@ -392,7 +364,10 @@ export const getPlayer: EPR = async (info, data, send) => { status: K.ARRAY('u32', extra.reward_status ?? Array(50).fill(0)), }, rivaldata: {}, - frienddata: {}, + frienddata: { + friend: innerFriendData + }, + thanks_medal: { medal: K.ITEM('s32', 0), grant_medal: K.ITEM('s32', 0), @@ -527,6 +502,42 @@ export const getPlayer: EPR = async (info, data, send) => { send.object(response); } +function getPlayerStickers(playerCard) : PlayerStickerResponse[] { + let stickers : PlayerStickerResponse[] = [] + if (_.isArray(playerCard)) { + for (const item of playerCard) { + const id = _.get(item, 'id'); + const posX = _.get(item, 'position.0'); + const posY = _.get(item, 'position.1'); + const scaleX = _.get(item, 'scale.0'); + const scaleY = _.get(item, 'scale.1'); + const rotation = _.get(item, 'rotation'); + + if ( + !isFinite(id) || + !isFinite(posX) || + !isFinite(posY) || + !isFinite(scaleX) || + !isFinite(scaleY) || + !isFinite(rotation) + ) { + continue; + } + + stickers.push({ + id: K.ITEM('s32', id), + pos_x: K.ITEM('float', posX), + pos_y: K.ITEM('float', posY), + scale_x: K.ITEM('float', scaleX), + scale_y: K.ITEM('float', scaleY), + rotate: K.ITEM('float', rotation), + }); + } + } + + return stickers +} + async function getOrRegisterPlayerInfo(refid: string, version: string, no: number) { let playerInfo = await DB.FindOne(refid, { collection: 'playerinfo', @@ -953,6 +964,8 @@ async function saveSinglePlayer(dataplayer: KDataReader, refid: string, no: numb await DB.Upsert(refid, { collection: 'extra', game, version }, extra) const playedStages = dataplayer.elements('stage'); + // logStagesPlayed(playedStages) + const scores = await updatePlayerScoreCollection(refid, playedStages, version, game) await saveScore(refid, version, game, scores); } @@ -1122,8 +1135,8 @@ function parseSecretMusic(playerData: KDataReader) : SecretMusicEntry[] return response } -function getSecretMusicResponse(profile: Profile) { - let response = [] +function getSecretMusicResponse(profile: Profile) : SecretMusicResponse[] { + let response : SecretMusicResponse[] = [] if (!profile.secretmusic?.music ) { return response @@ -1140,3 +1153,19 @@ function getSecretMusicResponse(profile: Profile) { return response } +function getFriendDataResponse(profile: Profile) { + let response = [] + return response; +} + +function logStagesPlayed(playedStages: KDataReader[]) { + + let result = "Stages played: " + for (let stage of playedStages) { + let id = stage.number('musicid') + result += `${id}, ` + } + + logger.debugLog(result) +} + diff --git a/gitadora@asphyxia/index.ts b/gitadora@asphyxia/index.ts index 3de472d..78f4ee5 100644 --- a/gitadora@asphyxia/index.ts +++ b/gitadora@asphyxia/index.ts @@ -25,14 +25,15 @@ export function register() { name: "Dummy Encore for SPE (Nextage Only)", desc: "Since Nextage's Special Premium Encore system is bit complicated, \n" + "SPE System isn't fully implemented. \n" - + "This thing is bandage of these problem as limiting some Encores for SPE.", + + "This option is a workaround for this issue as limiting some Encores for SPE.", type: "boolean", default: false }) R.Config("enable_custom_mdb", { name: "Enable Custom MDB", - desc: "For who uses own MDB. eg) Omnimix.", + desc: "If disabled, the server will provide the default MDB (song list) to Gitadora clients, depending on which version of the game they are running." + + "Enable this option to provide your own custom MDB instead. MDB files are stored in the 'gitadora@asphyxia/data/mdb' folder, and can be in .xml, .json or .b64 format.", type: "boolean", default: false, }) @@ -40,7 +41,7 @@ export function register() { R.DataFile("data/mdb/custom.xml", { accept: ".xml", name: "Custom MDB", - desc: "You need to enable Custom MDB option first." + desc: "You need to enable the 'Enable Custom MDB' option for the uploaded file to have any effect." }) R.WebUIEvent('updatePlayerInfo', updatePlayerInfo); diff --git a/gitadora@asphyxia/models/commonmusicdata.ts b/gitadora@asphyxia/models/commonmusicdata.ts new file mode 100644 index 0000000..e4ac6ab --- /dev/null +++ b/gitadora@asphyxia/models/commonmusicdata.ts @@ -0,0 +1,13 @@ +export interface CommonMusicDataField { + id: KITEM<"s32">; + cont_gf: KITEM<"bool">; + cont_dm: KITEM<"bool">; + is_secret: KITEM<"bool">; + is_hot: KITEM<"bool">; + data_ver: KITEM<"s32">; + diff: KARRAY<"u16">; + } + + export interface CommonMusicData { + music: CommonMusicDataField[] + } \ No newline at end of file diff --git a/gitadora@asphyxia/models/playerstickerresponse.ts b/gitadora@asphyxia/models/playerstickerresponse.ts new file mode 100644 index 0000000..14bd4f0 --- /dev/null +++ b/gitadora@asphyxia/models/playerstickerresponse.ts @@ -0,0 +1,8 @@ +export interface PlayerStickerResponse { + id: KITEM<'s32'>, + pos_x: KITEM<'float'> , + pos_y: KITEM<'float'>, + scale_x: KITEM<'float'> , + scale_y: KITEM<'float'>, + rotate: KITEM<'float'> +} \ No newline at end of file diff --git a/gitadora@asphyxia/models/secretmusicentry.ts b/gitadora@asphyxia/models/secretmusicentry.ts index cca28b1..f38f410 100644 --- a/gitadora@asphyxia/models/secretmusicentry.ts +++ b/gitadora@asphyxia/models/secretmusicentry.ts @@ -2,4 +2,4 @@ export interface SecretMusicEntry { musicid: number; seq: number; kind: number; -} +} \ No newline at end of file diff --git a/gitadora@asphyxia/models/secretmusicresponse.ts b/gitadora@asphyxia/models/secretmusicresponse.ts new file mode 100644 index 0000000..5f6d4e3 --- /dev/null +++ b/gitadora@asphyxia/models/secretmusicresponse.ts @@ -0,0 +1,5 @@ +export interface SecretMusicResponse { + musicid: KITEM<'s32'>; + seq: KITEM<'u16'>; + kind: KITEM<'s32'>; +} diff --git a/gitadora@asphyxia/utils/logger.ts b/gitadora@asphyxia/utils/logger.ts index 254ff74..30ce1e9 100644 --- a/gitadora@asphyxia/utils/logger.ts +++ b/gitadora@asphyxia/utils/logger.ts @@ -7,7 +7,6 @@ export default class Logger { this.category = (category == null) ? null : `[${category}]` } - public error(...args: any[]) { this.argsHandler(console.error, ...args) } @@ -18,7 +17,6 @@ export default class Logger { } } - public warn(...args: any[]) { this.argsHandler(console.warn, ...args) } @@ -29,7 +27,6 @@ export default class Logger { } } - public info(...args: any[]) { this.argsHandler(console.info, ...args) } @@ -40,7 +37,6 @@ export default class Logger { } } - public log(...args: any[]) { this.argsHandler(console.log, ...args) } @@ -51,7 +47,6 @@ export default class Logger { } } - private argsHandler(target: Function, ...args: any[]) { if (this.category == null) { target(...args) diff --git a/gitadora@asphyxia/webui/profile_player_info.pug b/gitadora@asphyxia/webui/profile_player_info.pug index e872b5c..db89174 100644 --- a/gitadora@asphyxia/webui/profile_player_info.pug +++ b/gitadora@asphyxia/webui/profile_player_info.pug @@ -1,6 +1,19 @@ //DATA// info: DB.Find(refid, { collection: 'playerinfo' }) + profile: DB.Find(refid, { collection: 'profile' }) +- +- + function getFullGameName(shortName) { + switch (shortName) { + case "dm" : + return "Drummania" + case "gf": + return "Guitar Freaks" + default: + return "Unknown" + } + } - div @@ -33,4 +46,39 @@ div button.button.is-primary(type="submit") span.icon i.mdi.mdi-check - span Submit \ No newline at end of file + span Submit + +div + each pr in profile + .card + .card-header + p.card-header-title + span.icon + i.mdi.mdi-account-details + | Profile Detail (#{getFullGameName(pr.game)} #{pr.version}) + .card-content + form(method="post") + .field + label.label Skill + .control + input.input(type="text" name="skill", value=(pr.skill/100) readonly) + .field + label.label Skill (All Songs) + .control + input.input(type="text" name="all_skill", value=(pr.all_skill/100) readonly) + .field + label.label Stages Cleared + .control + input.input(type="text" name="clear_num", value=pr.clear_num readonly) + .field + label.label Full Combos + .control + input.input(type="text" name="full_num", value=pr.full_num readonly) + .field + label.label Excellent Full Combos + .control + input.input(type="text" name="exce_num", value=pr.exce_num readonly) + .field + label.label Sessions + .control + input.input(type="text" name="session_cnt", value=pr.session_cnt readonly) \ No newline at end of file