Merge remote-tracking branch 'upstream/stable' into stable

This commit is contained in:
cracrayol 2021-05-04 12:52:58 +02:00
commit b7c7be2ef3
22 changed files with 938 additions and 51 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# Editor configs
.vscode
.idea
# External modules
node_modules

View File

@ -16,6 +16,7 @@ I don't actually follow any coding rules for this jank so neither should you. Th
I'll do my best to merge PR, but please make sure you are submitting code targeted for "public" releases. (Unless it is some ancient rare stuff and you feel generous enough to provide support for it)
- For new plugins: please use `@asphyxia` identifier for your plugin since you are submitting code as the community.
- This way we prevent third-party plugins (e.g. `popn` or `popn@someoneelse`) from conflicting with our database.
- For existing plugins: please inlude a changelog in your PR so it is easier for me to tell what it is for.
## How do I make plugins?

View File

@ -1,6 +1,6 @@
# BeatStream
Plugin Version: **v1.0.1**
Plugin Version: **v1.0.2**
Supported Versions:

View File

@ -20,8 +20,10 @@ export namespace Bst2HandlersWebUI {
sfxStreamNoteTail: number
}) => {
try {
let base = await DB.FindOne<IBst2Base>(data.refid, { collection: "bst.bst2.player.base" })
let customization = await DB.FindOne<IBst2Customization>(data.refid, { collection: "bst.bst2.player.customization" })
if (customization == null) throw new Error("No profile for refid=" + data.refid)
if (!customization || !base) throw new Error("No profile for refid=" + data.refid)
base.name = data.name
customization.custom[0] = data.rippleNote
customization.custom[2] = data.sfxNormalNote
customization.custom[3] = data.sfxRippleNote
@ -30,6 +32,7 @@ export namespace Bst2HandlersWebUI {
customization.custom[6] = data.backgroundBrightness
customization.custom[7] = (data.judgeText << 0) | (data.rippleNoteGuide << 1) | (data.streamNoteGuide << 2) | (data.sfxStreamNoteTail << 3) | (data.sfxFine << 4)
customization.custom[9] = data.judgeText
DBM.update<IBst2Base>(data.refid, { collection: "bst.bst2.player.base" }, base)
DBM.update<IBst2Customization>(data.refid, { collection: "bst.bst2.player.customization" }, customization)
UtilityHandlersWebUI.pushMessage("Save BeatStream Animtribe settings succeeded!", 2, WebUIMessageType.success, data.refid)
} catch (e) {

18
jubeat@asphyxia/README.md Normal file
View File

@ -0,0 +1,18 @@
# Jubeat
Plugin Version: **v1.0.0**
### Supported Versions
***
- knit
- knit APPEND
### Changelogs
***
#### 1.0.0
- Initial Release

View File

@ -0,0 +1,50 @@
import {getVersion} from "../utils";
export const shopinfo: EPR = (info, data, send) => {
const locId = $(data.shop).content("locationid");
const version = getVersion(info);
if (version === 0) return send.deny();
if (version === 3) return send.object({
data: {
cabid: K.ITEM('u32', 1),
locationid: K.ITEM('str', locId),
is_send: K.ITEM("u8", 1)
}
})
return send.deny();
}
export const demodata = {
getNews: (_, __, send) => send.object({ data: { officialnews: K.ATTR({ count: "0" }) } }),
getData: (_, data, send) => {
const newsId = $(data).number('officialnews.newsid');
return send.object({
data: {
officialnews: {
data: {
newsid: K.ITEM('s16', newsId),
image: K.ITEM('u8', 0, { size: '0' })
}
}
}
});
},
getHitchart: (_, __, send) => send.object({
data: {
hitchart: {
update: K.ITEM('str', ''),
hitchart_lic: K.ATTR({ count: "0" }),
hitchart_org: K.ATTR({ count: "0" }),
}
}
}),
};
export const netlog: EPR = (info, data, send) => {
const errMsg = $(data).str('msg');
console.error(errMsg);
return send.success();
}

View File

@ -0,0 +1,49 @@
export const check: EPR = (info, data, send) => {
const enter = $(data).bool('data.enter');
const time = $(data).number('data.time');
return send.object({
data: {
entrant_nr: K.ITEM('u32', 1, { time: String(time) }),
interval: K.ITEM('s16', 1),
entry_timeout: K.ITEM('s16', U.GetConfig("matching_entry_timeout")),
waitlist: K.ATTR({ count: "0" })
}
});
};
export const entry: EPR = (info, data, send) => {
const localMatchingNode = $(data).element("data.local_matching");
const connectNode = $(data).element("data.connect");
const musicNode = $(data).element('data.music');
const roomId = _.random(1, 999999999999999);
// TODO Local matching support
return send.object({
data: {
roomid: K.ITEM('s64', BigInt(roomId), { master: "1" }),
refresh_intr: K.ITEM('s16', 3),
music: {
id: K.ITEM("u32", musicNode.number("id")),
seq: K.ITEM("u8", musicNode.number("seq")),
}
}
});
};
export const refresh: EPR = (info, data, send) => {
return send.object({
data: {
refresh_intr: K.ITEM('s16', 2),
}
});
};
export const report: EPR = (info, data, send) => {
return send.object({
data: {
refresh_intr: K.ITEM('s16', 1),
}
});
};

View File

@ -0,0 +1,292 @@
import {getVersion} from "../utils";
import Profile from '../models/profile';
import {Score} from '../models/score';
export const profile: EPR = async (info, data, send) => {
let refId = $(data).str("data.player.pass.refid");
if (!refId) return send.deny();
const version = getVersion(info);
if (version === 0) return send.deny();
const name = $(data).str("data.player.name");
let profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
if (!profile) {
if (!name) return send.deny();
const newProfile = new Profile();
newProfile.jubeatId = _.random(1, 99999999);
newProfile.name = name;
newProfile.previous_version = version;
await DB.Upsert<Profile>(refId, { collection: "profile" }, newProfile);
profile = newProfile;
}
let migration = false;
if (profile.previous_version < version) {
migration = true;
profile.name = "";
await DB.Update<Profile>(refId, { collection: "profile" }, { $set: { name: "", previous_version: version } });
}
if (name) {
profile.name = name;
await DB.Update<Profile>(refId, { collection: "profile" }, { $set: { name } });
}
if (version === 3) {
if (U.GetConfig("unlock_all_songs")) {
profile.knit.item = {
secretList: [-1, -1],
themeList: -1,
markerList: [-1, -1],
titleList: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
};
profile.knit.item_new = {
secretList: [0, 0],
themeList: 0,
markerList: [0, 0],
titleList: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
};
}
return send.pugFile('templates/knit/profile.pug', { refId, migration, ...profile }, { compress: false });
}
return send.deny();
};
export const saveProfile: EPR = async (info, { data }, send) => {
console.log(U.toXML(data));
const player = $(data).element("player");
const refId = player.str("refid");
if (!refId) return send.deny();
const version = getVersion(info);
if (version === 0) return send.deny();
const profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
if (version === 3) {
profile.name = player.str("name");
profile.last.shopname = player.str("shopname", profile.last.shopname);
profile.last.areaname = player.str("areaname", profile.last.areaname);
profile.jubility = player.number("info.jubility", profile.jubility);
profile.jubilityYday = player.number("info.jubility_yday", profile.jubilityYday);
profile.knit.acvProg = player.number("info.acv_prog", profile.knit.acvProg);
profile.knit.acvWool = player.number("info.acv_wool", profile.knit.acvWool);
profile.knit.acvRouteProg = player.numbers("info.acv_route_prog", profile.knit.acvRouteProg);
profile.knit.acvPoint = player.number("info.acv_point", profile.knit.acvPoint);
profile.tuneCount = player.number("info.tune_cnt", profile.tuneCount);
profile.saveCount = player.number("info.save_cnt", profile.saveCount);
profile.savedCount = player.number("info.saved_cnt", profile.savedCount);
profile.fullcomboCount = player.number("info.fc_cnt", profile.fullcomboCount);
profile.fullcomboSeqCount = player.number("info.fc_seq_cnt", profile.fullcomboSeqCount);
profile.excellentCount = player.number("info.exc_cnt", profile.excellentCount);
profile.excellentSeqCount = player.number("info.exc_seq_cnt", profile.excellentSeqCount);
profile.matchCount = player.number("info.match_cnt", profile.matchCount);
profile.beatCount = player.number('info.beat_cnt', profile.beatCount);
profile.conciergeSelectedCount = player.number('info.con_sel_cnt', profile.conciergeSelectedCount);
profile.tagCount = player.number('info.tag_cnt', profile.tagCount);
profile.mynewsCount = player.number('info.mynews_cnt', profile.mynewsCount);
if (!U.GetConfig("unlock_all_songs")) {
profile.knit.item.secretList = player.numbers('item.secret_list', profile.knit.item.secretList);
profile.knit.item.themeList = player.number('item.theme_list', profile.knit.item.themeList);
profile.knit.item.markerList = player.numbers('item.marker_list', profile.knit.item.markerList);
profile.knit.item.titleList = player.numbers('item.title_list', profile.knit.item.titleList);
profile.knit.item_new.secretList = player.numbers('item.secret_new', profile.knit.item_new.secretList);
profile.knit.item_new.themeList = player.number('item.theme_new', profile.knit.item_new.themeList);
profile.knit.item_new.markerList = player.numbers('item.marker_new', profile.knit.item_new.markerList);
profile.knit.item_new.titleList = player.numbers('item.title_new', profile.knit.item_new.titleList);
}
profile.last.conciergeSuggestId = player.number('info.con_suggest_id', profile.last.conciergeSuggestId);
profile.last.playTime = BigInt(new Date().getMilliseconds());
// Append
const collabo = player.element("collabo");
if (collabo) {
profile.knit.collabo.success = collabo.bool("success");
profile.knit.collabo.completed = collabo.bool("completed");
}
const result = $(data).element("result");
if (result) {
const tunes = result.elements("tune");
for (const tune of tunes) {
const musicId = tune.number("music", 0);
profile.last.musicId = musicId;
profile.last.seqId = parseInt(tune.attr("player.score").seq) || 0;
profile.last.title = tune.number("title", profile.last.title);
profile.last.theme = tune.number("theme", profile.last.theme);
profile.last.marker = tune.number("marker", profile.last.marker);
profile.last.sort = tune.number("sort", profile.last.sort);
profile.last.filter = tune.number("filter", profile.last.filter);
profile.last.showRank = tune.number("combo_disp", profile.last.showRank);
profile.last.showCombo = tune.number("rank_sort", profile.last.showCombo);
profile.last.mselStat = tune.number("msel_stat", profile.last.mselStat);
const score = tune.number('player.score');
const seq = parseInt(tune.attr('player.score').seq);
const clear = parseInt(tune.attr('player.score').clear);
const combo = parseInt(tune.attr('player.score').combo);
const bestScore = tune.number('player.best_score');
const bestClear = tune.number('player.best_clear');
const playCount = tune.number('player.play_cnt');
const clearCount = tune.number('player.clear_cnt');
const fullcomboCount = tune.number('player.fc_cnt');
const excellentCount = tune.number('player.exc_cnt');
const mbar = tune.numbers('player.mbar');
await updateScore(refId, musicId, seq, score, clear, mbar, {
playCount,
clearCount,
fullcomboCount,
excellentCount
});
}
}
await DB.Update<Profile>(refId, { collection: "profile" }, profile);
return send.object({ data: { player: { session_id: K.ITEM('s32', 1) } } });
}
return send.deny();
};
export const loadScore: EPR = async (info, data, send) => {
const jubeatId = $(data).number("data.player.jid");
if (!jubeatId) return send.deny();
const profile = await DB.FindOne<Profile>(null, { collection: "profile", jubeatId });
if (!profile) return send.deny();
const version = getVersion(info);
if (version === 0) return send.deny();
const scores = await DB.Find<Score>(profile.__refid, { collection: "score" });
const scoreData: { [musicId: number]: any } = {};
for (const score of scores) {
if (!scoreData[score.musicId]) {
scoreData[score.musicId] = {
playCnt: [0, 0, 0],
clearCnt: [0, 0, 0],
fcCnt: [0, 0, 0],
exCnt: [0, 0, 0],
clear: [0, 0, 0],
score: [0, 0, 0],
bar: [Array(30).fill(0), Array(30).fill(0), Array(30).fill(0)]
};
}
const data = scoreData[score.musicId];
data.playCnt[score.seq] = score.playCount;
data.clearCnt[score.seq] = score.clearCount;
data.fcCnt[score.seq] = score.fullcomboCount;
data.exCnt[score.seq] = score.excellentCount;
data.clear[score.seq] = score.clearType;
data.score[score.seq] = score.score;
data.bar[score.seq] = score.bar;
}
if (version === 3) return send.object({
data: {
player: {
playdata: K.ATTR({ count: String(Object.keys(scoreData).length) }, {
musicdata: (() => {
const musicdata = [];
Object.entries(scoreData).forEach(([k, v]) => {
musicdata.push(K.ATTR({ music_id: String(k) }, {
play_cnt: K.ARRAY('s32', v.playCnt),
clear_cnt: K.ARRAY('s32', v.clearCnt),
fc_cnt: K.ARRAY('s32', v.fcCnt),
ex_cnt: K.ARRAY('s32', v.exCnt),
clear: K.ARRAY('s8', v.clear),
score: K.ARRAY('s32', v.score),
bar: v.bar.map((v, i) => K.ARRAY('u8', v, { seq: String(i) }))
}));
});
return musicdata;
})()
})
}
}
});
return send.deny();
};
const updateScore = async (refId: string, musicId: number, seq: number, score: number, clear: number, mbar: number[], data: any) => {
let raised;
const oldScore = await DB.FindOne<Score>(refId, { collection: "score", musicId, seq });
let scoreData = oldScore;
if (!oldScore) {
scoreData = new Score();
scoreData.musicId = musicId;
scoreData.seq = seq;
raised = true;
} else {
raised = score > oldScore.score;
score = Math.max(oldScore.score, score);
}
scoreData.clearType = Math.max(scoreData.clearType, clear);
scoreData.playCount = data.playCount;
scoreData.clearCount = data.clearCount;
scoreData.fullcomboCount = data.fullcomboCount;
scoreData.excellentCount = data.excellentCount;
scoreData.isHardmodeClear = false;
if (mbar && raised) {
scoreData.score = score;
scoreData.bar = mbar;
}
await DB.Upsert(refId, { collection: "score", musicId, seq }, scoreData);
};
export const meeting: EPR = (info, data, send) => {
return send.object({
data: {
meeting: {
single: K.ATTR({ count: '0' }),
tag: K.ATTR({ count: '0' }),
},
reward: {
total: K.ITEM('s32', 0),
point: K.ITEM('s32', 0)
}
}
});
};
export const getCollabo: EPR = (info, data, send) => send.object({
data: {
collabo: {
played: {
iidx: K.ITEM("s8", 1),
popn: K.ITEM("s8", 1),
ddr: K.ITEM("s8", 1),
reflec: K.ITEM("s8", 1),
gfdm: K.ITEM("s8", 1),
}
}
}
});

60
jubeat@asphyxia/index.ts Normal file
View File

@ -0,0 +1,60 @@
import {demodata, netlog, shopinfo} from "./handlers/common";
import {check, entry, refresh, report} from "./handlers/matching";
import {getCollabo, loadScore, meeting, profile, saveProfile} from "./handlers/profile";
export function register() {
if (CORE_VERSION_MAJOR <= 1 && CORE_VERSION_MINOR < 31) {
console.error("The current version of Asphyxia Core is not supported. Requires version '1.31' or later.");
return;
}
R.GameCode("J44");
R.Config("unlock_all_songs", {
name: "Unlock All Songs",
desc: "Tired of unlocking songs? Have this!",
type: "boolean",
default: false
});
R.Config("quick_matching_end", {
name: "Quick Matching End",
desc: "Supported from clan to festo.",
type: "boolean",
default: false
});
R.Config("matching_entry_timeout", {
name: "Online Matching Timeout",
desc: "If online matching songs are too boring, save time! (second)",
type: "integer",
default: 30,
range: [15, 99],
});
R.Route("gametop.regist", profile);
R.Route("gametop.get_pdata", profile);
R.Route("gametop.get_mdata", loadScore);
R.Route("gametop.get_meeting", meeting);
R.Route("gametop.get_collabo", getCollabo);
R.Route('gameend.regist', saveProfile);
R.Route('gameend.log', true);
R.Route('gameend.set_collabo', true);
R.Route("shopinfo.regist", shopinfo);
R.Route("netlog.send", netlog);
R.Route("demodata.get_news", demodata.getNews);
R.Route("demodata.get_data", demodata.getData);
R.Route("demodata.get_hitchart", demodata.getHitchart);
R.Route("lobby.check", check);
R.Route("lobby.entry", entry);
R.Route("lobby.refresh", refresh);
R.Route("lobby.report", report);
R.Unhandled((info, data, send) => {
console.log(info.module, info.method);
console.log(U.toXML(data));
return send.deny();
});
}

View File

@ -0,0 +1,101 @@
export default class Profile {
collection: "profile" = "profile";
jubeatId: number = _.random(1, 99999999);
name: string = "JUBEAT";
previous_version = 0;
jubility: number = 0;
jubilityYday: number = 0;
tuneCount: number = 0;
saveCount: number = 0;
savedCount: number = 0;
fullcomboCount: number = 0;
fullcomboSeqCount: number = 0;
excellentCount: number = 0;
excellentSeqCount: number = 0;
matchCount: number = 0;
beatCount: number = 0;
tagCount: number = 0;
mynewsCount: number = 0;
conciergeSelectedCount: number = 0;
last: {
shopname: string;
areaname: string;
playTime: bigint;
title: number;
theme: number;
marker: number;
showRank: number;
showCombo: number;
musicId: number;
seqId: number;
seqEditId: string;
sort: number;
filter: number;
mselStat: number;
conciergeSuggestId: number;
} = {
shopname: "NONE",
areaname: "NONE",
playTime: BigInt(0),
title: 0,
theme: 0,
marker: 0,
showRank: 1,
showCombo: 1,
musicId: 0,
seqId: 0,
seqEditId: "",
sort: 0,
filter: 0,
mselStat: 0,
conciergeSuggestId: 0
};
knit: {
acvProg: number;
acvWool: number;
acvRouteProg: number[];
acvPoint: number;
item: {
secretList: number[],
themeList: number,
markerList: number[],
titleList: number[]
},
item_new: {
secretList: number[],
themeList: number,
markerList: number[],
titleList: number[]
},
collabo: {
success: boolean;
completed: boolean;
}
} = {
acvProg: 0,
acvWool: 0,
acvRouteProg: [0, 0, 0, 0],
acvPoint: 0,
item: {
secretList: [0, 0],
themeList: 0,
markerList: [0, 0],
titleList: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
item_new: {
secretList: [0, 0],
themeList: 0,
markerList: [0, 0],
titleList: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
},
collabo: {
success: false,
completed: false
}
};
}

View File

@ -0,0 +1,14 @@
export class Score {
collection: "score" = "score";
musicId: number;
seq: number;
score: number = 0;
clearType: number = 0;
playCount: number = 0;
clearCount: number = 0;
fullcomboCount: number = 0;
excellentCount: number = 0;
isHardmodeClear: boolean;
bar: number[];
}

View File

@ -0,0 +1,89 @@
gametop
data
player
name(__type="str") #{name}
jid(__type="s32") #{jubeatId}
refid(__type="str") #{refId}
session_id(__type="s32") 1
info
inherit(__type="bool") #{migration ? 1 : 0}
jubility(__type="s16") #{jubility}
jubility_yday(__type="s16") #{jubilityYday}
acv_prog(__type="s8") #{knit.acvProg}
acv_wool(__type="s8") #{knit.acvWool}
acv_route_prog(__type="s8" __count="4") #{knit.acvRouteProg.join(" ")}
acv_point(__type="s32") #{knit.acvPoint}
tune_cnt(__type="s32") #{tuneCount}
save_cnt(__type="s32") #{saveCount}
saved_cnt(__type="s32") #{savedCount}
fc_cnt(__type="s32") #{fullcomboCount}
ex_cnt(__type="s32") #{excellentCount}
match_cnt(__type="s32") #{matchCount}
beat_cnt(__type="s32") #{beatCount}
mynews_cnt(__type="s32") #{mynewsCount}
con_sel_cnt(__type="s32") #{conciergeSelectedCount}
tag_cnt(__type="s32") #{tagCount}
mtg_entry_cnt(__type="s32") 0
tag_entry_cnt(__type="s32") 0
mtg_hold_cnt(__type="s32") 0
tag_hold_cnt(__type="s32") 0
mtg_result(__type="u8") 0
last
play_time(__type="s64") #{last.playTime || 0}
shopname(__type="str") #{last.shopname}
areaname(__type="str") #{last.areaname}
title(__type="s16") #{last.title}
theme(__type="s8") #{last.theme}
marker(__type="s8") #{last.marker}
rank_sort(__type="s8") #{last.showRank}
combo_disp(__type="s8") #{last.showCombo}
music_id(__type="s32") #{last.musicId}
seq_id(__type="s8") #{last.seqId}
sort(__type="s8") #{last.sort}
filter(__type="s32") #{last.filter}
msel_stat(__type="s8") #{last.mselStat}
con_suggest_id(__type="s8") #{last.conciergeSuggestId}
item
secret_list(__type="s32" __count="2") #{knit.item.secretList.join(" ")}
theme_list(__type="s16") #{knit.item.themeList}
marker_list(__type="s32" __count="2") #{knit.item.markerList.join(" ")}
title_list(__type="s32" __count="24") #{knit.item.titleList.join(" ")}
new
secret_list(__type="s32" __count="2") #{knit.item_new.secretList.join(" ")}
theme_list(__type="s16") #{knit.item_new.themeList}
marker_list(__type="s32" __count="2") #{knit.item_new.markerList.join(" ")}
title_list(__type="s32" __count="24") #{knit.item_new.titleList.join(" ")}
today_music
music_id(__type="s32") 0
news
checked(__type="s16") 0
friendlist(count="0")
lucky_music
music_id(__type="s32") 0
mylist(count="0")
group
group_id(__type="s32") 0
bingo
reward
total(__type="s32") 0
point(__type="s32") 0
collabo
success(__type="bool") #{knit.collabo.success ? 1 : 0}
completed(__type="bool") #{knit.collabo.completed ? 1 : 0}
history
play_hist(count="0")
match_hist(count="0")

6
jubeat@asphyxia/utils.ts Normal file
View File

@ -0,0 +1,6 @@
export function getVersion({ model }: EamuseInfo) {
const dateCode = model.split(':')[4];
if (model.startsWith("J44")) return 3;
return 0;
}

View File

@ -4,5 +4,6 @@ Plugin Version: **v1.1**
Supported Versions:
- BOOTH
- HEAVENLY HAVEN
- VIVID WAVE

View File

@ -6,6 +6,12 @@ export const common: EPR = async (info, data, send) => {
let courses = [];
let extend = [];
const version = parseInt(info.model.split(":")[4]);
if (version <= 2013052900) {
return send.pugFile('templates/booth/common.pug');
}
switch (info.method) {
case 'sv4_common': {
events = EVENT4;
@ -26,13 +32,13 @@ export const common: EPR = async (info, data, send) => {
if (U.GetConfig('unlock_all_songs')) {
for (let i = 1; i < 1700; ++i) {
for (let j = 0; j < 5; ++j) {
songs.push({
music_id: K.ITEM('s32', i),
music_type: K.ITEM('u8', j),
limited: K.ITEM('u8', 3),
});
}
}
}

View File

@ -1,17 +1,46 @@
import { Profile } from '../models/profile';
import { MusicRecord } from '../models/music_record';
import { IDToCode, GetCounter } from '../utils';
import { Mix } from '../models/mix';
import {Profile} from '../models/profile';
import {MusicRecord} from '../models/music_record';
import {getVersion, IDToCode, GetCounter} from '../utils';
import {Mix} from '../models/mix';
export const hiscore: EPR = async (info, data, send) => {
const records = await DB.Find<MusicRecord>(null, { collection: 'music' });
const version = getVersion(info);
const profiles = _.groupBy(
await DB.Find<Profile>(null, { collection: 'profile' }),
'__refid'
);
send.object({
if (version === 1) {
return send.object({
hiscore: K.ATTR({ type: "1" }, {
music: _.map(
_.groupBy(records, r => {
return `${r.mid}:${r.type}`;
}),
r => _.maxBy(r, 'score')
).map(r => (K.ATTR({ id: String(r.mid) }, {
note: (() => {
const notes = [];
for (let i = 1; i <= 3; i++) {
if (r.type !== i) continue;
notes.push(K.ATTR({ type: String(r.type) }, {
name: K.ITEM('str', profiles[r.__refid][0].name),
score: K.ITEM('u32', r.score)
}))
}
return notes;
})()
}))),
})
})
}
return send.object({
sc: {
d: _.map(
_.groupBy(records, r => {
@ -40,7 +69,7 @@ export const rival: EPR = async (info, data, send) => {
await DB.Find<Profile>(null, { collection: 'profile' })
).filter(p => p.__refid != refid);
send.object({
return send.object({
rival: await Promise.all(
rivals.map(async (p, index) => {
return {
@ -84,7 +113,7 @@ export const saveMix: EPR = async (info, data, send) => {
jacket: mix.number('jacket_id'),
});
send.object({
return send.object({
automation: {
mix_id: K.ITEM('s32', id),
mix_code: K.ITEM('str', doc.code),
@ -105,11 +134,10 @@ export const loadMix: EPR = async (info, data, send) => {
const mix = await DB.FindOne<Mix>({ collection: 'mix', code });
if (!mix) {
send.object({ result: K.ITEM('s32', 1) });
return;
return send.object({ result: K.ITEM('s32', 1) });
}
send.object({
return send.object({
automation: {
mix_id: K.ITEM('s32', mix.id),
mix_code: K.ITEM('str', mix.code),

View File

@ -1,18 +1,12 @@
import { Skill } from '../models/skill';
import { SDVX_AUTOMATION_SONGS } from '../data/vvw';
import { Item } from '../models/item';
import { Param } from '../models/param';
import { MusicRecord } from '../models/music_record';
import { CourseRecord } from '../models/course_record';
import { Profile } from '../models/profile';
import { IDToCode } from '../utils';
import { Mix } from '../models/mix';
function getVersion(info: EamuseInfo) {
if (info.method.startsWith('sv4')) return 4;
if (info.method.startsWith('sv5')) return 5;
return 0;
}
import {Skill} from '../models/skill';
import {SDVX_AUTOMATION_SONGS} from '../data/vvw';
import {Item} from '../models/item';
import {Param} from '../models/param';
import {MusicRecord} from '../models/music_record';
import {CourseRecord} from '../models/course_record';
import {Profile} from '../models/profile';
import {getVersion, IDToCode} from '../utils';
import {Mix} from '../models/mix';
async function getAutomationMixes(params: Param[]) {
const mixids = params
@ -30,12 +24,37 @@ function unlockNavigators(items: Partial<Item>[]) {
}
export const loadScore: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
const refid = $(data).str('refid', $(data).attr().dataid);
if (!refid) return send.deny();
const records = await DB.Find<MusicRecord>(refid, { collection: 'music' });
send.object({
const version = getVersion(info);
if (version === 1) {
return send.object({
music: records.map(r => (K.ATTR({ music_id: String(r.mid) }, {
type: (() => {
const records = [];
for (let i = 1; i <= 3; i++) {
if (r.type != i) continue;
records.push(K.ATTR({
type_id: String(i),
score: String(r.score),
clear_type: String(r.clear),
score_grade: String(r.grade),
cnt: "0"
}));
}
return records;
})()
})))
});
}
return send.object({
music: {
info: records.map(r => ({
param: K.ARRAY('u32', [
@ -62,9 +81,57 @@ export const loadScore: EPR = async (info, data, send) => {
};
export const saveScore: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
const refid = $(data).str('refid', $(data).attr().dataid);
if (!refid) return send.deny();
const version = getVersion(info);
// Booth - Save score
if (version === 1) {
try {
const mid = parseInt($(data).attr().music_id);
const type = parseInt($(data).attr().music_type);
if (_.isNil(mid) || _.isNil(type)) return send.deny();
const record = (await DB.FindOne<MusicRecord>(refid, {
collection: 'music',
mid,
type,
})) || {
collection: 'music',
mid,
type,
score: 0,
clear: 0,
grade: 0,
buttonRate: 0,
longRate: 0,
volRate: 0,
};
const score = $(data).attr().score ? parseInt($(data).attr().score) : 0;
const clear = $(data).attr().clear_type ? parseInt($(data).attr().clear_type) : 0;
const grade = $(data).attr().score_grade ? parseInt($(data).attr().score_grade) : 0;
if (score > record.score) {
record.score = score;
}
record.clear = Math.max(clear, record.clear);
record.grade = Math.max(grade, record.grade);
await DB.Upsert<MusicRecord>(
refid,
{ collection: 'music', mid, type },
record
);
return send.success();
} catch {
return send.deny();
}
}
const mid = $(data).number('music_id');
const type = $(data).number('music_type');
@ -103,7 +170,7 @@ export const saveScore: EPR = async (info, data, send) => {
record
);
send.success();
return send.success();
};
export const saveCourse: EPR = async (info, data, send) => {
@ -134,16 +201,47 @@ export const saveCourse: EPR = async (info, data, send) => {
}
);
send.success();
return send.success();
};
export const save: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
const refid = $(data).str('refid', $(data).attr().refid);
if (!refid) return send.deny();
const version = getVersion(info);
if (version == 0) return send.deny();
if (version === 1) {
try {
// Save Profile
await DB.Update<Profile>(
refid,
{ collection: 'profile' },
{
$set: {
headphone: $(data).number('headphone'),
hiSpeed: $(data).number('hispeed'),
appeal: $(data).number('appeal_id'),
boothFrame: [$(data).number('frame0'), $(data).number('frame1'), $(data).number('frame2'), $(data).number('frame3'), $(data).number('frame4')],
musicID: parseInt($(data).attr("last").music_id),
musicType: parseInt($(data).attr("last").music_type),
sortType: parseInt($(data).attr("last").sort_type),
mUserCnt: $(data).number('m_user_cnt'),
},
$inc: {
expPoint: $(data).number('gain_exp'),
packets: $(data).number('earned_gamecoin_packet'),
blocks: $(data).number('earned_gamecoin_block'),
},
}
);
return send.success();
} catch {
return send.deny();
}
}
// Save Profile
await DB.Update<Profile>(
refid,
@ -226,11 +324,11 @@ export const save: EPR = async (info, data, send) => {
}
);
send.success();
return send.success();
};
export const load: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
const refid = $(data).str('refid', $(data).attr().dataid);
if (!refid) return send.deny();
const version = getVersion(info);
@ -241,8 +339,8 @@ export const load: EPR = async (info, data, send) => {
});
if (!profile) {
send.object({ result: K.ITEM('u8', 1) });
return;
if (version === 1) return send.object(K.ATTR({ none: "1" }));
return send.object({ result: K.ITEM('u8', 1) });
}
let skill = (await DB.FindOne<Skill>(refid, {
@ -263,7 +361,11 @@ export const load: EPR = async (info, data, send) => {
const currentTime = time.getTime();
const mixes = version == 5 ? await getAutomationMixes(params) : [];
send.pugFile('templates/load.pug', {
if (version === 1) {
return send.pugFile('templates/booth/load.pug', { code: IDToCode(profile.id), ...profile });
}
return send.pugFile('templates/load.pug', {
courses,
items: U.GetConfig('unlock_all_navigators')
? unlockNavigators(items)
@ -279,10 +381,10 @@ export const load: EPR = async (info, data, send) => {
};
export const create: EPR = async (info, data, send) => {
const refid = $(data).str('refid');
const refid = $(data).str('refid', $(data).attr().refid);
if (!refid) return send.deny();
const name = $(data).str('name', 'GUEST');
const name = $(data).str('name', $(data).attr().name ? $(data).attr().name : 'GUEST');
let id = _.random(0, 99999999);
while (await DB.FindOne<Profile>(null, { collecttion: 'profile', id })) {
id = _.random(0, 99999999);
@ -314,10 +416,13 @@ export const create: EPR = async (info, data, send) => {
musicID: 0,
musicType: 0,
sortType: 0,
expPoint: 0,
mUserCnt: 0,
boothFrame: [0, 0, 0, 0, 0]
};
await DB.Upsert(refid, { collection: 'profile' }, profile);
send.object({ result: K.ITEM('u8', 0) });
return send.object({ result: K.ITEM('u8', 0) });
};
export const buy: EPR = async (info, data, send) => {
@ -356,11 +461,11 @@ export const buy: EPR = async (info, data, send) => {
);
}
send.object({
return send.object({
gamecoin_packet: K.ITEM('u32', updated.docs[0].packets),
gamecoin_block: K.ITEM('u32', updated.docs[0].blocks),
});
} else {
send.success();
return send.success();
}
};

View File

@ -1,5 +1,5 @@
import { common } from './handlers/common';
import { hiscore, rival, saveMix, loadMix } from './handlers/features';
import {common} from './handlers/common';
import {hiscore, rival, saveMix, loadMix} from './handlers/features';
import {
updateProfile,
updateMix,
@ -29,6 +29,7 @@ export function register() {
const MultiRoute = (method: string, handler: EPR | boolean) => {
// Helper for register multiple versions.
R.Route(`game.${method}`, handler);
R.Route(`game.sv4_${method}`, handler);
R.Route(`game.sv5_${method}`, handler);
};
@ -53,15 +54,24 @@ export function register() {
MultiRoute('load_ap', loadMix);
// Lazy
MultiRoute('lounge', false);
MultiRoute('shop', true);
MultiRoute('lounge', (_, __, send) => send.object({
interval: K.ITEM('u32', 30)
}));
MultiRoute('shop', (_, __, send) => send.object({
nxt_time: K.ITEM('u32', 1000 * 5 * 60)
}));
MultiRoute('save_e', true);
MultiRoute('play_e', true);
MultiRoute('play_s', true);
MultiRoute('entry_s', true);
MultiRoute('entry_e', true);
MultiRoute('exception', true);
R.Route('eventlog.write', true);
R.Route('eventlog.write', (_, __, send) => send.object({
gamesession: K.ITEM('s64', BigInt(1)),
logsendflg: K.ITEM('s32', 0),
logerrlevel: K.ITEM('s32', 0),
evtidnosendflg: K.ITEM('s32', 0)
}));
R.Unhandled();
}

View File

@ -11,6 +11,9 @@ export interface Profile {
packets: number;
blocks: number;
expPoint: number;
mUserCnt: number;
musicID: number;
musicType: number;
sortType: number;
@ -28,4 +31,6 @@ export interface Profile {
effCLeft: number;
effCRight: number;
narrowDown: number;
boothFrame: number[];
}

View File

@ -0,0 +1,16 @@
- let music = 0;
- let event = 0;
- let catalog = 0;
game
limited
while music < 200
music(id=music++, flag=2)
event
while event < 16
info(id=event++)
catalog
while catalog < 256
info(id=catalog++, currency=1, price=1)

View File

@ -0,0 +1,23 @@
game
name(__type="str") #{name}
code(__type="str") #{code}
gamecoin_packet(__type="u32") #{packets}
gamecoin_block(__type="u32") #{blocks}
exp_point(__type="u32") #{expPoint ? expPoint : 0}
m_user_cnt(__type="u32") #{mUserCnt ? mUserCnt : 0}
have_item(__type="bool" __count=512) #{Array(512).fill(1).join(" ")}
have_note(__type="bool" __count=512) #{Array(512).fill(1).join(" ")}
last(
music_id=musicID,
music_type=musicType,
sort_type=sortType,
headphone=headphone,
hispeed=hiSpeed,
appeal_id=appeal,
frame0=boothFrame ? boothFrame[0] : 0,
frame1=boothFrame ? boothFrame[1] : 0,
frame2=boothFrame ? boothFrame[2] : 0,
frame3=boothFrame ? boothFrame[3] : 0,
frame4=boothFrame ? boothFrame[4] : 0,
)

View File

@ -1,4 +1,5 @@
import { Counter } from './models/counter';
import {Counter} from './models/counter';
export function IDToCode(id: number) {
const padded = _.padStart(id.toString(), 8);
return `${padded.slice(0, 4)}-${padded.slice(4)}`;
@ -12,3 +13,11 @@ export async function GetCounter(key: string) {
)
).docs[0].value;
}
export function getVersion(info: EamuseInfo) {
const dateCode = parseInt(info.model.split(":")[4]);
if (dateCode <= 2013052900) return 1;
if (info.method.startsWith('sv4')) return 4;
if (info.method.startsWith('sv5')) return 5;
return 0;
}