mirror of
https://github.com/asphyxia-core/plugins.git
synced 2026-03-22 01:44:39 -05:00
Compare commits
288 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34b173ba49 | ||
|
|
e87a36d158 | ||
|
|
910c134f12 | ||
|
|
4e05f9107f | ||
|
|
45c7f0dcb1 | ||
|
|
5d484d8378 | ||
|
|
2cac29b513 | ||
|
|
b04e723fea | ||
|
|
7067c00c66 | ||
|
|
ec0f23177b | ||
|
|
3dcc69bbba | ||
|
|
73b27b4e0d | ||
|
|
0cbb84d46f | ||
|
|
997d141b3b | ||
|
|
163642e0c1 | ||
|
|
1bbebfb033 | ||
|
|
4aeb1249f6 | ||
|
|
0ad96458b6 | ||
|
|
502886ea93 | ||
|
|
98fe38982f | ||
|
|
d863a522b1 | ||
|
|
aaa4a93f57 | ||
|
|
ebf56f009d | ||
|
|
4427834df4 | ||
|
|
dc36427ffb | ||
|
|
3a215a1314 | ||
|
|
ee0ae068f6 | ||
|
|
8d1b13c730 | ||
|
|
3bad80d504 | ||
|
|
625552aa5c | ||
|
|
eb8ac47803 | ||
|
|
d66f9e1085 | ||
|
|
8bbe8390eb | ||
|
|
11cbbb9e7a | ||
|
|
5821ea8b95 | ||
|
|
7aff587928 | ||
|
|
1b277d768c | ||
|
|
bc4b54dfd8 | ||
|
|
1ee57ffa6e | ||
|
|
1b9afb6486 | ||
|
|
6783f0d13f | ||
|
|
1a027d86e5 | ||
|
|
14d3452fc8 | ||
|
|
6b5a90b05e | ||
|
|
0a0d463b63 | ||
|
|
a0f9167bfc | ||
|
|
27de09dae2 | ||
|
|
09407661ef | ||
|
|
f231932289 | ||
|
|
2159989026 | ||
|
|
863fa7a410 | ||
|
|
c3b2fab90c | ||
|
|
fd9866d483 | ||
|
|
ad36c48bc6 | ||
|
|
44458a1a75 | ||
|
|
8d06dcf25b | ||
|
|
3a73379fb9 | ||
|
|
993595ff99 | ||
|
|
1f05cef58b | ||
|
|
844dd6c4d1 | ||
|
|
06e7a067de | ||
|
|
5750f8f464 | ||
|
|
425b8962ee | ||
|
|
064838bca9 | ||
|
|
432cd116c1 | ||
|
|
6853773a41 | ||
|
|
4330b65ade | ||
|
|
a2e387d636 | ||
|
|
d8d2e59818 | ||
|
|
156d76dda3 | ||
|
|
1bb944e274 | ||
|
|
d490c53425 | ||
|
|
4a0e97106a | ||
|
|
851618de7f | ||
|
|
9c1bc88f96 | ||
|
|
20c2d7b83e | ||
|
|
9a48fadde0 | ||
|
|
e04588e942 | ||
|
|
6b24d34d89 | ||
|
|
4a0e22ff98 | ||
|
|
17e67042d6 | ||
|
|
9fa01f4104 | ||
|
|
7a272bd201 | ||
|
|
e1155c7a66 | ||
|
|
e55e659b3b | ||
|
|
bb3333152f | ||
|
|
8a9683589b | ||
|
|
28cf5af5da | ||
|
|
b7df5d1c64 | ||
|
|
ad074e5380 | ||
|
|
6244f37687 | ||
|
|
1d1e4575fe | ||
|
|
df66022b4e | ||
|
|
7f6836d1d8 | ||
|
|
f4d2a81f07 | ||
|
|
50f54ee2bb | ||
|
|
927030100a | ||
|
|
c2412571c6 | ||
|
|
4168a54bd3 | ||
|
|
c180ee208f | ||
|
|
28538c17dd | ||
|
|
8fa3349b88 | ||
|
|
6902182a8d | ||
|
|
05a500f6c2 | ||
|
|
998589c21b | ||
|
|
6e8c8017e5 | ||
|
|
1f4f881545 | ||
|
|
0b44be3640 | ||
|
|
386c4ccb7b | ||
|
|
b9bedd6d1f | ||
|
|
26b6140d2c | ||
|
|
8891f2b222 | ||
|
|
b2a5c63163 | ||
|
|
485d751683 | ||
|
|
ccd98fc76c | ||
|
|
5daaba9b12 | ||
|
|
a5e2b5db0d | ||
|
|
8503d5d295 | ||
|
|
fea596a158 | ||
|
|
9b7e4a405e | ||
|
|
1084ea71ec | ||
|
|
bc0d5bb0a4 | ||
|
|
0ce49ea16b | ||
|
|
51c06fbb1f | ||
|
|
046f442da2 | ||
|
|
573549d393 | ||
|
|
e94794f777 | ||
|
|
bbd424fd4c | ||
|
|
925f81f98c | ||
|
|
3cf798987e | ||
|
|
ff0c85c121 | ||
|
|
420961d82e | ||
|
|
0e289169b5 | ||
|
|
5bdd2f2c7a | ||
|
|
27116545a4 | ||
|
|
013fafaed1 | ||
|
|
a628fc437a | ||
|
|
87f6ba2abb | ||
|
|
09301ebada | ||
|
|
1c408d84c4 | ||
|
|
81630a86a2 | ||
|
|
b4588e6c47 | ||
|
|
78d8be9092 | ||
|
|
a4fef0a05b | ||
|
|
89deb428e1 | ||
|
|
e7c38f84dd | ||
|
|
5ad309c1af | ||
|
|
5ae06b259a | ||
|
|
33f5720460 | ||
|
|
249d80d6da | ||
|
|
7dd7c199da | ||
|
|
66381eff1f | ||
|
|
b84df19e7c | ||
|
|
b9a9001682 | ||
|
|
c4b52fd148 | ||
|
|
1649ab9bd9 | ||
|
|
ea00a2330e | ||
|
|
1b6ab0085f | ||
|
|
4a8e0707f0 | ||
|
|
0bf3cc30fc | ||
|
|
90651282c0 | ||
|
|
f501bab164 | ||
|
|
10ead0bdf4 | ||
|
|
3368953fbc | ||
|
|
ec4b59330d | ||
|
|
1a2e955dd4 | ||
|
|
a26b8db8df | ||
|
|
257c9962f5 | ||
|
|
d0c9a7f918 | ||
|
|
2fe270dbfa | ||
|
|
67599e37da | ||
|
|
ad8a6b20a3 | ||
|
|
6c8585a258 | ||
|
|
89be828ef8 | ||
|
|
d18edbcfa5 | ||
|
|
5c612929e2 | ||
|
|
b7377a3d0d | ||
|
|
1771619a3c | ||
|
|
b104e40fac | ||
|
|
ce10dca416 | ||
|
|
2929045847 | ||
|
|
0773375713 | ||
|
|
369297131f | ||
|
|
9448915ab7 | ||
|
|
5730dfa544 | ||
|
|
09b8e5f106 | ||
|
|
6d572cf737 | ||
|
|
de6e89e3f0 | ||
|
|
ea15d4628d | ||
|
|
4184cfbd25 | ||
|
|
a1b884dcd6 | ||
|
|
e4a247a715 | ||
|
|
3e52594f34 | ||
|
|
3bf7448e2c | ||
|
|
64c02a0dd3 | ||
|
|
eff4de9134 | ||
|
|
5e497b7f15 | ||
|
|
34f506c3ce | ||
|
|
732d5ee6dc | ||
|
|
05f6c2e13c | ||
|
|
7f8e3989ea | ||
|
|
3a8863b452 | ||
|
|
04c84d55f7 | ||
|
|
f49179a416 | ||
|
|
edc6b09a33 | ||
|
|
860e2c8c7b | ||
|
|
a8759b3a4e | ||
|
|
bfec542a6d | ||
|
|
8c79ec3739 | ||
|
|
d290608305 | ||
|
|
058351255c | ||
|
|
105c9796bd | ||
|
|
06d9cb58fb | ||
|
|
c18baaedd6 | ||
|
|
397144a2a9 | ||
|
|
d9b3051839 | ||
|
|
6b158087b5 | ||
|
|
d457186980 | ||
|
|
5b0cfb25b2 | ||
|
|
f25590a1ee | ||
|
|
1a9f32916c | ||
|
|
b82ff40c7f | ||
|
|
65ffaa5417 | ||
|
|
764f72e5d0 | ||
|
|
72aef41d0e | ||
|
|
b7c7be2ef3 | ||
|
|
db8ace3dbe | ||
|
|
18b82eb86e | ||
|
|
530c83d7f5 | ||
|
|
fd5161199e | ||
|
|
f3835a53b9 | ||
|
|
58743f618c | ||
|
|
55008b575c | ||
|
|
256fe48701 | ||
|
|
819561337e | ||
|
|
91f772d309 | ||
|
|
75b6cd9464 | ||
|
|
d94b4ea1bb | ||
|
|
79311d686c | ||
|
|
8d0f882822 | ||
|
|
fbebd9eac8 | ||
|
|
18f05da297 | ||
|
|
3c941c3b3e | ||
|
|
770ac0c66b | ||
|
|
5797c2ca97 | ||
|
|
80a29aed17 | ||
|
|
ee92f52b59 | ||
|
|
c3ad4e8c62 | ||
|
|
f96aecf53d | ||
|
|
a78917c06a | ||
|
|
b58b69886b | ||
|
|
d1eb39e944 | ||
|
|
0c925ca0e0 | ||
|
|
086e055455 | ||
|
|
b9257ce08a | ||
|
|
84113ca2e2 | ||
|
|
af7128e327 | ||
|
|
c3119b6e4b | ||
|
|
b90427387d | ||
|
|
efb28ad9a0 | ||
|
|
abd4525129 | ||
|
|
bc93c8603e | ||
|
|
ad677b4c2b | ||
|
|
718ded19cf | ||
|
|
716473277a | ||
|
|
6521aef4d8 | ||
|
|
e64542f233 | ||
|
|
c2e321e298 | ||
|
|
bfdeca09fd | ||
|
|
be159cef77 | ||
|
|
8144150de9 | ||
|
|
d967adfb4a | ||
|
|
6c5d1a055e | ||
|
|
3e60ef7030 | ||
|
|
1b9dcce6bf | ||
|
|
6f9604a198 | ||
|
|
d95b93077c | ||
|
|
8f4282e711 | ||
|
|
546175f1af | ||
|
|
b7bb38f626 | ||
|
|
f63ee8aae8 | ||
|
|
7ab416c917 | ||
|
|
7a97396340 | ||
|
|
a31def74ad | ||
|
|
5a4db13a88 | ||
|
|
7c776accf4 | ||
|
|
7007c6781d | ||
|
|
f72f723b17 |
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
|||
# Editor configs
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# External modules
|
||||
node_modules
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
9
bst@asphyxia/README.md
Normal file
9
bst@asphyxia/README.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# BeatStream
|
||||
|
||||
Plugin Version: **v1.0.2**
|
||||
|
||||
Supported Versions:
|
||||
|
||||
- BeatStream アニムトライヴ
|
||||
- Back end ✔
|
||||
- Web UI ✔
|
||||
247
bst@asphyxia/handlers/bst2/common.ts
Normal file
247
bst@asphyxia/handlers/bst2/common.ts
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
import { Bst2EventParamsMap, getKEventControl } from "../../models/bst2/event_params"
|
||||
import { Bst2AccountMap, Bst2BiscoMap, Bst2CourseMap, Bst2MusicRecordMap, Bst2PlayerMap, Bst2SurveyMap, Bst2TipsMap, Bst2UnlockingInfoMap, IBst2Account, IBst2Base, IBst2Bisco, IBst2Course, IBst2CrysisLog, IBst2Customization, IBst2Hacker, IBst2MusicRecord, IBst2Player, IBst2Survey, IBst2Tips, IBst2UnlockingInfo } from "../../models/bst2/profile"
|
||||
import { Bst2CourseLogMap, Bst2StageLogMap, IBst2StageLog } from "../../models/bst2/stagelog"
|
||||
import { bacK, BigIntProxy, boolme, fromMap, mapK, s16me, s32me, s8me, strme, toBigInt } from "../../utility/mapping"
|
||||
import { isToday } from "../../utility/utility_functions"
|
||||
import { DBM } from "../utility/db_manager"
|
||||
import { readPlayerPostProcess, writePlayerPreProcess } from "./processing"
|
||||
|
||||
export namespace Bst2HandlersCommon {
|
||||
export const Common: EPR = async (_0, _1, send) => await send.object({ event_ctrl: { data: getKEventControl() } })
|
||||
|
||||
export const BootPcb: EPR = async (_0, _1, send) => await send.object({ sinfo: { nm: K.ITEM("str", "Asphyxia"), cl_enbl: K.ITEM("bool", 1), cl_h: K.ITEM("u8", 0), cl_m: K.ITEM("u8", 0) } })
|
||||
|
||||
export const StartPlayer: EPR = async (_, data, send) => {
|
||||
let params = fromMap(Bst2EventParamsMap)
|
||||
let rid = $(data).str("rid")
|
||||
let account = DB.FindOne<IBst2Account>(rid, { collection: "bst.bst2.player.account" })
|
||||
if (account == null) params.playerId = -1
|
||||
params.startTime = BigInt(Date.now())
|
||||
send.object(mapK(params, Bst2EventParamsMap))
|
||||
}
|
||||
|
||||
export const PlayerSucceeded: EPR = async (_, data, send) => {
|
||||
let rid = $(data).str("rid")
|
||||
let account: IBst2Account = await DB.FindOne<IBst2Account>(rid, { collection: "bst.bst2.player.account" })
|
||||
let result
|
||||
if (account == null) {
|
||||
result = {
|
||||
play: false,
|
||||
data: { name: "" },
|
||||
record: {},
|
||||
hacker: {},
|
||||
phantom: {}
|
||||
}
|
||||
} else {
|
||||
let base: IBst2Base = await DB.FindOne<IBst2Base>(rid, { collection: "bst.bst2.player.base" })
|
||||
let records: IBst2MusicRecord[] = await DB.Find<IBst2MusicRecord>({ collection: "bst.bst2.playData.musicRecord#userId", userId: account.userId })
|
||||
result = {
|
||||
play: true,
|
||||
data: { name: base.name },
|
||||
record: {},
|
||||
hacker: {},
|
||||
phantom: {}
|
||||
}
|
||||
}
|
||||
send.object(mapK(result, {
|
||||
play: boolme(),
|
||||
data: { name: strme() },
|
||||
record: {},
|
||||
hacker: {},
|
||||
phantom: {}
|
||||
}))
|
||||
}
|
||||
|
||||
export const ReadPlayer: EPR = async (_, data, send) => {
|
||||
let refid = $(data).str("rid")
|
||||
let account = await DB.FindOne<IBst2Account>(refid, { collection: "bst.bst2.player.account" })
|
||||
if (account == null) return await send.deny()
|
||||
|
||||
let base = await DB.FindOne<IBst2Base>(refid, { collection: "bst.bst2.player.base" })
|
||||
let survey = await DB.FindOne<IBst2Survey>(refid, { collection: "bst.bst2.player.survey" }) || fromMap(Bst2SurveyMap)
|
||||
let unlocking = await DB.Find<IBst2UnlockingInfo>(refid, { collection: "bst.bst2.player.unlockingInfo" })
|
||||
let customize = await DB.FindOne<IBst2Customization>(refid, { collection: "bst.bst2.player.customization" })
|
||||
let tips = await DB.FindOne<IBst2Tips>(refid, { collection: "bst.bst2.player.tips" }) || fromMap(Bst2TipsMap)
|
||||
let hacker = await DB.Find<IBst2Hacker>(refid, { collection: "bst.bst2.player.hacker" })
|
||||
let crysis = await DB.Find<IBst2CrysisLog>(refid, { collection: "bst.bst2.player.event.crysis" })
|
||||
let bisco = await DB.FindOne<IBst2Bisco>(refid, { collection: "bst.bst2.player.bisco" }) || fromMap(Bst2BiscoMap)
|
||||
let records = await DB.Find<IBst2MusicRecord>({ collection: "bst.bst2.playData.musicRecord#userId", userId: account.userId })
|
||||
let courses = await DB.Find<IBst2Course>({ collection: "bst.bst2.playData.course#userId", userId: account.userId })
|
||||
|
||||
account.previousStartTime = account.standardTime
|
||||
account.standardTime = BigInt(Date.now())
|
||||
account.ea = true
|
||||
account.intrvld = 0
|
||||
account.playCount++
|
||||
account.playCountToday++
|
||||
let eventPlayLog: { crysis?: IBst2CrysisLog[] } = {}
|
||||
if (crysis.length != 0) eventPlayLog.crysis = crysis
|
||||
|
||||
let player: IBst2Player = {
|
||||
pdata: {
|
||||
account: account,
|
||||
base: base,
|
||||
survey: survey,
|
||||
opened: {},
|
||||
item: (unlocking.length == 0) ? {} : { info: unlocking },
|
||||
customize: customize,
|
||||
tips: tips,
|
||||
hacker: (hacker.length == 0) ? {} : { info: hacker },
|
||||
playLog: eventPlayLog,
|
||||
bisco: { pinfo: bisco },
|
||||
record: (records.length == 0) ? {} : { rec: records },
|
||||
course: (courses.length == 0) ? {} : { record: courses }
|
||||
}
|
||||
}
|
||||
send.object(readPlayerPostProcess(mapK(player, Bst2PlayerMap)))
|
||||
}
|
||||
|
||||
export const WritePlayer: EPR = async (_, data, send) => {
|
||||
let player = bacK(writePlayerPreProcess(data), Bst2PlayerMap).data
|
||||
let refid = player.pdata.account.refid
|
||||
let userId = player.pdata.account.userId
|
||||
let now = BigIntProxy(BigInt(Date.now()))
|
||||
|
||||
let opm = new DBM.DBOperationManager()
|
||||
|
||||
let oldAccount = await DB.FindOne<IBst2Account>(refid, { collection: "bst.bst2.player.account" })
|
||||
if (!oldAccount) {
|
||||
do {
|
||||
userId = Math.round(Math.random() * 99999999)
|
||||
} while ((await DB.Find<IBst2Account>(null, { collection: "bst.bst2.player.account", userId: userId })).length > 0)
|
||||
oldAccount = fromMap(Bst2AccountMap)
|
||||
oldAccount.userId = userId
|
||||
} else {
|
||||
oldAccount.playCount++
|
||||
if (!isToday(toBigInt(oldAccount.standardTime))) {
|
||||
oldAccount.dayCount++
|
||||
oldAccount.playCountToday = 1
|
||||
} else oldAccount.playCountToday++
|
||||
}
|
||||
oldAccount.standardTime = BigIntProxy(BigInt(Date.now()))
|
||||
opm.upsert<IBst2Account>(refid, { collection: "bst.bst2.player.account" }, oldAccount)
|
||||
if (player.pdata.base) opm.upsert<IBst2Base>(refid, { collection: "bst.bst2.player.base" }, player.pdata.base)
|
||||
if (player.pdata.item?.info?.length > 0) for (let u of player.pdata.item.info) opm.upsert<IBst2UnlockingInfo>(refid, { collection: "bst.bst2.player.unlockingInfo", type: u.type, id: u.id }, u)
|
||||
if (player.pdata.customize) opm.upsert<IBst2Customization>(refid, { collection: "bst.bst2.player.customization" }, player.pdata.customize)
|
||||
if (player.pdata.tips) opm.upsert<IBst2Base>(refid, { collection: "bst.bst2.player.base" }, player.pdata.base)
|
||||
if (player.pdata.hacker?.info?.length > 0) for (let h of player.pdata.hacker.info) {
|
||||
h.updateTime = now
|
||||
opm.upsert<IBst2Hacker>(refid, { collection: "bst.bst2.player.hacker", id: h.id }, h)
|
||||
}
|
||||
if (player.pdata.playLog?.crysis?.length > 0) for (let c of player.pdata.playLog.crysis) opm.upsert<IBst2CrysisLog>(refid, { collection: "bst.bst2.player.event.crysis", id: c.id, stageId: c.stageId }, c)
|
||||
|
||||
await DBM.operate(opm)
|
||||
send.object({ uid: K.ITEM("s32", oldAccount.userId) })
|
||||
}
|
||||
|
||||
export const WriteStageLog: EPR = async (_, data, send) => {
|
||||
await updateRecordFromStageLog(bacK(data, Bst2StageLogMap).data, false)
|
||||
send.success()
|
||||
}
|
||||
|
||||
export const WriteCourseStageLog: EPR = async (_, data, send) => {
|
||||
await updateRecordFromStageLog(bacK(data, Bst2StageLogMap).data, true)
|
||||
send.success()
|
||||
}
|
||||
|
||||
async function updateRecordFromStageLog(stageLog: IBst2StageLog, isCourseStage: boolean) {
|
||||
let query: Query<IBst2MusicRecord> = { collection: "bst.bst2.playData.musicRecord#userId", userId: stageLog.userId, musicId: stageLog.musicId, chart: stageLog.chart }
|
||||
let oldRecord = await DB.FindOne<IBst2MusicRecord>(query)
|
||||
|
||||
let time = Date.now()
|
||||
stageLog.time = time
|
||||
stageLog.isCourseStage = isCourseStage
|
||||
|
||||
if (oldRecord == null) {
|
||||
oldRecord = fromMap(Bst2MusicRecordMap)
|
||||
oldRecord.musicId = stageLog.musicId
|
||||
oldRecord.chart = stageLog.chart
|
||||
oldRecord.clearCount = (stageLog.medal >= 3) ? 1 : 0
|
||||
oldRecord.score = stageLog.score
|
||||
oldRecord.grade = stageLog.grade
|
||||
oldRecord.gaugeTimes10 = stageLog.gaugeTimes10
|
||||
oldRecord.playCount = 1
|
||||
oldRecord.medal = stageLog.medal
|
||||
oldRecord.combo = stageLog.combo
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.userId = stageLog.userId
|
||||
} else {
|
||||
if (stageLog.medal >= 3) oldRecord.clearCount++
|
||||
if (oldRecord.score < stageLog.score) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.score = stageLog.score
|
||||
}
|
||||
if (oldRecord.grade < stageLog.grade) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.grade = stageLog.grade
|
||||
}
|
||||
if (oldRecord.gaugeTimes10 < stageLog.gaugeTimes10) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.gaugeTimes10 = stageLog.gaugeTimes10
|
||||
}
|
||||
if (oldRecord.medal < stageLog.medal) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.medal = stageLog.medal
|
||||
}
|
||||
if (oldRecord.combo < stageLog.combo) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.combo = stageLog.combo
|
||||
}
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.playCount++
|
||||
}
|
||||
DBM.upsert(null, query, oldRecord)
|
||||
DBM.insert(null, stageLog)
|
||||
}
|
||||
|
||||
export const WriteCourseLog: EPR = async (_, data, send) => {
|
||||
let courseLog = bacK(data, Bst2CourseLogMap).data
|
||||
let query: Query<IBst2Course> = { collection: "bst.bst2.playData.course#userId", userId: courseLog.userId, courseId: courseLog.courseId }
|
||||
let oldRecord = await DB.FindOne<IBst2Course>(query)
|
||||
|
||||
let time = Date.now()
|
||||
courseLog.time = time
|
||||
|
||||
if (oldRecord == null) {
|
||||
oldRecord = fromMap(Bst2CourseMap)
|
||||
oldRecord.courseId = courseLog.courseId
|
||||
oldRecord.score = courseLog.score
|
||||
oldRecord.grade = courseLog.grade
|
||||
oldRecord.gauge = courseLog.gauge
|
||||
oldRecord.playCount = 1
|
||||
oldRecord.medal = courseLog.medal
|
||||
oldRecord.combo = courseLog.combo
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.userId = courseLog.userId
|
||||
} else {
|
||||
if (oldRecord.score < courseLog.score) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.score = courseLog.score
|
||||
}
|
||||
if (oldRecord.grade < courseLog.grade) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.grade = courseLog.grade
|
||||
}
|
||||
if (oldRecord.gauge < courseLog.gauge) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.gauge = courseLog.gauge
|
||||
}
|
||||
if (oldRecord.medal < courseLog.medal) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.medal = courseLog.medal
|
||||
}
|
||||
if (oldRecord.combo < courseLog.combo) {
|
||||
oldRecord.updateTime = time
|
||||
oldRecord.combo = courseLog.combo
|
||||
}
|
||||
oldRecord.lastPlayTime = time
|
||||
oldRecord.playCount++
|
||||
}
|
||||
DBM.upsert(null, query, oldRecord)
|
||||
DBM.insert(null, courseLog)
|
||||
|
||||
send.success()
|
||||
}
|
||||
}
|
||||
12
bst@asphyxia/handlers/bst2/processing.ts
Normal file
12
bst@asphyxia/handlers/bst2/processing.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { IBst2Player } from "../../models/bst2/profile"
|
||||
import { KITEM2 } from "../../utility/mapping"
|
||||
import { toFullWidth, toHalfWidth } from "../../utility/utility_functions"
|
||||
|
||||
export function readPlayerPostProcess(player: KITEM2<IBst2Player>): KITEM2<IBst2Player> {
|
||||
if (player.pdata.base?.name != null) player.pdata.base.name["@content"] = toFullWidth(player.pdata.base.name["@content"])
|
||||
return player
|
||||
}
|
||||
export function writePlayerPreProcess(player: KITEM2<IBst2Player>): KITEM2<IBst2Player> {
|
||||
if (player.pdata.base?.name != null) player.pdata.base.name["@content"] = toHalfWidth(player.pdata.base.name["@content"])
|
||||
return player
|
||||
}
|
||||
42
bst@asphyxia/handlers/bst2/webui.ts
Normal file
42
bst@asphyxia/handlers/bst2/webui.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { IBst2Base, IBst2Customization } from "../../models/bst2/profile"
|
||||
import { WebUIMessageType } from "../../models/utility/webui_message"
|
||||
import { DBM } from "../utility/db_manager"
|
||||
import { UtilityHandlersWebUI } from "../utility/webui"
|
||||
|
||||
export namespace Bst2HandlersWebUI {
|
||||
export const UpdateSettings = async (data: {
|
||||
refid: string
|
||||
name: string
|
||||
rippleNote: number
|
||||
sfxNormalNote: number
|
||||
sfxRippleNote: number
|
||||
sfxSlashNote: number
|
||||
sfxStreamNote: number
|
||||
backgroundBrightness: number
|
||||
judgeText: number
|
||||
rippleNoteGuide: number
|
||||
streamNoteGuide: number
|
||||
sfxFine: number
|
||||
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 || !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
|
||||
customization.custom[4] = data.sfxSlashNote
|
||||
customization.custom[5] = data.sfxStreamNote
|
||||
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) {
|
||||
UtilityHandlersWebUI.pushMessage("Error while save BeatStream Animtribe settings: " + e.message, 2, WebUIMessageType.error, data.refid)
|
||||
}
|
||||
}
|
||||
}
|
||||
21
bst@asphyxia/handlers/utility/batch.ts
Normal file
21
bst@asphyxia/handlers/utility/batch.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import { IBatchResult } from "../../models/utility/batch"
|
||||
import { IPluginVersion } from "../../models/utility/plugin_version"
|
||||
import { isHigherVersion } from "../../utility/utility_functions"
|
||||
import { DBM } from "./db_manager"
|
||||
|
||||
export namespace Batch {
|
||||
let registeredBatch = <{ id: string, version: string, batch: () => Promise<any> }[]>[]
|
||||
|
||||
export async function execute(version: string): Promise<void> {
|
||||
for (let b of registeredBatch) {
|
||||
if ((await DB.Find<IBatchResult>({ collection: "bst.batchResult", batchId: b.id })).length == 0) if (!isHigherVersion(version, b.version)) {
|
||||
await b.batch()
|
||||
await DBM.insert<IBatchResult>(null, { collection: "bst.batchResult", batchId: b.id })
|
||||
}
|
||||
}
|
||||
}
|
||||
export function register(id: string, version: string, batch: () => Promise<any>) {
|
||||
registeredBatch.push({ id: id, version: version, batch: batch })
|
||||
}
|
||||
|
||||
}
|
||||
7
bst@asphyxia/handlers/utility/batch_initialize.ts
Normal file
7
bst@asphyxia/handlers/utility/batch_initialize.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { Batch } from "./batch"
|
||||
import { DBM } from "./db_manager"
|
||||
import { bufferToBase64, log } from "../../utility/utility_functions"
|
||||
|
||||
export function initializeBatch() {
|
||||
/* Register batch here **/
|
||||
}
|
||||
17
bst@asphyxia/handlers/utility/common.ts
Normal file
17
bst@asphyxia/handlers/utility/common.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
export namespace UtilityHandlersCommon {
|
||||
export const WriteShopInfo: EPR = async (__, ___, send) => {
|
||||
let result = {
|
||||
sinfo: {
|
||||
lid: K.ITEM("str", "ea"),
|
||||
nm: K.ITEM("str", "Asphyxia shop"),
|
||||
cntry: K.ITEM("str", "Japan"),
|
||||
rgn: K.ITEM("str", "1"),
|
||||
prf: K.ITEM("s16", 13),
|
||||
cl_enbl: K.ITEM("bool", 0),
|
||||
cl_h: K.ITEM("u8", 8),
|
||||
cl_m: K.ITEM("u8", 0)
|
||||
}
|
||||
}
|
||||
send.object(result)
|
||||
}
|
||||
}
|
||||
212
bst@asphyxia/handlers/utility/db_manager.ts
Normal file
212
bst@asphyxia/handlers/utility/db_manager.ts
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
import { ICollection } from "../../models/utility/definitions"
|
||||
import { log } from "../../utility/utility_functions"
|
||||
|
||||
export namespace DBM {
|
||||
export interface IDBCollectionName extends ICollection<"dbManager.collectionName"> {
|
||||
name: string
|
||||
}
|
||||
export interface IDBOperation<T = any, TOperation extends "insert" | "update" | "upsert" | "remove" | "skip" = "insert" | "update" | "upsert" | "remove" | "skip"> {
|
||||
refid?: string
|
||||
query: TOperation extends "insert" ? null : Query<T>
|
||||
operation: TOperation
|
||||
doc: TOperation extends "remove" ? null : T | Doc<T>
|
||||
isPublicDoc?: boolean
|
||||
}
|
||||
export class DBOperationManager {
|
||||
public operations: IDBOperation[] = []
|
||||
|
||||
public push(...op: IDBOperation[]): void {
|
||||
this.operations.push(...op)
|
||||
}
|
||||
public update<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true): void {
|
||||
for (let o of this.operations) if (o.doc && DBOperationManager.isMatch(o.doc, query)) o.operation = "skip"
|
||||
this.operations.push({ refid: refid, query: query, operation: "update", doc: data, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public upsert<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true): void {
|
||||
for (let o of this.operations) if (o.doc && DBOperationManager.isMatch(o.doc, query)) o.operation = "skip"
|
||||
this.operations.push({ refid: refid, query: query, operation: "upsert", doc: data, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public insert<T extends ICollection<any>>(refid: string | null, data: Doc<T>, isPublicDoc: boolean = true): void {
|
||||
this.operations.push({ refid: refid, operation: "insert", query: null, doc: data, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public remove<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true): void {
|
||||
for (let o of this.operations) if (o.doc && DBOperationManager.isMatch(o.doc, query)) o.operation = "skip"
|
||||
this.operations.push({ refid: refid, query: query, operation: "remove", doc: null, isPublicDoc: isPublicDoc })
|
||||
}
|
||||
public async findOne<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true): Promise<T | Doc<T>> {
|
||||
for (let i = this.operations.length - 1; i >= 0; i--) {
|
||||
let o = this.operations[i]
|
||||
if (o.doc == null) continue
|
||||
if (DBOperationManager.isMatch(o.doc, query) && ((o.refid && refid) ? (o.refid == refid) : true)) return o.doc
|
||||
}
|
||||
return ((refid == null) && isPublicDoc) ? await DB.FindOne<T>(query) : await DB.FindOne<T>(refid, query)
|
||||
}
|
||||
public async find<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true): Promise<(T | Doc<T>)[]> {
|
||||
let result: (T | Doc<T>)[] = []
|
||||
for (let o of this.operations) {
|
||||
if (o.doc == null) continue
|
||||
if (DBOperationManager.isMatch(o.doc, query) && ((o.refid && refid) ? (o.refid == refid) : true)) result.push(o.doc)
|
||||
}
|
||||
return result.concat(await (((refid == null) && isPublicDoc) ? DB.Find<T>(query) : DB.Find<T>(refid, query)))
|
||||
}
|
||||
private static isMatch<T>(entry: T | Doc<T>, query: Query<T>): boolean {
|
||||
if (entry == null) return query == null
|
||||
if (query.$where && !query.$where.apply(entry)) return false
|
||||
let $orResult = null
|
||||
let skipKeys = ["$where", "_id"]
|
||||
for (let qk in query) {
|
||||
if (skipKeys.includes(qk)) continue
|
||||
switch (qk) {
|
||||
case "$or": {
|
||||
if ($orResult == null) $orResult = false
|
||||
for (let or of query.$or) if (this.isMatch(entry, or)) $orResult = true
|
||||
break
|
||||
}
|
||||
case "$and": {
|
||||
for (let and of query.$and) if (!this.isMatch(entry, and)) return false
|
||||
break
|
||||
}
|
||||
case "$not": {
|
||||
if (this.isMatch(entry, query.$not)) return false
|
||||
break
|
||||
}
|
||||
default: {
|
||||
let value = entry[qk]
|
||||
let q = query[qk]
|
||||
if (value == q) continue
|
||||
if ((typeof q != "object") && (typeof q != "function")) return false
|
||||
if ((q.$exists != null)) if ((q.$exists && (value == null)) || (!q.$exists && (value != null))) return false
|
||||
if (Array.isArray(value)) {
|
||||
if (q.$elemMatch && !this.isMatch(value, q.$elemMatch)) return false
|
||||
if (q.$size && (value.length != q.$size)) return false
|
||||
continue
|
||||
} else if ((typeof value == "number") || (typeof value == "string")) {
|
||||
if (q.$lt) if (value >= q.$lt) return false
|
||||
if (q.$lte) if (value > q.$lte) return false
|
||||
if (q.$gt) if (value <= q.$gt) return false
|
||||
if (q.$gte) if (value < q.$gte) return false
|
||||
if (q.$in) if (!value.toString().includes(q.$in)) return false
|
||||
if (q.$nin) if (value.toString().includes(q.$nin)) return false
|
||||
if (q.$ne) if (value == q.$ne) return false
|
||||
if (q.$regex) if (value.toString().match(q.$regex).length == 0) return false
|
||||
continue
|
||||
} else if (typeof value == "object") {
|
||||
if (!this.isMatch(value, q)) return false
|
||||
continue
|
||||
} else if (q != null) return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return ($orResult == null) || $orResult
|
||||
}
|
||||
}
|
||||
export async function getCollectionNames(filter?: string): Promise<IDBCollectionName[]> {
|
||||
let result = await DB.Find<IDBCollectionName>({ collection: "dbManager.collectionName" })
|
||||
if (filter != null) {
|
||||
let filters = filter.split(",")
|
||||
for (let i = 0; i < filter.length; i++) filters[i] = filters[i].trim()
|
||||
let i = 0
|
||||
while (i < result.length) {
|
||||
let removeFlag = false
|
||||
for (let f of filters) if (f.startsWith("!") ? !result[i].name.includes(f) : result[i].name.includes(f)) {
|
||||
result.splice(i, 1)
|
||||
removeFlag = true
|
||||
break
|
||||
}
|
||||
if (!removeFlag) i++
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async function checkData<T extends ICollection<any>>(data: T): Promise<void> {
|
||||
for (let k in data) if (k.startsWith("__")) delete data[k]
|
||||
if (await DB.FindOne<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection }) == null) {
|
||||
await DB.Insert<IDBCollectionName>({ collection: "dbManager.collectionName", name: data.collection })
|
||||
}
|
||||
}
|
||||
export async function update<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true) {
|
||||
checkData(data)
|
||||
if (refid == null) return isPublicDoc ? await DB.Update(query, data) : await DB.Update(null, query, data)
|
||||
else return await DB.Update(refid, query, data)
|
||||
}
|
||||
export async function upsert<T extends ICollection<any>>(refid: string | null, query: Query<T>, data: Doc<T>, isPublicDoc: boolean = true) {
|
||||
checkData(data)
|
||||
if (refid == null) return isPublicDoc ? await DB.Upsert(query, data) : await DB.Upsert(null, query, data)
|
||||
else return await DB.Upsert(refid, query, data)
|
||||
}
|
||||
export async function insert<T extends ICollection<any>>(refid: string | null, data: Doc<T>, isPublicDoc: boolean = true) {
|
||||
checkData(data)
|
||||
if (refid == null) return isPublicDoc ? await DB.Insert(data) : await DB.Insert(null, data)
|
||||
else return await DB.Insert(refid, data)
|
||||
}
|
||||
export async function remove<T extends ICollection<any>>(refid: string | null, query: Query<T>, isPublicDoc: boolean = true) {
|
||||
if (refid == null) return isPublicDoc ? await DB.Remove(query) : await DB.Remove(null, query)
|
||||
else return await DB.Remove(refid, query)
|
||||
}
|
||||
|
||||
export async function operate(operations: DBOperationManager) {
|
||||
let result = []
|
||||
for (let o of operations.operations) {
|
||||
if (o.operation == "skip") continue
|
||||
if (o.doc) delete o.doc._id
|
||||
try {
|
||||
switch (o.operation) {
|
||||
case "insert":
|
||||
result.push(await insert(o.refid, o.doc, o.isPublicDoc))
|
||||
break
|
||||
case "update":
|
||||
result.push(await update(o.refid, o.query, o.doc, o.isPublicDoc))
|
||||
break
|
||||
case "upsert":
|
||||
result.push(await upsert(o.refid, o.query, o.doc, o.isPublicDoc))
|
||||
break
|
||||
case "remove":
|
||||
result.push(await remove(o.refid, o.query, o.isPublicDoc))
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
await log(new Date().toLocaleString() + " Error: " + (e as Error).message)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export async function removeAllData(refid?: string, filter?: string) {
|
||||
for (let c of await getCollectionNames(filter)) remove(refid, { collection: c.name })
|
||||
|
||||
if ((refid == null) && (filter == null)) remove(null, { collection: "dbManager.collectionName" })
|
||||
}
|
||||
export async function overall(refid: string, userId: number, filter: string, operation: "delete" | "export" | "override", data?: any) {
|
||||
if (refid == null) return
|
||||
try {
|
||||
let collections = await DBM.getCollectionNames(filter)
|
||||
let traverse = async (f: (rid: string | null, query: Query<ICollection<any>>) => Promise<any>) => {
|
||||
let result = []
|
||||
for (let c of collections) {
|
||||
if (c.name.includes("#userId") && (userId != null)) result.concat(...await f(null, { collection: c.name, userId: userId }))
|
||||
else result.concat(...await f(refid, { collection: c.name }))
|
||||
}
|
||||
return result
|
||||
}
|
||||
switch (operation) {
|
||||
case "delete":
|
||||
await traverse((rid, query) => DBM.remove(rid, query))
|
||||
break
|
||||
case "export":
|
||||
let result = await traverse((rid, query) => DB.Find(rid, query))
|
||||
return JSON.stringify(result)
|
||||
case "override":
|
||||
if (!Array.isArray(data)) return "The data may not be an Asphyxia CORE savedata."
|
||||
await traverse((rid, query) => DBM.remove(rid, query))
|
||||
for (let d of data) if ((typeof (d?.collection) == "string") && (!d.collection.includes(filter))) DB.Insert(d)
|
||||
break
|
||||
}
|
||||
} catch (e) {
|
||||
return e.message
|
||||
}
|
||||
return null
|
||||
|
||||
}
|
||||
}
|
||||
15
bst@asphyxia/handlers/utility/initialize.ts
Normal file
15
bst@asphyxia/handlers/utility/initialize.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { initializeBatch } from "./batch_initialize"
|
||||
import { IPluginVersion } from "../../models/utility/plugin_version"
|
||||
import { isHigherVersion } from "../../utility/utility_functions"
|
||||
import { Batch } from "./batch"
|
||||
import { DBM } from "./db_manager"
|
||||
import { version } from "../../utility/about"
|
||||
|
||||
export async function initialize() {
|
||||
let oldVersion = await DB.FindOne<IPluginVersion>({ collection: "bst.pluginVersion" })
|
||||
if ((oldVersion == null) || isHigherVersion(oldVersion.version, version)) {
|
||||
initializeBatch()
|
||||
await Batch.execute(version)
|
||||
await DBM.upsert<IPluginVersion>(null, { collection: "bst.pluginVersion" }, { collection: "bst.pluginVersion", version: version })
|
||||
}
|
||||
}
|
||||
12
bst@asphyxia/handlers/utility/webui.ts
Normal file
12
bst@asphyxia/handlers/utility/webui.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { IWebUIMessage, WebUIMessageType } from "../../models/utility/webui_message"
|
||||
import { DBM } from "./db_manager"
|
||||
|
||||
export namespace UtilityHandlersWebUI {
|
||||
export function pushMessage(message: string, version: number, type: WebUIMessageType, rid?: string) {
|
||||
DBM.upsert<IWebUIMessage>(null, { collection: "utility.webuiMessage" }, { collection: "utility.webuiMessage", message: message, type: type, refid: rid, version: version })
|
||||
}
|
||||
|
||||
export const removeWebUIMessage = async () => {
|
||||
await DBM.remove<IWebUIMessage>(null, { collection: "utility.webuiMessage" })
|
||||
}
|
||||
}
|
||||
32
bst@asphyxia/index.ts
Normal file
32
bst@asphyxia/index.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { UtilityHandlersCommon } from "./handlers/utility/common"
|
||||
import { UtilityHandlersWebUI } from "./handlers/utility/webui"
|
||||
import { initialize } from "./handlers/utility/initialize"
|
||||
import { Bst2HandlersCommon } from "./handlers/bst2/common"
|
||||
import { Bst2HandlersWebUI } from "./handlers/bst2/webui"
|
||||
|
||||
export function register() {
|
||||
R.GameCode("NBT")
|
||||
|
||||
routeBst2()
|
||||
|
||||
R.WebUIEvent("removeWebUIMessage", UtilityHandlersWebUI.removeWebUIMessage)
|
||||
|
||||
R.Unhandled()
|
||||
|
||||
initialize()
|
||||
}
|
||||
|
||||
function routeBst2() {
|
||||
R.Route("info2.common", Bst2HandlersCommon.Common)
|
||||
R.Route("pcb2.boot", Bst2HandlersCommon.BootPcb)
|
||||
R.Route("player2.start", Bst2HandlersCommon.StartPlayer)
|
||||
R.Route("player2.continue", Bst2HandlersCommon.StartPlayer)
|
||||
R.Route("player2.succeed", Bst2HandlersCommon.PlayerSucceeded)
|
||||
R.Route("player2.read", Bst2HandlersCommon.ReadPlayer)
|
||||
R.Route("player2.write", Bst2HandlersCommon.WritePlayer)
|
||||
R.Route("player2.stagedata_write", Bst2HandlersCommon.WriteStageLog)
|
||||
R.Route("player2.course_stage_data_write", Bst2HandlersCommon.WriteCourseStageLog)
|
||||
R.Route("player2.course_data_write", Bst2HandlersCommon.WriteCourseLog)
|
||||
|
||||
R.WebUIEvent("bst2UpdateSettings", Bst2HandlersWebUI.UpdateSettings)
|
||||
}
|
||||
47
bst@asphyxia/models/bst2/event_params.ts
Normal file
47
bst@asphyxia/models/bst2/event_params.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { BigIntProxy, boolme, KITEM2, KM, s32me, u64me } from "../../utility/mapping"
|
||||
|
||||
export interface IFloorInfectionEventParams {
|
||||
id: number
|
||||
musicList: number
|
||||
isCompleted: boolean
|
||||
}
|
||||
export const FloorInfectionEventParamsMap: KM<IFloorInfectionEventParams> = {
|
||||
id: s32me("infection_id", 20),
|
||||
musicList: s32me("music_list", 7),
|
||||
isCompleted: boolme("is_complete", true)
|
||||
}
|
||||
|
||||
export interface IBst2EventParams {
|
||||
playerId: number
|
||||
startTime: bigint | BigIntProxy
|
||||
hasRbCollaboration: boolean
|
||||
hasPopnCollaboration: boolean
|
||||
floorInfection: { event: IFloorInfectionEventParams }
|
||||
museca: { isPlayedMuseca: boolean }
|
||||
}
|
||||
export const Bst2EventParamsMap: KM<IBst2EventParams> = {
|
||||
playerId: s32me("plyid"),
|
||||
startTime: u64me("start_time"),
|
||||
hasRbCollaboration: boolme("reflec_collabo", true),
|
||||
hasPopnCollaboration: boolme("pop_collabo", true),
|
||||
floorInfection: { event: FloorInfectionEventParamsMap, $targetKey: "floor_infection" },
|
||||
museca: { isPlayedMuseca: boolme("is_play_museca", true) },
|
||||
}
|
||||
|
||||
export interface IBst2EventControl {
|
||||
type: number
|
||||
phase: number
|
||||
}
|
||||
export const Bst2EventControlMap: KM<IBst2EventControl> = {
|
||||
type: s32me(),
|
||||
phase: s32me()
|
||||
}
|
||||
|
||||
let kEventControl: KITEM2<IBst2EventControl>[]
|
||||
export function getKEventControl(): KITEM2<IBst2EventControl>[] {
|
||||
if (kEventControl == null) {
|
||||
kEventControl = []
|
||||
for (let i = 0; i <= 40; i++) for (let j = 0; j <= 25; j++) kEventControl.push(<any>{ type: K.ITEM("s32", i), phase: K.ITEM("s32", j) })
|
||||
}
|
||||
return kEventControl
|
||||
}
|
||||
263
bst@asphyxia/models/bst2/profile.ts
Normal file
263
bst@asphyxia/models/bst2/profile.ts
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
import { BigIntProxy, boolme, colme, ignoreme, KM, s16me, s32me, s8me, strme, u16me, u64me, u8me } from "../../utility/mapping"
|
||||
import { FixedSizeArray } from "../../utility/type"
|
||||
import { ICollection } from "../utility/definitions"
|
||||
|
||||
export interface IBst2Account extends ICollection<"bst.bst2.player.account"> {
|
||||
userId: number
|
||||
isTakeOver: number
|
||||
playerId: number
|
||||
continueCount: number
|
||||
playCount: number
|
||||
playCountToday: number
|
||||
crd: number
|
||||
brd: number
|
||||
dayCount: number
|
||||
refid: string
|
||||
lobbyId: string
|
||||
mode: number
|
||||
version: number
|
||||
pp: boolean
|
||||
ps: boolean
|
||||
pay: number
|
||||
payedPlayCount: number
|
||||
standardTime: bigint | BigIntProxy
|
||||
intrvld?: number
|
||||
previousStartTime?: bigint | BigIntProxy
|
||||
ea?: boolean
|
||||
}
|
||||
export const Bst2AccountMap: KM<IBst2Account> = {
|
||||
collection: colme<IBst2Account>("bst.bst2.player.account"),
|
||||
userId: s32me("usrid"),//
|
||||
isTakeOver: s32me("is_takeover"),//
|
||||
playerId: s32me("plyid"),
|
||||
continueCount: s32me("continue_cnt"),
|
||||
playCount: s32me("tpc"),//
|
||||
playCountToday: s32me("dpc"),//
|
||||
crd: s32me(),//
|
||||
brd: s32me(),//
|
||||
dayCount: s32me("tdc"),//
|
||||
refid: strme("rid"),
|
||||
lobbyId: strme("lid", "Asphyxia"),
|
||||
mode: u8me(null, 2),
|
||||
version: s16me("ver"),//
|
||||
pp: boolme(),
|
||||
ps: boolme(),
|
||||
pay: s16me(),
|
||||
payedPlayCount: s16me("pay_pc"),
|
||||
standardTime: u64me("st", BigInt(Date.now())),//
|
||||
intrvld: s32me(),//
|
||||
previousStartTime: u64me("pst"),//
|
||||
ea: boolme()//
|
||||
}
|
||||
|
||||
export interface IBst2Base extends ICollection<"bst.bst2.player.base"> {
|
||||
name: string
|
||||
brnk: number
|
||||
bcnum: number
|
||||
lcnum: number
|
||||
volt: number
|
||||
gold: number
|
||||
lastMusicId: number
|
||||
lastChart: number
|
||||
lastSort: number
|
||||
lastTab: number
|
||||
splv: number
|
||||
preference: number
|
||||
lcid: number
|
||||
hat: number
|
||||
}
|
||||
export const Bst2BaseMap: KM<IBst2Base> = {
|
||||
collection: colme<IBst2Base>("bst.bst2.player.base"),
|
||||
name: strme(),
|
||||
brnk: s8me(),
|
||||
bcnum: s8me(),
|
||||
lcnum: s8me(),
|
||||
volt: s32me(),
|
||||
gold: s32me(),
|
||||
lastMusicId: s32me("lmid"),
|
||||
lastChart: s8me("lgrd"),
|
||||
lastSort: s8me("lsrt"),
|
||||
lastTab: s8me("ltab"),
|
||||
splv: s8me(),
|
||||
preference: s8me("pref"),
|
||||
lcid: s32me(),
|
||||
hat: s32me()
|
||||
}
|
||||
|
||||
export interface IBst2Survey extends ICollection<"bst.bst2.player.survey"> {
|
||||
motivate: number
|
||||
}
|
||||
export const Bst2SurveyMap: KM<IBst2Survey> = {
|
||||
collection: colme<IBst2Survey>("bst.bst2.player.survey"),
|
||||
motivate: s8me()
|
||||
}
|
||||
|
||||
export interface IBst2UnlockingInfo extends ICollection<"bst.bst2.player.unlockingInfo"> {
|
||||
type: number
|
||||
id: number
|
||||
param: number
|
||||
count: number
|
||||
}
|
||||
export const Bst2UnlockingInfoMap: KM<IBst2UnlockingInfo> = {
|
||||
collection: colme<IBst2UnlockingInfo>("bst.bst2.player.unlockingInfo"),
|
||||
type: s32me(),
|
||||
id: s32me(),
|
||||
param: s32me(),
|
||||
count: s32me()
|
||||
}
|
||||
|
||||
export interface IBst2Customization extends ICollection<"bst.bst2.player.customization"> {
|
||||
// [rippleNote, rippleNoteColor, sfxNormalNote, sfxRippleNote, sfxSlashNote, sfxStreamNote, backgroundBrightnessTimes2, (000{sfxFine}{sfxStreamTail}{streamNoteGuide}{rippleNoteGuide}{judgeText}, ?, ?, ?, ?, ?, ?, ?, ?)]
|
||||
custom: FixedSizeArray<number, 16>
|
||||
}
|
||||
export const Bst2CustomizationMap: KM<IBst2Customization> = {
|
||||
collection: colme<IBst2Customization>("bst.bst2.player.customization"),
|
||||
custom: u16me(null, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
||||
}
|
||||
|
||||
export interface IBst2Tips extends ICollection<"bst.bst2.player.tips"> {
|
||||
lastTips: number
|
||||
}
|
||||
export const Bst2TipsMap: KM<IBst2Tips> = {
|
||||
collection: colme<IBst2Tips>("bst.bst2.player.tips"),
|
||||
lastTips: s32me("last_tips")
|
||||
}
|
||||
|
||||
export interface IBst2Hacker extends ICollection<"bst.bst2.player.hacker"> {
|
||||
id: number
|
||||
state0: number
|
||||
state1: number
|
||||
state2: number
|
||||
state3: number
|
||||
state4: number
|
||||
updateTime: bigint | BigIntProxy
|
||||
}
|
||||
export const Bst2HackerMap: KM<IBst2Hacker> = {
|
||||
collection: colme<IBst2Hacker>("bst.bst2.player.hacker"),
|
||||
id: s32me(),
|
||||
state0: s8me(),
|
||||
state1: s8me(),
|
||||
state2: s8me(),
|
||||
state3: s8me(),
|
||||
state4: s8me(),
|
||||
updateTime: u64me("update_time")
|
||||
}
|
||||
|
||||
export interface IBst2CrysisLog extends ICollection<"bst.bst2.player.event.crysis"> {
|
||||
id: number
|
||||
stageId: number
|
||||
step: number
|
||||
gauge: number
|
||||
state: number
|
||||
}
|
||||
export const Bst2CrysisLogMap: KM<IBst2CrysisLog> = {
|
||||
collection: colme<IBst2CrysisLog>("bst.bst2.player.event.crysis"),
|
||||
id: s32me(),
|
||||
stageId: s32me("stage_no"),
|
||||
step: s8me(),
|
||||
gauge: s32me("r_gauge"),
|
||||
state: s8me("r_state")
|
||||
}
|
||||
|
||||
export interface IBst2Bisco extends ICollection<"bst.bst2.player.bisco"> {
|
||||
bnum: number
|
||||
jbox: number
|
||||
}
|
||||
export const Bst2BiscoMap: KM<IBst2Bisco> = {
|
||||
collection: colme<IBst2Bisco>("bst.bst2.player.bisco"),
|
||||
bnum: s32me(),
|
||||
jbox: s32me(),
|
||||
}
|
||||
|
||||
export interface IBst2MusicRecord extends ICollection<"bst.bst2.playData.musicRecord#userId"> {
|
||||
musicId: number
|
||||
chart: number
|
||||
playCount: number
|
||||
clearCount: number
|
||||
gaugeTimes10: number
|
||||
score: number
|
||||
grade: number
|
||||
medal: number
|
||||
combo: number
|
||||
userId: number
|
||||
updateTime: number
|
||||
lastPlayTime: number
|
||||
}
|
||||
export const Bst2MusicRecordMap: KM<IBst2MusicRecord> = {
|
||||
collection: colme<IBst2MusicRecord>("bst.bst2.playData.musicRecord#userId"),
|
||||
musicId: s32me("music_id"),
|
||||
chart: s32me("note_level"),
|
||||
playCount: s32me("play_count"),
|
||||
clearCount: s32me("clear_count"),
|
||||
gaugeTimes10: s32me("best_gauge"),
|
||||
score: s32me("best_score"),
|
||||
grade: s32me("best_grade"),
|
||||
medal: s32me("best_medal"),
|
||||
combo: ignoreme(),
|
||||
userId: ignoreme(),
|
||||
updateTime: ignoreme(),
|
||||
lastPlayTime: ignoreme()
|
||||
}
|
||||
|
||||
export interface IBst2Course extends ICollection<"bst.bst2.playData.course#userId"> {
|
||||
courseId: number
|
||||
playCount: number
|
||||
isTouched: boolean
|
||||
clearType: number
|
||||
gauge: number
|
||||
score: number
|
||||
grade: number
|
||||
medal: number
|
||||
combo: number
|
||||
userId: number
|
||||
updateTime: number
|
||||
lastPlayTime: number
|
||||
}
|
||||
export const Bst2CourseMap: KM<IBst2Course> = {
|
||||
collection: colme<IBst2Course>("bst.bst2.playData.course#userId"),
|
||||
courseId: s32me("course_id"),
|
||||
playCount: s32me("play"),
|
||||
isTouched: boolme("is_touch"),
|
||||
clearType: s32me("clear"),
|
||||
gauge: s32me("gauge"),
|
||||
score: s32me(),
|
||||
grade: s32me(),
|
||||
medal: s32me(),
|
||||
combo: s32me(),
|
||||
userId: ignoreme(),
|
||||
updateTime: ignoreme(),
|
||||
lastPlayTime: ignoreme()
|
||||
}
|
||||
|
||||
export interface IBst2Player {
|
||||
pdata: {
|
||||
account: IBst2Account
|
||||
base: IBst2Base
|
||||
opened: {}
|
||||
survey: IBst2Survey
|
||||
item: { info?: IBst2UnlockingInfo[] }
|
||||
customize: IBst2Customization
|
||||
tips: IBst2Tips
|
||||
hacker: { info?: IBst2Hacker[] }
|
||||
playLog: { crysis?: IBst2CrysisLog[] }
|
||||
bisco: { pinfo: IBst2Bisco }
|
||||
record: { rec?: IBst2MusicRecord[] }
|
||||
course: { record?: IBst2Course[] }
|
||||
}
|
||||
}
|
||||
export const Bst2PlayerMap: KM<IBst2Player> = {
|
||||
pdata: {
|
||||
account: Bst2AccountMap,
|
||||
base: Bst2BaseMap,
|
||||
opened: {},
|
||||
survey: Bst2SurveyMap,
|
||||
item: { info: { 0: Bst2UnlockingInfoMap } },
|
||||
customize: Bst2CustomizationMap,
|
||||
tips: Bst2TipsMap,
|
||||
hacker: { info: { 0: Bst2HackerMap } },
|
||||
playLog: { crysis: { 0: Bst2CrysisLogMap }, $targetKey: "play_log" },
|
||||
bisco: { pinfo: Bst2BiscoMap },
|
||||
record: { rec: { 0: Bst2MusicRecordMap } },
|
||||
course: { record: { 0: Bst2CourseMap } }
|
||||
}
|
||||
}
|
||||
80
bst@asphyxia/models/bst2/stagelog.ts
Normal file
80
bst@asphyxia/models/bst2/stagelog.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { colme, ignoreme, KM, s32me, strme } from "../../utility/mapping"
|
||||
import { ICollection } from "../utility/definitions"
|
||||
|
||||
export interface IBst2StageLog extends ICollection<"bst.bst2.playData.stageLog#userId"> {
|
||||
playerId: number
|
||||
continueCount: number
|
||||
stageId: number
|
||||
userId: number
|
||||
lobbyId: string
|
||||
musicId: number
|
||||
chart: number
|
||||
gaugeTimes10: number
|
||||
score: number
|
||||
combo: number
|
||||
grade: number
|
||||
medal: number
|
||||
fantasticCount: number
|
||||
greatCount: number
|
||||
fineCount: number
|
||||
missCount: number
|
||||
isCourseStage: boolean
|
||||
time: number
|
||||
}
|
||||
export const Bst2StageLogMap: KM<IBst2StageLog> = {
|
||||
collection: colme<IBst2StageLog>("bst.bst2.playData.stageLog#userId"),
|
||||
playerId: s32me("play_id"),
|
||||
continueCount: s32me("continue_count"),
|
||||
stageId: s32me("stage_no"),
|
||||
userId: s32me("user_id"),
|
||||
lobbyId: strme("location_id"),
|
||||
musicId: s32me("select_music_id"),
|
||||
chart: s32me("select_grade"),
|
||||
gaugeTimes10: s32me("result_clear_gauge"),
|
||||
score: s32me("result_score"),
|
||||
combo: s32me("result_max_combo"),
|
||||
grade: s32me("result_grade"),
|
||||
medal: s32me("result_medal"),
|
||||
fantasticCount: s32me("result_fanta"),
|
||||
greatCount: s32me("result_great"),
|
||||
fineCount: s32me("result_fine"),
|
||||
missCount: s32me("result_miss"),
|
||||
isCourseStage: ignoreme(),
|
||||
time: ignoreme(),
|
||||
}
|
||||
|
||||
export interface IBst2CourseLog extends ICollection<"bst.bst2.playData.courseLog#userId"> {
|
||||
playerId: number
|
||||
continueCount: number
|
||||
userId: number
|
||||
courseId: number
|
||||
gauge: number
|
||||
score: number
|
||||
grade: number
|
||||
medal: number
|
||||
combo: number
|
||||
fantasticCount: number
|
||||
greatCount: number
|
||||
fineCount: number
|
||||
missCount: number
|
||||
lobbyId: string
|
||||
time: number
|
||||
}
|
||||
export const Bst2CourseLogMap: KM<IBst2CourseLog> = {
|
||||
collection: colme<IBst2CourseLog>("bst.bst2.playData.courseLog#userId"),
|
||||
playerId: s32me("play_id"),
|
||||
continueCount: s32me("continue_count"),
|
||||
userId: s32me("user_id"),
|
||||
courseId: s32me("course_id"),
|
||||
lobbyId: strme("lid"),
|
||||
gauge: s32me(),
|
||||
score: s32me(),
|
||||
combo: s32me(),
|
||||
grade: s32me(),
|
||||
medal: s32me(),
|
||||
fantasticCount: s32me("fanta"),
|
||||
greatCount: s32me("great"),
|
||||
fineCount: s32me("fine"),
|
||||
missCount: s32me("miss"),
|
||||
time: ignoreme()
|
||||
}
|
||||
5
bst@asphyxia/models/utility/batch.ts
Normal file
5
bst@asphyxia/models/utility/batch.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { ICollection } from "./definitions"
|
||||
|
||||
export interface IBatchResult extends ICollection<"bst.batchResult"> {
|
||||
batchId: string
|
||||
}
|
||||
3
bst@asphyxia/models/utility/definitions.d.ts
vendored
Normal file
3
bst@asphyxia/models/utility/definitions.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export interface ICollection<TCollectionName extends string> {
|
||||
collection: TCollectionName
|
||||
}
|
||||
5
bst@asphyxia/models/utility/plugin_version.ts
Normal file
5
bst@asphyxia/models/utility/plugin_version.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { ICollection } from "./definitions"
|
||||
|
||||
export interface IPluginVersion<TMajor extends number = number, TMinor extends number = number, TRevision extends number = number> extends ICollection<"bst.pluginVersion"> {
|
||||
version: string
|
||||
}
|
||||
14
bst@asphyxia/models/utility/webui_message.ts
Normal file
14
bst@asphyxia/models/utility/webui_message.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { ICollection } from "./definitions"
|
||||
|
||||
export interface IWebUIMessage extends ICollection<"utility.webuiMessage"> {
|
||||
message: string
|
||||
type: WebUIMessageType
|
||||
refid?: string
|
||||
version: number
|
||||
}
|
||||
|
||||
export enum WebUIMessageType {
|
||||
info = 0,
|
||||
success = 1,
|
||||
error = 2
|
||||
}
|
||||
4
bst@asphyxia/utility/about.ts
Normal file
4
bst@asphyxia/utility/about.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export type Game = "bst"
|
||||
export const game: Game = "bst"
|
||||
export type PluginVersion = "1.0.0"
|
||||
export const version: PluginVersion = "1.0.0"
|
||||
480
bst@asphyxia/utility/mapping.ts
Normal file
480
bst@asphyxia/utility/mapping.ts
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
import { ICollection } from "../models/utility/definitions"
|
||||
|
||||
export type KArrayType = KNumberType | KBigIntType
|
||||
export type KGroupType = KNumberGroupType | KBigIntGroupType
|
||||
export type KType = KArrayType | KGroupType | "str" | "bin" | "ip4" | "bool"
|
||||
export type KTypeExtended = KType | null | "kignore"
|
||||
export type TypeForKItem = number | string | bigint | BigIntProxy | boolean | Buffer | number[] | bigint[] | boolean[] | BufferArray | NumberGroup<number[] | bigint[]>
|
||||
export type TypeForKObject<T> = T extends TypeForKItem ? never : T
|
||||
export type TypeForKArray = number[] | bigint[] | BufferArray
|
||||
|
||||
export type KKey<T> = keyof T & (
|
||||
T extends string ? Exclude<keyof T, keyof string> :
|
||||
T extends Buffer ? Exclude<keyof T, keyof Buffer> :
|
||||
T extends boolean ? Exclude<keyof T, keyof boolean> :
|
||||
T extends number[] | bigint[] | boolean[] ? Exclude<keyof T, (keyof number[]) | (keyof bigint[]) | (keyof boolean[])> :
|
||||
T extends any[] ? Exclude<keyof T, keyof any[]> | number :
|
||||
T extends number ? Exclude<keyof T, keyof number> :
|
||||
T extends bigint | BigIntProxy ? Exclude<keyof T, keyof bigint> :
|
||||
T extends BufferArray ? Exclude<keyof T, keyof BufferArray> :
|
||||
T extends NumberGroup<infer TGroup> ? Exclude<keyof T, keyof NumberGroup<TGroup>> :
|
||||
keyof T)
|
||||
|
||||
export type KTypeConvert<T extends string | Buffer | number | bigint | boolean | number[] | bigint[] | unknown> =
|
||||
T extends string ? "str" :
|
||||
T extends Buffer ? "bin" :
|
||||
T extends number ? KNumberType | "ip4" | "bool" :
|
||||
T extends bigint | BigIntProxy ? KBigIntType :
|
||||
T extends boolean | boolean[] ? "bool" :
|
||||
T extends number[] ? KNumberType : // KARRAY
|
||||
T extends bigint[] ? KBigIntType : // KARRAY
|
||||
T extends NumberGroup<number[]> ? KNumberGroupType :
|
||||
T extends NumberGroup<bigint[]> ? KBigIntGroupType :
|
||||
T extends BufferArray ? "u8" | "s8" :
|
||||
never
|
||||
|
||||
export type KArrayTypeConvert<T extends Buffer | number[] | bigint[] | unknown> =
|
||||
T extends Buffer ? "s8" | "u8" :
|
||||
T extends number[] ? KNumberType :
|
||||
T extends bigint[] ? KBigIntType :
|
||||
never
|
||||
|
||||
export type KTypeConvertBack<TKType extends KTypeExtended> =
|
||||
TKType extends "str" ? string :
|
||||
TKType extends "bin" ? { type: "Buffer"; data: number[] } :
|
||||
TKType extends "s8" | "u8" ? [number] | number[] | { type: "Buffer"; data: number[] } :
|
||||
TKType extends KNumberType ? [number] | number[] :
|
||||
TKType extends KBigIntType ? [bigint] | bigint[] :
|
||||
TKType extends KNumberGroupType ? number[] :
|
||||
TKType extends KBigIntGroupType ? bigint[] :
|
||||
unknown
|
||||
|
||||
export type NumberGroup<T extends number[] | bigint[] = number[]> = {
|
||||
"@numberGroupValue": T
|
||||
}
|
||||
export const NumberGroup = <T extends number[] | bigint[] = number[]>(ng: T) => <NumberGroup>{ "@numberGroupValue": ng }
|
||||
export function isNumberGroup(value: any): value is NumberGroup {
|
||||
try {
|
||||
return Array.isArray(BigInt(value["@numberGroupValue"]))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export type BufferArray = {
|
||||
"@bufferArrayValue": Buffer
|
||||
}
|
||||
export const BufferArray = (ba: Buffer) => <BufferArray>{ "@bufferArrayValue": ba }
|
||||
export function isBufferArray(value: any): value is BufferArray {
|
||||
try {
|
||||
return value["@bufferArrayValue"] instanceof Buffer
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export type BigIntProxy = {
|
||||
"@serializedBigInt": string
|
||||
}
|
||||
export const BigIntProxy = (value: bigint) => <BigIntProxy>{ "@serializedBigInt": value.toString() }
|
||||
export function isBigIntProxy(value: any): value is BigIntProxy {
|
||||
try {
|
||||
return BigInt(value["@serializedBigInt"]).toString() == value["@serializedBigInt"]
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export function toBigInt(value: bigint | BigIntProxy): bigint {
|
||||
if (value == null) return null
|
||||
if (value instanceof BigInt) return <bigint>value
|
||||
else if (value["@serializedBigInt"] != null) return BigInt(value["@serializedBigInt"])
|
||||
else return BigInt(0)
|
||||
}
|
||||
|
||||
export type KITEM2<T> = { [K in keyof T]?: K extends KKey<T> ? KITEM2<T[K]> : never } &
|
||||
{
|
||||
["@attr"]: KAttrMap2<T>
|
||||
["@content"]:
|
||||
T extends string | Buffer | boolean | number[] | bigint[] ? T :
|
||||
T extends number | bigint ? [T] :
|
||||
T extends BufferArray ? Buffer :
|
||||
T extends NumberGroup<infer TGroup> ? TGroup :
|
||||
T extends BigIntProxy ? [bigint] : never
|
||||
}
|
||||
|
||||
export type KAttrMap2<T> = { [key: string]: string } & {
|
||||
__type?: T extends TypeForKItem ? KTypeConvert<T> : never
|
||||
__count?: T extends TypeForKArray ? number : never
|
||||
}
|
||||
|
||||
export function ITEM2<T>(ktype: KTypeConvert<T>, value: T, attr?: KAttrMap2<T>): KITEM2<T> {
|
||||
// let result
|
||||
// if (value instanceof NumberGroup && IsNumberGroupKType(ktype)) {
|
||||
// result = K.ITEM(<KTypeConvert<T & NumberGroup>>ktype, value.value, attr)
|
||||
// } else if (Array.isArray(value) && IsNumericKType(ktype)) {
|
||||
// result = K.ARRAY(<KTypeConvert<T & number[]>>ktype, <any>value, <any>attr)
|
||||
// } else if (value instanceof BufferArray && IsNumericKType(ktype)) {
|
||||
// result = K.ARRAY(<KTypeConvert<T & BufferArray>>ktype, value.value, attr)
|
||||
// } else if (typeof value != "object" && typeof value != "function") {
|
||||
// result = K.ITEM(<any>ktype, <any>value, attr)
|
||||
// } else {
|
||||
// Object.assign(result, value, { ["@attr"]: attr })
|
||||
// result["@attr"].__type = ktype
|
||||
// }
|
||||
|
||||
// return <KITEM2<T>>result
|
||||
let result = <KITEM2<T>>{}
|
||||
result["@attr"] = Object.assign({}, attr, (!isNumberGroupKType(ktype) && isNumericKType(ktype) && Array.isArray(value)) ? { __type: ktype, __count: (<any[]>value).length } : { __type: ktype })
|
||||
|
||||
if ((ktype == "bool") && (typeof value == "boolean")) {
|
||||
result["@content"] = <any>(value ? [1] : [0])
|
||||
} else if ((ktype == "bin") && value instanceof Buffer) {
|
||||
result = <any>K.ITEM("bin", value, result["@attr"])
|
||||
} else if (((ktype == "s8") || (ktype == "u8")) && isBufferArray(value)) {
|
||||
result["@content"] = <any>value["@bufferArrayValue"].toJSON()
|
||||
result["@attr"].__count = <any>value["@bufferArrayValue"].byteLength
|
||||
} else if (isNumericKType(ktype) && !Array.isArray(value)) {
|
||||
result["@content"] = <any>[value]
|
||||
} else if (isNumberGroupKType(ktype) && isNumberGroup(value)) {
|
||||
result["@content"] = <any>value["@numberGroupValue"]
|
||||
} else if (isBigIntProxy(value)) {
|
||||
result["@content"] = <any>BigInt(value["@serializedBigInt"])
|
||||
}
|
||||
else {
|
||||
result["@content"] = <any>value
|
||||
}
|
||||
if (isKIntType(ktype) && Array.isArray(result["@content"])) for (let i = 0; i < result["@content"].length; i++) (<number[]>result["@content"])[i] = Math.trunc(result["@content"][i])
|
||||
return result
|
||||
}
|
||||
|
||||
export type KObjectMappingRecord<T> = { [K in KKey<T>]: T[K] extends TypeForKItem ? KObjectMappingElementInfer<T[K]> : KObjectMappingRecord<T[K]> } & KObjectMappingElementInfer<T>
|
||||
export interface KObjectMappingElement<T = any, TKType extends KTypeExtended = KTypeExtended> {
|
||||
$type?: TKType,
|
||||
$targetKey?: string,
|
||||
$convert?: (source: T) => T
|
||||
$convertBack?: (target: T) => T
|
||||
$fallbackValue?: TKType extends "kignore" ? T : never
|
||||
$defaultValue?: T
|
||||
}
|
||||
type KObjectMappingElementInfer<T> = KObjectMappingElement<T, (KTypeConvert<T> extends KType ? KTypeConvert<T> : never) | never | "kignore">
|
||||
|
||||
export type KAttrRecord<T> = { [K in keyof T]?: T extends TypeForKItem ? KAttrMap2<T[K]> : KAttrRecord<T[K]> } & { selfAttr?: KAttrMap2<T> }
|
||||
|
||||
export function getCollectionMappingElement<TCollection extends ICollection<any>>(collectionName: TCollection extends ICollection<infer TName> ? TName : never): KObjectMappingElement<TCollection extends ICollection<infer TName> ? TName : unknown, "kignore"> {
|
||||
return ignoreme("collection", collectionName)
|
||||
}
|
||||
|
||||
function isKType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s8", "u8", "s16", "u16", "s32", "u32", "time", "ip4", "float", "double", "bool", "s64", "u64", "2s8", "2u8", "2s16", "2u16", "2s32", "2u32", "2f", "2d", "3s8", "3u8", "3s16", "3u16", "3s32", "3u32", "3f", "3d", "4s8", "4u8", "4s16", "4u16", "4s32", "4u32", "4f", "4d", "2b", "3b", "4b", "vb", "2s64", "2u64", "3s64", "3u64", "4s64", "4u64", "vs8", "vu8", "vs16", "vu16", "str", "bin"].includes(type)
|
||||
}
|
||||
function isKIntType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s8", "u8", "s16", "u16", "s32", "u32", "2s8", "2u8", "2s16", "2u16", "2s32", "2u32", "3s8", "3u8", "3s16", "3u16", "3s32", "3u32", "4s8", "4u8", "4s16", "4u16", "4s32", "4u32", "2b", "3b", "4b", "vb", "vs8", "vu8", "vs16", "vu16"].includes(type)
|
||||
}
|
||||
function isKBigIntType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s64", "u64"].includes(type)
|
||||
}
|
||||
function isNumericKType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["s8", "u8", "s16", "u16", "s32", "u32", "time", "ip4", "float", "double", "bool", "s64", "u64"].includes(type)
|
||||
}
|
||||
function isNumberGroupKType<TType>(type: TType): boolean {
|
||||
return (typeof (type) == "string") && ["2s8", "2u8", "2s16", "2u16", "2s32", "2u32", "2f", "2d", "3s8", "3u8", "3s16", "3u16", "3s32", "3u32", "3f", "3d", "4s8", "4u8", "4s16", "4u16", "4s32", "4u32", "4f", "4d", "2b", "3b", "4b", "vb", "2s64", "2u64", "3s64", "3u64", "4s64", "4u64", "vs8", "vu8", "vs16", "vu16"].includes(type)
|
||||
}
|
||||
function isNumericKey(k: any): boolean {
|
||||
return (typeof k == "number") || (parseInt(k).toString() == k)
|
||||
}
|
||||
function increaseNumericKey<T>(k: T, step: number = 1): T {
|
||||
return (typeof k == "number") ? <T><unknown>(k + step) : (typeof k == "string" && parseInt(k).toString() == k) ? <T><unknown>(parseInt(k) + step) : k
|
||||
}
|
||||
function isEmptyKObject(o: object): boolean {
|
||||
return (Object.keys(o).length == 0) || ((Object.keys(o).length == 1) && (o["@attr"] != null))
|
||||
}
|
||||
function isKMapRecordReservedKey(key: string): boolean {
|
||||
return ["$type", "$targetKey", "$convert", "$convertBack", "$fallbackValue", "$defaultValue"].includes(key)
|
||||
}
|
||||
function isKArray<T>(data: KITEM2<T>): boolean {
|
||||
return (data["@attr"] != null) && (data["@attr"].__count != null)
|
||||
}
|
||||
|
||||
export function appendMappingElement<T>(map: KObjectMappingRecord<T>, element: KObjectMappingElementInfer<T>): KObjectMappingRecord<T> {
|
||||
let result = <KObjectMappingRecord<T>>{}
|
||||
Object.assign(result, map, element)
|
||||
return result
|
||||
}
|
||||
|
||||
export function mapKObject<T>(data: T, kMapRecord: KObjectMappingRecord<T>, kAttrRecord: KAttrRecord<T> = <KAttrRecord<T>>{}): KITEM2<T> {
|
||||
if (data == null) return <KITEM2<T>>{}
|
||||
let result: KITEM2<T> = <any>(((0 in data) && data instanceof Object) ? [] : {})
|
||||
if (kAttrRecord.selfAttr != null) result["@attr"] = kAttrRecord.selfAttr
|
||||
|
||||
if (data instanceof Object) {
|
||||
for (let __k in data) {
|
||||
let k: keyof T = __k
|
||||
let mapK: keyof T = __k
|
||||
let attrK: keyof T = __k
|
||||
if (!(k in kMapRecord) && isNumericKey(k)) {
|
||||
for (let i = parseInt(<string>k) - 1; i >= 0; i--) if (kMapRecord[i]) {
|
||||
mapK = <keyof T>i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!(k in kAttrRecord) && isNumericKey(k)) {
|
||||
for (let i = parseInt(<string>k) - 1; i >= 0; i--) if (kAttrRecord[i]) {
|
||||
attrK = <keyof T>i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (mapK in kMapRecord) {
|
||||
let target = <KITEM2<T>[keyof T]>{}
|
||||
let targetMap = kMapRecord[<KKey<T>>mapK]
|
||||
let targetKey: keyof T = (targetMap.$targetKey != null) ? <keyof T>targetMap.$targetKey : k
|
||||
let targetValue = (targetMap.$convert != null) ? <KTypeConvertBack<KTypeConvert<T[keyof T]>>>targetMap.$convert(<any>data[k]) : data[k]
|
||||
let targetAttr = kAttrRecord[attrK]
|
||||
if (targetMap.$type) {
|
||||
let tt = targetMap.$type
|
||||
if (tt == "kignore") continue
|
||||
target["@attr"] = <any>Object.assign({}, targetAttr, (!isNumberGroupKType(tt) && isNumericKType(tt) && Array.isArray(data[k]) && Array.isArray(targetValue)) ? { __type: tt, __count: (<any[]>targetValue).length } : { __type: tt })
|
||||
|
||||
if ((tt == "bool") && (typeof targetValue == "boolean")) {
|
||||
target["@content"] = <any>(targetValue ? [1] : [0])
|
||||
} else if ((tt == "bin") && targetValue instanceof Buffer) {
|
||||
target = <any>K.ITEM("bin", targetValue, target["@attr"])
|
||||
} else if (((tt == "s8") || (tt == "u8")) && isBufferArray(targetValue)) {
|
||||
target["@content"] = <any>targetValue["@bufferArrayValue"]
|
||||
} else if (isNumericKType(tt) && !Array.isArray(targetValue)) {
|
||||
target["@content"] = <any>[targetValue]
|
||||
} else if (isNumberGroupKType(tt) && isNumberGroup(targetValue)) {
|
||||
target["@content"] = <any>targetValue["@numberGroupValue"]
|
||||
} else if (isBufferArray(targetValue)) {
|
||||
target["@content"] = <any>targetValue["@bufferArrayValue"].toJSON()
|
||||
target["@attr"].__count = <any>targetValue["@bufferArrayValue"].byteLength
|
||||
} else if (isBigIntProxy(targetValue)) {
|
||||
target["@content"] = <any>BigInt(targetValue["@serializedBigInt"])
|
||||
} else {
|
||||
target["@content"] = <any>targetValue
|
||||
}
|
||||
if (isKIntType(tt) && Array.isArray(target["@content"])) for (let i = 0; i < target["@content"].length; i++) (<number[]>target["@content"])[i] = Math.trunc(target["@content"][i])
|
||||
} else {
|
||||
target = <any>mapKObject(<T[keyof T]>targetValue, <KObjectMappingRecord<T[keyof T]>><unknown>targetMap, <KAttrRecord<T[keyof T]>>targetAttr)
|
||||
}
|
||||
result[targetKey] = target
|
||||
}
|
||||
}
|
||||
} else result = ITEM2<T>(<KTypeConvert<T>>kAttrRecord.selfAttr.$type, data, kAttrRecord.selfAttr)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export type MapBackResult<T> = {
|
||||
data: T,
|
||||
attr?: KAttrRecord<T>
|
||||
}
|
||||
export function mapBackKObject<T extends object>(data: KITEM2<T>, kMapRecord?: KObjectMappingRecord<T>): MapBackResult<T> {
|
||||
if (kMapRecord == null) {
|
||||
if (data["@content"] || data["@attr"]) return { data: <any>data["@content"], attr: <any>data["@attr"] }
|
||||
else return { data: <T>data }
|
||||
}
|
||||
let result: T = <T>((Array.isArray(data) || 0 in kMapRecord) ? [] : {})
|
||||
let resultAttr: KAttrRecord<T> = <any>{ selfAttr: data["@attr"] ? data["@attr"] : null }
|
||||
|
||||
for (let __k in kMapRecord) {
|
||||
if (isKMapRecordReservedKey(__k)) continue
|
||||
let k = <keyof T>__k
|
||||
let preservK = <keyof T>__k
|
||||
do {
|
||||
let targetMap = kMapRecord[<KKey<T>>preservK]
|
||||
let targetKey = <keyof T>(targetMap.$targetKey ? targetMap.$targetKey : k)
|
||||
let doOnceFlag = (isNumericKey(targetKey) && (data[targetKey] == null) && !isEmptyKObject(data))
|
||||
let targetValue = <KITEM2<T>[keyof T]>(doOnceFlag ? data : data[targetKey])
|
||||
|
||||
if (targetMap.$type == "kignore") {
|
||||
result[k] = targetMap.$fallbackValue
|
||||
if ((targetValue != null) && (targetValue["@attr"] != null)) resultAttr[k] = <KAttrRecord<T>[keyof T]>{ selfAttr: targetValue["@attr"] }
|
||||
continue
|
||||
}
|
||||
|
||||
if (targetValue == null) {
|
||||
if (targetMap.$convertBack != null) result[k] = targetMap.$convertBack(<any>null)
|
||||
continue
|
||||
}
|
||||
|
||||
if (targetValue["@attr"] != null) {
|
||||
let targetAttr: KAttrMap2<T[keyof T]> = targetValue["@attr"]
|
||||
let targetResult
|
||||
|
||||
if (targetAttr.__type != null) { // KITEM
|
||||
targetResult = targetValue["@content"]
|
||||
if (isNumberGroupKType(targetAttr.__type)) { // KITEM2<NumberGroup>
|
||||
// TODO: bigint number group
|
||||
targetResult = NumberGroup(targetResult)
|
||||
} else if (targetAttr.__type == "bin") { // KITEM<"bin">
|
||||
targetResult = targetResult
|
||||
} else if ((targetAttr.__type == "s8" || targetAttr.__type == "u8") && (targetResult?.type == "Buffer") && Array.isArray(targetResult?.data)) { // KITEM2<BufferArray>
|
||||
targetResult = BufferArray(Buffer.from(<number[]>targetResult.data))
|
||||
} else if (targetAttr.__type == "bool") { // KITEM<"bool">
|
||||
targetResult = targetResult[0] == 1 ? true : false
|
||||
} else if (Array.isArray(targetResult) && (targetAttr.__count == null) && isNumericKType(targetAttr.__type)) { // KITEM<KNumberType>
|
||||
targetResult = ((targetAttr.__type == "s64") || (targetAttr.__type == "u64")) ? BigIntProxy(BigInt(targetResult[0])) : targetResult[0]
|
||||
}
|
||||
result[k] = (targetMap.$convertBack != null) ? targetMap.$convertBack(<any>targetResult) : targetResult
|
||||
} else { // KObject
|
||||
targetResult = (targetMap.$convertBack != null) ? targetMap.$convertBack(<any>targetValue) : targetValue;
|
||||
let partial = mapBackKObject<T[keyof T] & object>(targetResult, <any>targetMap)
|
||||
result[k] = partial.data
|
||||
resultAttr[k] = <any>partial.attr
|
||||
}
|
||||
} else { // KObject
|
||||
let targetResult = (targetMap.$convertBack != null) ? targetMap.$convertBack(<any>targetValue) : targetValue;
|
||||
let partial = <any>mapBackKObject<T[keyof T] & object>(<any>targetResult, <any>targetMap)
|
||||
result[k] = partial.data
|
||||
resultAttr[k] = <any>partial.attr
|
||||
}
|
||||
k = increaseNumericKey(k)
|
||||
if (doOnceFlag || (isNumericKey(k) && (data[<keyof T>(targetMap.$targetKey ? targetMap.$targetKey : k)] == null))) break
|
||||
} while (isNumericKey(k) && !(k in kMapRecord))
|
||||
}
|
||||
return { data: result, attr: resultAttr }
|
||||
}
|
||||
|
||||
export function s8me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "s8"> {
|
||||
return {
|
||||
$type: "s8",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u8me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "u8"> {
|
||||
return {
|
||||
$type: "u8",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function s16me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "s16"> {
|
||||
return {
|
||||
$type: "s16",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u16me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "u16"> {
|
||||
return {
|
||||
$type: "u16",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function s32me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "s32"> {
|
||||
return {
|
||||
$type: "s32",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u32me<T extends number | number[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "u32"> {
|
||||
return {
|
||||
$type: "u32",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function s64me(targetKey?: string, defaultValue?: bigint | BigIntProxy, convert?: (source: bigint | BigIntProxy) => bigint | BigIntProxy, convertBack?: (target: bigint | BigIntProxy) => bigint | BigIntProxy): KObjectMappingElement<bigint | BigIntProxy, "s64"> {
|
||||
return {
|
||||
$type: "s64",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function u64me(targetKey?: string, defaultValue?: bigint | BigIntProxy, convert?: (source: bigint | BigIntProxy) => bigint | BigIntProxy, convertBack?: (target: bigint | BigIntProxy) => bigint | BigIntProxy): KObjectMappingElement<bigint | BigIntProxy, "u64"> {
|
||||
return {
|
||||
$type: "u64",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
export function boolme<T extends boolean | boolean[]>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, "bool"> {
|
||||
return {
|
||||
$type: "bool",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export function strme<TName extends string>(targetKey?: string, defaultValue?: TName, convert?: (source: TName) => TName, convertBack?: (target: TName) => TName): KObjectMappingElement<TName, "str"> {
|
||||
return {
|
||||
$type: "str",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
export function binme(targetKey?: string, defaultValue?: Buffer, convert?: (source: Buffer) => Buffer, convertBack?: (target: Buffer) => Buffer): KObjectMappingElement<Buffer, "bin"> {
|
||||
return {
|
||||
$type: "bin",
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
export function ignoreme<T = any>(targetKey?: string, fallbackValue?: T): KObjectMappingElement<T, "kignore"> {
|
||||
return {
|
||||
$type: "kignore",
|
||||
$fallbackValue: fallbackValue
|
||||
}
|
||||
}
|
||||
export function me<T extends object>(targetKey?: string, defaultValue?: T, convert?: (source: T) => T, convertBack?: (target: T) => T): KObjectMappingElement<T, null> {
|
||||
return {
|
||||
$targetKey: targetKey,
|
||||
$convert: convert,
|
||||
$convertBack: convertBack,
|
||||
$defaultValue: defaultValue
|
||||
}
|
||||
}
|
||||
export const colme = getCollectionMappingElement
|
||||
export const appendme = appendMappingElement
|
||||
export const mapK = mapKObject
|
||||
export const bacK = mapBackKObject
|
||||
|
||||
export function fromMap<T>(map: KObjectMappingRecord<T>): T {
|
||||
let result = <T>{}
|
||||
if (map.$type == "kignore") return map.$fallbackValue
|
||||
if (map.$defaultValue != null) return map.$defaultValue
|
||||
if (map.$type != null) {
|
||||
if (isNumericKType(map.$type)) {
|
||||
if (map.$type == "bool") return <any>false
|
||||
else return <any>0
|
||||
} else if (isKBigIntType(map.$type)) return <any>BigInt(0)
|
||||
else if (isNumberGroupKType(map.$type)) return <any>NumberGroup([0])
|
||||
else if (map.$type == "str") return <any>""
|
||||
|
||||
else return null
|
||||
}
|
||||
for (let k in map) {
|
||||
if (isKMapRecordReservedKey(k)) continue
|
||||
let value = fromMap(map[k])
|
||||
if (value != null) result[k] = value
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export type KM<T> = KObjectMappingRecord<T>
|
||||
4
bst@asphyxia/utility/type.ts
Normal file
4
bst@asphyxia/utility/type.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export type FixedSizeArray<T, TSize extends number> = [T, ...T[]] & { readonly length: TSize }
|
||||
export function fillArray<T, TSize extends number>(size: TSize, fillValue: T): FixedSizeArray<T, TSize> {
|
||||
return <any>Array(size).fill(fillValue)
|
||||
}
|
||||
67
bst@asphyxia/utility/utility_functions.ts
Normal file
67
bst@asphyxia/utility/utility_functions.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
export function toFullWidth(s: string): string {
|
||||
let resultCharCodes: number[] = []
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
let cc = s.charCodeAt(i)
|
||||
if ((cc >= 33) && (cc <= 126)) resultCharCodes.push(cc + 65281 - 33)
|
||||
else if (cc == 32) resultCharCodes.push(12288) // Full-width space
|
||||
else resultCharCodes.push(cc)
|
||||
}
|
||||
return String.fromCharCode(...resultCharCodes)
|
||||
}
|
||||
export function toHalfWidth(s: string): string {
|
||||
let resultCharCodes: number[] = []
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
let cc = s.charCodeAt(i)
|
||||
if ((cc >= 65281) && (cc <= 65374)) resultCharCodes.push(cc - 65281 + 33)
|
||||
else if (cc == 12288) resultCharCodes.push(32) // Full-width space
|
||||
else resultCharCodes.push(cc)
|
||||
}
|
||||
return String.fromCharCode(...resultCharCodes)
|
||||
}
|
||||
export function isToday(st: bigint): boolean {
|
||||
let now = new Date()
|
||||
let today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
let tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
|
||||
return (st >= (today.valueOf())) && (st < (tomorrow.valueOf()))
|
||||
}
|
||||
export async function log(data: any, file?: string) {
|
||||
if (file == null) file = "./log.txt"
|
||||
let s = IO.Exists(file) ? await IO.ReadFile(file, "") : ""
|
||||
if (typeof data == "string") s += data + "\n"
|
||||
else {
|
||||
let n = ""
|
||||
try {
|
||||
n = JSON.stringify(data)
|
||||
} catch { }
|
||||
s += n + "\n"
|
||||
}
|
||||
await IO.WriteFile(file, s)
|
||||
}
|
||||
export function base64ToBuffer(str: string, size?: number): Buffer {
|
||||
if (size != null) {
|
||||
let rem = size - Math.trunc(size / 3) * 3
|
||||
str = str.replace("=", "A").replace("=", "A").padEnd(Math.trunc(size / 3) * 4 + rem + 1, "A")
|
||||
if (rem == 1) str += "=="
|
||||
else if (rem == 2) str += "="
|
||||
let result = Buffer.alloc(size, str, "base64")
|
||||
return result
|
||||
}
|
||||
else return Buffer.from(str, "base64")
|
||||
}
|
||||
export function bufferToBase64(buffer: Buffer, isTrimZero: boolean = true): string {
|
||||
if (isTrimZero) for (let i = buffer.length - 1; i >= 0; i--) if (buffer.readInt8(i) != 0) return buffer.toString("base64", 0, i + 1)
|
||||
return buffer.toString("base64")
|
||||
}
|
||||
export function isHigherVersion(left: string, right: string): boolean {
|
||||
let splitedLeft = left.split(".")
|
||||
let splitedRight = right.split(".")
|
||||
|
||||
if (parseInt(splitedLeft[0]) < parseInt(splitedRight[0])) return true
|
||||
else if (parseInt(splitedLeft[0]) == parseInt(splitedRight[0])) {
|
||||
if (parseInt(splitedLeft[1]) < parseInt(splitedRight[1])) return true
|
||||
else if (parseInt(splitedLeft[1]) == parseInt(splitedRight[1])) {
|
||||
if (parseInt(splitedLeft[2]) < parseInt(splitedRight[2])) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
243
bst@asphyxia/webui/css/webui_util.css
Normal file
243
bst@asphyxia/webui/css/webui_util.css
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
#tab-content, .tab-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tab-content.is-active, .tab-content.is-active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
tr#tab-content.is-active, tr.tab-content.is-active {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
#tabs li.disabled a {
|
||||
background-color: #c0c0c0;
|
||||
border-color: #c0c0c0;
|
||||
color: #7f7f7f;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
#form-pagination ul.pagination-list {
|
||||
margin: 0!important;
|
||||
}
|
||||
.pagination-link, .pagination-next, .pagination-previous {
|
||||
border-color: transparent;
|
||||
transition: .2s linear;
|
||||
}
|
||||
.pagination-next, .pagination-previous {
|
||||
color: #209CEE;
|
||||
}
|
||||
.pagination-next:not([disabled]):hover, .pagination-previous:not([disabled]):hover {
|
||||
color: #118fe4;
|
||||
}
|
||||
/* Set all link color to Asphyxia CORE blue */
|
||||
::selection {
|
||||
color: white;
|
||||
background-color: #209CEE;
|
||||
}
|
||||
a {
|
||||
color: #209CEE;
|
||||
}
|
||||
.tabs.is-toggle li.is-active a {
|
||||
background-color: #209CEE;
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.tabs li.is-active a {
|
||||
color: #209CEE;
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.pagination-link.is-current {
|
||||
background-color: #209CEE;
|
||||
border-color: #209CEE;
|
||||
cursor: default;
|
||||
}
|
||||
.select:not(.is-multiple):not(.is-loading):after {
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.select select:active, .select select:focus {
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.button.is-link {
|
||||
background-color: #209CEE;
|
||||
}
|
||||
.button.is-link.is-active, .button.is-link:active, .button.is-link.is-hovered, .button.is-link:hover {
|
||||
background-color: #118fe4;
|
||||
}
|
||||
.input:active, .input:focus {
|
||||
border-color: #209CEE;
|
||||
}
|
||||
.table tr.is-selected {
|
||||
background-color: #209CEE;
|
||||
}
|
||||
|
||||
#card-content.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
#card-content {
|
||||
display: block;
|
||||
}
|
||||
.marquee-label {
|
||||
display: inline-block;
|
||||
}
|
||||
.marquee-label-container {
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* from Bulma */
|
||||
.button.is-danger.is-light {
|
||||
background-color: #feecf0;
|
||||
color: #cc0f35;
|
||||
}
|
||||
.button.is-link.is-light {
|
||||
background-color: #edf8ff;
|
||||
color: #209CEE;
|
||||
}
|
||||
.button.is-danger.is-light.is-hovered, .button.is-danger.is-light:hover {
|
||||
background-color: #fde0e6;
|
||||
color: #cc0f35;
|
||||
}
|
||||
.button.is-link.is-light.is-hovered, .button.is-link.is-light:hover {
|
||||
background-color: #e0f1fc;
|
||||
color: #209CEE;
|
||||
}
|
||||
.tag.is-link.is-light {
|
||||
background-color: #edf8ff;
|
||||
color: #0D7DC6;
|
||||
}
|
||||
.tag.is-link.is-light:hover {
|
||||
background-color: #209CEE;
|
||||
color: white;
|
||||
}
|
||||
.tag.is-delete:hover {
|
||||
background-color: #FF3860!important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.pagination {
|
||||
flex-wrap: nowrap;
|
||||
justify-content: left;
|
||||
}
|
||||
}
|
||||
.pagination-list {
|
||||
flex-wrap: nowrap;
|
||||
list-style: none!important;
|
||||
margin-top: 0.25em!important;
|
||||
margin-bottom: 0.25em!important;
|
||||
}
|
||||
|
||||
.content li + li {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.one-quarter#forwide, .one-third#forwide {
|
||||
display: block;
|
||||
min-width: 100px;
|
||||
}
|
||||
.one-quarter#fornarrow, .one-third#fornarrow {
|
||||
display: none;
|
||||
min-width: 50px;
|
||||
}
|
||||
@media only screen and (max-width: 1023px) {
|
||||
.one-quarter#forwide {
|
||||
display: none;
|
||||
}
|
||||
.one-quarter#fornarrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 700px) {
|
||||
.one-third#forwide {
|
||||
display: none;
|
||||
}
|
||||
.one-third#fornarrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes notification-fadeout {
|
||||
0% {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
}
|
||||
80% {
|
||||
opacity: 1;
|
||||
display: block;
|
||||
}
|
||||
99.99% {
|
||||
opacity: 0;
|
||||
display: block;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.notification {
|
||||
animation: notification-fadeout 8s forwards;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.notification:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.modal {
|
||||
padding-bottom: 13px;
|
||||
}
|
||||
@media screen and (max-width:1024px) {
|
||||
.modal {
|
||||
transition: padding-left .2s ease-in-out 50ms;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width:1023px) {
|
||||
.modal {
|
||||
padding-left: 256px;
|
||||
transition: padding-left .2s ease-in-out 50ms;
|
||||
}
|
||||
}
|
||||
.tag {
|
||||
transition: linear .2s;
|
||||
}
|
||||
.tags .tag:not(:last-child) {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.modal table tr {
|
||||
border: solid #dbdbdb;
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
.modal table tbody tr:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.hidden-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
.hidden-x-wrapper {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.hidden-y-wrapper {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
.scrolling-wrapper {
|
||||
overflow: auto;
|
||||
}
|
||||
.scrolling-x-wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
.scrolling-y-wrapper {
|
||||
overflow-y: auto;
|
||||
}
|
||||
a.pagination-previous {
|
||||
overflow: hidden;
|
||||
}
|
||||
a.pagination-next {
|
||||
overflow: hidden;
|
||||
}
|
||||
.button.checkbox, .button.checkbox .checkmark {
|
||||
transition: linear .2s;
|
||||
}
|
||||
618
bst@asphyxia/webui/js/webui_util.js
Normal file
618
bst@asphyxia/webui/js/webui_util.js
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
function initializePaginatedContent() {
|
||||
let containers = document.querySelectorAll(".paginated-container")
|
||||
|
||||
for (let container of containers) {
|
||||
let pageSizeInput = container.querySelector("input.page-size")
|
||||
let paginations = container.querySelectorAll(".pagination")
|
||||
let contents = container.querySelectorAll(".paginated-content")
|
||||
let group = container.getAttribute("pagination-group")
|
||||
let flags = { isFirst: true }
|
||||
let refreshEllipsis = (param) => {
|
||||
if (flags.isFirst) return
|
||||
let maxWidth = container.offsetWidth / 2
|
||||
for (let pagination of paginations) {
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
if (buttons.childElementCount == 0) return
|
||||
let show = (index) => buttons.querySelector("li[tab-index=\"" + index + "\"]").style.display = "block"
|
||||
let hide = (index) => buttons.querySelector("li[tab-index=\"" + index + "\"]").style.display = "none"
|
||||
let previousButton = pagination.querySelector("a.pagination-previous")
|
||||
let nextButton = pagination.querySelector("a.pagination-next")
|
||||
let leftEllipsis = buttons.querySelector("li.ellipsis-left")
|
||||
let rightEllipsis = buttons.querySelector("li.ellipsis-right")
|
||||
let width = buttons.firstChild.offsetWidth.toString()
|
||||
leftEllipsis.style.width = width + "px"
|
||||
rightEllipsis.style.width = width + "px"
|
||||
let count = buttons.childElementCount - 2
|
||||
let maxButtonCount = Math.max((buttons.firstChild.offsetWidth == 0) ? 5 : Math.trunc(maxWidth / buttons.firstChild.offsetWidth), 5)
|
||||
let current = (param instanceof HTMLElement) ? param : buttons.querySelector("li.is-active")
|
||||
let index = parseInt((current == null) ? 0 : current.getAttribute("tab-index"))
|
||||
if (index == 0) previousButton.setAttribute("disabled", "")
|
||||
else previousButton.removeAttribute("disabled")
|
||||
if (index == (count - 1)) nextButton.setAttribute("disabled", "")
|
||||
else nextButton.removeAttribute("disabled")
|
||||
if (count <= maxButtonCount) {
|
||||
for (let i = 0; i < count; i++) buttons.querySelector("li[tab-index=\"" + i + "\"]").style.display = "block"
|
||||
leftEllipsis.style.display = "none"
|
||||
rightEllipsis.style.display = "none"
|
||||
} else {
|
||||
maxButtonCount = Math.trunc((maxButtonCount - 1) / 2) * 2 + 1
|
||||
let maxSurroundingButtonCount = (maxButtonCount - 5) / 2
|
||||
let maxNoEllipsisIndex = maxButtonCount - 2 - maxSurroundingButtonCount - 1
|
||||
|
||||
if (index <= maxNoEllipsisIndex) {
|
||||
for (let i = 0; i <= (maxNoEllipsisIndex + maxSurroundingButtonCount); i++) show(i)
|
||||
for (let i = (maxNoEllipsisIndex + maxSurroundingButtonCount) + 1; i < count - 1; i++) hide(i)
|
||||
show(count - 1)
|
||||
leftEllipsis.style.display = "none"
|
||||
rightEllipsis.style.display = "block"
|
||||
} else if (index >= (count - maxNoEllipsisIndex - 1)) {
|
||||
for (let i = 1; i < (count - maxNoEllipsisIndex - maxSurroundingButtonCount - 1); i++) hide(i)
|
||||
for (let i = (count - maxNoEllipsisIndex - maxSurroundingButtonCount - 1); i < count; i++) show(i)
|
||||
show(0)
|
||||
leftEllipsis.style.display = "block"
|
||||
rightEllipsis.style.display = "none"
|
||||
} else {
|
||||
for (let i = 1; i < (index - maxSurroundingButtonCount); i++) hide(i)
|
||||
for (let i = (index - maxSurroundingButtonCount); i <= (index + maxSurroundingButtonCount); i++) show(i)
|
||||
for (let i = (index + maxSurroundingButtonCount) + 1; i < count - 1; i++) hide(i)
|
||||
show(0)
|
||||
show(count - 1)
|
||||
leftEllipsis.style.display = "block"
|
||||
rightEllipsis.style.display = "block"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let refresh = () => {
|
||||
if ((pageSizeInput == null) || (parseInt(pageSizeInput.value) <= 0)) {
|
||||
for (let pagination of paginations) pagination.style.display = "none"
|
||||
return
|
||||
}
|
||||
let pageSize = parseInt(pageSizeInput.value)
|
||||
let pageCount = Math.ceil(contents.length / pageSize)
|
||||
if (!flags.isFirst && (flags.pageSize == pageSize) && (flags.pageCount == pageCount)) return
|
||||
for (let pagination of paginations) {
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
buttons.innerHTML = ""
|
||||
buttons.id = "tabs"
|
||||
}
|
||||
for (let i = 0; i < pageCount; i++) {
|
||||
for (let j = i * pageSize; j < (i + 1) * pageSize; j++) {
|
||||
if (contents[j] == null) break
|
||||
contents[j].classList.add("tab-content")
|
||||
contents[j].setAttribute("tab-group", group)
|
||||
contents[j].setAttribute("tab-index", i)
|
||||
if ((i == 0) && (flags.isFirst || (flags.pageCount != pageCount))) contents[j].classList.add("is-active")
|
||||
if (j == ((i + 1) * pageSize - 1)) for (let td of contents[j].querySelectorAll("td")) td.style.borderBottom = "0"
|
||||
}
|
||||
if (pageCount > 1) for (let pagination of paginations) {
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
let a = document.createElement("a")
|
||||
a.classList.add("pagination-link")
|
||||
a.innerText = i + 1
|
||||
let li = document.createElement("li")
|
||||
li.appendChild(a)
|
||||
if ((i == 0) && (flags.isFirst || (flags.pageCount != pageCount))) {
|
||||
li.classList.add("is-active")
|
||||
a.classList.add("is-current")
|
||||
}
|
||||
li.setAttribute("tab-group", group)
|
||||
li.setAttribute("tab-index", i)
|
||||
buttons.appendChild(li)
|
||||
li.addEventListener("click", () => {
|
||||
refreshEllipsis(li)
|
||||
})
|
||||
}
|
||||
}
|
||||
if (pageCount > 1) for (let pagination of paginations) {
|
||||
pagination.style.display = "flex"
|
||||
let buttons = pagination.querySelector("ul.pagination-list")
|
||||
let leftEllipsis = document.createElement("li")
|
||||
leftEllipsis.style.display = "none"
|
||||
leftEllipsis.classList.add("ellipsis-left", "ignore")
|
||||
leftEllipsis.innerHTML = "<span class=\"pagination-ellipsis\">…</span>"
|
||||
let rightEllipsis = document.createElement("li")
|
||||
rightEllipsis.style.display = "none"
|
||||
rightEllipsis.classList.add("ellipsis-right", "ignore")
|
||||
rightEllipsis.innerHTML = "<span class=\"pagination-ellipsis\">…</span>"
|
||||
buttons.firstChild.after(leftEllipsis)
|
||||
buttons.lastChild.before(rightEllipsis)
|
||||
|
||||
let previousButton = pagination.querySelector("a.pagination-previous")
|
||||
let nextButton = pagination.querySelector("a.pagination-next")
|
||||
previousButton.addEventListener("click", () => {
|
||||
let current = buttons.querySelector("li.is-active")
|
||||
let index = parseInt(current.getAttribute("tab-index"))
|
||||
if (index <= 0) return
|
||||
let prev = buttons.querySelector("li[tab-index=\"" + (index - 1) + "\"]")
|
||||
prev.dispatchEvent(new Event("click"))
|
||||
})
|
||||
nextButton.addEventListener("click", () => {
|
||||
let current = buttons.querySelector("li.is-active")
|
||||
let index = parseInt(current.getAttribute("tab-index"))
|
||||
if (index >= (buttons.childElementCount - 3)) return // includes left & right ellipsis
|
||||
let next = buttons.querySelector("li[tab-index=\"" + (index + 1) + "\"]")
|
||||
next.dispatchEvent(new Event("click"))
|
||||
})
|
||||
} else for (let pagination of paginations) pagination.style.display = "none"
|
||||
flags.pageCount = pageCount
|
||||
flags.pageSize = pageSize
|
||||
flags.isFirst = false
|
||||
}
|
||||
refresh()
|
||||
pageSizeInput.addEventListener("change", refresh)
|
||||
let o = new ResizeObserver(refreshEllipsis)
|
||||
o.observe(container)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTabs() {
|
||||
let tabs = document.querySelectorAll("#tabs li")
|
||||
let tabContents = document.querySelectorAll("#tab-content, .tab-content")
|
||||
let updateActiveTab = (tabGroup, tabIndex) => {
|
||||
for (let t of tabs) if (t && (t.getAttribute("tab-group") == tabGroup)) {
|
||||
if (t.getAttribute("tab-index") != tabIndex) {
|
||||
t.classList.remove("is-active")
|
||||
for (let a of t.querySelectorAll("a")) a.classList.remove("is-current")
|
||||
} else {
|
||||
t.classList.add("is-active")
|
||||
for (let a of t.querySelectorAll("a")) a.classList.add("is-current")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let updateActiveContent = (tabGroup, tabIndex) => {
|
||||
for (let item of tabContents) {
|
||||
let group = item.getAttribute("tab-group")
|
||||
let index = item.getAttribute("tab-index")
|
||||
if (item && (group == tabGroup)) item.classList.remove("is-active")
|
||||
if ((index == tabIndex) && (group == tabGroup)) item.classList.add("is-active")
|
||||
}
|
||||
}
|
||||
for (let t of tabs) {
|
||||
if (!t.classList.contains("disabled") && !t.classList.contains("ignore")) t.addEventListener("click", () => {
|
||||
let group = t.getAttribute("tab-group")
|
||||
let index = t.getAttribute("tab-index")
|
||||
updateActiveTab(group, index)
|
||||
updateActiveContent(group, index)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeToggles() {
|
||||
let toggles = document.querySelectorAll(".card-header .card-toggle")
|
||||
let contents = document.querySelectorAll(".card-content")
|
||||
|
||||
for (let t of toggles) {
|
||||
let card = t.getAttribute("card")
|
||||
if (card == null) continue
|
||||
let cc = []
|
||||
for (let c of contents) if (c.getAttribute("card") == card) cc.push(c)
|
||||
t.style.transition = "0.2s linear"
|
||||
t.addEventListener("click", (e) => {
|
||||
if (e.currentTarget.style.transform == "rotate(180deg)") {
|
||||
e.currentTarget.style.transform = ""
|
||||
for (let c of cc) c.classList.remove("is-hidden")
|
||||
} else {
|
||||
e.currentTarget.style.transform = "rotate(180deg)"
|
||||
for (let c of cc) c.classList.add("is-hidden")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeModals() {
|
||||
let modaltriggers = $(".modal-trigger")
|
||||
for (let t of modaltriggers) {
|
||||
let m = t.querySelector(".modal")
|
||||
let c = m.querySelectorAll("#close")
|
||||
t.addEventListener("click", (e) => { m.style.display = "flex" })
|
||||
for (let v of c) v.addEventListener("click", (e) => {
|
||||
m.style.display = "none"
|
||||
e.stopPropagation()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormSelects() {
|
||||
let formSelects = document.querySelectorAll("#form-select")
|
||||
for (let s of formSelects) {
|
||||
let input = s.querySelector("input#form-select-input")
|
||||
let select = s.querySelector("select#form-select-select")
|
||||
let options = select.querySelectorAll("option")
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let o = options[i]
|
||||
let value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
let enabled = (o.getAttribute("disabled") == null) ? true : false
|
||||
if (value == input.value) select.selectedIndex = i
|
||||
if (!enabled) o.style.display = "none"
|
||||
}
|
||||
select.addEventListener("change", () => {
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let o = options[i]
|
||||
if (o.selected) {
|
||||
input.value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
input.dispatchEvent(new Event("change"))
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormPaginations() {
|
||||
let formPags = document.querySelectorAll("#form-pagination")
|
||||
for (let p of formPags) {
|
||||
let input = p.querySelector("input#form-pagination-input")
|
||||
let options = p.querySelectorAll("ul.pagination-list li a.pagination-link")
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
let o = options[i]
|
||||
let value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
if (value == input.value) {
|
||||
if (!o.classList.contains("is-current")) o.classList.add("is-current")
|
||||
} else o.classList.remove("is-current")
|
||||
o.addEventListener("click", () => {
|
||||
for (let i = 0; i < options.length; i++) options[i].classList.remove("is-current")
|
||||
if (!o.classList.contains("is-current")) o.classList.add("is-current")
|
||||
input.value = (o.getAttribute("value") == null) ? i : o.getAttribute("value")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormValidation() {
|
||||
let forms = document.querySelectorAll("form#validatable")
|
||||
for (let f of forms) {
|
||||
let validatableFields = f.querySelectorAll(".field#validatable")
|
||||
let validatableButtons = f.querySelectorAll("button#validatable")
|
||||
|
||||
let getParams = (input) => {
|
||||
return {
|
||||
minLength: input.getAttribute("min-length"),
|
||||
maxLength: input.getAttribute("max-length"),
|
||||
recommendedLength: input.getAttribute("recommended-length"),
|
||||
minPattern: input.getAttribute("min-pattern"),
|
||||
recommendedPattern: input.getAttribute("recommended-pattern"),
|
||||
isNumeric: (input.getAttribute("numeric") != null) ? true : false
|
||||
}
|
||||
}
|
||||
let isValid = (value, params) => {
|
||||
let t = value.trim()
|
||||
if (params.minLength != null) if (t.length < parseInt(params.minLength)) return false
|
||||
if (params.maxLength != null) if (t.length > parseInt(params.maxLength)) return false
|
||||
if (params.minPattern != null) if (!(new RegExp(params.minPattern).test(t))) return false
|
||||
if (params.isNumeric == true) if (parseInt(t).toString() != t) return false
|
||||
return true
|
||||
}
|
||||
|
||||
let isFormValid = () => {
|
||||
for (let field of validatableFields) for (let i of field.querySelectorAll("input#validatable")) if (!isValid(i.value, getParams(i))) return false
|
||||
return true
|
||||
}
|
||||
|
||||
for (let field of validatableFields) {
|
||||
let inputs = field.querySelectorAll("input#validatable")
|
||||
let tips = field.querySelectorAll(".help")
|
||||
for (let i of inputs) i.addEventListener("change", () => {
|
||||
let params = getParams(i)
|
||||
// inputs
|
||||
if (isValid(i.value, params)) {
|
||||
i.classList.remove("is-danger")
|
||||
for (let t of tips) t.classList.remove("is-danger")
|
||||
} else if (!i.classList.contains("is-danger")) {
|
||||
i.classList.add("is-danger")
|
||||
for (let t of tips) t.classList.add("is-danger")
|
||||
}
|
||||
// buttons
|
||||
if (isFormValid()) {
|
||||
for (let b of validatableButtons) b.removeAttribute("disabled")
|
||||
} else {
|
||||
for (let b of validatableButtons) if (b.getAttribute("disabled") == null) b.setAttribute("disabled", "")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormCollections() {
|
||||
let collections = document.querySelectorAll("#form-collection")
|
||||
for (let c of collections) {
|
||||
let maxLength = parseInt(c.getAttribute("max-length"))
|
||||
let fallbackValue = JSON.parse(c.getAttribute("fallback"))
|
||||
let input = c.querySelector("#form-collection-input")
|
||||
let tags = c.querySelectorAll("#form-collection-tag")
|
||||
let modButton = c.querySelector("#form-collection-modify")
|
||||
let modTable = c.querySelector("table#multi-select")
|
||||
let modInput = modTable.querySelector("input#multi-select-input")
|
||||
let modTitle = modTable.querySelector("input#multi-select-title")
|
||||
let deleteButtonClickEventListener = (tag) => () => {
|
||||
let tvalue = JSON.parse(tag.getAttribute("value"))
|
||||
let value = JSON.parse(input.value)
|
||||
value.splice(value.indexOf(tvalue), 1)
|
||||
if (fallbackValue != null) value.push(fallbackValue)
|
||||
input.value = JSON.stringify(value)
|
||||
modInput.value = input.value
|
||||
modInput.dispatchEvent(new Event("change"))
|
||||
tag.remove()
|
||||
}
|
||||
|
||||
for (let t of tags) {
|
||||
let d = t.querySelector(".delete, .is-delete")
|
||||
d.addEventListener("click", deleteButtonClickEventListener(t))
|
||||
}
|
||||
modInput.value = input.value
|
||||
modInput.setAttribute("max-length", maxLength)
|
||||
modInput.setAttribute("fallback", JSON.stringify(fallbackValue))
|
||||
modInput.addEventListener("change", () => {
|
||||
let fallbackValue = JSON.parse(c.getAttribute("fallback"))
|
||||
let oldValue = JSON.parse(input.value)
|
||||
let newValue = JSON.parse(modInput.value)
|
||||
let tags = c.querySelectorAll("#form-collection-tag")
|
||||
for (let o of oldValue) if (!newValue.includes(o) && (o != fallbackValue)) {
|
||||
for (let t of tags) if (JSON.parse(t.getAttribute("value")) == o) t.remove()
|
||||
}
|
||||
for (let n = 0; n < newValue.length; n++) if (!oldValue.includes(newValue[n]) && (newValue[n] != fallbackValue)) {
|
||||
let tag = document.createElement("div")
|
||||
tag.classList.add("control")
|
||||
tag.id = "form-collection-tag"
|
||||
tag.setAttribute("value", newValue[n])
|
||||
tag.innerHTML = "<span class=\"tags has-addons\"><span class=\"tag is-link is-light\" id=\"form-collection-tag-title\">" + JSON.parse(modTitle.value)[n] + "</span><a class=\"tag is-delete\" /></span>"
|
||||
tag.querySelector("a.is-delete").addEventListener("click", deleteButtonClickEventListener(tag))
|
||||
modButton.before(tag)
|
||||
}
|
||||
input.value = modInput.value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function initializeMultiSelectTables() {
|
||||
let tables = document.querySelectorAll("table#multi-select")
|
||||
for (let table of tables) {
|
||||
let valueInput = table.querySelector("input#multi-select-input")
|
||||
let titleInput = table.querySelector("input#multi-select-title")
|
||||
let trimValues = (values, fallback) => {
|
||||
while (values.includes(fallback)) values.splice(values.indexOf(fallback), 1)
|
||||
return values
|
||||
}
|
||||
let fillValues = (values, fallback) => {
|
||||
let maxLength = (valueInput.getAttribute("max-length") == null) ? -1 : parseInt(valueInput.getAttribute("max-length"))
|
||||
while (values.length < maxLength) values.push(fallback)
|
||||
return values
|
||||
}
|
||||
let lines = table.querySelectorAll("tbody tr")
|
||||
let refresh = () => {
|
||||
let fallbackValue = JSON.parse(valueInput.getAttribute("fallback"))
|
||||
let value = trimValues(JSON.parse(valueInput.value), fallbackValue)
|
||||
let title = []
|
||||
for (let l of lines) {
|
||||
let lvalue = JSON.parse(l.getAttribute("multi-select-value"))
|
||||
if (value.includes(lvalue)) {
|
||||
if (!l.classList.contains("is-selected")) l.classList.add("is-selected")
|
||||
title[value.indexOf(lvalue)] = l.getAttribute("multi-select-title")
|
||||
l.style.fontWeight = "bold"
|
||||
} else {
|
||||
l.classList.remove("is-selected")
|
||||
l.style.fontWeight = ""
|
||||
}
|
||||
}
|
||||
titleInput.value = JSON.stringify(title)
|
||||
}
|
||||
|
||||
for (let l of lines) {
|
||||
l.onclick = () => {
|
||||
let fallbackValue = JSON.parse(valueInput.getAttribute("fallback"))
|
||||
let maxLength = (valueInput.getAttribute("max-length") == null) ? -1 : parseInt(valueInput.getAttribute("max-length"))
|
||||
let value = trimValues(JSON.parse(valueInput.value), fallbackValue)
|
||||
let lvalue = JSON.parse(l.getAttribute("multi-select-value"))
|
||||
if (value.includes(lvalue)) value.splice(value.indexOf(lvalue), 1)
|
||||
else if (maxLength >= 0) {
|
||||
if (value.length < maxLength) value.push(lvalue)
|
||||
else alert("Cannot add more items, items are up to " + maxLength + ".")
|
||||
} else value.push(lvalue)
|
||||
valueInput.value = JSON.stringify(fillValues(value, fallbackValue))
|
||||
refresh()
|
||||
valueInput.dispatchEvent(new Event("change"))
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
valueInput.addEventListener("change", refresh)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeFormNumerics() {
|
||||
let numerics = document.querySelectorAll("#form-numeric")
|
||||
for (let n of numerics) {
|
||||
let add = n.querySelector("#form-numeric-add")
|
||||
let sub = n.querySelector("#form-numeric-sub")
|
||||
let inputs = n.querySelectorAll("#form-numeric-input")
|
||||
add.addEventListener("click", (e) => {
|
||||
for (let i of inputs) {
|
||||
let maxValue = parseFloat(i.getAttribute("max-value"))
|
||||
let step = parseFloat(i.getAttribute("step"))
|
||||
|
||||
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||
let value = (parseFloat(i.value) * 10 + step * 10) / 10
|
||||
if (value * Math.sign(step) <= maxValue * Math.sign(step)) i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||
}
|
||||
e.stopPropagation()
|
||||
})
|
||||
sub.addEventListener("click", (e) => {
|
||||
for (let i of inputs) {
|
||||
let minValue = parseFloat(i.getAttribute("min-value"))
|
||||
let step = parseFloat(i.getAttribute("step"))
|
||||
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||
let value = (parseFloat(i.value) * 10 - step * 10) / 10
|
||||
if (value * Math.sign(step) >= minValue * Math.sign(step)) i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||
}
|
||||
e.stopPropagation()
|
||||
})
|
||||
for (let i of inputs) {
|
||||
let digitCount = (i.getAttribute("digit-count") == null) ? -1 : parseInt(i.getAttribute("digit-count"))
|
||||
let value = parseFloat(i.value)
|
||||
i.value = (digitCount >= 0) ? value.toFixed(digitCount) : value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeUploader() {
|
||||
let uploaders = document.querySelectorAll("div#uploader")
|
||||
for (let uploader of uploaders) {
|
||||
let input = uploader.querySelector("input#uploader-input")
|
||||
let text = uploader.querySelector("input#uploader-text")
|
||||
let placeholder = uploader.querySelector("#uploader-placeholder")
|
||||
let remove = uploader.querySelector("#uploader-delete")
|
||||
let reader = new FileReader()
|
||||
input.addEventListener("change", () => {
|
||||
if (input.files.length > 0) {
|
||||
remove.style.display = "block"
|
||||
placeholder.innerText = input.files[0].name
|
||||
reader.readAsText(input.files[0])
|
||||
reader.onload = () => text.value = reader.result
|
||||
} else {
|
||||
placeholder.innerText = ""
|
||||
remove.style.display = "none"
|
||||
text.value = null
|
||||
}
|
||||
})
|
||||
remove.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
input.value = null
|
||||
input.dispatchEvent(new Event("change"))
|
||||
})
|
||||
|
||||
remove.style.display = "none"
|
||||
}
|
||||
}
|
||||
|
||||
function checkImg() {
|
||||
let imgs = document.querySelectorAll("#exist-or-not")
|
||||
for (let img of imgs) {
|
||||
let general = img.querySelector("img#general")
|
||||
let specified = img.querySelector("img#specified")
|
||||
|
||||
if (specified.width == 0) specified.style.display = "none"
|
||||
else general.style.display = "none"
|
||||
}
|
||||
}
|
||||
|
||||
function initializeMarqueeLabels() {
|
||||
let marqueeContainers = document.querySelectorAll(".marquee-label-container")
|
||||
for (let c of marqueeContainers) {
|
||||
let marquees = c.querySelectorAll(".marquee-label")
|
||||
for (let marquee of marquees) {
|
||||
if (marquee.closest(".marquee-label-container") != c) continue
|
||||
let refresh = () => {
|
||||
let lpad = parseInt(window.getComputedStyle(c, null).getPropertyValue("padding-left"))
|
||||
if (lpad == NaN) lpad = 0
|
||||
let rpad = parseInt(window.getComputedStyle(c, null).getPropertyValue("padding-right"))
|
||||
if (rpad == NaN) rpad = 20
|
||||
let hpad = lpad + rpad
|
||||
let speed = marquee.getAttribute("speed")
|
||||
if (speed == null) speed = 1
|
||||
let stopingTime = 0.5
|
||||
let duration = (20 * (marquee.offsetWidth - c.offsetWidth + hpad)) / speed + 2 * stopingTime
|
||||
if ((marquee.offsetWidth > 0) && (marquee.offsetWidth > c.offsetWidth - hpad)) {
|
||||
marquee.animate([
|
||||
{ transform: "translateX(0)", offset: 0 },
|
||||
{ transform: "translateX(0)", easing: "cubic-bezier(0.67, 0, 0.33, 1)", offset: stopingTime / duration },
|
||||
{ transform: "translateX(" + (c.offsetWidth - marquee.offsetWidth - hpad) + "px)", easing: "cubic-bezier(0.67, 0, 0.33, 1)", offset: 1 - stopingTime / duration },
|
||||
{ transform: "translateX(" + (c.offsetWidth - marquee.offsetWidth - hpad) + "px)", offset: 1 }
|
||||
], { duration: (20 * (marquee.offsetWidth - c.offsetWidth) + 1000) / speed, direction: "alternate-reverse", iterations: Infinity })
|
||||
} else marquee.style.animation = "none"
|
||||
}
|
||||
let o = new ResizeObserver(refresh)
|
||||
o.observe(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeNotificatioAnimation() {
|
||||
let notifications = document.querySelectorAll(".notification.temporary")
|
||||
for (let n of notifications) {
|
||||
let remove = n.querySelector(".delete")
|
||||
let startSubmitter = n.querySelector("form.start")
|
||||
let startPath = startSubmitter.getAttribute("action")
|
||||
let startRequest = new XMLHttpRequest()
|
||||
startRequest.open("POST", startPath, true)
|
||||
startRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
let endSubmitter = n.querySelector("form.end")
|
||||
let endPath = startSubmitter.getAttribute("action")
|
||||
let endRequest = new XMLHttpRequest()
|
||||
endRequest.open("POST", endPath, true)
|
||||
endRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
if (startSubmitter != null) startRequest.send()
|
||||
let end = () => {
|
||||
n.style.display = "none"
|
||||
if (endSubmitter != null) endRequest.send()
|
||||
}
|
||||
|
||||
n.style.animationPlayState = "running"
|
||||
remove.addEventListener("click", end)
|
||||
n.addEventListener("animationend", end)
|
||||
n.addEventListener("webkitAnimationEnd", end)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeCheckBoxes() {
|
||||
let checks = document.querySelectorAll(".checkbox")
|
||||
for (let c of checks) {
|
||||
let input = c.querySelector("input[type=checkbox]")
|
||||
let mark = c.querySelector(".checkmark")
|
||||
let refresh = (value) => {
|
||||
value = input.getAttribute("checked")
|
||||
if (value == null) {
|
||||
input.removeAttribute("checked")
|
||||
mark.style.opacity = 0
|
||||
if (!c.classList.contains("is-light")) c.classList.add("is-light")
|
||||
} else {
|
||||
input.setAttribute("checked", "checked")
|
||||
mark.style.opacity = 100
|
||||
c.classList.remove("is-light")
|
||||
}
|
||||
}
|
||||
c.addEventListener("click", () => {
|
||||
let value = input.getAttribute("checked")
|
||||
if (value == null) input.setAttribute("checked", "checked")
|
||||
else input.removeAttribute("checked")
|
||||
refresh()
|
||||
})
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
function removeLoadingModal() {
|
||||
let loading = document.querySelector(".loading")
|
||||
setTimeout(() => (loading == null) ? null : loading.remove(), 505)
|
||||
try {
|
||||
let a = loading.animate([
|
||||
{ offset: 0, opacity: 1 },
|
||||
{ offset: 0.25, opacity: 0 },
|
||||
{ offset: 1, opacity: 0 }
|
||||
], { duration: 2000 })
|
||||
a.onfinish = loading.remove
|
||||
a.play()
|
||||
} catch { }
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
initializeNotificatioAnimation()
|
||||
initializePaginatedContent()
|
||||
initializeTabs()
|
||||
initializeToggles()
|
||||
initializeModals()
|
||||
initializeFormSelects()
|
||||
initializeFormNumerics()
|
||||
initializeFormPaginations()
|
||||
initializeFormValidation()
|
||||
initializeFormCollections()
|
||||
initializeMultiSelectTables()
|
||||
initializeUploader()
|
||||
checkImg()
|
||||
initializeMarqueeLabels()
|
||||
initializeCheckBoxes()
|
||||
|
||||
removeLoadingModal()
|
||||
})
|
||||
|
||||
750
bst@asphyxia/webui/profile_detail.pug
Normal file
750
bst@asphyxia/webui/profile_detail.pug
Normal file
File diff suppressed because one or more lines are too long
18
ddr@asphyxia/README.md
Normal file
18
ddr@asphyxia/README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Dance Dance Revolution
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Supported version
|
||||
|
||||
- Dance Dance Revolution A20
|
||||
- Dance Dance Revolution A
|
||||
|
||||
---
|
||||
|
||||
Changelogs
|
||||
|
||||
**v1.0.0**
|
||||
|
||||
- Initial release
|
||||
18
ddr@asphyxia/handlers/common.ts
Normal file
18
ddr@asphyxia/handlers/common.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
export const eventLog: EPR = (info, data, send) => {
|
||||
return send.object({
|
||||
gamesession: K.ITEM("s64", BigInt(1)),
|
||||
logsendflg: K.ITEM("s32", 0),
|
||||
logerrlevel: K.ITEM("s32", 0),
|
||||
evtidnosendflg: K.ITEM("s32", 0)
|
||||
});
|
||||
};
|
||||
|
||||
export const convcardnumber: EPR = (info, data, send) => {
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
|
||||
data: {
|
||||
card_number: K.ITEM("str", $(data).str("data.card_id").split("|")[0])
|
||||
}
|
||||
});
|
||||
};
|
||||
275
ddr@asphyxia/handlers/usergamedata.ts
Normal file
275
ddr@asphyxia/handlers/usergamedata.ts
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
import { CommonOffset, LastOffset, OptionOffset, Profile } from "../models/profile";
|
||||
import { formatCode } from "../utils";
|
||||
import { Score } from "../models/score";
|
||||
import { Ghost } from "../models/ghost";
|
||||
|
||||
enum GameStyle {
|
||||
SINGLE,
|
||||
DOUBLE,
|
||||
VERSUS
|
||||
}
|
||||
|
||||
export const usergamedata: EPR = async (info, data, send) => {
|
||||
const mode = $(data).str("data.mode");
|
||||
const refId = $(data).str("data.refid");
|
||||
|
||||
switch (mode) {
|
||||
case "userload":
|
||||
return send.object(await userload(refId));
|
||||
case "usernew":
|
||||
return send.object(await usernew(refId, data));
|
||||
case "usersave":
|
||||
return send.object(await usersave(refId, data));
|
||||
case "rivalload":
|
||||
return send.object(await rivalload(refId, data));
|
||||
case "ghostload":
|
||||
return send.object(await ghostload(refId, data));
|
||||
case "inheritance":
|
||||
return send.object(inheritance(refId));
|
||||
default:
|
||||
return send.deny();
|
||||
}
|
||||
};
|
||||
|
||||
const userload = async (refId: string) => {
|
||||
let resObj = {
|
||||
result: K.ITEM("s32", 0),
|
||||
is_new: K.ITEM("bool", false),
|
||||
music: [],
|
||||
eventdata: []
|
||||
};
|
||||
|
||||
if (!refId.startsWith("X000")) {
|
||||
const profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
|
||||
|
||||
if (!profile) resObj.is_new = K.ITEM("bool", true);
|
||||
|
||||
const scores = await DB.Find<Score>(refId, { collection: "score" });
|
||||
|
||||
for (const score of scores) {
|
||||
const note = [];
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
if (score.difficulty !== i) {
|
||||
note.push({
|
||||
count: K.ITEM("u16", 0),
|
||||
rank: K.ITEM("u8", 0),
|
||||
clearkind: K.ITEM("u8", 0),
|
||||
score: K.ITEM("s32", 0),
|
||||
ghostid: K.ITEM("s32", 0)
|
||||
});
|
||||
} else {
|
||||
note.push({
|
||||
count: K.ITEM("u16", 1),
|
||||
rank: K.ITEM("u8", score.rank),
|
||||
clearkind: K.ITEM("u8", score.clearKind),
|
||||
score: K.ITEM("s32", score.score),
|
||||
ghostid: K.ITEM("s32", score.songId)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resObj.music.push({
|
||||
mcode: K.ITEM("u32", score.songId),
|
||||
note
|
||||
});
|
||||
}
|
||||
|
||||
resObj["grade"] = {
|
||||
single_grade: K.ITEM("u32", profile.singleGrade || 0),
|
||||
dougle_grade: K.ITEM("u32", profile.doubleGrade || 0)
|
||||
};
|
||||
}
|
||||
|
||||
return resObj;
|
||||
};
|
||||
|
||||
const usernew = async (refId: string, data: any) => {
|
||||
const shopArea = $(data).str("data.shoparea", "");
|
||||
|
||||
let profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
|
||||
|
||||
if (!profile) {
|
||||
profile = (await DB.Upsert<Profile>(refId, { collection: "profile" }, {
|
||||
collection: "profile",
|
||||
ddrCode: _.random(1, 99999999),
|
||||
shopArea
|
||||
})).docs[0];
|
||||
}
|
||||
|
||||
return {
|
||||
result: K.ITEM("s32", 0),
|
||||
seq: K.ITEM("str", formatCode(profile.ddrCode)),
|
||||
code: K.ITEM("s32", profile.ddrCode),
|
||||
shoparea: K.ITEM("str", profile.shopArea),
|
||||
};
|
||||
};
|
||||
|
||||
const usersave = async (refId: string, serverData: any) => {
|
||||
const profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
|
||||
|
||||
if (profile) {
|
||||
const data = $(serverData).element("data");
|
||||
const notes = data.elements("note");
|
||||
const events = data.elements("event");
|
||||
|
||||
const common = profile.usergamedata.COMMON.strdata.split(",");
|
||||
const option = profile.usergamedata.OPTION.strdata.split(",");
|
||||
const last = profile.usergamedata.LAST.strdata.split(",");
|
||||
|
||||
if (data.bool("isgameover")) {
|
||||
const style = data.number("playstyle");
|
||||
|
||||
if (style === GameStyle.DOUBLE) {
|
||||
common[CommonOffset.DOUBLE_PLAYS] = (parseInt(common[CommonOffset.DOUBLE_PLAYS]) + 1) + "";
|
||||
} else {
|
||||
common[CommonOffset.SINGLE_PLAYS] = (parseInt(common[CommonOffset.SINGLE_PLAYS]) + 1) + "";
|
||||
}
|
||||
|
||||
common[CommonOffset.TOTAL_PLAYS] = (+common[CommonOffset.DOUBLE_PLAYS]) + (+common[CommonOffset.SINGLE_PLAYS]) + "";
|
||||
|
||||
const workoutEnabled = !!+common[CommonOffset.WEIGHT_DISPLAY];
|
||||
const workoutWeight = +common[CommonOffset.WEIGHT];
|
||||
|
||||
if (workoutEnabled && workoutWeight > 0) {
|
||||
let total = 0;
|
||||
|
||||
for (const note of notes) {
|
||||
total = total + note.number("calorie", 0);
|
||||
}
|
||||
|
||||
last[LastOffset.CALORIES] = total + "";
|
||||
}
|
||||
|
||||
for (const event of events) {
|
||||
const eventId = event.number("eventid", 0);
|
||||
const eventType = event.number("eventtype", 0);
|
||||
if (eventId === 0 || eventType === 0) continue;
|
||||
|
||||
const eventCompleted = event.number("comptime") !== 0;
|
||||
const eventProgress = event.number("savedata");
|
||||
|
||||
if (!profile.events) profile.events = {};
|
||||
profile.events[eventId] = {
|
||||
completed: eventCompleted,
|
||||
progress: eventProgress
|
||||
};
|
||||
}
|
||||
|
||||
const gradeNode = data.element("grade");
|
||||
|
||||
if (gradeNode) {
|
||||
const single = gradeNode.number("single_grade", 0);
|
||||
const double = gradeNode.number("double_grade", 0);
|
||||
|
||||
profile.singleGrade = single;
|
||||
profile.doubleGrade = double;
|
||||
}
|
||||
}
|
||||
|
||||
let scoreData: KDataReader | null;
|
||||
let stageNum = 0;
|
||||
|
||||
for (const note of notes) {
|
||||
if (note.number("stagenum") > stageNum) {
|
||||
scoreData = note;
|
||||
stageNum = note.number("stagenum");
|
||||
}
|
||||
}
|
||||
|
||||
if (scoreData) {
|
||||
const songId = scoreData.number("mcode");
|
||||
const difficulty = scoreData.number("notetype");
|
||||
const rank = scoreData.number("rank");
|
||||
const clearKind = scoreData.number("clearkind");
|
||||
const score = scoreData.number("score");
|
||||
const maxCombo = scoreData.number("maxcombo");
|
||||
const ghostSize = scoreData.number("ghostsize");
|
||||
const ghost = scoreData.str("ghost");
|
||||
|
||||
option[OptionOffset.SPEED] = scoreData.number("opt_speed").toString(16);
|
||||
option[OptionOffset.BOOST] = scoreData.number("opt_boost").toString(16);
|
||||
option[OptionOffset.APPEARANCE] = scoreData.number("opt_appearance").toString(16);
|
||||
option[OptionOffset.TURN] = scoreData.number("opt_turn").toString(16);
|
||||
option[OptionOffset.STEP_ZONE] = scoreData.number("opt_dark").toString(16);
|
||||
option[OptionOffset.SCROLL] = scoreData.number("opt_scroll").toString(16);
|
||||
option[OptionOffset.ARROW_COLOR] = scoreData.number("opt_arrowcolor").toString(16);
|
||||
option[OptionOffset.CUT] = scoreData.number("opt_cut").toString(16);
|
||||
option[OptionOffset.FREEZE] = scoreData.number("opt_freeze").toString(16);
|
||||
option[OptionOffset.JUMP] = scoreData.number("opt_jump").toString(16);
|
||||
option[OptionOffset.ARROW_SKIN] = scoreData.number("opt_arrowshape").toString(16);
|
||||
option[OptionOffset.FILTER] = scoreData.number("opt_filter").toString(16);
|
||||
option[OptionOffset.GUIDELINE] = scoreData.number("opt_guideline").toString(16);
|
||||
option[OptionOffset.GAUGE] = scoreData.number("opt_gauge").toString(16);
|
||||
option[OptionOffset.COMBO_POSITION] = scoreData.number("opt_judgepriority").toString(16);
|
||||
option[OptionOffset.FAST_SLOW] = scoreData.number("opt_timing").toString(16);
|
||||
|
||||
await DB.Upsert<Score>(refId, {
|
||||
collection: "score",
|
||||
songId,
|
||||
difficulty
|
||||
}, {
|
||||
$set: {
|
||||
rank,
|
||||
clearKind,
|
||||
score,
|
||||
maxCombo
|
||||
}
|
||||
});
|
||||
|
||||
await DB.Upsert<Ghost>(refId, {
|
||||
collection: "ghost",
|
||||
songId,
|
||||
difficulty
|
||||
}, {
|
||||
$set: {
|
||||
ghostSize,
|
||||
ghost
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await DB.Update<Profile>(refId, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.COMMON.strdata": common.join(","),
|
||||
"usergamedata.OPTION.strdata": option.join(","),
|
||||
"usergamedata.LAST.strdata": last.join(","),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
result: K.ITEM("s32", 0)
|
||||
};
|
||||
};
|
||||
|
||||
const rivalload = (refId: string, data: any) => {
|
||||
const loadFlag = $(data).number("data.loadflag");
|
||||
|
||||
const record = [];
|
||||
|
||||
return {
|
||||
result: K.ITEM("s32", 0),
|
||||
|
||||
data: {
|
||||
recordtype: K.ITEM("s32", loadFlag),
|
||||
record
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const ghostload = (refId: string, data: any) => {
|
||||
const ghostdata = {};
|
||||
|
||||
return {
|
||||
result: K.ITEM("s32", 0),
|
||||
ghostdata
|
||||
};
|
||||
};
|
||||
|
||||
const inheritance = (refId: string) => {
|
||||
return {
|
||||
result: K.ITEM("s32", 0),
|
||||
InheritanceStatus: K.ITEM("s32", 1)
|
||||
};
|
||||
};
|
||||
45
ddr@asphyxia/handlers/usergamedata_recv.ts
Normal file
45
ddr@asphyxia/handlers/usergamedata_recv.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { Profile } from "../models/profile";
|
||||
|
||||
export const usergamedata_recv: EPR = async (info, data, send) => {
|
||||
const refId = $(data).str("data.refid");
|
||||
const profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
|
||||
|
||||
let recordNum = 0;
|
||||
const record = [];
|
||||
|
||||
const d = [];
|
||||
const types = $(data).str("data.recv_csv").split(",").filter((_, i) => (i % 2 === 0));
|
||||
|
||||
for (const type of types) {
|
||||
let strdata = "<NODATA>";
|
||||
let bindata = "<NODATA>";
|
||||
|
||||
if (profile) {
|
||||
strdata = profile.usergamedata[type]["strdata"];
|
||||
bindata = profile.usergamedata[type]["bindata"];
|
||||
|
||||
if (type === "OPTION") {
|
||||
const split = strdata.split(",");
|
||||
|
||||
split[0] = U.GetConfig("save_option") ? "1" : "0";
|
||||
|
||||
strdata = split.join(",");
|
||||
}
|
||||
}
|
||||
|
||||
d.push({
|
||||
...K.ITEM("str", !profile ? strdata : Buffer.from(strdata).toString("base64")),
|
||||
...profile && { bin1: K.ITEM("str", Buffer.from(bindata).toString("base64")) }
|
||||
});
|
||||
recordNum++;
|
||||
}
|
||||
record.push({ d });
|
||||
|
||||
return send.object({
|
||||
result: K.ITEM("s32", 0),
|
||||
player: {
|
||||
record,
|
||||
record_num: K.ITEM("u32", recordNum)
|
||||
}
|
||||
});
|
||||
};
|
||||
31
ddr@asphyxia/handlers/usergamedata_send.ts
Normal file
31
ddr@asphyxia/handlers/usergamedata_send.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { Profile } from "../models/profile";
|
||||
|
||||
export const usergamedata_send: EPR = async (info, data, send) => {
|
||||
const refId = $(data).str("data.refid");
|
||||
|
||||
const profile = await DB.FindOne<Profile>(refId, { collection: "profile" });
|
||||
if (!profile) return send.deny();
|
||||
|
||||
for (const record of $(data).elements("data.record.d")) {
|
||||
const decodeStr = Buffer.from(record.str("", ""), "base64").toString("ascii");
|
||||
const decodeBin = Buffer.from(record.str("bin1", ""), "base64").toString("ascii");
|
||||
|
||||
const strdata = decodeStr.split(",");
|
||||
const type = Buffer.from(strdata[1]).toString("utf-8");
|
||||
|
||||
if (!profile.usergamedata) profile.usergamedata = {};
|
||||
if (!profile.usergamedata[type]) profile.usergamedata[type] = {};
|
||||
profile.usergamedata[type] = {
|
||||
strdata: strdata.slice(2, -1).join(","),
|
||||
bindata: decodeBin
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
await DB.Update<Profile>(refId, { collection: "profile" }, profile);
|
||||
|
||||
return send.object({ result: K.ITEM("s32", 0) });
|
||||
} catch {
|
||||
return send.deny();
|
||||
}
|
||||
};
|
||||
135
ddr@asphyxia/index.ts
Normal file
135
ddr@asphyxia/index.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
import { convcardnumber, eventLog } from "./handlers/common";
|
||||
import { usergamedata } from "./handlers/usergamedata";
|
||||
import { usergamedata_recv } from "./handlers/usergamedata_recv";
|
||||
import { usergamedata_send } from "./handlers/usergamedata_send";
|
||||
import { CommonOffset, OptionOffset, Profile } from "./models/profile";
|
||||
|
||||
export function register() {
|
||||
R.GameCode("MDX");
|
||||
|
||||
R.Config("save_option", {
|
||||
name: "Save option",
|
||||
desc: "Gets the previously set options as they are.",
|
||||
default: true,
|
||||
type: "boolean"
|
||||
});
|
||||
|
||||
R.Route("playerdata.usergamedata_advanced", usergamedata);
|
||||
R.Route("playerdata.usergamedata_recv", usergamedata_recv);
|
||||
R.Route("playerdata.usergamedata_send", usergamedata_send);
|
||||
|
||||
R.Route("system.convcardnumber", convcardnumber);
|
||||
R.Route("eventlog.write", eventLog);
|
||||
|
||||
R.WebUIEvent("updateName", async ({ refid, name }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.COMMON.strdata.split(",");
|
||||
strdata[CommonOffset.NAME] = name;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.COMMON.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateWeight", async ({ refid, weight }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.COMMON.strdata.split(",");
|
||||
strdata[CommonOffset.WEIGHT] = weight;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.COMMON.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateDisplayCalories", async ({ refid, selected }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.COMMON.strdata.split(",");
|
||||
strdata[CommonOffset.WEIGHT_DISPLAY] = selected;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.COMMON.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateArrowSkin", async ({ refid, selected }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.OPTION.strdata.split(",");
|
||||
strdata[OptionOffset.ARROW_SKIN] = selected;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.OPTION.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateGuideline", async ({ refid, selected }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.OPTION.strdata.split(",");
|
||||
strdata[OptionOffset.GUIDELINE] = selected;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.OPTION.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateFilter", async ({ refid, selected }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.OPTION.strdata.split(",");
|
||||
strdata[OptionOffset.FILTER] = selected;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.OPTION.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateJudgmentPriority", async ({ refid, selected }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.OPTION.strdata.split(",");
|
||||
strdata[OptionOffset.COMBO_POSITION] = selected;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.OPTION.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
R.WebUIEvent("updateDisplayTiming", async ({ refid, selected }) => {
|
||||
let strdata: Profile | string[] = await DB.FindOne<Profile>(refid, { collection: "profile" });
|
||||
|
||||
if (strdata) {
|
||||
strdata = strdata.usergamedata.OPTION.strdata.split(",");
|
||||
strdata[OptionOffset.FAST_SLOW] = selected;
|
||||
await DB.Update<Profile>(refid, { collection: "profile" }, {
|
||||
$set: {
|
||||
"usergamedata.OPTION.strdata": strdata.join(",")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
8
ddr@asphyxia/models/ghost.ts
Normal file
8
ddr@asphyxia/models/ghost.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
export interface Ghost {
|
||||
collection: "ghost";
|
||||
|
||||
songId: number;
|
||||
difficulty: number;
|
||||
ghostSize: number;
|
||||
ghost: string;
|
||||
}
|
||||
77
ddr@asphyxia/models/profile.ts
Normal file
77
ddr@asphyxia/models/profile.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
export enum CommonOffset {
|
||||
AREA = 1,
|
||||
SEQ_HEX = 1,
|
||||
WEIGHT_DISPLAY = 3,
|
||||
CHARACTER,
|
||||
EXTRA_CHARGE,
|
||||
TOTAL_PLAYS = 9,
|
||||
SINGLE_PLAYS = 11,
|
||||
DOUBLE_PLAYS,
|
||||
WEIGHT = 17,
|
||||
NAME = 25,
|
||||
SEQ
|
||||
}
|
||||
|
||||
export enum OptionOffset {
|
||||
SPEED = 1,
|
||||
BOOST,
|
||||
APPEARANCE,
|
||||
TURN,
|
||||
STEP_ZONE,
|
||||
SCROLL,
|
||||
ARROW_COLOR,
|
||||
CUT,
|
||||
FREEZE,
|
||||
JUMP,
|
||||
ARROW_SKIN,
|
||||
FILTER,
|
||||
GUIDELINE,
|
||||
GAUGE,
|
||||
COMBO_POSITION,
|
||||
FAST_SLOW
|
||||
}
|
||||
|
||||
export enum LastOffset {
|
||||
SONG = 3,
|
||||
CALORIES = 10
|
||||
}
|
||||
|
||||
export enum RivalOffset {
|
||||
RIVAL_1_ACTIVE = 1,
|
||||
RIVAL_2_ACTIVE,
|
||||
RIVAL_3_ACTIVE,
|
||||
RIVAL_1_DDRCODE = 9,
|
||||
RIVAL_2_DDRCODE,
|
||||
RIVAL_3_DDRCODE,
|
||||
}
|
||||
|
||||
export interface Profile {
|
||||
collection: "profile";
|
||||
|
||||
ddrCode: number;
|
||||
shopArea: string;
|
||||
|
||||
singleGrade?: number;
|
||||
doubleGrade?: number;
|
||||
|
||||
events?: {};
|
||||
|
||||
usergamedata?: {
|
||||
COMMON?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
OPTION?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
LAST?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
RIVAL?: {
|
||||
strdata?: string;
|
||||
bindata?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
49
ddr@asphyxia/models/score.ts
Normal file
49
ddr@asphyxia/models/score.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
export enum Difficulty {
|
||||
SINGLE_BEGINNER,
|
||||
SINGLE_BASIC,
|
||||
SINGLE_DIFFICULT,
|
||||
SINGLE_EXPERT,
|
||||
SINGLE_CHALLENGE,
|
||||
DOUBLE_BASIC,
|
||||
DOUBLE_DIFFICULT,
|
||||
DOUBLE_EXPERT,
|
||||
DOUBLE_CHALLENGE
|
||||
}
|
||||
|
||||
export enum Rank {
|
||||
AAA,
|
||||
AA_PLUS,
|
||||
AA,
|
||||
AA_MINUS,
|
||||
A_PLUS,
|
||||
A,
|
||||
A_MINUS,
|
||||
B_PLUS,
|
||||
B,
|
||||
B_MINUS,
|
||||
C_PLUS,
|
||||
C,
|
||||
C_MINUS,
|
||||
D_PLUS,
|
||||
D,
|
||||
E
|
||||
}
|
||||
|
||||
export enum ClearKind {
|
||||
NONE = 6,
|
||||
GOOD_COMBO,
|
||||
GREAT_COMBO,
|
||||
PERPECT_COMBO,
|
||||
MARVELOUS_COMBO
|
||||
}
|
||||
|
||||
export interface Score {
|
||||
collection: "score";
|
||||
|
||||
songId: number;
|
||||
difficulty: Difficulty;
|
||||
rank: Rank;
|
||||
clearKind: ClearKind;
|
||||
score: number;
|
||||
maxCombo: number;
|
||||
}
|
||||
13
ddr@asphyxia/utils.ts
Normal file
13
ddr@asphyxia/utils.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export function getVersion(info: EamuseInfo) {
|
||||
const dateCode = parseInt(info.model.split(":")[4]);
|
||||
|
||||
if (dateCode >= 2019022600 && dateCode <= 2020020300) return 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function formatCode(ddrCode: number) {
|
||||
const pad = (ddrCode + "").padStart(8, "0");
|
||||
|
||||
return pad.replace(/^([0-9]{4})([0-9]{4})$/, "$1-$2");
|
||||
}
|
||||
49
ddr@asphyxia/webui/js/profile_settings.js
Normal file
49
ddr@asphyxia/webui/js/profile_settings.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
$('#change-name').on('click', () => {
|
||||
const name = $('#dancer_name').val().toUpperCase();
|
||||
|
||||
emit('updateName', { refid, name }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-weight').on('click', () => {
|
||||
const weight1 = $('#weight_1').val();
|
||||
const weight2 = $('#weight_2').val();
|
||||
const weight = weight1 + '.' + weight2;
|
||||
|
||||
emit('updateWeight', { refid, weight }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-display-calories').on('click', () => {
|
||||
const selected = $('#display_calories option:selected').val();
|
||||
|
||||
emit('updateDisplayCalories', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-arrow-skin').on('click', () => {
|
||||
const selected = $('#arrow_skin option:selected').val();
|
||||
|
||||
emit('updateArrowSkin', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-guideline').on('click', () => {
|
||||
const selected = $('#guideline option:selected').val();
|
||||
|
||||
emit('updateGuideline', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-filter').on('click', () => {
|
||||
const selected = $('#filter option:selected').val();
|
||||
|
||||
emit('updateFilter', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-judgment-priority').on('click', () => {
|
||||
const selected = $('#judgment_priority option:selected').val();
|
||||
|
||||
emit('updateJudgmentPriority', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
|
||||
$('#change-display-timing').on('click', () => {
|
||||
const selected = $('#display_timing option:selected').val();
|
||||
|
||||
emit('updateDisplayTiming', { refid, selected }).then(() => location.reload());
|
||||
});
|
||||
147
ddr@asphyxia/webui/profile_settings.pug
Normal file
147
ddr@asphyxia/webui/profile_settings.pug
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
//DATA//
|
||||
profile: DB.FindOne(refid, { collection: "profile" })
|
||||
|
||||
-
|
||||
const onOff = [ "Off", "On" ];
|
||||
const characters = [ "All Character Random", "Man Random", "Female Random", "Yuni", "Rage", "Afro", "Jenny", "Emi", "Baby-Lon", "Gus", "Ruby", "Alice", "Julio", "Bonnie", "Zero", "Rinon" ];
|
||||
const arrowSkins = [ "Normal", "X", "Classic", "Cyber", "Medium", "Small", "Dot" ];
|
||||
const guidelines = [ "Off", "Border", "Center" ];
|
||||
const filters = [ "Off", "Dark", "Darker", "Darkest" ];
|
||||
const judgmentPrioritys = [ "Judgment priority", "Arrow priority" ];
|
||||
|
||||
if (profile.usergamedata)
|
||||
-
|
||||
const common = profile.usergamedata.COMMON.strdata.split(",");
|
||||
const option = profile.usergamedata.OPTION.strdata.split(",");
|
||||
|
||||
const name = common[25];
|
||||
const weight = common[17];
|
||||
const displayCalories = parseInt(common[3]);
|
||||
const character = parseInt(common[4]);
|
||||
const arrowSkin = parseInt(option[11]);
|
||||
const guideline = parseInt(option[13]);
|
||||
const filter = parseInt(option[12]);
|
||||
const judgmentPriority = parseInt(option[15]);
|
||||
const displayTiming = parseInt(option[16]);
|
||||
|
||||
div
|
||||
.card
|
||||
.card-header
|
||||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-cog
|
||||
| Profile Settings
|
||||
|
||||
.card-content
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Dancer Name
|
||||
.field-body
|
||||
p.control
|
||||
input.input(type="text", id="dancer_name", pattern="[A-Z]{8}", maxlength=8, value=name)
|
||||
p.control
|
||||
a.button.is-primary#change-name Change
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Workout Weight
|
||||
.field-body
|
||||
p.control
|
||||
input.input(type="number", id="weight_1", value=weight.split(".")[0])
|
||||
p.control
|
||||
input.input(type="number", id="weight_2", value=weight.split(".")[1])
|
||||
p.control
|
||||
a.button.is-primary#change-weight Change
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Workout Display Calories
|
||||
.field-body
|
||||
p.control
|
||||
.select
|
||||
select#display_calories
|
||||
if (displayCalories === 1)
|
||||
option(value=0) Off
|
||||
option(value=1, selected) On
|
||||
else
|
||||
option(value=0, selected) Off
|
||||
option(value=1) On
|
||||
p.control
|
||||
a.button.is-primary#change-display-calories Submit
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Arrow Skin
|
||||
.field-body
|
||||
p.control
|
||||
.select
|
||||
select#arrow_skin
|
||||
each v, i in arrowSkins
|
||||
if (arrowSkin === i)
|
||||
option(value=i, selected) #{v}
|
||||
else
|
||||
option(value=i) #{v}
|
||||
p.control
|
||||
a.button.is-primary#change-arrow-skin Submit
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Guideline
|
||||
.field-body
|
||||
p.control
|
||||
.select
|
||||
select#guideline
|
||||
each v, i in guidelines
|
||||
if (guideline === i)
|
||||
option(value=i, selected) #{v}
|
||||
else
|
||||
option(value=i) #{v}
|
||||
p.control
|
||||
a.button.is-primary#change-guideline Submit
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Filter concentration
|
||||
.field-body
|
||||
p.control
|
||||
.select
|
||||
select#filter
|
||||
each v, i in filters
|
||||
if (filter === i)
|
||||
option(value=i, selected) #{v}
|
||||
else
|
||||
option(value=i) #{v}
|
||||
p.control
|
||||
a.button.is-primary#change-filter Submit
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Judgment display priority
|
||||
.field-body
|
||||
p.control
|
||||
.select
|
||||
select#judgment_priority
|
||||
each v, i in judgmentPrioritys
|
||||
if (judgmentPriority === i)
|
||||
option(value=i, selected) #{v}
|
||||
else
|
||||
option(value=i) #{v}
|
||||
p.control
|
||||
a.button.is-primary#change-judgment-priority Submit
|
||||
|
||||
.field.is-horizontal.has-addons
|
||||
.field-label.is-normal
|
||||
label.label Display Timing judgment
|
||||
.field-body
|
||||
p.control
|
||||
.select
|
||||
select#display_timing
|
||||
each v, i in ["Off", "On"]
|
||||
if (displayTiming === i)
|
||||
option(value=i, selected) #{v}
|
||||
else
|
||||
option(value=i) #{v}
|
||||
p.control
|
||||
a.button.is-primary#change-display-timing Submit
|
||||
|
||||
script(src="static/js/profile_settings.js")
|
||||
1
gitadora@asphyxia/.gitignore
vendored
Normal file
1
gitadora@asphyxia/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
apisamples/
|
||||
|
|
@ -1,15 +1,122 @@
|
|||
GITADORA Plugin for Asphyxia-Core
|
||||
=================================
|
||||
This plugin is converted from public-exported Asphyxia's Routes.
|
||||

|
||||
|
||||
This plugin is based on converted from public-exported Asphyxia's Routes.
|
||||
|
||||
Supported Versions
|
||||
==================
|
||||
- Tri-Boost Re:EVOLVE
|
||||
- Matixx
|
||||
- Exchain
|
||||
- EXCHAIN
|
||||
- NEX+AGE
|
||||
- HIGH-VOLTAGE
|
||||
- FUZZ-UP
|
||||
- GALAXY WAVE
|
||||
|
||||
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.
|
||||
<br>If you encounter errors, Please try these steps:
|
||||
|
||||
1. Remove `gitadora@asphyxia` folder.
|
||||
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`
|
||||
|
||||
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 is unimplemented. However, a workaround is available. Try it.
|
||||
* Friends and Rivals are unimplemented.
|
||||
|
||||
Shared Data Options
|
||||
===================
|
||||
Two experimental options allow operators to share data across versions:
|
||||
|
||||
* **Shared Favorite Songs** (`shared_favorite_songs`, default: `false`): When enabled, favorite lists are unified across Guitar Freaks, DrumMania, and supported versions.
|
||||
* **Shared Song Scores** (`shared_song_scores`, default: `false`): When enabled, the server merges the best results for each chart across every stored version and saves them under a shared version identifier. The merged record uses the following shape (fields marked with `//` describe their meaning):
|
||||
|
||||
```
|
||||
scores: {
|
||||
"<musicid>": {
|
||||
update: [<seq>, <new_skill>], // Highest new_skill value seen and its associated seq
|
||||
diffs: {
|
||||
"<seq>": {
|
||||
perc: <number>, // Highest achievement percentage
|
||||
rank: <number>, // Highest rank reached for the chart
|
||||
clear: <boolean>, // Whether the chart has been cleared
|
||||
fc: <boolean>, // Whether a full combo was achieved
|
||||
ex: <boolean>, // Whether an excellent was achieved
|
||||
meter: "<string>",// Best meter value as a stringified bigint
|
||||
prog: <number>, // Highest progression value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Scores are stored under `version: "shared"` but are automatically applied to the active module when loading a profile, ensuring players benefit from their best combined results regardless of the client version.
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
v1.0.0 (Current)
|
||||
|
||||
v1.4.0
|
||||
----------------
|
||||
* Added support for Tri-Boost Re:EVOLVE, HIGH-VOLTAGE, FUZZ-UP, GALAXY WAVE
|
||||
* Bugfix for launch core with "--dev/--console"
|
||||
|
||||
v1.3.0
|
||||
----------------
|
||||
* Added experimental 'Shared Favorite Songs' option. If disabled, players will be able to keep separate lists of favorite songs for each version of Gitadora, as well as between Guitar Freaks and Drummania. Enable this option to have a single unified list of favorite songs for both games, and across all versions. Default is false, to match original arcade behaviour.
|
||||
* Added a leaderboards page to the WebUI. This page displays the rank of all players per game and version, ordered by Skill rating.
|
||||
* More code cleanups to Profiles.ts
|
||||
|
||||
v1.2.4
|
||||
----------------
|
||||
* Fixed note scroll speed defaulting to 0.5x for newly registered profiles.
|
||||
* Misc code cleanup. No changes expected to plugin behaviour.
|
||||
|
||||
v1.2.3
|
||||
----------------
|
||||
* Fixed bug preventing MDB files in XML format from loading (Thanks to DualEdge for reporting this ).
|
||||
|
||||
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. To use a custom MDB, enable it in the web UI, and place a 'custom.xml', 'custom.json' or 'custom.b64' file in the data/mdb subfolder.
|
||||
* Added several player profile stats to the web UI.
|
||||
* MDB loader now logs the number of loaded songs available to GF and DM when in dev mode.
|
||||
* 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.
|
||||
* Rewards field is now saved and loaded correctly. Fixes Github issue #34
|
||||
|
||||
NOTE: Rewards and secret music data is saved at the end of each session, so you will see the unlock notifications one last time after updating the plugin to this version.
|
||||
|
||||
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.
|
||||
* misc: Add logger for tracking problem.
|
||||
|
||||
v1.1.0
|
||||
------
|
||||
* NEX+AGE Support (Not full support.)
|
||||
* Restructure bit for maintaining.
|
||||
|
||||
v1.0.0
|
||||
------
|
||||
* Initial release for public
|
||||
5
gitadora@asphyxia/data/.gitignore
vendored
5
gitadora@asphyxia/data/.gitignore
vendored
|
|
@ -1,5 +0,0 @@
|
|||
mdb_ex.xml
|
||||
mdb_mt.xml
|
||||
mdb_ex.b64
|
||||
mdb_mt.b64
|
||||
custom_mdb.xml
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { CommonMusicData, readJSONOrXML, readXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML('data/mdb_ex.json', 'data/mdb_ex.xml', processRawData)
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData(path: string): Promise<CommonMusicData> {
|
||||
const data = await readXML(path)
|
||||
const mdb = $(data).elements("mdb.mdb_data");
|
||||
const music: any[] = [];
|
||||
for (const m of mdb) {
|
||||
const d = m.numbers("xg_diff_list");
|
||||
const contain = m.numbers("contain_stat");
|
||||
const gf = contain[0];
|
||||
const dm = contain[1];
|
||||
|
||||
if (gf == 0 && dm == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let type = gf;
|
||||
if (gf == 0) {
|
||||
type = dm;
|
||||
}
|
||||
|
||||
music.push({
|
||||
id: K.ITEM('s32', m.number("music_id")),
|
||||
cont_gf: K.ITEM('bool', gf == 0 ? 0 : 1),
|
||||
cont_dm: K.ITEM('bool', dm == 0 ? 0 : 1),
|
||||
is_secret: K.ITEM('bool', 0),
|
||||
is_hot: K.ITEM('bool', type == 2 ? 0 : 1),
|
||||
data_ver: K.ITEM('s32', m.number("data_ver")),
|
||||
diff: K.ARRAY('u16', [
|
||||
d[0],
|
||||
d[1],
|
||||
d[2],
|
||||
d[3],
|
||||
d[4],
|
||||
d[10],
|
||||
d[11],
|
||||
d[12],
|
||||
d[13],
|
||||
d[14],
|
||||
d[5],
|
||||
d[6],
|
||||
d[7],
|
||||
d[8],
|
||||
d[9],
|
||||
]),
|
||||
});
|
||||
}
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { CommonMusicData, readJSONOrXML, readXML } from './helper';
|
||||
|
||||
export async function processData() {
|
||||
const { music } = await readJSONOrXML('data/mdb_mt.json', 'data/mdb_mt.xml', processRawData)
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
|
||||
export async function processRawData(path: string): Promise<CommonMusicData> {
|
||||
const data = await readXML(path)
|
||||
const mdb = $(data).elements("mdb.mdb_data");
|
||||
const music: any[] = [];
|
||||
for (const m of mdb) {
|
||||
const d = m.numbers("xg_diff_list");
|
||||
const contain = m.numbers("contain_stat");
|
||||
const gf = contain[0];
|
||||
const dm = contain[1];
|
||||
|
||||
if (gf == 0 && dm == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let type = gf;
|
||||
if (gf == 0) {
|
||||
type = dm;
|
||||
}
|
||||
|
||||
music.push({
|
||||
id: K.ITEM('s32', m.number("music_id")),
|
||||
cont_gf: K.ITEM('bool', gf == 0 ? 0 : 1),
|
||||
cont_dm: K.ITEM('bool', dm == 0 ? 0 : 1),
|
||||
is_secret: K.ITEM('bool', 0),
|
||||
is_hot: K.ITEM('bool', type == 2 ? 0 : 1),
|
||||
data_ver: K.ITEM('s32', m.number("data_ver")),
|
||||
diff: K.ARRAY('u16', [
|
||||
d[0],
|
||||
d[1],
|
||||
d[2],
|
||||
d[3],
|
||||
d[4],
|
||||
d[10],
|
||||
d[11],
|
||||
d[12],
|
||||
d[13],
|
||||
d[14],
|
||||
d[5],
|
||||
d[6],
|
||||
d[7],
|
||||
d[8],
|
||||
d[9],
|
||||
]),
|
||||
});
|
||||
}
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
132
gitadora@asphyxia/data/extrastage.ts
Normal file
132
gitadora@asphyxia/data/extrastage.ts
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import { getVersion } from "../utils";
|
||||
|
||||
interface EncoreStageData {
|
||||
level: number
|
||||
musics: number[]
|
||||
unlock_challenge?: number[]
|
||||
}
|
||||
|
||||
export function getEncoreStageData(info: EamuseInfo): EncoreStageData {
|
||||
const fallback = { level: 10, musics: [0] }
|
||||
const level: number = U.GetConfig("encore_version")
|
||||
const ntDummyEncore = U.GetConfig("nextage_dummy_encore")
|
||||
switch (getVersion(info)) {
|
||||
case 'galaxywave':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2866, // Calm days
|
||||
2893, // 愛はToxic! feat.Lilymone
|
||||
2885, // Astrum
|
||||
2897, // DESPERATE ERROR
|
||||
2884, // Multiverse
|
||||
2919, // DOGMA
|
||||
2922, // Stay By My Side
|
||||
2937, // Prog for your Soul
|
||||
2963, // Zero Visibility
|
||||
2939, // Hopeful Daybreak!!!
|
||||
2956, // Over Time Groove
|
||||
]
|
||||
}
|
||||
case 'fuzzup':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2812, // THE LAST OF FIREFACE
|
||||
2814, // ENCOUNT
|
||||
2783, // Q転直下
|
||||
2848, // Bloody Iron Maiden
|
||||
2860, // Serious Joke
|
||||
2844, // HyperNebula
|
||||
2877, // AVEL
|
||||
2892, // Elliptic Orbits
|
||||
]
|
||||
}
|
||||
case 'highvoltage':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2686, // CYCLONICxSTORM
|
||||
2687, // Heptagram
|
||||
2700, // Saiph
|
||||
2706, // LUCID NIGHTMARE
|
||||
2740, // Mobius
|
||||
2748, // Under The Shades Of The Divine Ray
|
||||
2772, // REBELLION
|
||||
2812, // THE LAST OF FIREFACE
|
||||
]
|
||||
}
|
||||
case 'nextage':
|
||||
return {
|
||||
level,
|
||||
musics: !ntDummyEncore ? [
|
||||
2587, // 悪魔のハニープリン
|
||||
2531, // The ULTIMATES -reminiscence-
|
||||
2612, // ECLIPSE 2
|
||||
2622, // Slip Into My Royal Blood
|
||||
2686, // CYCLONICxSTORM
|
||||
// FIXME: Fix special encore.
|
||||
305, 602, 703, 802, 902, 1003, 1201, 1400, 1712, 1916, 2289, 2631, // DD13 and encores.
|
||||
1704, 1811, 2121, 2201, 2624, // Soranaki and encores.
|
||||
1907, 2020, 2282, 2341, 2666 // Stargazer and encores.
|
||||
] : [
|
||||
2622, 305, 1704, 1907, 2686 // Dummy.
|
||||
]
|
||||
}
|
||||
case 'exchain':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2246, // 箱庭の世界
|
||||
2498, // Cinnamon
|
||||
2500, // キヤロラ衛星の軌跡
|
||||
2529, // グリーンリーフ症候群
|
||||
2548, // Let's Dance
|
||||
2587, // 悪魔のハニープリン
|
||||
5020, // Timepiece phase II (CLASSIC)
|
||||
5033, // MODEL FT2 Miracle Version (CLASSIC)
|
||||
2586, // 美麗的夏日風
|
||||
5060, // EXCELSIOR DIVE (CLASSIC)
|
||||
2530, // The ULTIMATES -CHRONICLE-
|
||||
2581, // 幸せの代償
|
||||
5046, // Rock to Infinity (CLASSIC)
|
||||
]
|
||||
}
|
||||
case 'matixx':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2432, // Durian
|
||||
2445, // ヤオヨロズランズ
|
||||
2456, // Fate of the Furious
|
||||
2441, // PIRATES BANQUET
|
||||
2444, // Aion
|
||||
2381, // Duella Lyrica
|
||||
2471, // triangulum
|
||||
2476, // MODEL FT4
|
||||
2486, // 煉獄事変
|
||||
2496, // CAPTURING XANADU
|
||||
2497, // Physical Decay
|
||||
2499, // Cinnamon
|
||||
2498, // けもののおうじゃ★めうめう
|
||||
]
|
||||
}
|
||||
case 're':
|
||||
return {
|
||||
level,
|
||||
musics: [
|
||||
2341, // Anathema
|
||||
2384, // White Forest
|
||||
2393, // REFLEXES MANIPULATION
|
||||
2392, // 主亡き機械人形のまなざし
|
||||
2406, // Exclamation
|
||||
2414, // MEDUSA
|
||||
2422, // BLACK ROSES
|
||||
2411, // ギタドラシカ
|
||||
2432, // Durian
|
||||
]
|
||||
}
|
||||
default:
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
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 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, processHandler: (path: string) => Promise<CommonMusicData>): Promise<CommonMusicData> {
|
||||
if (!IO.Exists(jsonPath)) {
|
||||
const data = await processHandler(xmlPath)
|
||||
await IO.WriteFile(jsonPath, JSON.stringify(data))
|
||||
return data
|
||||
} else {
|
||||
const json = JSON.parse(await IO.ReadFile(jsonPath, 'utf-8'))
|
||||
return json
|
||||
}
|
||||
}
|
||||
11
gitadora@asphyxia/data/mdb/.gitignore
vendored
Normal file
11
gitadora@asphyxia/data/mdb/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
ex.xml
|
||||
mt.xml
|
||||
nt.xml
|
||||
hv.xml
|
||||
ex.json
|
||||
mt.json
|
||||
nt.json
|
||||
hv.json
|
||||
custom.xml
|
||||
custom.json
|
||||
blacklist.txt
|
||||
1
gitadora@asphyxia/data/mdb/ex.b64
Normal file
1
gitadora@asphyxia/data/mdb/ex.b64
Normal file
File diff suppressed because one or more lines are too long
12064
gitadora@asphyxia/data/mdb/fz.b64
Normal file
12064
gitadora@asphyxia/data/mdb/fz.b64
Normal file
File diff suppressed because it is too large
Load Diff
12499
gitadora@asphyxia/data/mdb/gw.b64
Normal file
12499
gitadora@asphyxia/data/mdb/gw.b64
Normal file
File diff suppressed because it is too large
Load Diff
1
gitadora@asphyxia/data/mdb/hv.b64
Normal file
1
gitadora@asphyxia/data/mdb/hv.b64
Normal file
File diff suppressed because one or more lines are too long
198
gitadora@asphyxia/data/mdb/index.ts
Normal file
198
gitadora@asphyxia/data/mdb/index.ts
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
import Logger from "../../utils/logger";
|
||||
import { CommonMusicData } from "../../models/commonmusicdata";
|
||||
|
||||
export enum DATAVersion {
|
||||
GALAXYWAVE = "gw",
|
||||
FUZZUP = "fz",
|
||||
HIGHVOLTAGE = "hv",
|
||||
NEXTAGE = "nt",
|
||||
EXCHAIN = "ex",
|
||||
MATTIX = "mt",
|
||||
TBRE = "re"
|
||||
}
|
||||
|
||||
const allowedFormats = ['.json', '.xml', '.b64']
|
||||
const mdbFolder = "data/mdb/"
|
||||
|
||||
type processRawDataHandler = (path: string) => Promise<CommonMusicData>
|
||||
|
||||
const logger = new Logger("mdb")
|
||||
|
||||
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 readMDBFile(path: string, processHandler?: processRawDataHandler): Promise<CommonMusicData> {
|
||||
|
||||
if (!IO.Exists(path)) {
|
||||
throw "Unable to find MDB file at " + path
|
||||
}
|
||||
|
||||
logger.debugInfo(`Loading MDB data from ${path}.`)
|
||||
|
||||
let result : CommonMusicData;
|
||||
const fileType = path.substring(path.lastIndexOf('.')).toLowerCase()
|
||||
|
||||
switch (fileType) {
|
||||
case '.json':
|
||||
const str = await IO.ReadFile(path, 'utf-8');
|
||||
result = JSON.parse(str)
|
||||
break;
|
||||
case '.xml':
|
||||
processHandler = processHandler ?? defaultProcessRawXmlData
|
||||
result = await processHandler(path)
|
||||
// Uncomment to save the loaded XML file as JSON.
|
||||
// await IO.WriteFile(path.replace(".xml", ".json"), JSON.stringify(result))
|
||||
break;
|
||||
case '.b64':
|
||||
const buff = await IO.ReadFile(path, 'utf-8');
|
||||
const bufferCtor = (globalThis as {
|
||||
Buffer?: {
|
||||
from(input: string, encoding: string): { toString(encoding: string): string }
|
||||
}
|
||||
}).Buffer
|
||||
if (!bufferCtor) {
|
||||
throw new Error('Buffer is not available in the current environment.')
|
||||
}
|
||||
const json = bufferCtor.from(buff, 'base64').toString('utf-8')
|
||||
// Uncomment to save the decoded base64 file as JSON.
|
||||
// await IO.WriteFile(path.replace(".b64",".json"), json)
|
||||
result = JSON.parse(json)
|
||||
break;
|
||||
default:
|
||||
throw `Invalid MDB file type: ${fileType}. Only .json, .xml, .b64 are supported.`
|
||||
}
|
||||
|
||||
// Some MDB sources may not provide seq_release_state. Ensure it is present for every song entry.
|
||||
result.music.forEach((entry) => {
|
||||
if (entry.seq_release_state == null) {
|
||||
entry.seq_release_state = K.ITEM('s32', 1)
|
||||
}
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
export function gameVerToDataVer(ver: string): DATAVersion {
|
||||
switch(ver) {
|
||||
case 'galaxywave':
|
||||
return DATAVersion.GALAXYWAVE
|
||||
case 'fuzzup':
|
||||
return DATAVersion.FUZZUP
|
||||
case 'highvoltage':
|
||||
return DATAVersion.HIGHVOLTAGE
|
||||
case 'nextage':
|
||||
return DATAVersion.NEXTAGE
|
||||
case 'exchain':
|
||||
return DATAVersion.EXCHAIN
|
||||
case 'matixx':
|
||||
return DATAVersion.MATTIX
|
||||
default:
|
||||
return DATAVersion.TBRE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find a .json, .xml, or .b64 file (in that order) matching the given name in the specified folder.
|
||||
* @param fileNameWithoutExtension - The name of the file to find (without the extension).
|
||||
* @param path - The path to the folder to search. If left null, the default MDB folder ('data/mdb' in the plugin folder) will be used.
|
||||
* @returns - The path of the first matching file found, or null if no file was found.
|
||||
*/
|
||||
export function findMDBFile(fileNameWithoutExtension: string, path: string = null): string {
|
||||
|
||||
path = path ?? mdbFolder
|
||||
if (!IO.Exists(path)) {
|
||||
throw `Path does not exist: ${path}`
|
||||
}
|
||||
|
||||
if (!path.endsWith("/")) {
|
||||
path += "/"
|
||||
}
|
||||
|
||||
for (const ext of allowedFormats) {
|
||||
const candidateFileNames = ext === ".xml"
|
||||
? [
|
||||
`mdb_${fileNameWithoutExtension}${ext}`,
|
||||
`${fileNameWithoutExtension}${ext}`,
|
||||
]
|
||||
: [`${fileNameWithoutExtension}${ext}`]
|
||||
|
||||
for (const fileName of candidateFileNames) {
|
||||
const filePath = path + fileName
|
||||
if (IO.Exists(filePath)) {
|
||||
return filePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export async function loadSongsForGameVersion(gameVer: string, processHandler?: processRawDataHandler) {
|
||||
const ver = gameVerToDataVer(gameVer)
|
||||
|
||||
let mdbFile = findMDBFile(ver, mdbFolder)
|
||||
|
||||
if (mdbFile == null) {
|
||||
throw `No valid MDB files were found in the data/mdb subfolder. Ensure that this folder contains at least one of the following: ${ver}.json, mdb_${ver}.xml (${ver}.xml as fallback) or ${ver}.b64`
|
||||
}
|
||||
|
||||
const music = await readMDBFile(mdbFile, processHandler ?? defaultProcessRawXmlData)
|
||||
return music
|
||||
}
|
||||
|
||||
export async function defaultProcessRawXmlData(path: string): Promise<CommonMusicData> {
|
||||
const data = await readXML(path)
|
||||
const mdb = $(data).elements("mdb.mdb_data");
|
||||
const music: any[] = [];
|
||||
for (const m of mdb) {
|
||||
const d = m.numbers("xg_diff_list");
|
||||
const contain = m.numbers("contain_stat");
|
||||
const gf = contain[0];
|
||||
const dm = contain[1];
|
||||
|
||||
if (gf == 0 && dm == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let type = gf;
|
||||
if (gf == 0) {
|
||||
type = dm;
|
||||
}
|
||||
|
||||
music.push({
|
||||
id: K.ITEM('s32', m.number("music_id")),
|
||||
cont_gf: K.ITEM('bool', gf == 0 ? 0 : 1),
|
||||
cont_dm: K.ITEM('bool', dm == 0 ? 0 : 1),
|
||||
is_secret: K.ITEM('bool', m.number("is_secret", 0)),
|
||||
is_hot: K.ITEM('bool', type == 2 ? 0 : 1),
|
||||
data_ver: K.ITEM('s32', m.number("data_ver", 255)),
|
||||
seq_release_state: K.ITEM('s32', 1),
|
||||
diff: K.ARRAY('u16', [
|
||||
d[0],
|
||||
d[1],
|
||||
d[2],
|
||||
d[3],
|
||||
d[4],
|
||||
d[10],
|
||||
d[11],
|
||||
d[12],
|
||||
d[13],
|
||||
d[14],
|
||||
d[5],
|
||||
d[6],
|
||||
d[7],
|
||||
d[8],
|
||||
d[9],
|
||||
]),
|
||||
});
|
||||
}
|
||||
return {
|
||||
music,
|
||||
};
|
||||
}
|
||||
1
gitadora@asphyxia/data/mdb/mt.b64
Normal file
1
gitadora@asphyxia/data/mdb/mt.b64
Normal file
File diff suppressed because one or more lines are too long
1
gitadora@asphyxia/data/mdb/nt.b64
Normal file
1
gitadora@asphyxia/data/mdb/nt.b64
Normal file
File diff suppressed because one or more lines are too long
7276
gitadora@asphyxia/data/mdb/re.b64
Normal file
7276
gitadora@asphyxia/data/mdb/re.b64
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
99
gitadora@asphyxia/handlers/FavoriteMusic.ts
Normal file
99
gitadora@asphyxia/handlers/FavoriteMusic.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import { Extra } from "../models/extra";
|
||||
import { FavoriteMusic } from "../models/favoritemusic";
|
||||
import { isSharedFavoriteMusicEnabled } from "../utils/index";
|
||||
import Logger from "../utils/logger";
|
||||
|
||||
const logger = new Logger("FavoriteMusic");
|
||||
|
||||
/**
|
||||
* Extracts favorite music data from the given extra data container, and saves it to the database as shared favorite music data for the player with the given refid.
|
||||
* This function has no effect if the 'Shared favorite music' option is not enabled.
|
||||
* Note that shared favorite music is shared across both Guitar Freaks and Drummania, as well as all supported versions of the game.
|
||||
* @param refid The refid of the player.
|
||||
* @param extra The extra data container of the player, containing the favorite music lists to be saved.
|
||||
* @returns {boolean} - whether the favorite music data was successfully saved.
|
||||
*/
|
||||
export async function saveSharedFavoriteMusicFromExtra(refid: string, extra: Extra) : Promise<boolean>
|
||||
{
|
||||
if (!isSharedFavoriteMusicEnabled()) {
|
||||
return false
|
||||
}
|
||||
|
||||
let result : FavoriteMusic = {
|
||||
collection: 'favoritemusic',
|
||||
pluginVer: 1,
|
||||
list_1: extra.list_1,
|
||||
list_2: extra.list_2,
|
||||
list_3: extra.list_3,
|
||||
recommend_musicid_list: extra.recommend_musicid_list,
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await saveFavoriteMusic(refid, result)
|
||||
logger.debugInfo(`Saved shared favorite music for profile ${refid} successfully.`);
|
||||
return true
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
logger.error(`Failed to save shared favorite music for profile ${refid}.`);
|
||||
logger.error(e);
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves shared favorite music data from the database for the player with the given refid, and applies the data to the provided extra data container.
|
||||
* This function has no effect if the 'Shared favorite music' option is not enabled.
|
||||
* Note that shared favorite music is shared across both Guitar Freaks and Drummania, as well as all supported versions of the game.
|
||||
* @param refid - The refid of the player.
|
||||
* @param extra - The destination object where favorite music data should be applied.
|
||||
*/
|
||||
export async function applySharedFavoriteMusicToExtra(refid : string, extra : Extra) : Promise<void>
|
||||
{
|
||||
if (!isSharedFavoriteMusicEnabled()) {
|
||||
return
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
let favoriteMusic = await loadFavoriteMusic(refid)
|
||||
|
||||
if (favoriteMusic == null) {
|
||||
return
|
||||
}
|
||||
|
||||
extra.list_1 = favoriteMusic.list_1
|
||||
extra.list_2 = favoriteMusic.list_2
|
||||
extra.list_3 = favoriteMusic.list_3
|
||||
extra.recommend_musicid_list = favoriteMusic.recommend_musicid_list
|
||||
logger.debugInfo(`Loaded shared favorite music for profile ${refid} successfully.`);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
logger.error(`Failed to load shared favorite music for profile ${refid}.`);
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveFavoriteMusic(refid: string, data : FavoriteMusic) : Promise<any>
|
||||
{
|
||||
return await DB.Upsert<FavoriteMusic>(refid, {
|
||||
collection: 'favoritemusic',
|
||||
}, data)
|
||||
}
|
||||
|
||||
export async function loadFavoriteMusic(refid : string) : Promise<FavoriteMusic | null>
|
||||
{
|
||||
const favoriteMusic = await DB.FindOne<FavoriteMusic>(refid, {
|
||||
collection: 'favoritemusic'
|
||||
})
|
||||
|
||||
if (!favoriteMusic) {
|
||||
logger.debugInfo(`No shared favourite music available for profile ${refid}. Using game specific favorites. Favorites will be saved as shared favorites at the end of the game session.`);
|
||||
return null
|
||||
}
|
||||
|
||||
return favoriteMusic
|
||||
}
|
||||
|
||||
|
|
@ -1,80 +1,41 @@
|
|||
import { getVersion } from "../utils";
|
||||
import { processData as ExchainMusic } from "../data/Exchain"
|
||||
import { processData as MatixxMusic } from "../data/Matixx"
|
||||
import { CommonMusicDataField, readJSONOrXML, readXML } from "../data/helper";
|
||||
import { findMDBFile, readMDBFile, loadSongsForGameVersion } from "../data/mdb";
|
||||
import { CommonMusicDataField } from "../models/commonmusicdata";
|
||||
import Logger from "../utils/logger"
|
||||
import { getPlayableMusicResponse, PlayableMusicResponse } from "../models/Responses/playablemusicresponse";
|
||||
import { isAsphyxiaDebugMode } from "../utils/index";
|
||||
|
||||
const logger = new Logger("MusicList")
|
||||
|
||||
export const playableMusic: EPR = async (info, data, send) => {
|
||||
const version = getVersion(info);
|
||||
const start = Date.now()
|
||||
let music: CommonMusicDataField[] = [];
|
||||
try {
|
||||
if (U.GetConfig("enable_custom_mdb")) {
|
||||
const data = await readXML('data/custom_mdb.xml')
|
||||
const mdb = $(data).elements("mdb.mdb_data");
|
||||
|
||||
for (const m of mdb) {
|
||||
const d = m.numbers("xg_diff_list");
|
||||
const contain = m.numbers("contain_stat");
|
||||
const gf = contain[0];
|
||||
const dm = contain[1];
|
||||
|
||||
if (gf == 0 && dm == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let type = gf;
|
||||
if (gf == 0) {
|
||||
type = dm;
|
||||
}
|
||||
|
||||
music.push({
|
||||
id: K.ITEM('s32', m.number("music_id")),
|
||||
cont_gf: K.ITEM('bool', gf == 0 ? 0 : 1),
|
||||
cont_dm: K.ITEM('bool', dm == 0 ? 0 : 1),
|
||||
is_secret: K.ITEM('bool', 0),
|
||||
is_hot: K.ITEM('bool', type == 2 ? 0 : 1),
|
||||
data_ver: K.ITEM('s32', m.number("data_ver", 115)),
|
||||
diff: K.ARRAY('u16', [
|
||||
d[0],
|
||||
d[1],
|
||||
d[2],
|
||||
d[3],
|
||||
d[4],
|
||||
d[10],
|
||||
d[11],
|
||||
d[12],
|
||||
d[13],
|
||||
d[14],
|
||||
d[5],
|
||||
d[6],
|
||||
d[7],
|
||||
d[8],
|
||||
d[9],
|
||||
]),
|
||||
});
|
||||
}
|
||||
let customMdb = findMDBFile("custom")
|
||||
music = (await readMDBFile(customMdb)).music
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
console.error("Fallback: Using default MDB method.")
|
||||
logger.warn("Read Custom MDB failed. Using default MDB as a fallback.")
|
||||
logger.debugWarn(e.stack);
|
||||
music = [];
|
||||
}
|
||||
|
||||
if (music.length == 0) {
|
||||
if (version == 'exchain') {
|
||||
music = _.get(await ExchainMusic(), 'music', []);
|
||||
} else {
|
||||
music = _.get(await MatixxMusic(), 'music', []);
|
||||
}
|
||||
music = (await loadSongsForGameVersion(version)).music
|
||||
}
|
||||
|
||||
const end = Date.now()
|
||||
const timeDiff = end - start
|
||||
logger.debugInfo(`MDB loading took ${timeDiff} ms`)
|
||||
|
||||
let response : PlayableMusicResponse = getPlayableMusicResponse(music)
|
||||
await send.object(response)
|
||||
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
await IO.WriteFile(`apisamples/playableMusicList.json`, JSON.stringify(music, null, 4))
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
await send.object({
|
||||
hot: {
|
||||
major: K.ITEM('s32', 1),
|
||||
minor: K.ITEM('s32', 1),
|
||||
},
|
||||
musicinfo: K.ATTR({ nr: `${music.length}` }, {
|
||||
music,
|
||||
}),
|
||||
});
|
||||
};
|
||||
90
gitadora@asphyxia/handlers/SharedScores.ts
Normal file
90
gitadora@asphyxia/handlers/SharedScores.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import { PLUGIN_VER } from "../const";
|
||||
import { Scores } from "../models/scores";
|
||||
|
||||
type ScoreDiff = Scores['scores'][string]['diffs'][string];
|
||||
type ScoreEntry = Scores['scores'][string];
|
||||
|
||||
function selectBetterMeter(existing?: string, incoming?: string): string {
|
||||
if (!incoming) return existing ?? "0";
|
||||
if (!existing) return incoming;
|
||||
|
||||
try {
|
||||
return BigInt(incoming) > BigInt(existing) ? incoming : existing;
|
||||
} catch (e) {
|
||||
return incoming || existing;
|
||||
}
|
||||
}
|
||||
|
||||
function mergeScoreDiff(existing: ScoreDiff | undefined, incoming: ScoreDiff): ScoreDiff {
|
||||
if (!existing) return incoming;
|
||||
|
||||
return {
|
||||
perc: Math.max(existing.perc ?? 0, incoming.perc ?? 0),
|
||||
rank: Math.max(existing.rank ?? 0, incoming.rank ?? 0),
|
||||
meter: selectBetterMeter(existing.meter, incoming.meter),
|
||||
prog: Math.max(existing.prog ?? 0, incoming.prog ?? 0),
|
||||
clear: (existing.clear ?? false) || (incoming.clear ?? false),
|
||||
fc: (existing.fc ?? false) || (incoming.fc ?? false),
|
||||
ex: (existing.ex ?? false) || (incoming.ex ?? false),
|
||||
};
|
||||
}
|
||||
|
||||
function mergeScoreEntry(existing: ScoreEntry | undefined, incoming: ScoreEntry): ScoreEntry {
|
||||
const mergedDiffs: ScoreEntry['diffs'] = existing ? { ...existing.diffs } : {};
|
||||
|
||||
for (const [seq, diff] of Object.entries(incoming.diffs)) {
|
||||
mergedDiffs[seq] = mergeScoreDiff(mergedDiffs[seq], diff);
|
||||
}
|
||||
|
||||
const mergedUpdate = existing?.update ? [...existing.update] : [0, 0];
|
||||
if (incoming.update && (mergedUpdate[1] ?? 0) < incoming.update[1]) {
|
||||
mergedUpdate[0] = incoming.update[0];
|
||||
mergedUpdate[1] = incoming.update[1];
|
||||
}
|
||||
|
||||
return {
|
||||
update: mergedUpdate,
|
||||
diffs: mergedDiffs,
|
||||
};
|
||||
}
|
||||
|
||||
function mergeScoreCollections(target: Scores['scores'], incoming: Scores['scores']): Scores['scores'] {
|
||||
const merged = { ...target } as Scores['scores'];
|
||||
|
||||
for (const [mid, entry] of Object.entries(incoming)) {
|
||||
merged[mid] = mergeScoreEntry(merged[mid], entry);
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
async function persistSharedScores(refid: string, game: 'gf' | 'dm', scores: Scores['scores']) {
|
||||
await DB.Upsert<Scores>(refid, { collection: 'scores', game, version: 'shared' }, {
|
||||
collection: 'scores',
|
||||
version: 'shared',
|
||||
pluginVer: PLUGIN_VER,
|
||||
game,
|
||||
scores,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and merge scores across all versions for a player/game pair and persist them under version "shared".
|
||||
*/
|
||||
export async function getMergedSharedScores(refid: string, game: 'gf' | 'dm'): Promise<Scores['scores']> {
|
||||
const scoreDocs = await DB.Find<Scores>(refid, { collection: 'scores', game });
|
||||
const mergedScores = scoreDocs.reduce<Scores['scores']>((acc, doc) => mergeScoreCollections(acc, doc.scores), {} as Scores['scores']);
|
||||
|
||||
await persistSharedScores(refid, game, mergedScores);
|
||||
return mergedScores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the provided score set into the shared scores document for the player/game pair.
|
||||
*/
|
||||
export async function mergeScoresIntoShared(refid: string, game: 'gf' | 'dm', scores: Scores['scores']) {
|
||||
const existingShared = await DB.FindOne<Scores>(refid, { collection: 'scores', game, version: 'shared' });
|
||||
const mergedScores = mergeScoreCollections(existingShared?.scores ?? {}, scores);
|
||||
|
||||
await persistSharedScores(refid, game, mergedScores);
|
||||
}
|
||||
|
|
@ -1,3 +1,10 @@
|
|||
/// <reference lib="es2020.bigint" />
|
||||
|
||||
import { getEncoreStageData } from "../data/extrastage";
|
||||
import Logger from "../utils/logger";
|
||||
import { getVersion } from "../utils";
|
||||
|
||||
const logger = new Logger('info');
|
||||
export const shopInfoRegist: EPR = async (info, data, send) => {
|
||||
send.object({
|
||||
data: {
|
||||
|
|
@ -14,13 +21,393 @@ export const shopInfoRegist: EPR = async (info, data, send) => {
|
|||
}
|
||||
|
||||
export const gameInfoGet: EPR = async (info, data, send) => {
|
||||
|
||||
const eventData = getEventDataResponse()
|
||||
const extraData = getEncoreStageData(info)
|
||||
const VER = getVersion(info)
|
||||
if (VER == "galaxywave"){
|
||||
await send.object({
|
||||
now_date: K.ITEM('u64', BigInt(Date.now())),
|
||||
extra: {
|
||||
extra_lv: K.ITEM('s32', extraData.level),
|
||||
extramusic: {
|
||||
music: extraData.musics.map(mid => {
|
||||
return {
|
||||
musicid: K.ITEM('s32', mid),
|
||||
get_border: K.ITEM('u8', 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
infect_music: { term: K.ITEM('u8', 0) },
|
||||
unlock_challenge: { term: K.ITEM('s32', 0) },
|
||||
battle: { term: K.ITEM('s32', 0) },
|
||||
battle_chara: { term: K.ITEM('s32', 0) },
|
||||
data_ver_limit: { term: K.ITEM('s32', 0) },
|
||||
ea_pass_propel: { term: K.ITEM('s32', 0) },
|
||||
monthly_skill: {
|
||||
term: K.ITEM('u8', 0),
|
||||
target_music: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
update_prog: { term: K.ITEM('s32', 0) },
|
||||
rockwave: {
|
||||
event_list: {
|
||||
event: {
|
||||
data_id: K.ITEM('s32', 0),
|
||||
data_version: K.ITEM('s32', 0),
|
||||
event_id: K.ITEM('s32', 0),
|
||||
event_type: K.ITEM('s32', 0),
|
||||
start_date: K.ITEM('u64', BigInt(0)),
|
||||
end_date: K.ITEM('u64', BigInt(0)),
|
||||
is_open: K.ITEM('bool', 0),
|
||||
bg_no: K.ITEM('s32', 0),
|
||||
target_musicid: K.ITEM('s32', 0),
|
||||
clear_border: K.ITEM('s32', 0),
|
||||
reward_musicid: K.ITEM('s32', 0),
|
||||
reward_musicid_border_list: K.ITEM('s32', 0),
|
||||
reward_stickerid: K.ITEM('s32', 0),
|
||||
reward_stickerid_list: K.ITEM('s32', 0),
|
||||
reward_stickerid_border_list: K.ITEM('s32', 0),
|
||||
firstbit: K.ITEM('s32', 0),
|
||||
quest_no: K.ITEM('s32', 0),
|
||||
target_music_list: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
}
|
||||
},
|
||||
ranking_list: K.ITEM('u64', BigInt(0)),
|
||||
}
|
||||
}
|
||||
},
|
||||
general_term: {
|
||||
termdata: {
|
||||
type: K.ITEM('str', ''),
|
||||
term: K.ITEM('s32', 0),
|
||||
state: K.ITEM('s32', 0),
|
||||
start_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
end_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
}
|
||||
},
|
||||
jubeat_omiyage_challenge: {},
|
||||
kac2017: {},
|
||||
nostalgia_concert: {},
|
||||
trbitemdata: {},
|
||||
ctrl_movie: {},
|
||||
ng_jacket: {},
|
||||
ng_recommend_music: {},
|
||||
ranking: {
|
||||
skill_0_999: {},
|
||||
skill_1000_1499: {},
|
||||
skill_1500_1999: {},
|
||||
skill_2000_2499: {},
|
||||
skill_2500_2999: {},
|
||||
skill_3000_3499: {},
|
||||
skill_3500_3999: {},
|
||||
skill_4000_4499: {},
|
||||
skill_4500_4999: {},
|
||||
skill_5000_5499: {},
|
||||
skill_5500_5999: {},
|
||||
skill_6000_6499: {},
|
||||
skill_6500_6999: {},
|
||||
skill_7000_7499: {},
|
||||
skill_7500_7999: {},
|
||||
skill_8000_8499: {},
|
||||
skill_8500_9999: {},
|
||||
total: {},
|
||||
original: {},
|
||||
bemani: {},
|
||||
famous: {},
|
||||
anime: {},
|
||||
band: {},
|
||||
western: {},
|
||||
},
|
||||
processing_report_state: K.ITEM('u8', 0),
|
||||
assert_report_state: K.ITEM('u8', 0),
|
||||
recommendmusic: { '@attr': { nr: 0 } },
|
||||
demomusic: { '@attr': { nr: 0 } },
|
||||
event_skill: {},
|
||||
temperature: { is_send: K.ITEM('bool', 0) },
|
||||
bemani_summer_2018: { is_open: K.ITEM('bool', 0) },
|
||||
kac2018: {
|
||||
event: {
|
||||
term: K.ITEM('s32', 0),
|
||||
since: K.ITEM('u64', BigInt(0)),
|
||||
till: K.ITEM('u64', BigInt(0)),
|
||||
is_open: K.ITEM('bool', 0),
|
||||
target_music: {
|
||||
music_id: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
},
|
||||
...eventData,
|
||||
});
|
||||
} else if (VER == "fuzzup"){
|
||||
await send.object({
|
||||
now_date: K.ITEM('u64', BigInt(Date.now())),
|
||||
extra: {
|
||||
extra_lv: K.ITEM('u8', extraData.level),
|
||||
extramusic: {
|
||||
music: extraData.musics.map(mid => {
|
||||
return {
|
||||
musicid: K.ITEM('s32', mid),
|
||||
get_border: K.ITEM('u8', 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
infect_music: { term: K.ITEM('u8', 0) },
|
||||
unlock_challenge: { term: K.ITEM('u8', 0) },
|
||||
battle: { term: K.ITEM('u8', 0) },
|
||||
battle_chara: { term: K.ITEM('u8', 0) },
|
||||
data_ver_limit: { term: K.ITEM('s32', 0) },
|
||||
ea_pass_propel: { term: K.ITEM('u8', 0) },
|
||||
monthly_skill: {
|
||||
term: K.ITEM('u8', 0),
|
||||
target_music: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
update_prog: { term: K.ITEM('u8', 0) },
|
||||
rockwave: { event_list: {} },
|
||||
general_term: {},
|
||||
jubeat_omiyage_challenge: {},
|
||||
kac2017: {},
|
||||
nostalgia_concert: {},
|
||||
trbitemdata: {},
|
||||
ctrl_movie: {},
|
||||
ng_jacket: {},
|
||||
ng_recommend_music: {},
|
||||
ranking: {
|
||||
skill_0_999: {},
|
||||
skill_1000_1499: {},
|
||||
skill_1500_1999: {},
|
||||
skill_2000_2499: {},
|
||||
skill_2500_2999: {},
|
||||
skill_3000_3499: {},
|
||||
skill_3500_3999: {},
|
||||
skill_4000_4499: {},
|
||||
skill_4500_4999: {},
|
||||
skill_5000_5499: {},
|
||||
skill_5500_5999: {},
|
||||
skill_6000_6499: {},
|
||||
skill_6500_6999: {},
|
||||
skill_7000_7499: {},
|
||||
skill_7500_7999: {},
|
||||
skill_8000_8499: {},
|
||||
skill_8500_9999: {},
|
||||
total: {},
|
||||
original: {},
|
||||
bemani: {},
|
||||
famous: {},
|
||||
anime: {},
|
||||
band: {},
|
||||
western: {},
|
||||
},
|
||||
processing_report_state: K.ITEM('u8', 0),
|
||||
assert_report_state: K.ITEM('u8', 0),
|
||||
recommendmusic: { '@attr': { nr: 0 } },
|
||||
demomusic: { '@attr': { nr: 0 } },
|
||||
event_skill: {},
|
||||
temperature: { is_send: K.ITEM('bool', 0) },
|
||||
bemani_summer_2018: { is_open: K.ITEM('bool', 0) },
|
||||
kac2018: {
|
||||
event: {
|
||||
term: K.ITEM('s32', 0),
|
||||
since: K.ITEM('u64', BigInt(0)),
|
||||
till: K.ITEM('u64', BigInt(0)),
|
||||
is_open: K.ITEM('bool', 0),
|
||||
target_music: {
|
||||
music_id: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
},
|
||||
livehouse: {
|
||||
event_list: {
|
||||
event: {
|
||||
is_open: K.ITEM('bool', 0),
|
||||
term: K.ITEM('u8', 0),
|
||||
start_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
end_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
livehouse_name: K.ITEM('str', 'Asphyxia'),
|
||||
reward_list: {
|
||||
reward: {
|
||||
reward_id: K.ITEM('s32', 0),
|
||||
reward_kind: K.ITEM('s32', 0),
|
||||
reward_itemid: K.ITEM('s32', 0),
|
||||
unlock_border: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
requirements_musicid: K.ITEM('s32', 0),
|
||||
member_table: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
bonus: {
|
||||
term: K.ITEM('u8', 0),
|
||||
stage_bonus: K.ITEM('s32', 0),
|
||||
charm_bonus: K.ITEM('s32', 0),
|
||||
start_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
end_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
},
|
||||
},
|
||||
...eventData,
|
||||
});
|
||||
}//Older
|
||||
else {
|
||||
await send.object({
|
||||
now_date: K.ITEM('u64', BigInt(Date.now())),
|
||||
extra: {
|
||||
extra_lv: K.ITEM('u8', extraData.level),
|
||||
extramusic: {
|
||||
music: extraData.musics.map(mid => {
|
||||
return {
|
||||
musicid: K.ITEM('s32', mid),
|
||||
get_border: K.ITEM('u8', 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
infect_music: { term: K.ITEM('u8', 0) },
|
||||
unlock_challenge: { term: K.ITEM('u8', 0) },
|
||||
battle: { term: K.ITEM('u8', 0) },
|
||||
battle_chara: { term: K.ITEM('u8', 0) },
|
||||
data_ver_limit: { term: K.ITEM('u8', 0) },
|
||||
ea_pass_propel: { term: K.ITEM('u8', 0) },
|
||||
monthly_skill: {
|
||||
term: K.ITEM('u8', 0),
|
||||
target_music: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
update_prog: { term: K.ITEM('u8', 0) },
|
||||
rockwave: { event_list: {} },
|
||||
general_term: {},
|
||||
jubeat_omiyage_challenge: {},
|
||||
kac2017: {},
|
||||
nostalgia_concert: {},
|
||||
trbitemdata: {},
|
||||
ctrl_movie: {},
|
||||
ng_jacket: {},
|
||||
ng_recommend_music: {},
|
||||
ranking: {
|
||||
skill_0_999: {},
|
||||
skill_1000_1499: {},
|
||||
skill_1500_1999: {},
|
||||
skill_2000_2499: {},
|
||||
skill_2500_2999: {},
|
||||
skill_3000_3499: {},
|
||||
skill_3500_3999: {},
|
||||
skill_4000_4499: {},
|
||||
skill_4500_4999: {},
|
||||
skill_5000_5499: {},
|
||||
skill_5500_5999: {},
|
||||
skill_6000_6499: {},
|
||||
skill_6500_6999: {},
|
||||
skill_7000_7499: {},
|
||||
skill_7500_7999: {},
|
||||
skill_8000_8499: {},
|
||||
skill_8500_9999: {},
|
||||
total: {},
|
||||
original: {},
|
||||
bemani: {},
|
||||
famous: {},
|
||||
anime: {},
|
||||
band: {},
|
||||
western: {},
|
||||
},
|
||||
processing_report_state: K.ITEM('u8', 0),
|
||||
assert_report_state: K.ITEM('u8', 0),
|
||||
recommendmusic: { '@attr': { nr: 0 } },
|
||||
demomusic: { '@attr': { nr: 0 } },
|
||||
event_skill: {},
|
||||
temperature: { is_send: K.ITEM('bool', 0) },
|
||||
bemani_summer_2018: { is_open: K.ITEM('bool', 0) },
|
||||
kac2018: {
|
||||
event: {
|
||||
term: K.ITEM('s32', 0),
|
||||
since: K.ITEM('u64', BigInt(0)),
|
||||
till: K.ITEM('u64', BigInt(0)),
|
||||
is_open: K.ITEM('bool', 0),
|
||||
target_music: {
|
||||
music_id: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
},
|
||||
KAC2016: {
|
||||
is_entry: K.ITEM('bool', 0),
|
||||
term: K.ITEM('u8', 0),
|
||||
musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
KAC2016_skill_ranking: { term: K.ITEM('u8', 0) },
|
||||
season_sticker: { term: K.ITEM('u8', 0) },
|
||||
paseli_point_lose: { term: K.ITEM('u8', 0) },
|
||||
nostal_link: { term: K.ITEM('u8', 0) },
|
||||
encore_advent: { term: K.ITEM('u8', 0) },
|
||||
sdvx_stamprally: { term: K.ITEM('u8', 0) },
|
||||
sdvx_stamprally2: { term: K.ITEM('u8', 0) },
|
||||
floor_policy_2_info: { term: K.ITEM('u8', 0) },
|
||||
long_otobear_fes_2: {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
...eventData,
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function getEventDataResponse() {
|
||||
const addition: any = {
|
||||
monstar_subjugation: {
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
bear_fes: {},
|
||||
nextadium: {},
|
||||
galaxy_parade: {
|
||||
corner_list: {
|
||||
corner: {
|
||||
is_open: K.ITEM('bool', 0),
|
||||
data_ver: K.ITEM('s32', 0),
|
||||
genre: K.ITEM('s32', 0),
|
||||
corner_id: K.ITEM('s32', 0),
|
||||
corner_name: K.ITEM('str', ''),
|
||||
start_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
end_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
requirements_musicid: K.ITEM('s32', 0),
|
||||
reward_list: {
|
||||
reward: {
|
||||
reward_id: K.ITEM('s32', 0),
|
||||
reward_kind: K.ITEM('s32', 0),
|
||||
reward_itemid: K.ITEM('s32', 0),
|
||||
unlock_border: K.ITEM('s32', 0),
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
gacha_table: {
|
||||
chara_odds: {
|
||||
chara_id: K.ITEM('s32', 0),
|
||||
odds: K.ITEM('s32', 0),
|
||||
}
|
||||
},
|
||||
bonus: {
|
||||
term: K.ITEM('s32', 0),
|
||||
stage_bonus: K.ITEM('s32', 0),
|
||||
charm_bonus: K.ITEM('s32', 0),
|
||||
start_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
end_date_ms: K.ITEM('u64', BigInt(0)),
|
||||
}
|
||||
},
|
||||
};
|
||||
const time = BigInt(31536000);
|
||||
|
||||
for (let i = 1; i <= 20; ++i) {
|
||||
const obj = {
|
||||
term: K.ITEM('u8', 0),
|
||||
|
|
@ -33,7 +420,8 @@ export const gameInfoGet: EPR = async (info, data, send) => {
|
|||
term: K.ITEM('u8', 0),
|
||||
start_date_ms: K.ITEM('u64', time),
|
||||
end_date_ms: K.ITEM('u64', time),
|
||||
bonus_musicid: {},
|
||||
//bonus_musicid: {},
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
addition[`sdvx_stamprally3`] = obj;
|
||||
addition[`chronicle_1`] = obj;
|
||||
|
|
@ -42,7 +430,20 @@ export const gameInfoGet: EPR = async (info, data, send) => {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -50,94 +451,42 @@ export const gameInfoGet: EPR = async (info, data, send) => {
|
|||
addition['monstar_subjugation'][`monstar_subjugation_${i}`] = obj;
|
||||
addition['bear_fes'][`bear_fes_${i}`] = obj;
|
||||
}
|
||||
|
||||
if (i <= 2) {
|
||||
addition[`gitadora_oracle_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
addition[`gitadora_oracle_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
}
|
||||
|
||||
if (i <= 3) {
|
||||
addition[`kouyou_challenge_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
addition[`dokidoki_valentine2_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
addition[`wakuteka_whiteday2_${i}`] = { term: K.ITEM('u8', 0) };
|
||||
addition[`ohanami_challenge_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
addition[`otobear_in_the_tsubo_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
addition[`summer_craft_${i}`] = {
|
||||
term: K.ITEM('u8', 0),
|
||||
bonus_musicid: K.ITEM('s32', 0),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await send.object({
|
||||
now_date: K.ITEM('u64', time),
|
||||
extra: {
|
||||
extra_lv: K.ITEM('u8', 10),
|
||||
extramusic: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
get_border: K.ITEM('u8', 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
infect_music: { term: K.ITEM('u8', 0) },
|
||||
unlock_challenge: { term: K.ITEM('u8', 0) },
|
||||
battle: { term: K.ITEM('u8', 0) },
|
||||
battle_chara: { term: K.ITEM('u8', 0) },
|
||||
data_ver_limit: { term: K.ITEM('u8', 0) },
|
||||
ea_pass_propel: { term: K.ITEM('u8', 0) },
|
||||
monthly_skill: {
|
||||
term: K.ITEM('u8', 0),
|
||||
target_music: {
|
||||
music: {
|
||||
musicid: K.ITEM('s32', 0),
|
||||
},
|
||||
},
|
||||
},
|
||||
update_prog: { term: K.ITEM('u8', 0) },
|
||||
rockwave: { event_list: {} },
|
||||
general_term: {},
|
||||
jubeat_omiyage_challenge: {},
|
||||
kac2017: {},
|
||||
nostalgia_concert: {},
|
||||
trbitemdata: {},
|
||||
ctrl_movie: {},
|
||||
ng_jacket: {},
|
||||
ng_recommend_music: {},
|
||||
ranking: {
|
||||
skill_0_999: {},
|
||||
skill_1000_1499: {},
|
||||
skill_1500_1999: {},
|
||||
skill_2000_2499: {},
|
||||
skill_2500_2999: {},
|
||||
skill_3000_3499: {},
|
||||
skill_3500_3999: {},
|
||||
skill_4000_4499: {},
|
||||
skill_4500_4999: {},
|
||||
skill_5000_5499: {},
|
||||
skill_5500_5999: {},
|
||||
skill_6000_6499: {},
|
||||
skill_6500_6999: {},
|
||||
skill_7000_7499: {},
|
||||
skill_7500_7999: {},
|
||||
skill_8000_8499: {},
|
||||
skill_8500_9999: {},
|
||||
total: {},
|
||||
original: {},
|
||||
bemani: {},
|
||||
famous: {},
|
||||
anime: {},
|
||||
band: {},
|
||||
western: {},
|
||||
},
|
||||
processing_report_state: K.ITEM('u8', 0),
|
||||
assert_report_state: K.ITEM('u8', 0),
|
||||
recommendmusic: { '@attr': { nr: 0 } },
|
||||
demomusic: { '@attr': { nr: 0 } },
|
||||
event_skill: {},
|
||||
temperature: { is_send: K.ITEM('bool', 0) },
|
||||
bemani_summer_2018: { is_open: K.ITEM('bool', 0) },
|
||||
kac2018: {
|
||||
event: {
|
||||
term: K.ITEM('s32', 0),
|
||||
since: K.ITEM('u64', BigInt(0)),
|
||||
till: K.ITEM('u64', BigInt(0)),
|
||||
is_open: K.ITEM('bool', 0),
|
||||
target_music: {
|
||||
music_id: K.ARRAY('s32', [0, 0, 0, 0, 0, 0]),
|
||||
},
|
||||
},
|
||||
},
|
||||
...addition,
|
||||
});
|
||||
};
|
||||
|
||||
return addition
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,37 +1,79 @@
|
|||
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 { isRequiredVersion } from "./utils";
|
||||
import { isAsphyxiaDebugMode, isRequiredCoreVersion } from "./utils";
|
||||
import Logger from "./utils/logger";
|
||||
|
||||
const logger = new Logger("main")
|
||||
|
||||
export function register() {
|
||||
if(!isRequiredVersion(1, 20)) {
|
||||
console.error("You need newer version of Core. v1.20 or newer required.")
|
||||
if(!isRequiredCoreVersion(1, 20)) {
|
||||
console.error("A newer version of Asphyxia Core (v1.20 or later) is required.")
|
||||
}
|
||||
|
||||
R.GameCode('M32');
|
||||
|
||||
R.Config("encore_version", {
|
||||
name: "Encore Version",
|
||||
desc: "Set encore version",
|
||||
type: "integer",
|
||||
default: 13,
|
||||
})
|
||||
|
||||
R.Config("nextage_dummy_encore", {
|
||||
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 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",
|
||||
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,
|
||||
})
|
||||
|
||||
R.DataFile("data/custom_mdb.xml", {
|
||||
R.Config("shared_favorite_songs", {
|
||||
name: "Shared Favorite Songs (Experimental)",
|
||||
desc: "If disabled, players will be able to keep separate lists of favorite songs for each version of Gitadora, as well as between Guitar Freaks and Drummania. " +
|
||||
"Enable this option to have a single unified list of favorite songs for both games, and across all versions. Default is false, to match original arcade behaviour.",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
|
||||
R.Config("shared_song_scores", {
|
||||
name: "Shared Song Scores (Experimental)",
|
||||
desc: "If disabled, players will keep separate scoreboards per version. Enable to merge best scores across all versions and games into a shared store.",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
|
||||
R.DataFile("data/mdb/custom.xml", {
|
||||
accept: ".xml",
|
||||
name: "Custom MDB",
|
||||
desc: "You need to enable Custom MDB option first."
|
||||
desc: "Remember to enable the 'Enable Custom MDB' option for the uploaded file to have any effect."
|
||||
})
|
||||
|
||||
R.WebUIEvent('updatePlayerInfo', updatePlayerInfo);
|
||||
|
||||
const MultiRoute = (method: string, handler: EPR | boolean) => {
|
||||
// Helper for register multiple versions.
|
||||
R.Route(`${method}`, handler);
|
||||
R.Route(`re_${method}`, handler);
|
||||
R.Route(`matixx_${method}`, handler);
|
||||
R.Route(`exchain_${method}`, handler);
|
||||
R.Route(`matixx_${method}`, handler);
|
||||
// TODO: NEXTAGE
|
||||
// TODO: TB, TBRE and more older version?
|
||||
R.Route(`nextage_${method}`, handler)
|
||||
R.Route(`highvoltage_${method}`, handler)
|
||||
R.Route(`fuzzup_${method}`, handler)
|
||||
R.Route(`galaxywave_${method}`, handler)
|
||||
R.Route(`galaxywave_delta_${method}`, handler)
|
||||
// TODO: TB, and more older version?
|
||||
};
|
||||
|
||||
// Info
|
||||
|
|
@ -45,5 +87,14 @@ 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)
|
||||
|
||||
R.Unhandled(async (info, data, send) => {
|
||||
if (["eventlog"].includes(info.module)) return;
|
||||
logger.error(`Received Unhandled Request on Method "${info.method}" by ${info.model}/${info.module}`)
|
||||
logger.debugError(`Received Request: ${JSON.stringify(data, null, 4)}`)
|
||||
})
|
||||
}
|
||||
79
gitadora@asphyxia/models/Responses/battledataresponse.ts
Normal file
79
gitadora@asphyxia/models/Responses/battledataresponse.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
export interface BattleDataResponse
|
||||
{
|
||||
info: {
|
||||
orb: KITEM<'s32'>,
|
||||
get_gb_point: KITEM<'s32'>,
|
||||
send_gb_point: KITEM<'s32'>,
|
||||
}
|
||||
greeting: {
|
||||
greeting_1: KITEM<'str'>,
|
||||
greeting_2: KITEM<'str'>,
|
||||
greeting_3: KITEM<'str'>,
|
||||
greeting_4: KITEM<'str'>,
|
||||
greeting_5: KITEM<'str'>,
|
||||
greeting_6: KITEM<'str'>,
|
||||
greeting_7: KITEM<'str'>,
|
||||
greeting_8: KITEM<'str'>,
|
||||
greeting_9: KITEM<'str'>,
|
||||
|
||||
}
|
||||
setting: {
|
||||
matching: KITEM<'s32'>,
|
||||
info_level: KITEM<'s32'>,
|
||||
}
|
||||
|
||||
score: {
|
||||
battle_class: KITEM<'s32'>,
|
||||
max_battle_class: KITEM<'s32'>,
|
||||
battle_point: KITEM<'s32'>,
|
||||
win: KITEM<'s32'>,
|
||||
lose: KITEM<'s32'>,
|
||||
draw: KITEM<'s32'>,
|
||||
consecutive_win: KITEM<'s32'>,
|
||||
max_consecutive_win: KITEM<'s32'>,
|
||||
glorious_win: KITEM<'s32'>,
|
||||
max_defeat_skill: KITEM<'s32'>,
|
||||
latest_result: KITEM<'s32'>,
|
||||
|
||||
}
|
||||
history: {}
|
||||
}
|
||||
|
||||
export function getDefaultBattleDataResponse() : BattleDataResponse {
|
||||
return {
|
||||
info: {
|
||||
orb: K.ITEM('s32', 0),
|
||||
get_gb_point: K.ITEM('s32', 0),
|
||||
send_gb_point: K.ITEM('s32', 0),
|
||||
},
|
||||
greeting: {
|
||||
greeting_1: K.ITEM('str', ''),
|
||||
greeting_2: K.ITEM('str', ''),
|
||||
greeting_3: K.ITEM('str', ''),
|
||||
greeting_4: K.ITEM('str', ''),
|
||||
greeting_5: K.ITEM('str', ''),
|
||||
greeting_6: K.ITEM('str', ''),
|
||||
greeting_7: K.ITEM('str', ''),
|
||||
greeting_8: K.ITEM('str', ''),
|
||||
greeting_9: K.ITEM('str', ''),
|
||||
},
|
||||
setting: {
|
||||
matching: K.ITEM('s32', 0),
|
||||
info_level: K.ITEM('s32', 0),
|
||||
},
|
||||
score: {
|
||||
battle_class: K.ITEM('s32', 0),
|
||||
max_battle_class: K.ITEM('s32', 0),
|
||||
battle_point: K.ITEM('s32', 0),
|
||||
win: K.ITEM('s32', 0),
|
||||
lose: K.ITEM('s32', 0),
|
||||
draw: K.ITEM('s32', 0),
|
||||
consecutive_win: K.ITEM('s32', 0),
|
||||
max_consecutive_win: K.ITEM('s32', 0),
|
||||
glorious_win: K.ITEM('s32', 0),
|
||||
max_defeat_skill: K.ITEM('s32', 0),
|
||||
latest_result: K.ITEM('s32', 0),
|
||||
},
|
||||
history: {},
|
||||
}
|
||||
}
|
||||
30
gitadora@asphyxia/models/Responses/checkplayerresponse.ts
Normal file
30
gitadora@asphyxia/models/Responses/checkplayerresponse.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
export interface CheckPlayerResponse {
|
||||
player: {
|
||||
name: KITEM<'str'>,
|
||||
charaid: KITEM<'s32'>,
|
||||
did: KITEM<'s32'>,
|
||||
skilldata: {
|
||||
skill: KITEM<'s32'>
|
||||
all_skill: KITEM<'s32'>
|
||||
old_skill: KITEM<'s32'>
|
||||
old_all_skill: KITEM<'s32'>
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function getCheckPlayerResponse(playerNo : number, name: string, id: number) : CheckPlayerResponse
|
||||
{
|
||||
return {
|
||||
player: K.ATTR({ no: `${playerNo}`, state: '2' }, {
|
||||
name: K.ITEM('str', name),
|
||||
charaid: K.ITEM('s32', 0),
|
||||
did: K.ITEM('s32', 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),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
22
gitadora@asphyxia/models/Responses/playablemusicresponse.ts
Normal file
22
gitadora@asphyxia/models/Responses/playablemusicresponse.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import { CommonMusicDataField } from "../commonmusicdata"
|
||||
|
||||
export interface PlayableMusicResponse
|
||||
{
|
||||
hot: {
|
||||
major: KITEM<'s32'>,
|
||||
minor: KITEM<'s32'>
|
||||
}
|
||||
musicinfo: KATTR<any>
|
||||
}
|
||||
|
||||
export function getPlayableMusicResponse(music : CommonMusicDataField[]) : PlayableMusicResponse {
|
||||
return {
|
||||
hot: {
|
||||
major: K.ITEM('s32', 1),
|
||||
minor: K.ITEM('s32', 1),
|
||||
},
|
||||
musicinfo: K.ATTR({ nr: `${music.length}` }, {
|
||||
music,
|
||||
}),
|
||||
}
|
||||
}
|
||||
71
gitadora@asphyxia/models/Responses/playerplayinforesponse.ts
Normal file
71
gitadora@asphyxia/models/Responses/playerplayinforesponse.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { Profile } from "../profile";
|
||||
|
||||
export interface PlayerPlayInfoResponse {
|
||||
cabid: KITEM<'s32'>,
|
||||
play: KITEM<'s32'>,
|
||||
playtime: KITEM<'s32'>,
|
||||
playterm: KITEM<'s32'>,
|
||||
session_cnt: KITEM<'s32'>,
|
||||
matching_num: KITEM<'s32'>,
|
||||
extra_stage: KITEM<'s32'>,
|
||||
extra_play: KITEM<'s32'>,
|
||||
extra_clear: KITEM<'s32'>,
|
||||
encore_play: KITEM<'s32'>,
|
||||
encore_clear: KITEM<'s32'>,
|
||||
pencore_play: KITEM<'s32'>,
|
||||
pencore_clear: KITEM<'s32'>,
|
||||
max_clear_diff: KITEM<'s32'>,
|
||||
max_full_diff: KITEM<'s32'>,
|
||||
max_exce_diff: KITEM<'s32'>,
|
||||
clear_num: KITEM<'s32'>,
|
||||
full_num: KITEM<'s32'>,
|
||||
exce_num: KITEM<'s32'>,
|
||||
no_num: KITEM<'s32'>,
|
||||
e_num: KITEM<'s32'>,
|
||||
d_num: KITEM<'s32'>,
|
||||
c_num: KITEM<'s32'>,
|
||||
b_num: KITEM<'s32'>,
|
||||
a_num: KITEM<'s32'>,
|
||||
s_num: KITEM<'s32'>,
|
||||
ss_num: KITEM<'s32'>,
|
||||
last_category: KITEM<'s32'>,
|
||||
last_musicid: KITEM<'s32'>,
|
||||
last_seq: KITEM<'s32'>,
|
||||
disp_level: KITEM<'s32'>,
|
||||
}
|
||||
export function getPlayerPlayInfoResponse(profile : Profile) : PlayerPlayInfoResponse {
|
||||
return {
|
||||
cabid: K.ITEM('s32', 0),
|
||||
play: K.ITEM('s32', profile.play),
|
||||
playtime: K.ITEM('s32', profile.playtime),
|
||||
playterm: K.ITEM('s32', profile.playterm),
|
||||
session_cnt: K.ITEM('s32', profile.session_cnt),
|
||||
matching_num: K.ITEM('s32', 0),
|
||||
extra_stage: K.ITEM('s32', profile.extra_stage),
|
||||
extra_play: K.ITEM('s32', profile.extra_play),
|
||||
extra_clear: K.ITEM('s32', profile.extra_clear),
|
||||
encore_play: K.ITEM('s32', profile.encore_play),
|
||||
encore_clear: K.ITEM('s32', profile.encore_clear),
|
||||
pencore_play: K.ITEM('s32', profile.pencore_play),
|
||||
pencore_clear: K.ITEM('s32', profile.pencore_clear),
|
||||
max_clear_diff: K.ITEM('s32', profile.max_clear_diff),
|
||||
max_full_diff: K.ITEM('s32', profile.max_full_diff),
|
||||
max_exce_diff: K.ITEM('s32', profile.max_exce_diff),
|
||||
clear_num: K.ITEM('s32', profile.clear_num),
|
||||
full_num: K.ITEM('s32', profile.full_num),
|
||||
exce_num: K.ITEM('s32', profile.exce_num),
|
||||
no_num: K.ITEM('s32', profile.no_num),
|
||||
e_num: K.ITEM('s32', profile.e_num),
|
||||
d_num: K.ITEM('s32', profile.d_num),
|
||||
c_num: K.ITEM('s32', profile.c_num),
|
||||
b_num: K.ITEM('s32', profile.b_num),
|
||||
a_num: K.ITEM('s32', profile.a_num),
|
||||
s_num: K.ITEM('s32', profile.s_num),
|
||||
ss_num: K.ITEM('s32', profile.ss_num),
|
||||
last_category: K.ITEM('s32', profile.last_category),
|
||||
last_musicid: K.ITEM('s32', profile.last_musicid),
|
||||
last_seq: K.ITEM('s32', profile.last_seq),
|
||||
disp_level: K.ITEM('s32', profile.disp_level),
|
||||
}
|
||||
}
|
||||
|
||||
110
gitadora@asphyxia/models/Responses/playerrecordresponse.ts
Normal file
110
gitadora@asphyxia/models/Responses/playerrecordresponse.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
import { Profile } from "../profile"
|
||||
import { Record } from "../record"
|
||||
|
||||
export interface PlayerRecordResponse {
|
||||
max_record: {
|
||||
skill: KITEM<'s32'>,
|
||||
all_skill: KITEM<'s32'>,
|
||||
clear_diff: KITEM<'s32'>,
|
||||
full_diff: KITEM<'s32'>,
|
||||
exce_diff: KITEM<'s32'>,
|
||||
clear_music_num: KITEM<'s32'>,
|
||||
full_music_num: KITEM<'s32'>,
|
||||
exce_music_num: KITEM<'s32'>,
|
||||
clear_seq_num: KITEM<'s32'>,
|
||||
classic_all_skill: KITEM<'s32'>
|
||||
},
|
||||
diff_record: {
|
||||
diff_100_nr: KITEM<'s32'>,
|
||||
diff_150_nr: KITEM<'s32'>,
|
||||
diff_200_nr: KITEM<'s32'>,
|
||||
diff_250_nr: KITEM<'s32'>,
|
||||
diff_300_nr: KITEM<'s32'>,
|
||||
diff_350_nr: KITEM<'s32'>,
|
||||
diff_400_nr: KITEM<'s32'>,
|
||||
diff_450_nr: KITEM<'s32'>,
|
||||
diff_500_nr: KITEM<'s32'>,
|
||||
diff_550_nr: KITEM<'s32'>,
|
||||
diff_600_nr: KITEM<'s32'>,
|
||||
diff_650_nr: KITEM<'s32'>,
|
||||
diff_700_nr: KITEM<'s32'>,
|
||||
diff_750_nr: KITEM<'s32'>,
|
||||
diff_800_nr: KITEM<'s32'>,
|
||||
diff_850_nr: KITEM<'s32'>,
|
||||
diff_900_nr: KITEM<'s32'>,
|
||||
diff_950_nr: KITEM<'s32'>,
|
||||
diff_100_clear: KARRAY<'s32'>
|
||||
diff_150_clear: KARRAY<'s32'>
|
||||
diff_200_clear: KARRAY<'s32'>
|
||||
diff_250_clear: KARRAY<'s32'>
|
||||
diff_300_clear: KARRAY<'s32'>
|
||||
diff_350_clear: KARRAY<'s32'>
|
||||
diff_400_clear: KARRAY<'s32'>
|
||||
diff_450_clear: KARRAY<'s32'>
|
||||
diff_500_clear: KARRAY<'s32'>
|
||||
diff_550_clear: KARRAY<'s32'>
|
||||
diff_600_clear: KARRAY<'s32'>
|
||||
diff_650_clear: KARRAY<'s32'>
|
||||
diff_700_clear: KARRAY<'s32'>
|
||||
diff_750_clear: KARRAY<'s32'>
|
||||
diff_800_clear: KARRAY<'s32'>
|
||||
diff_850_clear: KARRAY<'s32'>
|
||||
diff_900_clear: KARRAY<'s32'>
|
||||
diff_950_clear: KARRAY<'s32'>
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlayerRecordResponse(profile: Profile, rec: Record) : PlayerRecordResponse {
|
||||
return {
|
||||
max_record: {
|
||||
skill: K.ITEM('s32', profile.max_skill),
|
||||
all_skill: K.ITEM('s32', profile.max_all_skill),
|
||||
clear_diff: K.ITEM('s32', profile.clear_diff),
|
||||
full_diff: K.ITEM('s32', profile.full_diff),
|
||||
exce_diff: K.ITEM('s32', profile.exce_diff),
|
||||
clear_music_num: K.ITEM('s32', profile.clear_music_num),
|
||||
full_music_num: K.ITEM('s32', profile.full_music_num),
|
||||
exce_music_num: K.ITEM('s32', profile.exce_music_num),
|
||||
clear_seq_num: K.ITEM('s32', profile.clear_seq_num),
|
||||
classic_all_skill: K.ITEM('s32', profile.classic_all_skill),
|
||||
},
|
||||
diff_record: {
|
||||
diff_100_nr: K.ITEM('s32', rec.diff_100_nr),
|
||||
diff_150_nr: K.ITEM('s32', rec.diff_150_nr),
|
||||
diff_200_nr: K.ITEM('s32', rec.diff_200_nr),
|
||||
diff_250_nr: K.ITEM('s32', rec.diff_250_nr),
|
||||
diff_300_nr: K.ITEM('s32', rec.diff_300_nr),
|
||||
diff_350_nr: K.ITEM('s32', rec.diff_350_nr),
|
||||
diff_400_nr: K.ITEM('s32', rec.diff_400_nr),
|
||||
diff_450_nr: K.ITEM('s32', rec.diff_450_nr),
|
||||
diff_500_nr: K.ITEM('s32', rec.diff_500_nr),
|
||||
diff_550_nr: K.ITEM('s32', rec.diff_550_nr),
|
||||
diff_600_nr: K.ITEM('s32', rec.diff_600_nr),
|
||||
diff_650_nr: K.ITEM('s32', rec.diff_650_nr),
|
||||
diff_700_nr: K.ITEM('s32', rec.diff_700_nr),
|
||||
diff_750_nr: K.ITEM('s32', rec.diff_750_nr),
|
||||
diff_800_nr: K.ITEM('s32', rec.diff_800_nr),
|
||||
diff_850_nr: K.ITEM('s32', rec.diff_850_nr),
|
||||
diff_900_nr: K.ITEM('s32', rec.diff_900_nr),
|
||||
diff_950_nr: K.ITEM('s32', rec.diff_950_nr),
|
||||
diff_100_clear: K.ARRAY('s32', rec.diff_100_clear),
|
||||
diff_150_clear: K.ARRAY('s32', rec.diff_150_clear),
|
||||
diff_200_clear: K.ARRAY('s32', rec.diff_200_clear),
|
||||
diff_250_clear: K.ARRAY('s32', rec.diff_250_clear),
|
||||
diff_300_clear: K.ARRAY('s32', rec.diff_300_clear),
|
||||
diff_350_clear: K.ARRAY('s32', rec.diff_350_clear),
|
||||
diff_400_clear: K.ARRAY('s32', rec.diff_400_clear),
|
||||
diff_450_clear: K.ARRAY('s32', rec.diff_450_clear),
|
||||
diff_500_clear: K.ARRAY('s32', rec.diff_500_clear),
|
||||
diff_550_clear: K.ARRAY('s32', rec.diff_550_clear),
|
||||
diff_600_clear: K.ARRAY('s32', rec.diff_600_clear),
|
||||
diff_650_clear: K.ARRAY('s32', rec.diff_650_clear),
|
||||
diff_700_clear: K.ARRAY('s32', rec.diff_700_clear),
|
||||
diff_750_clear: K.ARRAY('s32', rec.diff_750_clear),
|
||||
diff_800_clear: K.ARRAY('s32', rec.diff_800_clear),
|
||||
diff_850_clear: K.ARRAY('s32', rec.diff_850_clear),
|
||||
diff_900_clear: K.ARRAY('s32', rec.diff_900_clear),
|
||||
diff_950_clear: K.ARRAY('s32', rec.diff_950_clear),
|
||||
},
|
||||
};
|
||||
}
|
||||
46
gitadora@asphyxia/models/Responses/playerstickerresponse.ts
Normal file
46
gitadora@asphyxia/models/Responses/playerstickerresponse.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
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'>
|
||||
}
|
||||
|
||||
export function getPlayerStickerResponse(playerCard : any[]) : PlayerStickerResponse[] {
|
||||
let stickers : PlayerStickerResponse[] = []
|
||||
if (!_.isArray(playerCard)) {
|
||||
return stickers
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
41
gitadora@asphyxia/models/Responses/saveprofileresponse.ts
Normal file
41
gitadora@asphyxia/models/Responses/saveprofileresponse.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { PlayerRanking } from "../playerranking"
|
||||
|
||||
export interface SaveProfileResponse
|
||||
{
|
||||
skill: {
|
||||
rank: KITEM<'s32'>,
|
||||
total_nr: KITEM<'s32'>
|
||||
}
|
||||
all_skill: {
|
||||
rank: KITEM<'s32'>,
|
||||
total_nr: KITEM<'s32'>
|
||||
}
|
||||
kac2018: {
|
||||
data: {
|
||||
term: KITEM<'s32'>,
|
||||
total_score: KITEM<'s32'>,
|
||||
score: KARRAY<'s32'>,
|
||||
music_type: KARRAY<'s32'>,
|
||||
play_count: KARRAY<'s32'>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getSaveProfileResponse(playerNo: number, ranking : PlayerRanking)
|
||||
{
|
||||
const result : SaveProfileResponse = 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]),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
25
gitadora@asphyxia/models/Responses/secretmusicresponse.ts
Normal file
25
gitadora@asphyxia/models/Responses/secretmusicresponse.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { Profile } from "../profile";
|
||||
|
||||
export interface SecretMusicResponse {
|
||||
musicid: KITEM<'s32'>;
|
||||
seq: KITEM<'u16'>;
|
||||
kind: KITEM<'s32'>;
|
||||
}
|
||||
|
||||
export function getSecretMusicResponse(profile: Profile) : SecretMusicResponse[] {
|
||||
let response : SecretMusicResponse[] = []
|
||||
|
||||
if (!profile.secretmusic?.music ) {
|
||||
return response
|
||||
}
|
||||
|
||||
for (let music of profile.secretmusic.music) {
|
||||
response.push({
|
||||
musicid: K.ITEM('s32', music.musicid),
|
||||
seq: K.ITEM('u16', music.seq),
|
||||
kind: K.ITEM('s32', music.kind)
|
||||
})
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
14
gitadora@asphyxia/models/commonmusicdata.ts
Normal file
14
gitadora@asphyxia/models/commonmusicdata.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
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">;
|
||||
seq_release_state: KITEM<"s32">;
|
||||
}
|
||||
|
||||
export interface CommonMusicData {
|
||||
music: CommonMusicDataField[]
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface Extra {
|
||||
collection: 'extra';
|
||||
|
||||
|
|
@ -11,4 +13,29 @@ export interface Extra {
|
|||
list_1: number[];
|
||||
list_2: number[];
|
||||
list_3: number[];
|
||||
}
|
||||
recommend_musicid_list: number[];
|
||||
reward_status: number[];
|
||||
}
|
||||
|
||||
export function getDefaultExtra(game: 'gf' | 'dm', version: string, id: number) : Extra {
|
||||
const result : Extra = {
|
||||
collection: 'extra',
|
||||
pluginVer: PLUGIN_VER,
|
||||
|
||||
game,
|
||||
version,
|
||||
id,
|
||||
playstyle: Array(50).fill(0),
|
||||
custom: Array(50).fill(0),
|
||||
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),
|
||||
}
|
||||
result.playstyle[1] = 1 // Note scroll speed (should default to 1.0x)
|
||||
result.playstyle[36] = 20 // Target Timing Adjustment
|
||||
result.playstyle[48] = 20 // Note Display Adjustment
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
10
gitadora@asphyxia/models/favoritemusic.ts
Normal file
10
gitadora@asphyxia/models/favoritemusic.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export interface FavoriteMusic {
|
||||
collection: 'favoritemusic',
|
||||
|
||||
pluginVer: number;
|
||||
list_1: number[];
|
||||
list_2: number[];
|
||||
list_3: number[];
|
||||
recommend_musicid_list: number[];
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface PlayerInfo {
|
||||
collection: 'playerinfo',
|
||||
|
||||
pluginVer: Number;
|
||||
pluginVer: number;
|
||||
|
||||
id: number;
|
||||
version: string,
|
||||
|
|
@ -16,4 +18,15 @@ export interface PlayerInfo {
|
|||
}[];
|
||||
|
||||
// TODO: Add Board things.
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultPlayerInfo(version: string, id: number) : PlayerInfo {
|
||||
return {
|
||||
collection: 'playerinfo',
|
||||
pluginVer: PLUGIN_VER,
|
||||
id,
|
||||
version,
|
||||
name: 'ASPHYXIA-CORE USER',
|
||||
title: 'Please edit on WebUI',
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7
gitadora@asphyxia/models/playerranking.ts
Normal file
7
gitadora@asphyxia/models/playerranking.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export interface PlayerRanking
|
||||
{
|
||||
refid: string;
|
||||
skill: number;
|
||||
all_skill: number;
|
||||
totalPlayers: number;
|
||||
}
|
||||
8
gitadora@asphyxia/models/playerstickerresponse.ts
Normal file
8
gitadora@asphyxia/models/playerstickerresponse.ts
Normal file
|
|
@ -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'>
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
import { PLUGIN_VER } from "../const";
|
||||
import { SecretMusicEntry } from "./secretmusicentry";
|
||||
|
||||
export interface Profile {
|
||||
collection: 'profile';
|
||||
|
||||
|
|
@ -54,4 +57,70 @@ export interface Profile {
|
|||
exce_music_num: number;
|
||||
clear_seq_num: number;
|
||||
classic_all_skill: number;
|
||||
secretmusic: {
|
||||
music: SecretMusicEntry[];
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultProfile (game: 'gf' | 'dm', version: string, id: number): Profile {
|
||||
return {
|
||||
collection: 'profile',
|
||||
pluginVer: PLUGIN_VER,
|
||||
|
||||
game,
|
||||
version,
|
||||
id,
|
||||
|
||||
play: 0,
|
||||
playtime: 0,
|
||||
playterm: 0,
|
||||
session_cnt: 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,
|
||||
progress: 0,
|
||||
disp_state: 0,
|
||||
skill: 0,
|
||||
all_skill: 0,
|
||||
extra_gauge: 0,
|
||||
encore_gauge: 0,
|
||||
encore_cnt: 0,
|
||||
encore_success: 0,
|
||||
unlock_point: 0,
|
||||
max_skill: 0,
|
||||
max_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,
|
||||
secretmusic: {
|
||||
music: []
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface Record {
|
||||
collection: 'record';
|
||||
|
||||
|
|
@ -41,4 +43,50 @@ export interface Record {
|
|||
diff_850_clear: number[];
|
||||
diff_900_clear: number[];
|
||||
diff_950_clear: number[];
|
||||
}
|
||||
}
|
||||
|
||||
export function getDefaultRecord(game: 'gf' | 'dm', version: string): Record {
|
||||
return {
|
||||
collection: 'record',
|
||||
pluginVer: PLUGIN_VER,
|
||||
game,
|
||||
version,
|
||||
|
||||
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: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_150_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_200_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_250_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_300_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_350_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_400_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_450_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_500_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_550_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_600_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_650_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_700_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_750_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_800_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_850_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_900_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
diff_950_clear: [0, 0, 0, 0, 0, 0, 0],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { PLUGIN_VER } from "../const";
|
||||
|
||||
export interface Scores {
|
||||
collection: 'scores';
|
||||
|
||||
game: 'gf' | 'dm';
|
||||
version: string;
|
||||
version?: string;
|
||||
pluginVer: number
|
||||
|
||||
scores: {
|
||||
|
|
@ -22,3 +24,13 @@ export interface Scores {
|
|||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function getDefaultScores (game: 'gf' | 'dm', version: string): Scores {
|
||||
return {
|
||||
collection: 'scores',
|
||||
version,
|
||||
pluginVer: PLUGIN_VER,
|
||||
game,
|
||||
scores: {}
|
||||
}
|
||||
};
|
||||
5
gitadora@asphyxia/models/secretmusicentry.ts
Normal file
5
gitadora@asphyxia/models/secretmusicentry.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export interface SecretMusicEntry {
|
||||
musicid: number;
|
||||
seq: number;
|
||||
kind: number;
|
||||
}
|
||||
5
gitadora@asphyxia/models/secretmusicresponse.ts
Normal file
5
gitadora@asphyxia/models/secretmusicresponse.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export interface SecretMusicResponse {
|
||||
musicid: KITEM<'s32'>;
|
||||
seq: KITEM<'u16'>;
|
||||
kind: KITEM<'s32'>;
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
export const isGF = (info: EamuseInfo) => {
|
||||
return info.model.split(':')[2] == 'A';
|
||||
};
|
||||
|
||||
export const isDM = (info: EamuseInfo) => {
|
||||
return info.model.split(':')[2] == 'B';
|
||||
};
|
||||
|
||||
export const getVersion = (info: EamuseInfo) => {
|
||||
const moduleName: string = info.module;
|
||||
return moduleName.match(/([^_]*)_(.*)/)[1];
|
||||
};
|
||||
|
||||
export function isRequiredVersion(major: number, minor: number) {
|
||||
// version value exposed since Core v1.19
|
||||
const core_major = typeof CORE_VERSION_MAJOR === "number" ? CORE_VERSION_MAJOR : 1
|
||||
const core_minor = typeof CORE_VERSION_MINOR === "number" ? CORE_VERSION_MINOR : 18
|
||||
return core_major >= major && core_minor >= minor
|
||||
}
|
||||
39
gitadora@asphyxia/utils/index.ts
Normal file
39
gitadora@asphyxia/utils/index.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
export const isGF = (info: EamuseInfo) => {
|
||||
return info.model.split(':')[2] == 'A';
|
||||
};
|
||||
|
||||
export const isDM = (info: EamuseInfo) => {
|
||||
return info.model.split(':')[2] == 'B';
|
||||
};
|
||||
|
||||
export const getVersion = (info: EamuseInfo) => {
|
||||
const moduleName: string = info.module;
|
||||
const moduleMatch = moduleName.match(/([^_]*)_(.*)/);
|
||||
|
||||
if (moduleMatch && moduleMatch[1]) {
|
||||
return moduleMatch[1];
|
||||
}
|
||||
|
||||
console.error(`Unable to parse version from module name "${moduleName}".`);
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
export function isRequiredCoreVersion(major: number, minor: number) {
|
||||
// version value exposed since Core v1.19
|
||||
const core_major = typeof CORE_VERSION_MAJOR === "number" ? CORE_VERSION_MAJOR : 1
|
||||
const core_minor = typeof CORE_VERSION_MINOR === "number" ? CORE_VERSION_MINOR : 18
|
||||
return core_major > major || (core_major === major && core_minor >= minor)
|
||||
};
|
||||
|
||||
export function isAsphyxiaDebugMode() : boolean {
|
||||
const argv = (globalThis as { process?: { argv?: string[] } }).process?.argv ?? [];
|
||||
return argv.includes("--dev") || argv.includes("--console");
|
||||
}
|
||||
|
||||
export function isSharedFavoriteMusicEnabled() : boolean{
|
||||
return Boolean(U.GetConfig("shared_favorite_songs"))
|
||||
}
|
||||
|
||||
export function isSharedSongScoresEnabled() : boolean{
|
||||
return Boolean(U.GetConfig("shared_song_scores"))
|
||||
}
|
||||
57
gitadora@asphyxia/utils/logger.ts
Normal file
57
gitadora@asphyxia/utils/logger.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { isAsphyxiaDebugMode } from ".";
|
||||
|
||||
export default class Logger {
|
||||
public category: string | null;
|
||||
|
||||
public constructor(category?: string) {
|
||||
this.category = (category == null) ? null : `[${category}]`
|
||||
}
|
||||
|
||||
public error(...args: any[]) {
|
||||
this.argsHandler(console.error, ...args)
|
||||
}
|
||||
|
||||
public debugError(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.error, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
public warn(...args: any[]) {
|
||||
this.argsHandler(console.warn, ...args)
|
||||
}
|
||||
|
||||
public debugWarn(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.warn, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
public info(...args: any[]) {
|
||||
this.argsHandler(console.info, ...args)
|
||||
}
|
||||
|
||||
public debugInfo(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.info, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
public log(...args: any[]) {
|
||||
this.argsHandler(console.log, ...args)
|
||||
}
|
||||
|
||||
public debugLog(...args: any[]) {
|
||||
if (isAsphyxiaDebugMode()) {
|
||||
this.argsHandler(console.log, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
private argsHandler(target: Function, ...args: any[]) {
|
||||
if (this.category == null) {
|
||||
target(...args)
|
||||
} else {
|
||||
target(this.category, ...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
107
gitadora@asphyxia/webui/leaderboards.pug
Normal file
107
gitadora@asphyxia/webui/leaderboards.pug
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
//DATA//
|
||||
infos: DB.Find(null, { collection: 'playerinfo' })
|
||||
profiles: DB.Find(null, { collection: 'profile' })
|
||||
-
|
||||
|
||||
-
|
||||
function getFullGameName(shortName) {
|
||||
switch (shortName) {
|
||||
case "dm" :
|
||||
return "DrumMania"
|
||||
case "gf":
|
||||
return "GuitarFreaks"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
function getFullGameVersion(shortVer) {
|
||||
switch (shortVer) {
|
||||
case "re" :
|
||||
return "Tri-Boost Re:EVOLVE"
|
||||
case "matixx":
|
||||
return "Matixx"
|
||||
case "EXCHAIN":
|
||||
return "exchain"
|
||||
case "nextage":
|
||||
return "NEX+AGE"
|
||||
case "highvoltage":
|
||||
return "HIGH-VOLTAGE"
|
||||
case "fuzzup":
|
||||
return "FUZZ-UP"
|
||||
case "galaxywave":
|
||||
return "GALAXY WAVE"
|
||||
case "galaxywave_delta":
|
||||
return "GALAXY WAVE DELTA"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
const versions = ["re", "matixx", "exchain", "nextage", "highvoltage", "fuzzup", "galaxywave", "galaxywave_delta"]
|
||||
const games = ["gf", "dm"]
|
||||
|
||||
function generateLeaderboards(infos, profiles) {
|
||||
let result = []
|
||||
|
||||
for (const version of versions) {
|
||||
for (const game of games) {
|
||||
result.push(generateLeaderboard(infos, profiles, version, game))
|
||||
}
|
||||
}
|
||||
|
||||
// Hide versions and games with no entries
|
||||
result = result.filter((e) => e.entries.length > 0)
|
||||
return result
|
||||
}
|
||||
|
||||
function generateLeaderboard(infos, profiles, version, game) {
|
||||
let entries = []
|
||||
let idx = 1
|
||||
let currentProfiles = profiles.filter((e) => e.game === game && e.version === version)
|
||||
currentProfiles = currentProfiles.sort((a, b) => b.skill - a.skill)
|
||||
|
||||
for (const profile of currentProfiles) {
|
||||
const info = infos.find(i => i.__refid === profile.__refid)
|
||||
const name = info ? info.name : "Unknown"
|
||||
const scoreData = {
|
||||
rank: idx,
|
||||
name: name,
|
||||
skill: profile.skill / 100,
|
||||
all_skill: profile.all_skill / 100,
|
||||
clear_music_num : profile.clear_music_num,
|
||||
clear_diff: profile.clear_diff / 100
|
||||
}
|
||||
entries.push(scoreData)
|
||||
idx++
|
||||
}
|
||||
|
||||
let result = {
|
||||
version: version,
|
||||
game: game,
|
||||
entries: entries
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
-
|
||||
|
||||
each board in generateLeaderboards(infos, profiles)
|
||||
h3 #{getFullGameName(board.game)} #{getFullGameVersion(board.version)}
|
||||
table
|
||||
tr
|
||||
th Rank
|
||||
th Name
|
||||
th Skill
|
||||
th All Skill
|
||||
th Songs Cleared
|
||||
th Hardest Clear
|
||||
each e in board.entries
|
||||
tr
|
||||
td #{e.rank}
|
||||
td #{e.name}
|
||||
td #{e.skill}
|
||||
td #{e.all_skill}
|
||||
td #{e.clear_music_num}
|
||||
td #{e.clear_diff}
|
||||
|
||||
|
|
@ -1,6 +1,42 @@
|
|||
//DATA//
|
||||
info: DB.Find(refid, { collection: 'playerinfo' })
|
||||
profile: DB.Find(refid, { collection: 'profile' })
|
||||
-
|
||||
|
||||
-
|
||||
function getFullGameName(shortName) {
|
||||
switch (shortName) {
|
||||
case "gf":
|
||||
return "GuitarFreaks"
|
||||
case "dm" :
|
||||
return "DrumMania"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
function getFullGameVersion(shortVer) {
|
||||
switch (shortVer) {
|
||||
case "re" :
|
||||
return "Tri-Boost Re:EVOLVE"
|
||||
case "matixx":
|
||||
return "Matixx"
|
||||
case "EXCHAIN":
|
||||
return "exchain"
|
||||
case "nextage":
|
||||
return "NEX+AGE"
|
||||
case "highvoltage":
|
||||
return "HIGH-VOLTAGE"
|
||||
case "fuzzup":
|
||||
return "FUZZ-UP"
|
||||
case "galaxywave":
|
||||
return "GALAXY WAVE"
|
||||
case "galaxywave_delta":
|
||||
return "GALAXY WAVE DELTA"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
-
|
||||
|
||||
div
|
||||
|
|
@ -10,7 +46,7 @@ div
|
|||
p.card-header-title
|
||||
span.icon
|
||||
i.mdi.mdi-account-edit
|
||||
| User Detail (#{i.version})
|
||||
| User Detail (#{getFullGameVersion(i.version)})
|
||||
.card-content
|
||||
form(method="post" action="/emit/updatePlayerInfo")
|
||||
.field
|
||||
|
|
@ -33,4 +69,51 @@ div
|
|||
button.button.is-primary(type="submit")
|
||||
span.icon
|
||||
i.mdi.mdi-check
|
||||
span Submit
|
||||
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)} #{getFullGameVersion(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 Songs 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 Highest Difficulty Cleared
|
||||
.control
|
||||
input.input(type="text" name="max_clear_diff", value=(pr.max_clear_diff/100) readonly)
|
||||
.field
|
||||
label.label Highest Difficulty Full Combo
|
||||
.control
|
||||
input.input(type="text" name="max_full_diff", value=(pr.max_full_diff/100) readonly)
|
||||
.field
|
||||
label.label Highest Difficulty Excellent Full Combo
|
||||
.control
|
||||
input.input(type="text" name="max_exce_diff", value=(pr.max_exce_diff/100) readonly)
|
||||
.field
|
||||
label.label Sessions
|
||||
.control
|
||||
input.input(type="text" name="session_cnt", value=pr.session_cnt readonly)
|
||||
170
iidx@asphyxia/README.md
Normal file
170
iidx@asphyxia/README.md
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
# beatmaniaIIDX
|
||||
|
||||
Plugin Version: **v0.1.17**
|
||||
|
||||
---
|
||||
|
||||
Supported Versions
|
||||
- beatmaniaIIDX 14 GOLD (2007072301)
|
||||
- beatmaniaIIDX 15 DJ TROOPERS (2008031100)
|
||||
- beatmaniaIIDX 16 EMPRESS (2009072200)
|
||||
- beatmaniaIIDX 17 SIRIUS (2010071200)
|
||||
- beatmaniaIIDX 18 Resort Anthem (2011071200)
|
||||
- beatmaniaIIDX 19 Lincle (2012090300)
|
||||
- beatmaniaIIDX 20 tricoro (2013090900)
|
||||
- beatmaniaIIDX 21 SPADA (2014071600)
|
||||
- beatmaniaIIDX 22 PENDUAL (2015080500)
|
||||
- beatmaniaIIDX 23 copula (2016083100)
|
||||
- beatmaniaIIDX 24 SINOBUZ (2017082800)
|
||||
- beatmaniaIIDX 25 CANNON BALLERS (2018091900)
|
||||
- beatmaniaIIDX 26 Rootage (2019090200)
|
||||
- beatmaniaIIDX 27 HEROIC VERSE (2020092900)
|
||||
- beatmaniaIIDX 28 BISTROVER (2021091500)
|
||||
- beatmaniaIIDX 29 CastHour (2022082400)
|
||||
- beatmaniaIIDX 30 RESIDENT (2023090500)
|
||||
- beatmaniaIIDX 31 EPOLIS (2024082600)
|
||||
- beatmaniaIIDX 32 Pinky Crush (2025082500)
|
||||
|
||||
---
|
||||
|
||||
Features
|
||||
- STEP UP (Partial)
|
||||
- SKILL ANALYZER
|
||||
- EVENT (Partial)
|
||||
- ARENA (LOCAL only)
|
||||
- RANDOME LANE TICKET
|
||||
- FAVORITE/SONG SELECTION NOTES
|
||||
- ORIGINAL FILTER
|
||||
|
||||
---
|
||||
|
||||
Known Issues
|
||||
- Clear Lamps may display invalid lamps due to missing conversion code
|
||||
- DJ LEVEL folders are broken in ~ DJ TROOPERS due to missing rank\_id
|
||||
- LEGGENDARIA play records before HEROIC VERSE may not display on higher version due to missing conversion code
|
||||
- SUPER FUTURE 2323 play records doesn't display on other version due to missing conversion code
|
||||
- ONE MORE EXTRA STAGE progress won't save (can't test this due to skill issue)
|
||||
- Some of licensed songs are locked behind (kinda solved with music\_open but needs to be verified)
|
||||
- Some of badges aren't saving in RESIDENT ~ (needs to figure out name to id)
|
||||
|
||||
---
|
||||
|
||||
Changelogs
|
||||
|
||||
**v0.1.0**
|
||||
- Added Initial support for Lincle
|
||||
|
||||
**v0.1.1**
|
||||
- Added Initial support for HEROIC VERSE
|
||||
- Expanded score array to adapting newer difficulty (SPN ~ DPA [6] -> SPB ~ DPL [10])
|
||||
- This borked previous score datas recorded with v0.1.0
|
||||
- All score data now shared with all version
|
||||
- as it doesn't have music\_id conversion, it will display incorrect data on certain versions
|
||||
- Added Initial customize support (no webui)
|
||||
|
||||
**v0.1.2**
|
||||
- Added Initial support for BISTROVER
|
||||
- Added Initial Rival support (partial webui)
|
||||
|
||||
**v0.1.3**
|
||||
- Added Initial support for CastHour
|
||||
|
||||
**v0.1.4**
|
||||
- Added Initial support for RESIDENT
|
||||
|
||||
**v0.1.5**
|
||||
- Added Initial support for Resort Anthem
|
||||
- LEAGUE, STORY does not work yet
|
||||
- Fixed where s\_hispeed/d\_hispeed doesn't save correctly
|
||||
|
||||
**v0.1.6**
|
||||
- Added Initial support for tricoro
|
||||
- Some of event savings are broken
|
||||
- Added movie\_upload url setting on plugin setting (BISTROVER ~)
|
||||
- This uses JSON instead of XML and this requires additional setup (can't test or implement this as I don't own NVIDIA GPU)
|
||||
|
||||
**v0.1.7**
|
||||
- Added Initial support for SPADA
|
||||
- Some of event savings are broken
|
||||
- Fixed where rtype didn't save correctly (BISTROVER ~)
|
||||
|
||||
**v0.1.8**
|
||||
- Added RIVAL pacemaker support
|
||||
- Added Initial support for PENDUAL
|
||||
- Some of event savings are broken
|
||||
- Fixed where old\_linkage\_secret\_flg is missing on pc.get response (RESIDENT)
|
||||
- Fixed where game could crash due to invalid rival qprodata
|
||||
- Fixed where lift isn't saving (SPADA)
|
||||
|
||||
**v0.1.9**
|
||||
- Added Initial support for copula
|
||||
- Some of event savings are broken
|
||||
- Added shop.getconvention/shop.setconvention/shop.getname/shop.savename response
|
||||
|
||||
**v0.1.10**
|
||||
- Added Initial support for SINOBUZ ~ Rootage
|
||||
- Converted from asphyxia\_route\_public
|
||||
|
||||
**v0.1.11**
|
||||
- Added Shop Ranking support
|
||||
- Changed pc.common/gameSystem.systemInfo response not to use pugFile
|
||||
- IIDX\_CPUS on models/arena.ts came from asphyxia\_route\_public
|
||||
|
||||
**v0.1.12**
|
||||
- Exposed some of pc.common attributes to plugin settings (WIP)
|
||||
- Added Experimental WebUI (WIP)
|
||||
- Added music.crate/music.breg response
|
||||
- CLEAR RATE and BEGINNER clear lamp may not work on certain versions
|
||||
- Added Initial support for SIRIUS
|
||||
- Fixed where Venue Top didn't save correctly (BISTROVER ~)
|
||||
- Fixed where music.appoint send empty response even rival has score data but player doesn't have score data
|
||||
- Fixed where FAVORITE may work only on specific version
|
||||
- Fixed where shop name always displayed as "CORE" instead of saved one
|
||||
- Fixed where rlist STEP UP achieve value was fixed value instead of saved one
|
||||
- Fixed where fcombo isn't saving (Resort Anthem)
|
||||
- Removed shop.savename as not working as intented
|
||||
|
||||
**v0.1.13**
|
||||
- Added Initial support for DJ TROOPERS
|
||||
- Added Initial support for EMPRESS
|
||||
- Fixed where EXPERT result does not display total cleared users and ranking position
|
||||
|
||||
**v0.1.14**
|
||||
- Added Experimental OMEGA-Attack event saving support on tricoro
|
||||
- Reworked on SINOBUZ ~ Rootage responses
|
||||
- Fixed where Base64toBuffer returns invalid value sometimes
|
||||
- Fixed where timing display option isn't saving on certain versions
|
||||
|
||||
**v0.1.15**
|
||||
- Added Initial support for GOLD
|
||||
- Added Disable Beginner Option
|
||||
- Added Experimental Badge saving support
|
||||
- Added Experimental score import/export
|
||||
- Fixed where plugin may fail to register due to missing types in dev mode (invalid setup for dev, just enough to get around)
|
||||
- Fixed where unable to login after first-play (SPADA, SINOBUZ, Rootage)
|
||||
- Fixed where pacemaker isn't working as intended due to malformed ghost data on music.appoint response (~ DJ TROOPERS)
|
||||
- Fixed where pacemaker isn't working as intented due to wrong condition check (HEROIC VERSE ~)
|
||||
- Fixed where pacemaker sub-type isn't load correctly (HEROIC VERSE ~)
|
||||
- Fixed where QPRO data doesn't get saved in WebUI
|
||||
|
||||
**v0.1.16**
|
||||
- Added Initial support for EPOLIS
|
||||
- Added music\_open on gameSystem.systemInfo response
|
||||
- Added EXTRA FAVORITE support
|
||||
- Fixed where lightning settings doesn't get saved on logout
|
||||
- Fixed where Disable Music Preview, Disable HCN Color, VEFX Lock settings doesn't reflect
|
||||
- Fixed where MISS COUNT has 0 as default (including score import)
|
||||
- Fixed where MISS COUNT doesn't get updated when exscore is same
|
||||
- Fixed where lightning model settings saved incorrectly
|
||||
- Fixed where unable to import score if user has DP scores
|
||||
- Fixed where unable to achieve dan if you failed once
|
||||
- Fixed where unable to login (tricoro, CastHour, Rootage)
|
||||
- Fixed where unable to specify rival in WebUI
|
||||
- Fixed where music.arenaCPU isn't working as intended due to change of type (EPOLIS ~)
|
||||
- Fixed where qpro head equip request handle as hand equip (@anzuwork)
|
||||
- Added error message for invalid score database entries
|
||||
- Reverted `v0.1.15` dev mode related code changes (now requires proper dev setup, refer parent README.md)
|
||||
- WebUI is now display values of corresponding version
|
||||
|
||||
**v0.1.17**
|
||||
- Added Initial support for Pinky Crush
|
||||
94
iidx@asphyxia/data/grade.json
Normal file
94
iidx@asphyxia/data/grade.json
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"31": {
|
||||
"0": {
|
||||
"15": {
|
||||
"music_id": [ 25090, 23068, 19004, 29045 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"16": {
|
||||
"music_id": [ 23005, 27078, 22065, 27060 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"17": {
|
||||
"music_id": [ 29007, 26108, 19002, 18004 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"18": {
|
||||
"music_id": [ 25007, 18032, 16020, 12004 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"15": {
|
||||
"music_id": [ 15032, 29033, 27092, 30020 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"16": {
|
||||
"music_id": [ 10028, 26070, 28091, 23075 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"17": {
|
||||
"music_id": [ 26012, 28002, 17017, 28005 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"18": {
|
||||
"music_id": [ 28008, 15001, 19002, 9028 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"32": {
|
||||
"0": {
|
||||
"15": {
|
||||
"music_id": [ 19022, 30033, 27013, 29045 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"16": {
|
||||
"music_id": [ 27034, 24023, 16009, 25085 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"17": {
|
||||
"music_id": [ 26087, 19002, 29050, 30024 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"18": {
|
||||
"music_id": [ 30052, 18032, 16020, 12004 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
}
|
||||
},
|
||||
"1": {
|
||||
"15": {
|
||||
"music_id": [ 12002, 31063, 23046, 30020 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"16": {
|
||||
"music_id": [ 26106, 14021, 29052, 23075 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"17": {
|
||||
"music_id": [ 29042, 26043, 17017, 28005 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
},
|
||||
"18": {
|
||||
"music_id": [ 25007, 29017, 19002, 9028 ],
|
||||
"class_id": [ 3, 3, 3, 3 ],
|
||||
"kind": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
iidx@asphyxia/data/music_open.json
Normal file
82
iidx@asphyxia/data/music_open.json
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"26": {
|
||||
"26002": { "kind": 0 },
|
||||
"26006": { "kind": 0 },
|
||||
"26022": { "kind": 0 },
|
||||
"26045": { "kind": 0 }
|
||||
},
|
||||
"27": {
|
||||
"27070": { "kind": 0 },
|
||||
"27071": { "kind": 0 },
|
||||
"27073": { "kind": 0 }
|
||||
},
|
||||
"28": {
|
||||
"28001": { "kind": 0 },
|
||||
"28036": { "kind": 0 },
|
||||
"28038": { "kind": 0 },
|
||||
"28072": { "kind": 0 },
|
||||
"28074": { "kind": 0 }
|
||||
},
|
||||
"29": {
|
||||
"29071": { "kind": 0 },
|
||||
"29072": { "kind": 0 },
|
||||
"29075": { "kind": 0 },
|
||||
"29081": { "kind": 0 },
|
||||
"29082": { "kind": 0 },
|
||||
"29085": { "kind": 0 },
|
||||
"29101": { "kind": 0 },
|
||||
"29102": { "kind": 0 },
|
||||
"29103": { "kind": 0 }
|
||||
},
|
||||
"30": {
|
||||
"30038": { "kind": 0 },
|
||||
"30082": { "kind": 0 },
|
||||
"30083": { "kind": 0 },
|
||||
"30084": { "kind": 0 },
|
||||
"30085": { "kind": 0 },
|
||||
"30101": { "kind": 0 },
|
||||
"30102": { "kind": 0 },
|
||||
"30104": { "kind": 0 },
|
||||
"30105": { "kind": 0 }
|
||||
},
|
||||
"31": {
|
||||
"31021": { "kind": 0 },
|
||||
"31022": { "kind": 0 },
|
||||
"31023": { "kind": 0 },
|
||||
"31024": { "kind": 0 },
|
||||
"31025": { "kind": 0 },
|
||||
"31065": { "kind": 0 },
|
||||
"31066": { "kind": 0 },
|
||||
"31097": { "kind": 0 },
|
||||
"31098": { "kind": 0 },
|
||||
"31099": { "kind": 0 },
|
||||
"31100": { "kind": 0 },
|
||||
"31101": { "kind": 0 },
|
||||
"31102": { "kind": 0 },
|
||||
"31110": { "kind": 0 },
|
||||
"31112": { "kind": 0 },
|
||||
"31113": { "kind": 0 }
|
||||
},
|
||||
"32": {
|
||||
"32022": { "kind": 0 },
|
||||
"32049": { "kind": 0 },
|
||||
"32078": { "kind": 0 },
|
||||
"32079": { "kind": 0 },
|
||||
"32080": { "kind": 0 },
|
||||
"32081": { "kind": 0 },
|
||||
"32082": { "kind": 0 },
|
||||
"32083": { "kind": 0 },
|
||||
"32084": { "kind": 0 },
|
||||
"32085": { "kind": 0 },
|
||||
"32096": { "kind": 0 },
|
||||
"32097": { "kind": 0 },
|
||||
"32098": { "kind": 0 },
|
||||
"32019": { "kind": 0 },
|
||||
"32101": { "kind": 0 },
|
||||
"32102": { "kind": 0 },
|
||||
"32103": { "kind": 0 },
|
||||
"32104": { "kind": 0 },
|
||||
"32110": { "kind": 0 },
|
||||
"32111": { "kind": 0 }
|
||||
}
|
||||
}
|
||||
177
iidx@asphyxia/handlers/gamesystem.ts
Normal file
177
iidx@asphyxia/handlers/gamesystem.ts
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
import { IIDX_CPUS } from "../models/arena";
|
||||
import { GetVersion } from "../util";
|
||||
|
||||
export const gssysteminfo: EPR = async (info, data, send) => {
|
||||
const version = GetVersion(info);
|
||||
if (version < 24) return send.success();
|
||||
|
||||
let result: any = {
|
||||
arena_schedule: {
|
||||
phase: K.ITEM("u8", U.GetConfig("ArenaPhase")),
|
||||
start: K.ITEM("u32", 1605784800),
|
||||
end: K.ITEM("u32", 4102326000)
|
||||
},
|
||||
arena_music_difficult: [],
|
||||
maching_class_range: [],
|
||||
arena_cpu_define: [],
|
||||
}
|
||||
|
||||
// following datas are made up needs to figure out correct way to do it //
|
||||
let music_open = JSON.parse(await IO.ReadFile("data/music_open.json", "utf-8"));
|
||||
if (!_.isNil(music_open[version])) {
|
||||
result = Object.assign(result, { music_open: [] });
|
||||
|
||||
Object.keys(music_open).forEach(v => {
|
||||
Object.keys(music_open[v]).forEach(m => {
|
||||
if (Number(v) > version) return;
|
||||
|
||||
result.music_open.push({
|
||||
music_id: K.ITEM("s32", Number(m)),
|
||||
kind: K.ITEM("s32", music_open[v][m].kind),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 32:
|
||||
result.arena_schedule.phase = K.ITEM("u8", 3);
|
||||
result.arena_schedult = Object.assign(result.arena_schedule, { season: K.ITEM("u8", 0) }); // arena season for online //
|
||||
|
||||
case 31:
|
||||
result.arena_schedult = Object.assign(result.arena_schedule, { rule_type: K.ITEM("u8", 0) }); // arena rule for online //
|
||||
|
||||
result = Object.assign(result, { grade_course: [] });
|
||||
|
||||
// following datas are made up needs to figure out correct way to do it //
|
||||
let grade = JSON.parse(await IO.ReadFile("data/grade.json", "utf-8"));
|
||||
if (!_.isNil(grade[version])) {
|
||||
Object.keys(grade[version]).forEach(s => {
|
||||
Object.keys(grade[version][s]).forEach(c => {
|
||||
result.grade_course.push({
|
||||
play_style: K.ITEM("s32", Number(s)),
|
||||
grade_id: K.ITEM("s32", Number(c)),
|
||||
is_valid: K.ITEM("bool", true),
|
||||
music_id_0: K.ITEM("s32", grade[version][s][c].music_id[0]),
|
||||
class_id_0: K.ITEM("s32", grade[version][s][c].class_id[0]),
|
||||
music_id_1: K.ITEM("s32", grade[version][s][c].music_id[1]),
|
||||
class_id_1: K.ITEM("s32", grade[version][s][c].class_id[1]),
|
||||
music_id_2: K.ITEM("s32", grade[version][s][c].music_id[2]),
|
||||
class_id_2: K.ITEM("s32", grade[version][s][c].class_id[2]),
|
||||
music_id_3: K.ITEM("s32", grade[version][s][c].music_id[3]),
|
||||
class_id_3: K.ITEM("s32", grade[version][s][c].class_id[3]),
|
||||
index: K.ITEM("s32", result.grade_course.length),
|
||||
cube_num: K.ITEM("s32", 0),
|
||||
kind: K.ITEM("s32", grade[version][s][c].kind),
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// arena_music_difficult //
|
||||
for (let s = 0; s < 2; ++s) {
|
||||
for (let c = 0; c < 20; ++c) {
|
||||
result.arena_music_difficult.push({
|
||||
play_style: K.ITEM("s32", s),
|
||||
arena_class: K.ITEM("s32", c),
|
||||
low_difficult: K.ITEM("s32", 1),
|
||||
high_difficult: K.ITEM("s32", 12),
|
||||
is_leggendaria: K.ITEM("bool", 1),
|
||||
force_music_list_id: K.ITEM("s32", 0),
|
||||
});
|
||||
|
||||
result.maching_class_range.push({
|
||||
play_style: K.ITEM("s32", s),
|
||||
matching_class: K.ITEM("s32", c),
|
||||
low_arena_class: K.ITEM("s32", 1),
|
||||
high_arena_class: K.ITEM("s32", 20),
|
||||
});
|
||||
|
||||
result.arena_cpu_define.push({
|
||||
play_style: K.ITEM("s32", s),
|
||||
arena_class: K.ITEM("s32", c),
|
||||
grade_id: K.ITEM("s32", IIDX_CPUS[s][c][0]),
|
||||
low_music_difficult: K.ITEM("s32", IIDX_CPUS[s][c][1]),
|
||||
high_music_difficult: K.ITEM("s32", IIDX_CPUS[s][c][2]),
|
||||
is_leggendaria: K.ITEM("bool", IIDX_CPUS[s][c][3]),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 29:
|
||||
result = Object.assign(result, {
|
||||
CommonBossPhase: K.ATTR({ val: String(3) }),
|
||||
Event1InternalPhase: K.ATTR({ val: String(U.GetConfig("ch_event")) }),
|
||||
ExtraBossEventPhase: K.ATTR({ val: String(U.GetConfig("ch_extraboss")) }),
|
||||
isNewSongAnother12OpenFlg: K.ATTR({ val: String(Number(U.GetConfig("NewSongAnother12"))) }),
|
||||
gradeOpenPhase: K.ATTR({ val: String(U.GetConfig("Grade")) }),
|
||||
isEiseiOpenFlg: K.ATTR({ val: String(Number(U.GetConfig("Eisei"))) }),
|
||||
WorldTourismOpenList: K.ATTR({ val: String(-1) }),
|
||||
BPLBattleOpenPhase: K.ATTR({ val: String(2) }),
|
||||
});
|
||||
break;
|
||||
case 30:
|
||||
result = Object.assign(result, {
|
||||
CommonBossPhase: K.ATTR({ val: String(3) }),
|
||||
Event1InternalPhase: K.ATTR({ val: String(U.GetConfig("rs_event")) }),
|
||||
ExtraBossEventPhase: K.ATTR({ val: String(U.GetConfig("rs_extraboss")) }),
|
||||
isNewSongAnother12OpenFlg: K.ATTR({ val: String(Number(U.GetConfig("NewSongAnother12"))) }),
|
||||
gradeOpenPhase: K.ATTR({ val: String(U.GetConfig("Grade")) }),
|
||||
isEiseiOpenFlg: K.ATTR({ val: String(Number(U.GetConfig("Eisei"))) }),
|
||||
WorldTourismOpenList: K.ATTR({ val: String(-1) }),
|
||||
BPLBattleOpenPhase: K.ATTR({ val: String(2) }),
|
||||
})
|
||||
break;
|
||||
case 31:
|
||||
let totalMetron = 0;
|
||||
let eventData = await DB.Find(null, {
|
||||
collection: "event_1",
|
||||
version: version,
|
||||
event_data: "myepo_map",
|
||||
});
|
||||
|
||||
if (!_.isNil(eventData)) {
|
||||
eventData.forEach((res: any) => {
|
||||
totalMetron += Number(res.metron_total_get);
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(result, {
|
||||
CommonBossPhase: K.ATTR({ val: String(3) }),
|
||||
Event1Value: K.ATTR({ val: String(U.GetConfig("ep_event")) }),
|
||||
Event1Phase: K.ATTR({ val: String(U.GetConfig("ep_event1")) }),
|
||||
Event2Phase: K.ATTR({ val: String(U.GetConfig("ep_event2")) }),
|
||||
ExtraBossEventPhase: K.ATTR({ val: String(U.GetConfig("ep_extraboss")) }),
|
||||
isNewSongAnother12OpenFlg: K.ATTR({ val: String(Number(U.GetConfig("NewSongAnother12"))) }),
|
||||
isKiwamiOpenFlg: K.ATTR({ val: String(Number(U.GetConfig("Eisei"))) }),
|
||||
WorldTourismOpenList: K.ATTR({ val: String(-1) }),
|
||||
BPLBattleOpenPhase: K.ATTR({ val: String(2) }),
|
||||
UnlockLeggendaria: K.ATTR({ val: String(1) }),
|
||||
BPLSerialCodePhase: K.ATTR({ val: String(0) }),
|
||||
Event1AllPlayerTotalGetMetron: K.ATTR({ val: String(totalMetron) }), // total amount of all users metron //
|
||||
});
|
||||
break;
|
||||
case 32:
|
||||
result = Object.assign(result, {
|
||||
Event1Value: K.ATTR({ val: String(U.GetConfig("pc_event")) }), // TEST //
|
||||
Event1Phase: K.ATTR({ val: String(U.GetConfig("pc_event1")) }), // TEST //
|
||||
Event2Phase: K.ATTR({ val: String(U.GetConfig("pc_event2")) }), // TEST //
|
||||
ExtraBossEventPhase: K.ATTR({ val: String(U.GetConfig("pc_extraboss")) }), // TEST //
|
||||
isNewSongAnother12OpenFlg: K.ATTR({ val: String(Number(U.GetConfig("NewSongAnother12"))) }),
|
||||
isKiwamiOpenFlg: K.ATTR({ val: String(Number(U.GetConfig("Eisei"))) }),
|
||||
WorldTourismOpenList: K.ATTR({ val: String(-1) }),
|
||||
OldBPLBattleOpenPhase: K.ATTR({ val: String(3) }),
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return send.object(result);
|
||||
};
|
||||
208
iidx@asphyxia/handlers/grade.ts
Normal file
208
iidx@asphyxia/handlers/grade.ts
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
import { pcdata } from "../models/pcdata";
|
||||
import { grade } from "../models/grade";
|
||||
import { IDtoRef, GetVersion } from "../util";
|
||||
import { eisei_grade } from "../models/lightning";
|
||||
import { badge } from "../models/badge";
|
||||
|
||||
export const graderaised: EPR = async (info, data, send) => {
|
||||
const version = GetVersion(info);
|
||||
const iidxid = Number($(data).attr().iidxid);
|
||||
const refid = await IDtoRef(iidxid);
|
||||
const gid = Number($(data).attr().gid);
|
||||
const gtype = Number($(data).attr().gtype);
|
||||
|
||||
let cflg = Number($(data).attr().cflg);
|
||||
let achi = Number($(data).attr().achi);
|
||||
|
||||
let pcdata = await DB.FindOne<pcdata>(refid, { collection: "pcdata", version: version });
|
||||
let grade = await DB.FindOne<grade>(refid, {
|
||||
collection: "grade",
|
||||
version: version,
|
||||
style: gtype,
|
||||
gradeId: gid,
|
||||
});
|
||||
|
||||
if (version >= 23) cflg = Number($(data).attr().cstage);
|
||||
|
||||
const isTDJ = !_.isNil($(data).element("lightning_play_data")); // lightning model //
|
||||
const hasEiseiData = (!_.isNil($(data).element("eisei_data")) || !_.isNil($(data).element("eisei_grade_data")) || !_.isNil($(data).element("kiwami_data")));
|
||||
if (isTDJ && hasEiseiData) {
|
||||
let eisei_clear_type: number;
|
||||
let eisei_grade_id: number;
|
||||
let eisei_grade_type: number;
|
||||
let eisei_stage_num: number;
|
||||
let eisei_option: number;
|
||||
|
||||
let eisei_past_achievement: number[];
|
||||
let eisei_past_selected_course: number[];
|
||||
let eisei_max_past_achievement: number[];
|
||||
let eisei_max_past_selected_course: number[];
|
||||
|
||||
switch (version) {
|
||||
case 27:
|
||||
eisei_clear_type = Number($(data).attr("eisei_data").clear_type);
|
||||
eisei_grade_id = Number($(data).attr("eisei_data").grade_id);
|
||||
eisei_grade_type = Number($(data).attr("eisei_data").grade_type);
|
||||
eisei_stage_num = Number($(data).attr("eisei_data").stage_num);
|
||||
|
||||
eisei_past_achievement = $(data).element("eisei_data").numbers("past_achievement");
|
||||
eisei_max_past_achievement = $(data).element("eisei_data").numbers("max_past_achievement");
|
||||
break;
|
||||
case 30:
|
||||
eisei_clear_type = Number($(data).element("eisei_data").attr().clear_type);
|
||||
eisei_grade_id = Number($(data).element("eisei_data").attr().grade_id);
|
||||
eisei_grade_type = Number($(data).element("eisei_data").attr().grade_type);
|
||||
eisei_stage_num = Number($(data).element("eisei_data").attr().stage_num);
|
||||
eisei_option = Number($(data).element("eisei_data").attr().option);
|
||||
|
||||
eisei_past_achievement = $(data).element("eisei_data").numbers("past_achievement");
|
||||
eisei_past_selected_course = $(data).element("eisei_data").numbers("past_selected_course");
|
||||
eisei_max_past_achievement = $(data).element("eisei_data").numbers("max_past_achievement");
|
||||
eisei_max_past_selected_course = $(data).element("eisei_data").numbers("max_past_selected_course");
|
||||
break;
|
||||
case 31:
|
||||
case 32:
|
||||
eisei_clear_type = Number($(data).attr("kiwami_data").clear_type);
|
||||
eisei_grade_id = Number($(data).attr("kiwami_data").grade_id);
|
||||
eisei_grade_type = Number($(data).attr("kiwami_data").grade_type);
|
||||
eisei_stage_num = Number($(data).attr("kiwami_data").stage_num);
|
||||
eisei_option = Number($(data).attr("kiwami_data").option);
|
||||
|
||||
eisei_past_achievement = $(data).element("kiwami_data").numbers("past_achievement");
|
||||
eisei_past_selected_course = $(data).element("kiwami_data").numbers("past_selected_course");
|
||||
eisei_max_past_achievement = $(data).element("kiwami_data").numbers("max_past_achievement");
|
||||
eisei_max_past_selected_course = $(data).element("kiwami_data").numbers("max_past_selected_course");
|
||||
break;
|
||||
|
||||
default:
|
||||
eisei_clear_type = Number($(data).attr("eisei_grade_data").clear_type);
|
||||
eisei_grade_id = Number($(data).attr("eisei_grade_data").grade_id);
|
||||
eisei_grade_type = Number($(data).attr("eisei_grade_data").grade_type);
|
||||
eisei_stage_num = Number($(data).attr("eisei_grade_data").stage_num);
|
||||
|
||||
eisei_past_achievement = $(data).element("eisei_grade_data").numbers("past_achievement");
|
||||
eisei_past_selected_course = $(data).element("eisei_grade_data").numbers("past_selected_course");
|
||||
eisei_max_past_achievement = $(data).element("eisei_grade_data").numbers("max_past_achievement");
|
||||
eisei_max_past_selected_course = $(data).element("eisei_grade_data").numbers("max_past_selected_course");
|
||||
break;
|
||||
}
|
||||
|
||||
await DB.Upsert<eisei_grade>(
|
||||
refid,
|
||||
{
|
||||
collection: "eisei_grade",
|
||||
version: version,
|
||||
grade_type: eisei_grade_type,
|
||||
grade_id: eisei_grade_id,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
clear_type: eisei_clear_type,
|
||||
stage_num: eisei_stage_num,
|
||||
option: eisei_option,
|
||||
|
||||
past_achievement: eisei_past_achievement,
|
||||
past_selected_course: eisei_past_selected_course,
|
||||
max_past_achievement: eisei_max_past_achievement,
|
||||
max_past_selected_course: eisei_max_past_selected_course,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return send.object(
|
||||
K.ATTR({
|
||||
pnum: "1", // This isn't visible to user and seems leftover //
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
let updatePcdata = false;
|
||||
let updateGrade = false;
|
||||
if (_.isNil(pcdata)) return send.deny();
|
||||
if (_.isNil(grade)) {
|
||||
if (cflg == 4) {
|
||||
if (gtype == 0) pcdata.sgid = Math.max(gid, pcdata.sgid);
|
||||
else pcdata.dgid = Math.max(gid, pcdata.dgid);
|
||||
|
||||
updatePcdata = true;
|
||||
}
|
||||
|
||||
updateGrade = true;
|
||||
} else {
|
||||
if (cflg >= grade.maxStage || achi >= grade.archive) {
|
||||
cflg = Math.max(cflg, grade.maxStage);
|
||||
achi = Math.max(achi, grade.archive);
|
||||
|
||||
updateGrade = true;
|
||||
}
|
||||
|
||||
if (cflg == 4) {
|
||||
if (gtype == 0) pcdata.sgid = Math.max(gid, pcdata.sgid);
|
||||
else pcdata.dgid = Math.max(gid, pcdata.dgid);
|
||||
|
||||
updatePcdata = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updatePcdata) {
|
||||
await DB.Upsert<pcdata>(
|
||||
refid,
|
||||
{
|
||||
collection: "pcdata",
|
||||
version: version,
|
||||
},
|
||||
{
|
||||
$set: pcdata
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (updateGrade) {
|
||||
await DB.Upsert<grade>(
|
||||
refid,
|
||||
{
|
||||
collection: "grade",
|
||||
version: version,
|
||||
style: gtype,
|
||||
gradeId: gid,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
maxStage: cflg,
|
||||
archive: achi,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!_.isNil($(data).element("badge"))) {
|
||||
await DB.Upsert<badge>(
|
||||
refid,
|
||||
{
|
||||
collection: "badge",
|
||||
version: version,
|
||||
category_name: "grade",
|
||||
flg_id: Number($(data).attr("badge").badge_flg_id),
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
flg: Number($(data).attr("badge").badge_flg),
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let gradeUser = await DB.Find<grade>(null, {
|
||||
collection: "grade",
|
||||
version: version,
|
||||
style: gtype,
|
||||
gradeId: gid,
|
||||
maxStage: 4,
|
||||
});
|
||||
|
||||
return send.object(
|
||||
K.ATTR({
|
||||
pnum: String(gradeUser.length),
|
||||
})
|
||||
);
|
||||
};
|
||||
1033
iidx@asphyxia/handlers/music.ts
Normal file
1033
iidx@asphyxia/handlers/music.ts
Normal file
File diff suppressed because it is too large
Load Diff
5350
iidx@asphyxia/handlers/pc.ts
Normal file
5350
iidx@asphyxia/handlers/pc.ts
Normal file
File diff suppressed because it is too large
Load Diff
173
iidx@asphyxia/handlers/ranking.ts
Normal file
173
iidx@asphyxia/handlers/ranking.ts
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
import { expert, ranking } from "../models/ranking";
|
||||
import { profile } from "../models/profile";
|
||||
import { GetVersion, IDtoRef } from "../util";
|
||||
|
||||
export const rankingentry: EPR = async (info, data, send) => {
|
||||
// pside //
|
||||
const version = GetVersion(info);
|
||||
const refid = await IDtoRef(Number($(data).attr().iidxid));
|
||||
|
||||
const coid = Number($(data).attr().coid);
|
||||
const clid = Number($(data).attr().clid);
|
||||
|
||||
const opname = $(data).attr().opname;
|
||||
const oppid = Number($(data).attr().oppid);
|
||||
const pgnum = Number($(data).attr().pgnum);
|
||||
const gnum = Number($(data).attr().gnum);
|
||||
const opt = Number($(data).attr().opt);
|
||||
const opt2 = Number($(data).attr().opt2);
|
||||
const clr = Number($(data).attr().clr);
|
||||
|
||||
const exscore = (pgnum * 2 + gnum);
|
||||
const cstage = Number($(data).attr().cstage);
|
||||
|
||||
const expert_data = await DB.FindOne<expert>(refid, {
|
||||
collection: "expert",
|
||||
version: version,
|
||||
coid: coid,
|
||||
});
|
||||
|
||||
let pgArray = Array<number>(6).fill(0); // PGREAT //
|
||||
let gArray = Array<number>(6).fill(0); // GREAT //
|
||||
let cArray = Array<number>(6).fill(0); // CLEAR FLAGS //
|
||||
let optArray = Array<number>(6).fill(0); // USED OPTION (SP/DP) //
|
||||
let opt2Array = Array<number>(6).fill(0); // USED OPTION (DP) //
|
||||
let esArray = Array<number>(6).fill(0); // EXSCORE //
|
||||
if (_.isNil(expert_data)) {
|
||||
cArray[clid] = clr;
|
||||
pgArray[clid] = pgnum;
|
||||
gArray[clid] = gnum;
|
||||
optArray[clid] = opt;
|
||||
opt2Array[clid] = opt2;
|
||||
esArray[clid] = exscore;
|
||||
}
|
||||
else {
|
||||
cArray = expert_data.cArray;
|
||||
pgArray = expert_data.pgArray;
|
||||
gArray = expert_data.gArray;
|
||||
optArray = expert_data.optArray;
|
||||
opt2Array = expert_data.opt2Array;
|
||||
esArray = expert_data.esArray;
|
||||
|
||||
const pExscore = esArray[clid];
|
||||
if (exscore > pExscore) {
|
||||
pgArray[clid] = pgnum;
|
||||
gArray[clid] = gnum;
|
||||
optArray[clid] = opt;
|
||||
opt2Array[clid] = opt2;
|
||||
esArray[clid] = exscore;
|
||||
}
|
||||
|
||||
cArray[clid] = Math.max(cArray[clid], clr);
|
||||
}
|
||||
|
||||
await DB.Upsert<expert>(
|
||||
refid,
|
||||
{
|
||||
collection: "expert",
|
||||
version: version,
|
||||
coid: coid,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
cArray,
|
||||
pgArray,
|
||||
gArray,
|
||||
optArray,
|
||||
opt2Array,
|
||||
esArray,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const profile = await DB.FindOne<profile>(refid, {
|
||||
collection: "profile",
|
||||
});
|
||||
const name = profile.name;
|
||||
await DB.Upsert<ranking>(
|
||||
{
|
||||
collection: "ranking",
|
||||
version: version,
|
||||
coid: coid,
|
||||
clid: clid,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
pgnum: pgnum,
|
||||
gnum: gnum,
|
||||
name: name,
|
||||
opname: opname,
|
||||
pid: oppid,
|
||||
udate: 0,
|
||||
|
||||
exscore: exscore,
|
||||
maxStage: cstage,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let expertUser = await DB.Find<ranking>({
|
||||
collection: "ranking",
|
||||
version: version,
|
||||
coid: coid,
|
||||
clid: clid,
|
||||
});
|
||||
expertUser.sort((a: ranking, b: ranking) => b.exscore - a.exscore);
|
||||
let rankPos = expertUser.findIndex((a: ranking) => a.name == name);
|
||||
|
||||
return send.object(K.ATTR({
|
||||
anum: String(expertUser.length),
|
||||
jun: String(rankPos + 1),
|
||||
}));
|
||||
};
|
||||
|
||||
export const rankingoentry: EPR = async (info, data, send) => {
|
||||
const version = GetVersion(info);
|
||||
const refid = await IDtoRef(Number($(data).attr().iidxid));
|
||||
|
||||
const coid = Number($(data).attr().coid);
|
||||
const clid = Number($(data).attr().clid);
|
||||
|
||||
const pgnum = Number($(data).attr().pgnum);
|
||||
const gnum = Number($(data).attr().gnum);
|
||||
const opt = Number($(data).attr().opt);
|
||||
const opt2 = Number($(data).attr().opt2);
|
||||
const clr = Number($(data).attr().clr);
|
||||
|
||||
const exscore = (pgnum * 2 + gnum);
|
||||
|
||||
// TODO:: figure out what this does //
|
||||
|
||||
return send.success();
|
||||
};
|
||||
|
||||
export const rankinggetranker: EPR = async (info, data, send) => {
|
||||
const version = GetVersion(info);
|
||||
const ranking = await DB.Find<ranking>({
|
||||
collection: "ranking",
|
||||
version: version,
|
||||
coid: Number($(data).attr().coid),
|
||||
clid: Number($(data).attr().clid),
|
||||
});
|
||||
let result = {
|
||||
ranker: [],
|
||||
}
|
||||
|
||||
if (_.isNil(ranking)) return send.success();
|
||||
|
||||
ranking.sort((a: ranking, b: ranking) => b.exscore - a.exscore);
|
||||
ranking.forEach((res) => {
|
||||
result.ranker.push(
|
||||
K.ATTR({
|
||||
gnum: String(res.gnum),
|
||||
pgnum: String(res.pgnum),
|
||||
name: res.name,
|
||||
opname: res.opname,
|
||||
pid: String(res.pid),
|
||||
udate: String(res.udate),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return send.object(result);
|
||||
};
|
||||
89
iidx@asphyxia/handlers/shop.ts
Normal file
89
iidx@asphyxia/handlers/shop.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import { convention_data, shop_data } from "../models/shop";
|
||||
import { GetVersion } from "../util";
|
||||
|
||||
export const shopgetname: EPR = async (info, data, send) => {
|
||||
const shop_data = await DB.FindOne<shop_data>({
|
||||
collection: "shop_data",
|
||||
});
|
||||
|
||||
if (_.isNil(shop_data)) {
|
||||
await DB.Insert<shop_data>({
|
||||
collection: "shop_data",
|
||||
|
||||
opname: "CORE",
|
||||
pid: 53,
|
||||
cls_opt: 0,
|
||||
});
|
||||
|
||||
return send.object(
|
||||
K.ATTR({
|
||||
opname: "CORE",
|
||||
pid: "53",
|
||||
cls_opt: "0",
|
||||
hr: "0",
|
||||
mi: "0",
|
||||
}),
|
||||
{ encoding: "shift_jis" }
|
||||
);
|
||||
}
|
||||
|
||||
return send.object(
|
||||
K.ATTR({
|
||||
opname: shop_data.opname,
|
||||
pid: String(shop_data.pid),
|
||||
cls_opt: String(shop_data.cls_opt),
|
||||
hr: "0",
|
||||
mi: "0",
|
||||
}),
|
||||
{ encoding: "shift_jis" }
|
||||
);
|
||||
};
|
||||
|
||||
export const shopsavename: EPR = async (info, data, send) => {
|
||||
// removed saving code as opname attribute being sent as shift_jis but KDataReader read as utf-8 //
|
||||
return send.success();
|
||||
};
|
||||
|
||||
export const shopgetconvention: EPR = async (info, data, send) => {
|
||||
const version = GetVersion(info);
|
||||
|
||||
const convention_data = await DB.FindOne<convention_data>({
|
||||
collection: "shop_convention",
|
||||
version: version,
|
||||
});
|
||||
if (_.isNil(convention_data)) return send.deny();
|
||||
|
||||
return send.object(
|
||||
K.ATTR({
|
||||
music_0: String(convention_data.music_0),
|
||||
music_1: String(convention_data.music_1),
|
||||
music_2: String(convention_data.music_2),
|
||||
music_3: String(convention_data.music_3),
|
||||
},
|
||||
{
|
||||
valid: K.ITEM("bool", convention_data.valid),
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const shopsetconvention: EPR = async (info, data, send) => {
|
||||
const version = GetVersion(info);
|
||||
|
||||
await DB.Upsert<convention_data>(
|
||||
{
|
||||
collection: "shop_convention",
|
||||
version: version,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
music_0: $(data).number("music_0"),
|
||||
music_1: $(data).number("music_1"),
|
||||
music_2: $(data).number("music_2"),
|
||||
music_3: $(data).number("music_3"),
|
||||
valid: $(data).bool("valid"),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return send.success();
|
||||
};
|
||||
429
iidx@asphyxia/handlers/webui.ts
Normal file
429
iidx@asphyxia/handlers/webui.ts
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
import { profile } from "../models/profile";
|
||||
import { rival } from "../models/rival";
|
||||
import { custom } from "../models/custom";
|
||||
import { score, old_score } from "../models/score";
|
||||
import { lightning_custom } from "../models/lightning";
|
||||
|
||||
export const updateRivalSettings = async (data) => {
|
||||
let update_array = [];
|
||||
|
||||
if (!(_.isEmpty(data.sp_rival1))) {
|
||||
let update_data = {
|
||||
play_style: 1,
|
||||
index: 0,
|
||||
rival_refid: data.sp_rival1,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 1,
|
||||
index: 0,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.sp_rival2))) {
|
||||
let update_data = {
|
||||
play_style: 1,
|
||||
index: 1,
|
||||
rival_refid: data.sp_rival2,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 1,
|
||||
index: 1,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.sp_rival3))) {
|
||||
let update_data = {
|
||||
play_style: 1,
|
||||
index: 2,
|
||||
rival_refid: data.sp_rival3,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 1,
|
||||
index: 2,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.sp_rival4))) {
|
||||
let update_data = {
|
||||
play_style: 1,
|
||||
index: 3,
|
||||
rival_refid: data.sp_rival4,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 1,
|
||||
index: 3,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.sp_rival5))) {
|
||||
let update_data = {
|
||||
play_style: 1,
|
||||
index: 4,
|
||||
rival_refid: data.sp_rival5,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 1,
|
||||
index: 4,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.dp_rival1))) {
|
||||
let update_data = {
|
||||
play_style: 2,
|
||||
index: 0,
|
||||
rival_refid: data.dp_rival1,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 2,
|
||||
index: 0,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.dp_rival2))) {
|
||||
let update_data = {
|
||||
play_style: 2,
|
||||
index: 1,
|
||||
rival_refid: data.dp_rival2,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 2,
|
||||
index: 1,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.dp_rival3))) {
|
||||
let update_data = {
|
||||
play_style: 2,
|
||||
index: 2,
|
||||
rival_refid: data.dp_rival3,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 2,
|
||||
index: 2,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.dp_rival4))) {
|
||||
let update_data = {
|
||||
play_style: 2,
|
||||
index: 3,
|
||||
rival_refid: data.dp_rival4,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 2,
|
||||
index: 3,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!(_.isEmpty(data.dp_rival5))) {
|
||||
let update_data = {
|
||||
play_style: 2,
|
||||
index: 4,
|
||||
rival_refid: data.dp_rival5,
|
||||
};
|
||||
|
||||
update_array.push(update_data);
|
||||
} else {
|
||||
await DB.Remove<rival>(data.refid,
|
||||
{
|
||||
collection: "rival",
|
||||
play_style: 2,
|
||||
index: 4,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
for (let i = 0; i < update_array.length; i++) {
|
||||
await DB.Upsert<rival>(data.refid, {
|
||||
collection: "rival",
|
||||
play_style: update_array[i].play_style,
|
||||
index: update_array[i].index,
|
||||
}, {
|
||||
$set: {
|
||||
rival_refid: update_array[i].rival_refid,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export const updateCustomSettings = async (data) => {
|
||||
const profile = await DB.FindOne<profile>(data.refid, {
|
||||
collection: "profile",
|
||||
});
|
||||
|
||||
let customize = {
|
||||
frame: Number(data.frame),
|
||||
turntable: Number(data.turntable),
|
||||
note_burst: Number(data.note_burst),
|
||||
menu_music: Number(data.menu_music),
|
||||
lane_cover: Number(data.lane_cover),
|
||||
category_vox: Number(data.category_vox),
|
||||
note_skin: Number(data.note_skin),
|
||||
full_combo_splash: Number(data.full_combo_splash),
|
||||
disable_musicpreview: StoB(data.disable_musicpreview),
|
||||
|
||||
note_beam: Number(data.note_beam),
|
||||
judge_font: Number(data.judge_font),
|
||||
pacemaker_cover: Number(data.pacemaker_cover),
|
||||
vefx_lock: StoB(data.vefx_lock),
|
||||
effect: Number(data.effect),
|
||||
bomb_size: Number(data.bomb_size),
|
||||
disable_hcn_color: StoB(data.disable_hcn_color),
|
||||
first_note_preview: Number(data.first_note_preview),
|
||||
|
||||
rank_folder: StoB(data.rank_folder),
|
||||
clear_folder: StoB(data.clear_folder),
|
||||
diff_folder: StoB(data.diff_folder),
|
||||
alpha_folder: StoB(data.alpha_folder),
|
||||
rival_folder: StoB(data.rival_folder),
|
||||
rival_battle_folder: StoB(data.rival_battle_folder),
|
||||
rival_info: StoB(data.rival_info),
|
||||
hide_playcount: StoB(data.hide_playcount),
|
||||
disable_graph_cutin: StoB(data.disable_graph_cutin),
|
||||
classic_hispeed: StoB(data.classic_hispeed),
|
||||
rival_played_folder: StoB(data.rival_played_folder),
|
||||
hide_iidxid: StoB(data.hide_iidxid),
|
||||
disable_beginner_option: StoB(data.disable_beginner_option),
|
||||
|
||||
qpro_head: Number(data.qpro_head),
|
||||
qpro_hair: Number(data.qpro_hair),
|
||||
qpro_face: Number(data.qpro_face),
|
||||
qpro_hand: Number(data.qpro_hand),
|
||||
qpro_body: Number(data.qpro_body),
|
||||
qpro_back: Number(data.qpro_back),
|
||||
}
|
||||
|
||||
await DB.Upsert<custom>(data.refid, {
|
||||
collection: "custom",
|
||||
version: Number(data.version)
|
||||
},
|
||||
{
|
||||
$set: customize
|
||||
});
|
||||
|
||||
if (!_.isEmpty(data.name) && data.name != profile.name) {
|
||||
// TODO:: check name is in valid format //
|
||||
await DB.Upsert<profile>(data.refid, {
|
||||
collection: "profile",
|
||||
}, {
|
||||
$set: {
|
||||
name: data.name
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (data.version > 27) {
|
||||
await DB.Upsert<lightning_custom>(data.refid, {
|
||||
collection: "lightning_custom",
|
||||
version: Number(data.version)
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
premium_skin: Number(data.lm_skin),
|
||||
premium_bg: Number(data.lm_bg),
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const importScoreData = async (data, send: WebUISend) => {
|
||||
if (_.isEmpty(data.data)) {
|
||||
console.error("[Score Importer] Supplied data is empty");
|
||||
return send.error(400, "Empty data");
|
||||
}
|
||||
|
||||
let content = null;
|
||||
let version = 0;
|
||||
let count = 0;
|
||||
try {
|
||||
content = JSON.parse(data.data);
|
||||
version = content.version;
|
||||
count = content.count;
|
||||
}
|
||||
catch {
|
||||
console.error("[Score Importer] Invaild data has been supplied");
|
||||
return send.error(400, "Invalid data");
|
||||
}
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
let sd_ver1: old_score[] = content.data;
|
||||
for (let a = 0; a < count; a++) {
|
||||
let result = {
|
||||
pgArray: Array<number>(10).fill(0),
|
||||
gArray: Array<number>(10).fill(0),
|
||||
mArray: Array<number>(10).fill(-1),
|
||||
cArray: Array<number>(10).fill(0),
|
||||
rArray: Array<number>(10).fill(-1),
|
||||
esArray: Array<number>(10).fill(0),
|
||||
|
||||
optArray: Array<number>(10).fill(0),
|
||||
opt2Array: Array<number>(10).fill(0),
|
||||
}
|
||||
|
||||
if (!_.isNil(sd_ver1[a].spmArray)) {
|
||||
for (let b = 0; b < 5; b++) {
|
||||
result.cArray[b] = sd_ver1[a].spmArray[2 + b];
|
||||
result.esArray[b] = sd_ver1[a].spmArray[7 + b];
|
||||
if (sd_ver1[a].spmArray[12 + b] != -1) result.mArray[b] = sd_ver1[a].spmArray[12 + b];
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.isNil(sd_ver1[a].dpmArray)) {
|
||||
for (let b = 5; b < 10; b++) {
|
||||
result.cArray[b] = sd_ver1[a].dpmArray[2 + (b - 5)];
|
||||
result.esArray[b] = sd_ver1[a].dpmArray[7 + (b - 5)];
|
||||
if (sd_ver1[a].dpmArray[12 + (b - 5)] != -1) result.mArray[b] = sd_ver1[a].dpmArray[12 + (b - 5)];
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.isNil(sd_ver1[a].optArray)) {
|
||||
result.optArray = sd_ver1[a].optArray;
|
||||
}
|
||||
|
||||
if (!_.isNil(sd_ver1[a].opt2Array)) {
|
||||
result.opt2Array = sd_ver1[a].opt2Array;
|
||||
}
|
||||
|
||||
for (let b = 0; b < 10; b++) {
|
||||
if (_.isNil(sd_ver1[a][b])) continue;
|
||||
result[b] = sd_ver1[a][b];
|
||||
|
||||
if (!_.isNil(sd_ver1[a][b + 10])) {
|
||||
result[b + 10] = sd_ver1[a][b + 10];
|
||||
}
|
||||
}
|
||||
|
||||
await DB.Upsert<score>(data.refid,
|
||||
{
|
||||
collection: "score",
|
||||
mid: sd_ver1[a].music_id
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
...result
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
let sd_ver2: score[] = content.data;
|
||||
for (let a = 0; a < count; a++) {
|
||||
let result = {
|
||||
pgArray: sd_ver2[a].pgArray,
|
||||
gArray: sd_ver2[a].gArray,
|
||||
mArray: sd_ver2[a].mArray,
|
||||
cArray: sd_ver2[a].cArray,
|
||||
rArray: sd_ver2[a].rArray,
|
||||
esArray: sd_ver2[a].esArray,
|
||||
|
||||
optArray: sd_ver2[a].optArray,
|
||||
opt2Array: sd_ver2[a].opt2Array,
|
||||
};
|
||||
|
||||
for (let b = 0; b < 10; b++) {
|
||||
if (_.isNil(sd_ver2[a][b])) continue;
|
||||
result[b] = sd_ver2[a][b];
|
||||
|
||||
if (!_.isNil(sd_ver2[a][b + 10])) {
|
||||
result[b + 10] = sd_ver2[a][b + 10];
|
||||
}
|
||||
}
|
||||
|
||||
await DB.Upsert<score>(data.refid,
|
||||
{
|
||||
collection: "score",
|
||||
mid: sd_ver2[a].mid
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
...result,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error("[Score Importer] Unregistered score data version");
|
||||
return send.error(400, "Invalid data version");
|
||||
}
|
||||
}
|
||||
|
||||
export const exportScoreData = async (data, send: WebUISend) => {
|
||||
const score = await DB.Find<score>(data.refid, {
|
||||
collection: "score"
|
||||
});
|
||||
|
||||
if (score == null) return send.error(400, "No data");
|
||||
|
||||
let result = {
|
||||
version: 2,
|
||||
count: score.length,
|
||||
data: {
|
||||
...score,
|
||||
}
|
||||
}
|
||||
|
||||
send.json(result);
|
||||
}
|
||||
|
||||
function StoB(value: string) {
|
||||
return value == "on" ? true : false;
|
||||
};
|
||||
598
iidx@asphyxia/index.ts
Normal file
598
iidx@asphyxia/index.ts
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
import { pccommon, pcreg, pcget, pcgetname, pctakeover, pcvisit, pcsave, pcoldget, pcgetlanegacha, pcdrawlanegacha, pcshopregister } from "./handlers/pc";
|
||||
import { shopgetname, shopsavename, shopgetconvention, shopsetconvention } from "./handlers/shop";
|
||||
import { musicreg, musicgetrank, musicappoint, musicarenacpu, musiccrate, musicbreg, musicgetralive } from "./handlers/music";
|
||||
import { graderaised } from "./handlers/grade";
|
||||
import { gssysteminfo } from "./handlers/gamesystem";
|
||||
import { updateRivalSettings, updateCustomSettings, importScoreData, exportScoreData } from "./handlers/webui";
|
||||
import { GetVersion } from "./util";
|
||||
import { rankingentry, rankinggetranker, rankingoentry } from "./handlers/ranking";
|
||||
|
||||
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.Contributor("duel0213");
|
||||
|
||||
R.GameCode("GLD");
|
||||
R.GameCode("HDD");
|
||||
R.GameCode("I00");
|
||||
R.GameCode("JDJ");
|
||||
R.GameCode("JDZ");
|
||||
R.GameCode("KDZ");
|
||||
R.GameCode("LDJ");
|
||||
|
||||
// common //
|
||||
R.Config("BeatPhase", {
|
||||
name: "Beat #",
|
||||
desc: "1 / 2 / 3 / FREE", // This can be event phase on old versions //
|
||||
type: "integer",
|
||||
default: 3, // BEAT FREE //
|
||||
});
|
||||
// ~ Resort Anthem (common) / /
|
||||
R.Config("cmd_gmbl", {
|
||||
name: "G.JUDGE",
|
||||
desc: "Enable G.JUDGE Command (~ Resort Anthem)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("cmd_gmbla", {
|
||||
name: "G.JUDGE-A",
|
||||
desc: "Enable G.JUDGE-A Command (~ Resort Anthem)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("cmd_regl", {
|
||||
name: "REGUL-SPEED",
|
||||
desc: "Enable REGUL-SPEED Command (~ Resort Anthem)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("cmd_rndp", {
|
||||
name: "RANDOM+",
|
||||
desc: "Enable RANDOM+ Command (~ Resort Anthem)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("cmd_hrnd", {
|
||||
name: "H-RANDOM",
|
||||
desc: "Enable H-RANDOM Command (~ Resort Anthem)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("cmd_alls", {
|
||||
name: "ALL-SCRATCH",
|
||||
desc: "Enable ALL-SCRATCH Command (~ Resort Anthem)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
// SPADA ~ (common) //
|
||||
R.Config("NewSongAnother12", {
|
||||
name: "New Song Another",
|
||||
desc: "Enables ANOTHER difficulty of current version's new songs that has Level 12",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
// PENDUAL ~ (common) //
|
||||
R.Config("ExpertPhase", {
|
||||
name: "Expert Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("ExpertRandomPhase", {
|
||||
name: "Expert Random Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
// HEROIC VERSE ~ (common) //
|
||||
R.Config("ArenaPhase", {
|
||||
name: "ARENA Phase",
|
||||
type: "integer",
|
||||
default: 2, // ADVERTISE //
|
||||
});
|
||||
// BISTROVER ~ (common) //
|
||||
R.Config("MovieUpload", {
|
||||
name: "Movie Upload URL",
|
||||
type: "string",
|
||||
desc: "API address for play video uploading feature (JSON)",
|
||||
default: "http://localhost/"
|
||||
});
|
||||
R.Config("Eisei", {
|
||||
name: "Eisei Grade Courses",
|
||||
desc: "Enable EISEI/KIWAMI Grade Courses",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
// CastHour ~ RESIDENT (common) //
|
||||
R.Config("Grade", {
|
||||
name: "Grade Open Phase",
|
||||
desc: "RED / KAIDEN",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
})
|
||||
|
||||
// SIRIUS //
|
||||
R.Config("sr_league", {
|
||||
name: "League Phase (SR)",
|
||||
type: "integer",
|
||||
default: 0,
|
||||
});
|
||||
|
||||
// Resort Anthem //
|
||||
R.Config("ra_league", {
|
||||
name: "League Phase (RA)",
|
||||
type: "integer",
|
||||
default: 0,
|
||||
});
|
||||
R.Config("ra_story", {
|
||||
name: "Story Phase (RA)",
|
||||
type: "integer",
|
||||
default: 0,
|
||||
});
|
||||
R.Config("ra_event", {
|
||||
name: "Tour Phase (RA)",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("ra_lincle", {
|
||||
name: "Lincle LINK Phase (RA)",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
|
||||
// Lincle //
|
||||
R.Config("lc_lincle", {
|
||||
name: "Lincle LINK Phase (LC)",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("lc_boss", {
|
||||
name: "Lincle Kingdom Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
|
||||
// tricoro //
|
||||
R.Config("tr_limit", {
|
||||
name: "Limit Burst Phase (TR)",
|
||||
type: "integer",
|
||||
default: 24, // TODO:: verify //
|
||||
});
|
||||
R.Config("tr_boss", {
|
||||
name: "Event Phase (TR)",
|
||||
desc: "RED / BLUE / YELLOW",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("tr_red", {
|
||||
name: "RED Phase",
|
||||
desc: "LEGEND CROSS Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("tr_yellow", {
|
||||
name: "YELLOW Phase",
|
||||
desc: "ぼくらの宇宙戦争 Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("tr_medal", {
|
||||
name: "Medal Phase (TR)",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("tr_cafe", {
|
||||
name: "Café de Tran",
|
||||
desc: "Enable Café de Tran Event (tricoro)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("tr_tripark", {
|
||||
name: "Everyone's SPACEWAR!!",
|
||||
desc: "Enable クプロ・ミミニャミ・パステルくんのみんなで宇宙戦争!! Event (tricoro)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
|
||||
// SPADA //
|
||||
R.Config("sp_limit", {
|
||||
name: "Limit Burst Phase (SP)",
|
||||
type: "integer",
|
||||
default: 24,
|
||||
});
|
||||
R.Config("sp_boss", {
|
||||
name: "Event Phase (SP)",
|
||||
desc: "Spada†leggendaria Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("sp_boss1", {
|
||||
name: "Qprogue Phase (SP)",
|
||||
type: "integer",
|
||||
default: 4,
|
||||
});
|
||||
R.Config("sp_cafe", {
|
||||
name: "Café de Tran",
|
||||
desc: "Enable Café de Tran Event (SPADA)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("sp_tripark", {
|
||||
name: "Everyone's SPACEWAR!!",
|
||||
desc: "Enable クプロ・ミミニャミ・パステルくんのみんなで宇宙戦争!! Event (SPADA)",
|
||||
type: "boolean",
|
||||
default: true,
|
||||
});
|
||||
R.Config("sp_triparkskip", {
|
||||
name: "Everyone's SPACEWAR!! Skip",
|
||||
desc: "Skips クプロ・ミミニャミ・パステルくんのみんなで宇宙戦争!! Event Scenes",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("sp_superstar", {
|
||||
name: "SUPER STAR -MITSURU-",
|
||||
desc: "SUPER STAR 満 -MITSURU- 完全復活祭 Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
|
||||
// PENDUAL //
|
||||
R.Config("pd_preplay", {
|
||||
name: "SUPER FUTURE 2323 Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("pd_tohoremix", {
|
||||
name: "BEMANI X TOHO",
|
||||
desc: "BEMANI×TOHO REITAISAI 2015 project Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("pd_limit", {
|
||||
name: "Chrono Chaser Phase",
|
||||
type: "integer",
|
||||
default: 9,
|
||||
});
|
||||
R.Config("pd_boss", {
|
||||
name: "Event Phase (PD)",
|
||||
desc: "Chrono Seeker / Qpronicle Chord / PENDUAL TALISMAN",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("pd_chronodiver", {
|
||||
name: "Chrono Seeker",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("pd_qproniclechord", {
|
||||
name: "Qpronicle Chord",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("pd_cccollabo", {
|
||||
name: "Coca-Cola×BEMANI",
|
||||
desc: "Coca-Cola×BEMANI 店舗限定ロケテスト Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("pd_timephase", {
|
||||
name: "Time Phase",
|
||||
type: "integer",
|
||||
desc: "Default / Present / Future",
|
||||
default: 0,
|
||||
});
|
||||
|
||||
// copula //
|
||||
R.Config("cp_boss", {
|
||||
name: "Event Phase (CP)",
|
||||
desc: "開通!とことこライン / Mystery Line",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("cp_event1", {
|
||||
name: "開通!とことこライン",
|
||||
desc: "開通!とことこライン Phase",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
R.Config("cp_event2", {
|
||||
name: "Mystery Line",
|
||||
desc: "Mystery Line Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("cp_extraboss",
|
||||
{
|
||||
name: "Extra Boss Phase (CP)",
|
||||
desc: "Extra Boss Phase",
|
||||
type: "integer",
|
||||
default: 30,
|
||||
});
|
||||
R.Config("cp_bemanisummer", {
|
||||
name: "BEMANI Summer 2016",
|
||||
desc: "NEW Generation 夏の流星フェスタ2016 Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
|
||||
// SINOBUZ //
|
||||
R.Config("sb_boss", {
|
||||
name: "Event Phase (SB)",
|
||||
desc: "攻城シノバズ伝 / 忍々七鍵伝",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("sb_event1", {
|
||||
name: "攻城シノバズ伝",
|
||||
desc: "攻城シノバズ伝 Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("sb_event2", {
|
||||
name: "忍々七鍵伝",
|
||||
desc: "忍々七鍵伝 Phase",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
R.Config("sb_extraboss",
|
||||
{
|
||||
name: "BUZRA ARTS",
|
||||
desc: "BUZRA ARTS Phase",
|
||||
type: "integer",
|
||||
default: 35,
|
||||
});
|
||||
|
||||
// CANNON BALLERS //
|
||||
R.Config("cb_boss", {
|
||||
name: "Event Phase (SB)",
|
||||
desc: "激走!キャノンレーサー",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
R.Config("cb_event1", {
|
||||
name: "激走!キャノンレーサー",
|
||||
desc: "激走!キャノンレーサー Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("cb_extraboss",
|
||||
{
|
||||
name: "IIDX AIR RACE",
|
||||
desc: "IIDX AIR RACE Phase",
|
||||
type: "integer",
|
||||
default: 35,
|
||||
});
|
||||
|
||||
// Rootage //
|
||||
R.Config("rt_boss", {
|
||||
name: "Event Phase (RT)",
|
||||
desc: "蜃気楼の図書館 / DELABITY LABORATORY",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("rt_event1", {
|
||||
name: "蜃気楼の図書館",
|
||||
desc: "蜃気楼の図書館 Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("rt_event2", {
|
||||
name: "DELABITY LABORATORY",
|
||||
desc: "DELABITY LABORATORY Phase",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("rt_extraboss",
|
||||
{
|
||||
name: "ARC SCORE",
|
||||
desc: "ARC SCORE Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
|
||||
// HEROIC VERSE //
|
||||
R.Config("hv_boss", {
|
||||
name: "Event Phase (HV)",
|
||||
desc: "HEROIC WORKOUT!!",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
R.Config("hv_event", {
|
||||
name: "HEROIC WORKOUT!!",
|
||||
desc: "HEROIC WORKOUT!! Phase",
|
||||
type: "integer",
|
||||
default: 4,
|
||||
});
|
||||
R.Config("hv_extraboss",
|
||||
{
|
||||
name: "SHADOW REBELLION",
|
||||
desc: "SHADOW REBELLION Phase",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
|
||||
// BISTROVER //
|
||||
R.Config("bo_boss", {
|
||||
name: "Event Phase (BO)",
|
||||
desc: "召しませ!BISTROVER",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
R.Config("bo_extraboss", {
|
||||
name: "BISTRO LANDING",
|
||||
desc: "BISTRO LANDING Phase",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
R.Config("bo_event", {
|
||||
name: "召しませ!BISTROVER",
|
||||
desc: "召しませ!BISTROVER Phase",
|
||||
type: "integer",
|
||||
default: 1,
|
||||
});
|
||||
|
||||
// CastHour //
|
||||
R.Config("ch_event", {
|
||||
name: "CastHour Space",
|
||||
desc: "CastHour Space Phase",
|
||||
type: "integer",
|
||||
default: 5,
|
||||
});
|
||||
R.Config("ch_extraboss", {
|
||||
name: "Extra Boss Phase (CH)",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
|
||||
// RESIDENT //
|
||||
R.Config("rs_event", {
|
||||
name: "RESIDENT PARTY",
|
||||
desc: "RESIDENT PARTY Phase",
|
||||
type: "integer",
|
||||
default: 5,
|
||||
});
|
||||
R.Config("rs_extraboss", {
|
||||
name: "Extra Boss Phase (RS)",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
|
||||
// EPOLIS //
|
||||
R.Config("ep_event", {
|
||||
name: "Event Phase (EP)",
|
||||
desc: "MY POLIS DESIGNER / EPOLIS RESTORATION",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("ep_event1", {
|
||||
name: "MY POLIS DESIGNER",
|
||||
desc: "MY POLIS DESIGNER Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("ep_event2", {
|
||||
name: "EPOLIS RESTORATION",
|
||||
desc: "EPOLIS RESTORATION Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("ep_extraboss", {
|
||||
name: "EPOLIS SINGULARITY",
|
||||
desc: "EPOLIS SINGULARITY Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
|
||||
// Pinky Crush //
|
||||
R.Config("pc_event", {
|
||||
name: "Event Phase (PC)",
|
||||
desc: "ピンキージャンプアップ! / ピンキーアンダーグラウンド",
|
||||
type: "integer",
|
||||
default: 2,
|
||||
});
|
||||
R.Config("pc_event1", {
|
||||
name: "ピンキージャンプアップ!",
|
||||
desc: "ピンキージャンプアップ! Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("pc_event2", {
|
||||
name: "ピンキーアンダーグラウンド",
|
||||
desc: "ピンキーアンダーグラウンド Phase",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
R.Config("pc_extraboss", {
|
||||
name: "Extra Boss Phase (PC)",
|
||||
type: "integer",
|
||||
default: 3,
|
||||
});
|
||||
|
||||
// TODO:: Make a list of customize items //
|
||||
R.WebUIEvent("iidxGetProfile", async (data, send: WebUISend) => {
|
||||
const pcdata = await DB.FindOne(data.refid, {
|
||||
collection: "pcdata",
|
||||
version: Number(data.version),
|
||||
});
|
||||
|
||||
return send.json({
|
||||
pcdata,
|
||||
});
|
||||
});
|
||||
R.WebUIEvent("iidxGetSetting", async (data, send: WebUISend) => {
|
||||
const custom = await DB.FindOne(data.refid, {
|
||||
collection: "custom",
|
||||
version: Number(data.version),
|
||||
});
|
||||
|
||||
const lm_custom = await DB.FindOne(data.refid, {
|
||||
collection: "lightning_custom",
|
||||
version: Number(data.version),
|
||||
});
|
||||
|
||||
return send.json({
|
||||
custom,
|
||||
lm_custom,
|
||||
});
|
||||
});
|
||||
R.WebUIEvent("iidxUpdateRival", updateRivalSettings);
|
||||
R.WebUIEvent("iidxUpdateCustom", updateCustomSettings);
|
||||
R.WebUIEvent("iidxImportScoreData", importScoreData);
|
||||
R.WebUIEvent("iidxExportScoreData", exportScoreData);
|
||||
|
||||
const MultiRoute = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`${method}`, handler);
|
||||
R.Route(`IIDX21${method}`, handler);
|
||||
R.Route(`IIDX22${method}`, handler);
|
||||
R.Route(`IIDX23${method}`, handler);
|
||||
R.Route(`IIDX24${method}`, handler);
|
||||
R.Route(`IIDX25${method}`, handler);
|
||||
R.Route(`IIDX26${method}`, handler);
|
||||
R.Route(`IIDX27${method}`, handler);
|
||||
R.Route(`IIDX28${method}`, handler);
|
||||
R.Route(`IIDX29${method}`, handler);
|
||||
R.Route(`IIDX30${method}`, handler);
|
||||
R.Route(`IIDX31${method}`, handler);
|
||||
R.Route(`IIDX32${method}`, handler);
|
||||
};
|
||||
|
||||
MultiRoute("pc.common", pccommon);
|
||||
MultiRoute("pc.reg", pcreg);
|
||||
MultiRoute("pc.get", pcget);
|
||||
MultiRoute("pc.getname", pcgetname);
|
||||
MultiRoute("pc.oldget", pcoldget);
|
||||
MultiRoute("pc.takeover", pctakeover);
|
||||
MultiRoute("pc.visit", pcvisit);
|
||||
MultiRoute("pc.save", pcsave);
|
||||
MultiRoute("pc.shopregister", pcshopregister);
|
||||
MultiRoute("pc.getLaneGachaTicket", pcgetlanegacha);
|
||||
MultiRoute("pc.drawLaneGacha", pcdrawlanegacha);
|
||||
MultiRoute("pc.consumeLaneGachaTicket", true);
|
||||
|
||||
MultiRoute("shop.getname", shopgetname);
|
||||
MultiRoute("shop.savename", shopsavename);
|
||||
MultiRoute("shop.getconvention", shopgetconvention);
|
||||
MultiRoute("shop.setconvention", shopsetconvention);
|
||||
|
||||
MultiRoute("music.crate", musiccrate);
|
||||
MultiRoute("music.getrank", musicgetrank);
|
||||
MultiRoute("music.getralive", musicgetralive);
|
||||
MultiRoute("music.appoint", musicappoint);
|
||||
MultiRoute("music.reg", musicreg);
|
||||
MultiRoute("music.breg", musicbreg);
|
||||
MultiRoute("music.arenaCPU", musicarenacpu);
|
||||
|
||||
MultiRoute("grade.raised", graderaised);
|
||||
|
||||
MultiRoute("ranking.entry", rankingentry);
|
||||
MultiRoute("ranking.oentry", rankingoentry);
|
||||
MultiRoute("ranking.getranker", rankinggetranker);
|
||||
|
||||
MultiRoute("gameSystem.systemInfo", gssysteminfo);
|
||||
|
||||
R.Unhandled((req: EamuseInfo, data: any, send: EamuseSend) => {
|
||||
console.warn(`Unhandled Request : [${GetVersion(req)}], ${req.module}.${req.method}, ${JSON.stringify(data)}`);
|
||||
return send.success();
|
||||
});
|
||||
}
|
||||
50
iidx@asphyxia/models/activity.ts
Normal file
50
iidx@asphyxia/models/activity.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
export interface activity {
|
||||
collection: "activity";
|
||||
version: number;
|
||||
|
||||
date: number;
|
||||
play_style: number;
|
||||
|
||||
music_num: number;
|
||||
play_time: number;
|
||||
keyboard_num: number;
|
||||
scratch_num: number;
|
||||
clear_update_num: number[];
|
||||
score_update_num: number[];
|
||||
}
|
||||
|
||||
export interface activity_mybest {
|
||||
collection: "activity_mybest";
|
||||
version: number;
|
||||
|
||||
play_style: number;
|
||||
play_side: number;
|
||||
music_id: number;
|
||||
note_id: number;
|
||||
|
||||
target_graph: number;
|
||||
target_score: number;
|
||||
pacemaker: number;
|
||||
best_clear: number;
|
||||
best_score: number;
|
||||
best_misscount: number;
|
||||
now_clear: number;
|
||||
now_score: number;
|
||||
now_misscount: number;
|
||||
now_pgreat: number;
|
||||
now_great: number;
|
||||
now_good: number;
|
||||
now_bad: number;
|
||||
now_poor: number;
|
||||
now_combo: number;
|
||||
now_fast: number;
|
||||
now_slow: number;
|
||||
option: number;
|
||||
option_2: number;
|
||||
ghost_gauge_data: string;
|
||||
gauge_type: number;
|
||||
result_type: number;
|
||||
is_special_result: number;
|
||||
|
||||
update_date: number;
|
||||
}
|
||||
46
iidx@asphyxia/models/arena.ts
Normal file
46
iidx@asphyxia/models/arena.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
export const IIDX_CPUS = [
|
||||
[
|
||||
[6, 4, 5, 0],
|
||||
[7, 5, 6, 0],
|
||||
[8, 6, 6, 0],
|
||||
[9, 6, 7, 0],
|
||||
[10, 7, 7, 0],
|
||||
[10, 7, 8, 0],
|
||||
[11, 8, 8, 0],
|
||||
[11, 8, 9, 0],
|
||||
[12, 9, 9, 0],
|
||||
[12, 9, 10, 0],
|
||||
[13, 9, 10, 0],
|
||||
[13, 10, 10, 0],
|
||||
[14, 10, 11, 0],
|
||||
[14, 10, 11, 1],
|
||||
[15, 11, 11, 1],
|
||||
[15, 11, 12, 1],
|
||||
[16, 11, 12, 1],
|
||||
[16, 11, 12, 1],
|
||||
[17, 12, 12, 1],
|
||||
[18, 12, 12, 1],
|
||||
],
|
||||
[
|
||||
[6, 3, 5, 0],
|
||||
[7, 3, 5, 0],
|
||||
[8, 4, 5, 0],
|
||||
[8, 4, 5, 0],
|
||||
[9, 5, 6, 0],
|
||||
[9, 5, 6, 0],
|
||||
[10, 6, 6, 0],
|
||||
[10, 6, 7, 0],
|
||||
[11, 7, 7, 0],
|
||||
[11, 7, 8, 0],
|
||||
[12, 8, 8, 0],
|
||||
[12, 8, 9, 0],
|
||||
[13, 9, 9, 0],
|
||||
[13, 9, 10, 0],
|
||||
[14, 9, 10, 0],
|
||||
[15, 10, 10, 0],
|
||||
[15, 10, 11, 0],
|
||||
[16, 11, 11, 1],
|
||||
[17, 11, 12, 1],
|
||||
[18, 12, 12, 1],
|
||||
],
|
||||
];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user