mirror of
https://github.com/asphyxia-core/plugins.git
synced 2026-03-21 17:34:46 -05:00
Museca 1+1/2 and Museca Plus
This commit is contained in:
parent
aefac94c05
commit
9cf950c759
20
museca@asphyxia/README.md
Normal file
20
museca@asphyxia/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
MUSECA
|
||||
======
|
||||
|
||||
Plugin Version: **v1.0.0**
|
||||
|
||||
Supported Versions
|
||||
------------------
|
||||
- 1+1/2
|
||||
- [MUSECA PLUS](https://museca.plus/)
|
||||
|
||||
Only Initial support for now.
|
||||
=============================
|
||||
Mission and some other features may not work properly.
|
||||
Currently, Score saving is only feature that verified.
|
||||
|
||||
Changelog
|
||||
=========
|
||||
1.0.0 (Current)
|
||||
---------------
|
||||
Initial Support.
|
||||
1
museca@asphyxia/data/.gitignore
vendored
Normal file
1
museca@asphyxia/data/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.xml
|
||||
12
museca@asphyxia/data/CommunityPlusMDB.ts
Normal file
12
museca@asphyxia/data/CommunityPlusMDB.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { processMdbData,readJSONOrXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML("./data/mdb_community_plus.json", "./data/mdb_community_plus.xml")
|
||||
return {
|
||||
music
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData() {
|
||||
return await processMdbData("./data/mdb_community_plus.xml")
|
||||
}
|
||||
12
museca@asphyxia/data/OnePlusHalfMDB.ts
Normal file
12
museca@asphyxia/data/OnePlusHalfMDB.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { processMdbData,readJSONOrXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML("./data/mdb_one_plus_half.json", "./data/mdb_one_plus_half.xml")
|
||||
return {
|
||||
music
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData() {
|
||||
return await processMdbData("./data/mdb_one_plus_half.xml");
|
||||
}
|
||||
47
museca@asphyxia/data/helper.ts
Normal file
47
museca@asphyxia/data/helper.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
export async function readXML(path: string) {
|
||||
const xml = await IO.ReadFile(path, 'utf-8');
|
||||
const json = U.parseXML(xml, false)
|
||||
return json
|
||||
}
|
||||
|
||||
export async function readJSON(path: string) {
|
||||
const str = await IO.ReadFile(path, 'utf-8');
|
||||
const json = JSON.parse(str)
|
||||
return json
|
||||
}
|
||||
|
||||
export async function readJSONOrXML(jsonPath: string, xmlPath: string) {
|
||||
const str: string | null = await IO.ReadFile(jsonPath, 'utf-8');
|
||||
if (str == null || str.length == 0) {
|
||||
const data = await processMdbData(xmlPath)
|
||||
await IO.WriteFile(jsonPath, JSON.stringify(data))
|
||||
return data
|
||||
} else {
|
||||
const json = JSON.parse(str)
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
export async function processMdbData(path: string) {
|
||||
const data = await readXML(path);
|
||||
const mdb = $(data).elements("mdb.music");
|
||||
const diff_list = ["novice", "advanced", "exhaust", "infinite"]
|
||||
const music: any[] = [];
|
||||
for (const m of mdb) {
|
||||
for (const [i, d] of diff_list.entries()) {
|
||||
const elem = m.element(`difficulty.${d}`)
|
||||
if (elem.number("difnum", 0) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
music.push({
|
||||
music_id: K.ITEM("s32", parseInt(m.attr().id)),
|
||||
music_type: K.ITEM("u8", i),
|
||||
limited: K.ITEM("u8", elem.number("limited"))
|
||||
});
|
||||
}
|
||||
};
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
1
museca@asphyxia/data/mdb_community_plus.json
Normal file
1
museca@asphyxia/data/mdb_community_plus.json
Normal file
File diff suppressed because one or more lines are too long
1
museca@asphyxia/data/mdb_one_plus_half.json
Normal file
1
museca@asphyxia/data/mdb_one_plus_half.json
Normal file
File diff suppressed because one or more lines are too long
84
museca@asphyxia/handlers/common.ts
Normal file
84
museca@asphyxia/handlers/common.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import * as path from "path"
|
||||
import { processMdbData } from "../data/helper"
|
||||
import { processData as processCommunityPlusData } from "../data/CommunityPlusMDB"
|
||||
import { processData as processOnePlusHalfData } from "../data/OnePlusHalfMDB"
|
||||
|
||||
|
||||
export const shop: EPR = async (info, data, send) => {
|
||||
// Ignore shop name setter.
|
||||
send.object({
|
||||
nxt_time: K.ITEM("u32", 1000 * 5 * 60)
|
||||
})
|
||||
}
|
||||
|
||||
export const common: EPR = async (info, data, send) => {
|
||||
let { music } = U.GetConfig("enable_custom_mdb")
|
||||
? await processMdbData(path.normalize(U.GetConfig("custom_mdb_path")))
|
||||
: (await processValidData(info))
|
||||
|
||||
if (music.length === 0) {
|
||||
music = (await processValidData(info)).music
|
||||
}
|
||||
|
||||
if (U.GetConfig("unlock_all_songs")) {
|
||||
music.forEach(element => {
|
||||
element.limited = K.ITEM("u8", 3)
|
||||
});
|
||||
}
|
||||
|
||||
// Flags
|
||||
const event_list = [1, 83, 130, 194, 195, 98, 145, 146, 147, 148, 149, 56, 86, 105, 140, 211, 143]
|
||||
const event = event_list.map((e) => {
|
||||
return {
|
||||
info: {
|
||||
event_id: K.ITEM("u32", e)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
send.object({
|
||||
music_limited: {
|
||||
info: music
|
||||
},
|
||||
event,
|
||||
// TODO: Skill course, Extended option.
|
||||
// skill_course,
|
||||
// extend
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Implement this.
|
||||
export const hiscore: EPR = async (info, data, send) => {
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const frozen: EPR = async (info, data, send) => {
|
||||
send.object({
|
||||
result: K.ITEM("u8", 0)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Implement this fully.
|
||||
export const lounge: EPR = async (info, data, send) => {
|
||||
send.object({
|
||||
interval: K.ITEM("u32", 10),
|
||||
// wait
|
||||
})
|
||||
}
|
||||
|
||||
export const exception: EPR = async (info, data, send) => {
|
||||
send.success()
|
||||
}
|
||||
|
||||
async function processValidData(info: EamuseInfo) {
|
||||
const version = parseInt(info.model.trim().substr(10), 10)
|
||||
if (version >= 2020102200) {
|
||||
// MUSECA PLUS
|
||||
return await processCommunityPlusData();
|
||||
} else /** if (version > 2016071300) */ {
|
||||
return await processOnePlusHalfData()
|
||||
} /** else {
|
||||
// Museca 1
|
||||
return await processOneData()
|
||||
}**/
|
||||
}
|
||||
219
museca@asphyxia/handlers/player.ts
Normal file
219
museca@asphyxia/handlers/player.ts
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
import { Profile } from "../models/profile";
|
||||
import { Scores } from "../models/scores";
|
||||
import { IDToCode } from "../utils";
|
||||
|
||||
export const load: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const profile = await DB.FindOne<Profile>(refid, { collection: "profile" })
|
||||
if (profile == null) {
|
||||
// Request New Profile from game side.
|
||||
return send.object({
|
||||
result: K.ITEM("u8", 1)
|
||||
})
|
||||
}
|
||||
|
||||
const item = _.map(profile.item, (v, k) => {
|
||||
return {
|
||||
type: K.ITEM("u8", v.type),
|
||||
id: K.ITEM("u32", parseInt(k)),
|
||||
param: K.ITEM("u32", v.param)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
send.object({
|
||||
hidden_param: K.ARRAY("s32", profile.hidden_param),
|
||||
play_count: K.ITEM("u32", profile.play_count),
|
||||
daily_count: K.ITEM("u32", profile.daily_count),
|
||||
play_chain: K.ITEM("u32", profile.play_chain),
|
||||
last: {
|
||||
headphone: K.ITEM("u8", profile.last.headphone),
|
||||
appeal_id: K.ITEM("u16", profile.last.appeal_id),
|
||||
comment_id: K.ITEM("u16", profile.last.comment_id),
|
||||
music_id: K.ITEM("s32", profile.last.music_id),
|
||||
music_type: K.ITEM("u8", profile.last.music_type),
|
||||
sort_type: K.ITEM("u8", profile.last.sort_type),
|
||||
narrow_down: K.ITEM("u8", profile.last.narrow_down),
|
||||
gauge_option: K.ITEM("u8", profile.last.gauge_option),
|
||||
},
|
||||
blaster_energy: K.ITEM("u32", profile.blaster_energy),
|
||||
blaster_count: K.ITEM("u32", profile.blaster_count),
|
||||
code: K.ITEM("str", IDToCode(profile.code)),
|
||||
name: K.ITEM("str", profile.name),
|
||||
creator_id: K.ITEM("u32", profile.creator_id),
|
||||
skill_level: K.ITEM("s16", profile.skill_level),
|
||||
skill_name_id: K.ITEM("s16", profile.skill_name_id),
|
||||
gamecoin_packet: K.ITEM("u32", profile.gamecoin_packet),
|
||||
gamecoin_block: K.ITEM("u32", profile.gamecoin_block),
|
||||
item: {
|
||||
info: item
|
||||
},
|
||||
param: {},
|
||||
result: K.ITEM("u8", 0),
|
||||
ea_shop: {
|
||||
packet_booster: K.ITEM("s32", 0),
|
||||
block_booster: K.ITEM("s32", 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const load_m: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('dataid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const scores = (await DB.FindOne<Scores>(refid, { collection: 'scores'})).scores
|
||||
|
||||
const music: any[] = [];
|
||||
for (const mid in scores) {
|
||||
for (const type in scores[mid]) {
|
||||
let score = scores[mid][type]
|
||||
music.push({
|
||||
music_id: K.ITEM("u32", parseInt(mid)),
|
||||
music_type: K.ITEM("u32", parseInt(type)),
|
||||
score: K.ITEM("u32", score.score),
|
||||
cnt: K.ITEM("u32", score.count),
|
||||
clear_type: K.ITEM("u32", score.clear_type),
|
||||
score_grade: K.ITEM("u32", score.score_grade),
|
||||
btn_rate: K.ITEM("u32", score.btn_rate),
|
||||
long_rate: K.ITEM("u32", score.long_rate),
|
||||
vol_rate: K.ITEM("u32", score.vol_rate)
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
send.object({
|
||||
new: {
|
||||
music
|
||||
},
|
||||
// This field seems used on Museca 1, Ignore this.
|
||||
old: {}
|
||||
})
|
||||
}
|
||||
|
||||
export const save: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const dbItem = (await DB.FindOne<Profile>(refid, { collection: "profile" })).item
|
||||
for(const item of $(data).elements("item.info")) {
|
||||
dbItem[item.number("id")] = {
|
||||
type: item.number("type"),
|
||||
param : item.number("param")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await DB.Upsert<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
last: {
|
||||
headphone: $(data).number("headphone"),
|
||||
appeal_id: $(data).number("appeal_id"),
|
||||
comment_id: $(data).number("comment_id"),
|
||||
music_id: $(data).number("music_id"),
|
||||
music_type: $(data).number("music_type"),
|
||||
sort_type: $(data).number("sort_type"),
|
||||
narrow_down: $(data).number("narrow_down"),
|
||||
gauge_option: $(data).number("gauge_option"),
|
||||
},
|
||||
hidden_param: $(data).numbers("hidden_param"),
|
||||
blaster_count: $(data).number("blaster_count"),
|
||||
item: dbItem,
|
||||
},
|
||||
$inc: {
|
||||
blaster_energy: $(data).number("earned_blaster_energy"),
|
||||
gamecoin_block: $(data).number("earned_gamecoin_block"),
|
||||
gamecoin_packet: $(data).number("earned_gamecoin_packet")
|
||||
}
|
||||
})
|
||||
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const save_m: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const scores = (await DB.FindOne<Scores>(refid, { collection: "scores" })).scores
|
||||
const mid = $(data).number("music_id")
|
||||
const type = $(data).number("music_type")
|
||||
|
||||
if (!scores[mid]) {
|
||||
scores[mid] = {};
|
||||
}
|
||||
|
||||
scores[mid][type] = {
|
||||
score: Math.max(_.get(scores[mid][type], 'score', 0), $(data).number("score")),
|
||||
clear_type: Math.max(_.get(scores[mid][type], 'clear_type', 0), $(data).number("clear_type")),
|
||||
score_grade: Math.max(_.get(scores[mid][type], 'score_grade', 0), $(data).number("score_grade")),
|
||||
count: _.get(scores[mid][type], 'count', 0) + 1,
|
||||
btn_rate: Math.max(_.get(scores[mid][type], 'btn_rate', 0), $(data).number("btn_rate")),
|
||||
long_rate: Math.max(_.get(scores[mid][type], 'long_rate', 0), $(data).number("long_rate")),
|
||||
vol_rate: Math.max(_.get(scores[mid][type], 'vol_rate', 0), $(data).number("vol_rate")),
|
||||
};
|
||||
|
||||
const store: Scores = {
|
||||
collection: "scores",
|
||||
scores
|
||||
}
|
||||
|
||||
await DB.Upsert<Scores>(refid, { collection: "scores" }, store)
|
||||
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const newProfile: EPR = async (info, data, send) => {
|
||||
const refid = $(data).str('refid');
|
||||
if (!refid) return send.deny();
|
||||
|
||||
const name = $(data).str('name', 'NONAME');
|
||||
let code = _.random(0, 99999999);
|
||||
while (await DB.FindOne<Profile>(null, { collecttion: 'profile', code })) {
|
||||
code = _.random(0, 99999999);
|
||||
}
|
||||
|
||||
let defItem = {};
|
||||
for(let i = 1; i < 801; i++) {
|
||||
defItem[i] = {
|
||||
type: 4,
|
||||
param : 1
|
||||
}
|
||||
}
|
||||
|
||||
const profile: Profile = {
|
||||
collection: "profile",
|
||||
code,
|
||||
name,
|
||||
|
||||
hidden_param: Array(20).fill(0),
|
||||
play_count: 0,
|
||||
daily_count: 0,
|
||||
play_chain: 0,
|
||||
last: {
|
||||
headphone: 0,
|
||||
appeal_id: 0,
|
||||
comment_id: 0,
|
||||
music_id: 0,
|
||||
music_type: 0,
|
||||
sort_type: 0,
|
||||
narrow_down: 0,
|
||||
gauge_option: 0,
|
||||
},
|
||||
blaster_energy: 0,
|
||||
blaster_count: 0,
|
||||
creator_id: 0,
|
||||
skill_level: 0,
|
||||
skill_name_id: 0,
|
||||
gamecoin_packet: 0,
|
||||
gamecoin_block: 0,
|
||||
|
||||
item: defItem,
|
||||
|
||||
packet_booster: 0,
|
||||
block_booster: 0,
|
||||
}
|
||||
await DB.Upsert<Profile>(refid, { collection: "profile"}, profile)
|
||||
await DB.Upsert<Scores>(refid, { collection: "scores" }, { collection: "scores", scores: {}})
|
||||
send.success()
|
||||
}
|
||||
54
museca@asphyxia/index.ts
Normal file
54
museca@asphyxia/index.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { common, exception, lounge, shop, hiscore, frozen } from "./handlers/common";
|
||||
import { load, load_m, newProfile, save, save_m } from "./handlers/player";
|
||||
|
||||
export function register() {
|
||||
R.GameCode('PIX');
|
||||
|
||||
R.Config("unlock_all_songs", {
|
||||
name: "Force unlock all songs",
|
||||
type: "boolean",
|
||||
default: false
|
||||
})
|
||||
|
||||
R.Config("enable_custom_mdb", {
|
||||
name: "Enable Custom MDB",
|
||||
desc: "For who uses own MDB",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
|
||||
R.Config("custom_mdb_path", {
|
||||
name: "Custom MDB PATH",
|
||||
desc: "You need to enable Custom MDB option first. USE ABSOLUTE PATH !!",
|
||||
type: "string",
|
||||
default: "",
|
||||
})
|
||||
|
||||
const Route = (method: string, handler: EPR | boolean) => {
|
||||
// Helper for register multiple versions.
|
||||
// Use this when plugin supports first version.
|
||||
R.Route(`game_3.${method}`, handler);
|
||||
};
|
||||
|
||||
// Common
|
||||
Route("common", common)
|
||||
Route("shop", shop)
|
||||
Route("exception", exception)
|
||||
Route("hiscore", hiscore),
|
||||
Route("lounge", lounge),
|
||||
Route("frozen", frozen)
|
||||
Route("play_e", true)
|
||||
|
||||
// Player
|
||||
Route("new", newProfile)
|
||||
Route("save", save)
|
||||
Route("save_m", save_m)
|
||||
Route("load", load)
|
||||
Route("load_m", load_m)
|
||||
|
||||
R.Unhandled(async (info, data, send) => {
|
||||
if (["eventlog"].includes(info.module)) return;
|
||||
console.error(`Received Unhandled Response on ${info.method} by ${info.model}/${info.module}`)
|
||||
console.error(`Received Request: ${JSON.stringify(data, null, 4)}`)
|
||||
})
|
||||
}
|
||||
38
museca@asphyxia/models/profile.ts
Normal file
38
museca@asphyxia/models/profile.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
export interface Profile {
|
||||
collection: 'profile';
|
||||
|
||||
code: number;
|
||||
name: string;
|
||||
|
||||
hidden_param: number[];
|
||||
play_count: number;
|
||||
daily_count: number;
|
||||
play_chain: number;
|
||||
last: {
|
||||
headphone: number;
|
||||
appeal_id: number;
|
||||
comment_id: number;
|
||||
music_id: number;
|
||||
music_type: number;
|
||||
sort_type: number;
|
||||
narrow_down: number;
|
||||
gauge_option: number;
|
||||
},
|
||||
blaster_energy: number;
|
||||
blaster_count: number;
|
||||
creator_id: number;
|
||||
skill_level: number;
|
||||
skill_name_id: number;
|
||||
gamecoin_packet: number;
|
||||
gamecoin_block: number;
|
||||
|
||||
item: {
|
||||
[id: number]: {
|
||||
type: number,
|
||||
param: number
|
||||
}
|
||||
}
|
||||
|
||||
packet_booster: number;
|
||||
block_booster: number;
|
||||
}
|
||||
17
museca@asphyxia/models/scores.ts
Normal file
17
museca@asphyxia/models/scores.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export interface Scores {
|
||||
collection: 'scores',
|
||||
|
||||
scores: {
|
||||
[mid: string]: {
|
||||
[type: string]: {
|
||||
score: number;
|
||||
count: number;
|
||||
clear_type: number;
|
||||
score_grade: number;
|
||||
btn_rate: number;
|
||||
long_rate: number;
|
||||
vol_rate: number;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
4
museca@asphyxia/utils.ts
Normal file
4
museca@asphyxia/utils.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export function IDToCode(id: number) {
|
||||
const padded = _.padStart(id.toString(), 8);
|
||||
return `${padded.slice(0, 4)}-${padded.slice(4)}`;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user