diff --git a/gitadora@asphyxia/.gitignore b/gitadora@asphyxia/.gitignore
new file mode 100644
index 0000000..ff6b0ac
--- /dev/null
+++ b/gitadora@asphyxia/.gitignore
@@ -0,0 +1 @@
+apisamples/
\ No newline at end of file
diff --git a/gitadora@asphyxia/README.md b/gitadora@asphyxia/README.md
index 72c32eb..7cd500e 100644
--- a/gitadora@asphyxia/README.md
+++ b/gitadora@asphyxia/README.md
@@ -1,6 +1,6 @@
GITADORA Plugin for Asphyxia-Core
=================================
-
+
This plugin is based on converted from public-exported Asphyxia's Routes.
@@ -14,10 +14,10 @@ Supported Versions
When Plugin Doesn't work correctly / Startup Error on Plugin
------------------------------------------------------------
The folder structure between v1.0 and v1.1 is quite different. Do not overwrite plugin folder.
-
If encounter error, Please try these step:
+
If you encounter errors, Please try these steps:
1. Remove `gitadora@asphyxia` folder.
-2. C-C and C-V the newest version of `gitadora@asphyxia`
+2. Ctrl-C and Ctrl-V the newest version of `gitadora@asphyxia`
3. (Custom MDB Users) Reupload MDB or move `data/custom_mdb.xml` to `data/mdb/custom.xml`
@@ -29,7 +29,20 @@ Known Issues
Release Notes
=============
-v1.1.1 (Current)
+
+v1.2.0
+----------------
+* Fixed server error when saving profiles for two Guitar Freaks players at the end of a session. Fixes Github issue #39.
+* Fixed another server error when two players are present, but only one player is using a profile.
+* Added support for the "ranking" field. Gitadora will now correctly display your server ranking (based on Skill) on the post-game screen.
+* "Recommended to friends" songs are now saved and loaded correctly. Since you don't have any friends, this won't be terribly useful, but it does at least provide an extra five slots for saving your favourite songs.
+* Fixed "Recommended to friends" song list being incorrectly initialized to "I think about you".
+* misc: Added logging for profile loading/saving when Asphyxia is running in dev mode.
+* misc: Added more logging to mdb (song database) loading.
+* misc: Removed some unneeded duplicate code.
+* misc: Latest getPlayer() and savePlayers() API requests and responses are now saved to file when Asphyxia is in dev mode. Useful for debugging.
+
+v1.1.1
----------------
* fix: Error when create new profile on exchain.
* fix: last song doesn't work correctly.
diff --git a/gitadora@asphyxia/data/mdb/index.ts b/gitadora@asphyxia/data/mdb/index.ts
index f84d6cb..d78e877 100644
--- a/gitadora@asphyxia/data/mdb/index.ts
+++ b/gitadora@asphyxia/data/mdb/index.ts
@@ -1,3 +1,5 @@
+import Logger from "../../utils/logger";
+
export interface CommonMusicDataField {
id: KITEM<"s32">;
cont_gf: KITEM<"bool">;
@@ -21,13 +23,17 @@ export enum DATAVersion {
type processRawDataHandler = (path: string) => Promise
+const logger = new Logger("mdb")
+
export async function readXML(path: string) {
+ logger.debugInfo(`Loading MDB data from ${path}.`)
const xml = await IO.ReadFile(path, 'utf-8');
const json = U.parseXML(xml, false)
return json
}
export async function readJSON(path: string) {
+ logger.debugInfo(`Loading MDB data from ${path}.`)
const str = await IO.ReadFile(path, 'utf-8');
const json = JSON.parse(str)
return json
@@ -35,16 +41,19 @@ export async function readJSON(path: string) {
export async function readJSONOrXML(jsonPath: string, xmlPath: string, processHandler: processRawDataHandler): Promise {
if (!IO.Exists(jsonPath)) {
+ logger.debugInfo(`Loading MDB data from ${xmlPath}.`)
const data = await processHandler(xmlPath)
await IO.WriteFile(jsonPath, JSON.stringify(data))
return data
} else {
+ logger.debugInfo(`Loading MDB data from ${jsonPath}.`)
const json = JSON.parse(await IO.ReadFile(jsonPath, 'utf-8'))
return json
}
}
export async function readB64JSON(b64path: string) {
+ logger.debugInfo(`Loading MDB data from ${b64path}.`)
const buff = await IO.ReadFile(b64path, 'utf-8');
return JSON.parse(Buffer.from(buff, 'base64').toString('utf-8'));
}
@@ -66,7 +75,7 @@ export function gameVerToDataVer(ver: string): DATAVersion {
export async function processDataBuilder(gameVer: string, processHandler?: processRawDataHandler) {
const ver = gameVerToDataVer(gameVer)
const base = `data/mdb/${ver}`
- if (IO.Exists(`${base}.b64`)) {
+ if (IO.Exists(`${base}.b64`)) {
return await readB64JSON(`${base}.b64`);
}
const { music } = await readJSONOrXML(`${base}.json`, `${base}.xml`, processHandler ?? defaultProcessRawData)
@@ -74,7 +83,6 @@ export async function processDataBuilder(gameVer: string, processHandler?: proce
return { music };
}
-
export async function defaultProcessRawData(path: string): Promise {
const data = await readXML(path)
const mdb = $(data).elements("mdb.mdb_data");
diff --git a/gitadora@asphyxia/handlers/MusicList.ts b/gitadora@asphyxia/handlers/MusicList.ts
index d33e988..10841b1 100644
--- a/gitadora@asphyxia/handlers/MusicList.ts
+++ b/gitadora@asphyxia/handlers/MusicList.ts
@@ -22,7 +22,6 @@ export const playableMusic: EPR = async (info, data, send) => {
music = _.get(await processDataBuilder(version), 'music', []);
}
-
await send.object({
hot: {
major: K.ITEM('s32', 1),
diff --git a/gitadora@asphyxia/handlers/profiles.ts b/gitadora@asphyxia/handlers/profiles.ts
index 121549b..38a3cf5 100644
--- a/gitadora@asphyxia/handlers/profiles.ts
+++ b/gitadora@asphyxia/handlers/profiles.ts
@@ -1,94 +1,78 @@
import { PlayerInfo } from "../models/playerinfo";
+import { PlayerRanking } from "../models/playerranking";
import { Profile } from "../models/profile";
import { Record } from "../models/record";
import { Extra } from "../models/extra";
import { getVersion, isDM } from "../utils";
import { Scores } from "../models/scores";
import { PLUGIN_VER } from "../const";
+import Logger from "../utils/logger"
+import { isAsphyxiaDebugMode } from "../Utils/index";
+
+const logger = new Logger("profiles")
export const regist: EPR = async (info, data, send) => {
+
const refid = $(data).str('player.refid');
- if (!refid) return send.deny();
+ if (!refid) {
+ logger.error("Request data is missing required parameter: player.refid")
+ return send.deny();
+ }
const no = getPlayerNo(data);
const version = getVersion(info);
+ const playerInfo = await getOrRegisterPlayerInfo(refid, version, no);
- const playerInfo = await DB.FindOne(refid, {
- collection: 'playerinfo',
- version
+ await send.object({
+ player: K.ATTR({ no: `${no}` }, {
+ is_succession: K.ITEM("bool", 0), //FIX THIS with upsert result.
+ did: K.ITEM("s32", playerInfo.id)
+ })
})
- if (playerInfo) {
- send.object({
- player: K.ATTR({ no: `${no}` }, {
- is_succession: K.ITEM("bool", 0), //FIX THIS with upsert result.
- did: K.ITEM("s32", playerInfo.id)
- })
- })
- } else {
- let info = await registerUser(refid, version)
- send.object({
- player: K.ATTR({ no: `${no}` }, {
- is_succession: K.ITEM("bool", 0), //FIX THIS with upsert result.
- did: K.ITEM("s32", info.id)
- })
- })
- }
}
export const check: EPR = async (info, data, send) => {
+
const refid = $(data).str('player.refid');
- if (!refid) return send.deny();
+ if (!refid) {
+ logger.error("Request data is missing required parameter: player.refid")
+ return send.deny();
+ }
const no = getPlayerNo(data);
const version = getVersion(info)
+ const playerInfo = await getOrRegisterPlayerInfo(refid, version, no)
- const playerInfo = await DB.FindOne(refid, {
- collection: 'playerinfo',
- version
+ await send.object({
+ player: K.ATTR({ no: `${no}`, state: '2' }, {
+ name: K.ITEM('str', playerInfo.name),
+ charaid: K.ITEM('s32', 0),
+ did: K.ITEM('s32', playerInfo.id),
+ skilldata: {
+ skill: K.ITEM('s32', 0),
+ all_skill: K.ITEM('s32', 0),
+ old_skill: K.ITEM('s32', 0),
+ old_all_skill: K.ITEM('s32', 0),
+ }
+ })
})
-
- if (playerInfo) {
- send.object({
- player: K.ATTR({ no: `${no}`, state: '2' }, {
- name: K.ITEM('str', playerInfo.name),
- charaid: K.ITEM('s32', 0),
- did: K.ITEM('s32', playerInfo.id),
- skilldata: {
- skill: K.ITEM('s32', 0),
- all_skill: K.ITEM('s32', 0),
- old_skill: K.ITEM('s32', 0),
- old_all_skill: K.ITEM('s32', 0),
- }
- })
- })
- } else {
- let info = await registerUser(refid, version)
- send.object({
- player: K.ATTR({ no: `${no}`, state: '2' }, {
- name: K.ITEM('str', info.name),
- charaid: K.ITEM('s32', 0),
- did: K.ITEM('s32', info.id),
- skilldata: {
- skill: K.ITEM('s32', 0),
- all_skill: K.ITEM('s32', 0),
- old_skill: K.ITEM('s32', 0),
- old_all_skill: K.ITEM('s32', 0),
- }
- })
- })
- }
}
export const getPlayer: EPR = async (info, data, send) => {
const refid = $(data).str('player.refid');
- if (!refid) return send.deny();
+ if (!refid) {
+ logger.error("Request data is missing required parameter: player.refid")
+ return send.deny();
+ }
const no = getPlayerNo(data);
const version = getVersion(info);
const time = BigInt(31536000);
const dm = isDM(info);
+ const game = dm ? 'dm' : 'gf';
+ logger.debugInfo(`Loading ${game} profile for player ${no} with refid: ${refid}`)
const name = await DB.FindOne(refid, {
collection: 'playerinfo',
version
@@ -343,6 +327,7 @@ export const getPlayer: EPR = async (info, data, send) => {
list_2: K.ARRAY('s32', extra.list_2),
list_3: K.ARRAY('s32', extra.list_3),
},
+ recommend_musicid_list: K.ARRAY('s32', extra.recommend_musicid_list ?? Array(5).fill(-1)),
record,
groove: {
extra_gauge: K.ITEM('s32', profile.extra_gauge),
@@ -354,6 +339,8 @@ export const getPlayer: EPR = async (info, data, send) => {
musiclist: { '@attr': { nr: musicdata.length }, musicdata },
};
+ const playerRanking = await getPlayerRanking(refid, version, game)
+
const addition: any = {
monstar_subjugation: {},
bear_fes: {},
@@ -387,7 +374,7 @@ export const getPlayer: EPR = async (info, data, send) => {
}
}
- send.object({
+ const response = {
player: K.ATTR({ 'no': `${no}` }, {
now_date: K.ITEM('u64', time),
secretmusic: { // TODO: FIX THIS
@@ -404,7 +391,7 @@ export const getPlayer: EPR = async (info, data, send) => {
},
reward: {
status: K.ARRAY('u32', Array(50).fill(0)),
- },
+ },
rivaldata: {},
frienddata: {},
thanks_medal: {
@@ -412,7 +399,7 @@ export const getPlayer: EPR = async (info, data, send) => {
grant_medal: K.ITEM('s32', 0),
grant_total_medal: K.ITEM('s32', 0),
},
- recommend_musicid_list: K.ARRAY('s32', [0, 0, 0, 0, 0]),
+ recommend_musicid_list: K.ARRAY('s32', extra.recommend_musicid_list ?? Array(5).fill(-1)),
skindata: {
skin: K.ARRAY('u32', Array(100).fill(-1)),
},
@@ -454,8 +441,8 @@ export const getPlayer: EPR = async (info, data, send) => {
},
is_free_ok: K.ITEM('bool', 0),
ranking: {
- skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
- all_skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
+ skill: { rank: K.ITEM('s32', playerRanking.skill), total_nr: K.ITEM('s32', playerRanking.totalPlayers) },
+ all_skill: { rank: K.ITEM('s32', playerRanking.all_skill), total_nr: K.ITEM('s32', playerRanking.totalPlayers) },
},
stage_result: {},
monthly_skill: {},
@@ -532,7 +519,26 @@ export const getPlayer: EPR = async (info, data, send) => {
...playerData,
finish: K.ITEM('bool', 1),
}),
+ }
+
+ if (isAsphyxiaDebugMode()) {
+ await IO.WriteFile(`apisamples/lastGetPlayerRequest.json`, JSON.stringify(data, null, 4))
+ await IO.WriteFile(`apisamples/lastGetPlayerResponse.json`, JSON.stringify(response, null, 4))
+ }
+ send.object(response);
+}
+
+async function getOrRegisterPlayerInfo(refid: string, version: string, no: number) {
+ let playerInfo = await DB.FindOne(refid, {
+ collection: 'playerinfo',
+ version
});
+
+ if (!playerInfo) {
+ logger.debugInfo(`Registering new profile for player ${no} with refid: ${refid}`);
+ playerInfo = await registerUser(refid, version);
+ }
+ return playerInfo;
}
function getPlayerNo(data: any): number {
@@ -725,7 +731,8 @@ async function registerUser(refid: string, version: string, id = _.random(0, 999
list_1: Array(100).fill(-1),
list_2: Array(100).fill(-1),
list_3: Array(100).fill(-1),
-
+ recommend_musicid_list: Array(5).fill(-1),
+ reward_status: Array(50).fill(0),
}
}
@@ -755,20 +762,61 @@ async function registerUser(refid: string, version: string, id = _.random(0, 999
return defaultInfo
}
-export const savePlayer: EPR = async (info, data, send) => {
- const refid = $(data).str('player.refid');
- if (!refid) return send.deny();
+export const savePlayers: EPR = async (info, data, send) => {
- const no = getPlayerNo(data);
const version = getVersion(info);
const dm = isDM(info);
-
const game = dm ? 'dm' : 'gf';
+ let players = $(data).elements("player")
+
+ let response = {
+ player: [],
+ gamemode: _.get(data, 'gamemode'),
+ };
+
+ try
+ {
+ for (let player of players) {
+
+ const no = parseInt(player.attr().no || '1', 10)
+ // Only save players that are using a profile. Don't try to save guest players.
+ const hasCard = player.attr().card === 'use'
+ if (!hasCard) {
+ logger.debugInfo(`Skipping save for guest ${game} player ${no}.`)
+ continue
+ }
+
+ const refid = player.str('refid')
+ if (!refid) {
+ throw "Request data is missing required parameter: player.refid"
+ }
+
+ await saveSinglePlayer(player, refid, no, version, game);
+
+ let ranking = await getPlayerRanking(refid, version, game)
+ let responsePart = getSaveProfileResponse(no, ranking)
+ response.player.push(responsePart)
+ }
+
+ if (isAsphyxiaDebugMode()) {
+ await IO.WriteFile(`apisamples/lastSavePlayersRequest.json`, JSON.stringify(data, null, 4))
+ await IO.WriteFile(`apisamples/lastSavePlayersResponse.json`, JSON.stringify(response, null, 4))
+ }
+ await send.object(response);
+ }
+ catch (e) {
+ logger.error(e)
+ return send.deny();
+ }
+};
+
+async function saveSinglePlayer(dataplayer: KDataReader, refid: string, no: number, version: string, game: 'gf' | 'dm')
+{
+ logger.debugInfo(`Saving ${game} profile for player ${no} with refid: ${refid}`)
const profile = await getProfile(refid, version, game) as any;
const extra = await getExtra(refid, version, game) as any;
const rec = await getRecord(refid, version, game) as any;
- const dataplayer = $(data).element("player")
const autoSet = (field: keyof Profile, path: string, array = false): void => {
if (array) {
@@ -781,7 +829,7 @@ export const savePlayer: EPR = async (info, data, send) => {
const autoExtra = (field: keyof Extra, path: string, array = false): void => {
if (array) {
extra[field] = dataplayer.numbers(path, extra[field])
- } else {
+ } else {
extra[field] = dataplayer.number(path, extra[field])
}
};
@@ -887,6 +935,7 @@ export const savePlayer: EPR = async (info, data, send) => {
autoExtra('list_1', 'favoritemusic.music_list_1', true);
autoExtra('list_2', 'favoritemusic.music_list_2', true);
autoExtra('list_3', 'favoritemusic.music_list_3', true);
+ autoExtra('recommend_musicid_list', 'recommend_musicid_list', true);
autoExtra('playstyle', 'customdata.playstyle', true);
autoExtra('custom', 'customdata.custom', true);
@@ -895,7 +944,7 @@ export const savePlayer: EPR = async (info, data, send) => {
await DB.Upsert(refid, { collection: 'record', game, version }, rec)
await DB.Upsert(refid, { collection: 'extra', game, version }, extra)
- const stages = $(data).elements('player.stage');
+ const stages = dataplayer.elements('stage');
const scores = (await getScore(refid, version, game)).scores;
for (const stage of stages) {
const mid = stage.number('musicid', -1);
@@ -937,25 +986,55 @@ export const savePlayer: EPR = async (info, data, send) => {
};
}
- await saveScore(refid, version, game, scores);
+ await saveScore(refid, version, game, scores);
+}
+async function getPlayerRanking(refid: string, version: string, game: 'gf' | 'dm') : Promise {
+ let profiles = await getAllProfiles(version, game)
+ let playerCount = profiles.length
+ let sortedProfilesA = profiles.sort((a,b) => b.skill - a.skill)
+ let sortedProfilesB = profiles.sort((a,b) => b.all_skill - a.all_skill)
- await send.object({
- player: K.ATTR({ no: `${no}` }, {
- skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
- all_skill: { rank: K.ITEM('s32', 1), total_nr: K.ITEM('s32', 1) },
- kac2018: {
- data: {
- term: K.ITEM('s32', 0),
- total_score: K.ITEM('s32', 0),
- score: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
- music_type: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
- play_count: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
- },
+ let idxA = _.findIndex(sortedProfilesA, (e) => e.__refid === refid)
+ idxA = idxA > -1 ? idxA + 1 : playerCount // Default to last place if not found in the DB.
+ let idxB = _.findIndex(sortedProfilesB, (e) => e.__refid === refid)
+ idxB = idxB > -1 ? idxB + 1 : playerCount // Default to last place if not found in the DB.
+
+ return {
+ refid,
+ skill: idxA,
+ all_skill: idxB,
+ totalPlayers: playerCount
+ }
+}
+
+function getSaveProfileResponse(playerNo: number, ranking : PlayerRanking)
+{
+
+ const result = K.ATTR({ no: `${playerNo}` }, {
+ skill: { rank: K.ITEM('s32', ranking.skill), total_nr: K.ITEM('s32', ranking.totalPlayers) },
+ all_skill: { rank: K.ITEM('s32', ranking.all_skill), total_nr: K.ITEM('s32', ranking.totalPlayers) },
+ kac2018: {
+ data: {
+ term: K.ITEM('s32', 0),
+ total_score: K.ITEM('s32', 0),
+ score: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
+ music_type: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
+ play_count: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
},
- }),
- gamemode: _.get(data, 'gamemode'),
- });
-};
+ },
+ })
+
+ return result
+
+}
+
+async function getAllProfiles( version: string, game: 'gf' | 'dm') {
+ return await DB.Find(null, {
+ collection: 'profile',
+ version: version,
+ game: game
+ })
+}
async function getProfile(refid: string, version: string, game: 'gf' | 'dm') {
return await DB.FindOne(refid, {
diff --git a/gitadora@asphyxia/index.ts b/gitadora@asphyxia/index.ts
index 7902cc6..3de472d 100644
--- a/gitadora@asphyxia/index.ts
+++ b/gitadora@asphyxia/index.ts
@@ -1,6 +1,6 @@
import { gameInfoGet, shopInfoRegist } from "./handlers/info";
import { playableMusic } from "./handlers/MusicList"
-import { getPlayer, check, regist, savePlayer } from "./handlers/profiles";
+import { getPlayer, check, regist, savePlayers } from "./handlers/profiles";
import { updatePlayerInfo } from "./handlers/webui";
import { isAsphyxiaDebugMode, isRequiredCoreVersion } from "./utils";
import Logger from "./utils/logger";
@@ -64,7 +64,7 @@ export function register() {
MultiRoute('cardutil.regist', regist);
MultiRoute('cardutil.check', check);
MultiRoute('gametop.get', getPlayer);
- MultiRoute('gameend.regist', savePlayer);
+ MultiRoute('gameend.regist', savePlayers);
// Misc
R.Route('bemani_gakuen.get_music_info', true)
diff --git a/gitadora@asphyxia/models/extra.ts b/gitadora@asphyxia/models/extra.ts
index f00caf9..5a2ad6e 100644
--- a/gitadora@asphyxia/models/extra.ts
+++ b/gitadora@asphyxia/models/extra.ts
@@ -11,4 +11,6 @@ export interface Extra {
list_1: number[];
list_2: number[];
list_3: number[];
+ recommend_musicid_list: number[];
+ reward_status: number[];
}
\ No newline at end of file
diff --git a/gitadora@asphyxia/models/playerranking.ts b/gitadora@asphyxia/models/playerranking.ts
new file mode 100644
index 0000000..84a8dc2
--- /dev/null
+++ b/gitadora@asphyxia/models/playerranking.ts
@@ -0,0 +1,7 @@
+export interface PlayerRanking
+{
+ refid: string;
+ skill: number;
+ all_skill: number;
+ totalPlayers: number;
+}
\ No newline at end of file