Music Shake 첫 박 시각 · 카운트다운 · 옛 일시 정지 판 정리

This commit is contained in:
Sn-Kinos
2026-10-06 12:23:47 +09:00
parent 4e8d989f29
commit 14fef64999
24 changed files with 300 additions and 34 deletions
@@ -178,6 +178,8 @@ export const fnQuestProgress = onCall(async (req) => {
total: count(data, 'total', 1_000_000),
positionMs: count(data, 'positionMs', 86_400_000),
...(hits !== undefined ? { hits } : {}),
// 판을 시작할 때의 첫 박 — 운영값이 바뀌면 앱이 옛 일시 정지를 버리고 새 판으로 시작한다.
...(typeof data.firstBeatMs === 'number' ? { firstBeatMs: count(data, 'firstBeatMs', 86_400_000) } : {}),
...(tally ? { judgement: tally } : {}),
updatedAt: FieldValue.serverTimestamp(),
},
+8 -1
View File
@@ -119,6 +119,13 @@ export const fnQuestVerifyMusicShake = onCall(async (req) => {
if (typeof questSegments === 'number' && questSegments !== totalSegments) {
fail(ERR.questAbnormal, '곡의 구간 수가 퀘스트 정의와 다릅니다.');
}
// 첫 박 시각(영상 시각 기준 박 위치 `firstBeatMs + i·60/bpm`)도 같은 기준이어야 한다.
const questFirstBeat = quest.get('firstBeatMs');
const firstBeatMs = typeof questFirstBeat === 'number' ? questFirstBeat : 0;
const sentFirstBeat = req.data?.firstBeatMs;
if (sentFirstBeat !== undefined && sentFirstBeat !== firstBeatMs) {
fail(ERR.questAbnormal, '첫 박 시각이 퀘스트 정의와 다릅니다.');
}
const limit = await dailyLimitOf(questId);
const state = await progress(uid, questId, key);
if (state.count >= limit) {
@@ -134,7 +141,7 @@ export const fnQuestVerifyMusicShake = onCall(async (req) => {
type: 'musicShake',
fandomId: optionalStr(req.data, 'fandomId') ?? '',
value: judgement.perfect,
evidence: { musicShake: { trackId, totalSegments, judgements: judgement } },
evidence: { musicShake: { trackId, totalSegments, firstBeatMs, judgements: judgement } },
questId,
verified: grade !== 'F',
rejectReason: grade === 'F' ? ERR.questIncomplete : null,