- 주제(title), 곡 개수(max_index), 스폰 위치, 음원 명령 블록 좌표/볼륨, 이미지 표시 영역 좌표를 모두 init/config.mcfunction 에서 관리 - tellraw 접두사([ … ])가 storage 의 title 을 참조하도록 변경 — 주제 변경 시 config 한 줄만 고치면 채팅 접두사까지 일관 적용 - load.mcfunction 의 인라인 data merge 블록을 제거하고 init/config 호출로 대체 (스코어보드/보스바 같은 런타임 인프라는 load 본문에 유지) - README 의 "곡 목록 수정" 절을 "설정 (한 곳에서 수정)" 으로 재작성 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
264 lines
12 KiB
Markdown
264 lines
12 KiB
Markdown
# mc_datapack
|
||
|
||
마인크래프트 자작 데이터팩 모음 저장소.
|
||
|
||
현재 포함된 데이터팩:
|
||
|
||
- `music_quiz/` — 음악퀴즈 데이터팩
|
||
|
||
---
|
||
|
||
## music_quiz — 음악퀴즈 데이터팩
|
||
|
||
플레이어가 노래를 듣고 정답을 맞히는 멀티플레이 음악 퀴즈.
|
||
관리자가 버튼/대화창으로 라운드를 진행하고, 참가자는 트리거 명령으로
|
||
정답·스킵·힌트·다시듣기 투표에 참여한다.
|
||
|
||
### 호환 버전
|
||
|
||
- **Minecraft 26.1.2** (pack_format `75`) 기준.
|
||
- 1.21.6에서 도입된 `dialog` 시스템, 1.21+의 단수형 `function/` 태그 폴더,
|
||
매크로 함수(`function ... with storage`) 기능을 사용한다.
|
||
- 텍스트 컴포넌트는 JSON 표기로 작성돼 있으며, 1.21.5 이후의 SNBT 파서와도
|
||
호환된다 (JSON은 SNBT의 부분집합).
|
||
|
||
### 의존 Bukkit 플러그인
|
||
|
||
데이터팩 자체만으로는 동작하지 않고, 다음 Bukkit 플러그인이 서버에 설치돼
|
||
있어야 한다:
|
||
|
||
- **YP** (`yp playall song<n>`) — 음원 재생용. 명령 블록을 통해 호출된다.
|
||
- **TS** (`ts placeloc <image>.jpg ...`) — 정답 이미지 표시용.
|
||
|
||
서버 측 상태는 `status` 스코어보드의 `yp`, `ts`, `skript` 값으로 추적하며,
|
||
플러그인 미설치 시 `mq:check/server`가 안내 타이틀을 띄운다.
|
||
|
||
### 게임 흐름과 상태 (`init` 스코어보드)
|
||
|
||
게임 전체 상태는 `main` 스코어보드의 가상 플레이어 `init` 값으로 관리한다.
|
||
|
||
| `init` | 단계 |
|
||
|--------|------|
|
||
| `0` | 정지/대기 (기본 상태) |
|
||
| `1` | 게임 시작 — page1 대화창에서 `ready` 트리거 대기 |
|
||
| `2` | 카운트다운 (3·2·1) |
|
||
| `4` | 곡 셋업 (한 틱 사이) |
|
||
| `5` | 곡 재생 + 정답 입력 단계 |
|
||
| `6` | 정답 공개·점수 부여 |
|
||
| `10` | 엔딩 시퀀스 |
|
||
|
||
`main` 스코어보드의 `timer`는 활성 상태에서만 증가하는 카운터로,
|
||
`mq:repeat/timer`가 이를 보고 상태 전이를 처리한다. 매 틱 디스패치는
|
||
`init` 값에 따라 게이팅된다.
|
||
|
||
### 트리거 명령
|
||
|
||
다음 명령은 `trigger` 타입 스코어보드로 등록돼 있어 어드벤처 모드의
|
||
일반 플레이어도 사용할 수 있다.
|
||
|
||
- `trigger ready` — 시작 안내 대화창에서 시작 확정
|
||
- `trigger cancel` — 시작 취소
|
||
- `trigger stop` — 다수결 종료
|
||
- `trigger skip` — 다수결 스킵 (현재 곡 패스)
|
||
- `trigger hint` — 다수결 힌트 요청 (자음 절반 가리기 — `func:hint`)
|
||
- `trigger replay` — 다수결 다시 듣기
|
||
|
||
스킵/힌트/리플레이는 `mq:repeat/triggers/trigger` 매크로 안에서
|
||
`max_player = ceil(전체/2)` 다수결 계산을 거친 뒤 실행된다. 트리거
|
||
정의 자체는 `mq:init/triggers`에서 storage 리스트로 관리된다.
|
||
|
||
### 입력 버튼
|
||
|
||
관리자가 사용하는 6개의 물리 스톤 버튼. 좌표·표면 방향·실행 명령은
|
||
`mq:init/buttons`에서 storage 리스트(`mq:main button_defs`)로 관리되며,
|
||
`mq:repeat/buttons/handler`가 매 틱 storage 인덱스로 `btn` 매크로를 호출한다.
|
||
|
||
- `start` / `stop` / `skip` / `hint` / `replay` / `test`
|
||
|
||
버튼 본체는 `interaction` 엔티티 + `redstone_block`-`red_wool` 토글
|
||
패턴으로 디바운스를 처리한다.
|
||
|
||
### 파일 구조
|
||
|
||
```
|
||
music_quiz/
|
||
├── pack.mcmeta # pack_format 75
|
||
└── data/
|
||
├── minecraft/tags/function/
|
||
│ ├── load.json # → mq:load
|
||
│ └── tick.json # → mq:tick
|
||
├── func/function/ # 공용 헬퍼 (length/half/shuffle/text_list/join…)
|
||
│ ─ mq:commands/hint에서 사용
|
||
└── mq/
|
||
├── function/
|
||
│ ├── load.mcfunction # 스코어보드·storage 초기화, init/* 호출
|
||
│ ├── tick.mcfunction # 매 틱 서브함수 디스패치 (init 게이팅)
|
||
│ ├── tellraw.mcfunction # 매크로 prefix 메시지 헬퍼
|
||
│ ├── init/ # 사용자 설정·정적 데이터 (수정 포인트)
|
||
│ │ ├── config.mcfunction # 주제·곡 개수·스폰·명령 블록 좌표
|
||
│ │ ├── songs.mcfunction # 곡 목록
|
||
│ │ ├── buttons.mcfunction # 버튼 좌표·실행 명령
|
||
│ │ └── triggers.mcfunction # 다수결 트리거 정의
|
||
│ ├── check/server.mcfunction # YP / TS 설치 확인
|
||
│ ├── commands/ # start·stop·skip·hint·replay·test
|
||
│ ├── quiz/ # 게임 진행 로직
|
||
│ │ ├── start·select·play·correct·end·setanswer.mcfunction
|
||
│ │ └── macro/ # 매크로 진입점 (summon·setanswer·command_block)
|
||
│ ├── repeat/ # tick에서 호출되는 매 틱 처리
|
||
│ │ ├── players·check_answer·timer·check_server.mcfunction
|
||
│ │ ├── buttons/{handler,btn}.mcfunction
|
||
│ │ └── triggers/{handler,trigger}.mcfunction
|
||
│ ├── players/login.mcfunction
|
||
│ └── images/{image,image_set,image_delete}.mcfunction
|
||
├── dialog/page{1,2,3}.json
|
||
└── advancement/player/login.json
|
||
```
|
||
|
||
### 사용 스코어보드 / 스토리지
|
||
|
||
스코어보드:
|
||
|
||
- `main` — 게임 핵심 상태 (`init`, `index`, `max_index`, `timer`, `score`, `song_idx`)
|
||
- `status` — 외부 의존(YP·TS·Skript) 및 글로벌 타이머
|
||
- `buttons` — 물리 버튼 상태 머신
|
||
- `answer` — 플레이어별 정답 입력값 (`1`=정답, `2`=오답)
|
||
- `func.temp` — 산술용 임시 상수 (`two=2` 등)
|
||
- `leave_game` — `custom:leave_game` 통계 (재접속 감지)
|
||
- `score` — 사이드바 표시용 점수
|
||
- `ready` / `cancel` / `stop` / `skip` / `hint` / `replay` — 플레이어 트리거
|
||
|
||
스토리지:
|
||
|
||
- `mq:main` — 게임 전역 데이터
|
||
- `title`, `max_index`, `spawn` — 설정
|
||
- `answer` = `{title, author, alias}` — 현재 곡 정답
|
||
- `command_block` — 명령 블록 좌표/볼륨, 이미지 표시 영역, 작업용 alias 사본
|
||
- `songs` — 곡 목록 (`mq:init/songs` 가 채움)
|
||
- `button_defs` — 버튼 정의 (`mq:init/buttons` 가 채움)
|
||
- `trigger_defs` — 트리거 정의 (`mq:init/triggers` 가 채움)
|
||
- `mq:tmp` — setanswer 룩업용 임시 인덱스
|
||
- `func:temp` — `func:` 헬퍼 함수용 임시 NBT
|
||
|
||
### 설정 (한 곳에서 수정)
|
||
|
||
세계마다 다른 값은 모두 `data/mq/function/init/` 폴더에서 편집한다.
|
||
`/reload` 후 반영된다.
|
||
|
||
- **`init/config.mcfunction`** — 주제, 곡 개수, 스폰 위치, 음원 명령 블록
|
||
좌표, 정답 이미지 표시 영역. `title` 은 `[ … ]` 채팅 접두사로도 사용된다.
|
||
- **`init/songs.mcfunction`** — 곡 목록 (한 줄에 한 곡씩 append).
|
||
`alias` 배열의 문자열은 정답 판정 시 동의어로 인정된다. 곡 수를 바꾸면
|
||
`init/config.mcfunction` 의 `max_index` 도 함께 맞춰야 한다.
|
||
- **`init/buttons.mcfunction`** — 6개 물리 버튼의 좌표·표면 방향·실행 명령.
|
||
- **`init/triggers.mcfunction`** — 다수결 트리거(`stop`/`skip`/`hint`/`replay`)
|
||
표시 이름·실행 명령.
|
||
|
||
`load.mcfunction` 은 위 4개를 호출해 `mq:main` 스토리지를 적재한다.
|
||
스코어보드·보스바 같은 런타임 인프라는 `load.mcfunction` 본문에 남아 있다.
|
||
|
||
다이얼로그 페이지(`data/mq/dialog/page{1,2,3}.json`) 의 타이틀과 설명문구는
|
||
JSON 텍스트 컴포넌트가 storage 참조를 일관되게 지원하지 않으므로 직접
|
||
편집해야 한다.
|
||
|
||
### 설치
|
||
|
||
1. 서버 월드 폴더 `datapacks/`에 `music_quiz/` 디렉터리째 복사.
|
||
2. YP, TS 플러그인 설치 확인.
|
||
3. 서버 `/reload` — 리로드 성공 메시지가 채팅에 표시되면 정상.
|
||
4. 좌표 `144, 62, -225` 부근에 6개 버튼이 자동 배치된다.
|
||
5. `start` 버튼을 눌러 게임 시작.
|
||
|
||
### 좌표 의존성 (주의)
|
||
|
||
다음 좌표가 데이터팩 안에 박혀 있어, 다른 월드에서 그대로 사용하려면
|
||
값을 바꿔야 한다:
|
||
|
||
- 명령 블록: `144 59 -219` — `mq:load` 의 `command_block` 초기값
|
||
- 플레이어 스폰: `144 61 -219` (`yaw 180`) — `mq:load` 의 `spawn` 초기값
|
||
- 버튼 좌표: `140..148, 62, -225` / `144, 62, -213` — `mq:init/buttons`
|
||
- 이미지 표시 영역: `131 77 -262` ~ `157 91 -262` — `mq:load` 의 `command_block.x1..z2`
|
||
|
||
---
|
||
|
||
## 변경 이력
|
||
|
||
### 2026-05-13 — 26.1.2 호환 + 1차 정리 (`b1babad`)
|
||
|
||
이전 푸시본(`6841b7a 이전퀴즈 데이터팩`)을 26.1.2 기준으로 정비.
|
||
|
||
- `pack_format` 69 → 75 (MC 26.1.2 / 1.21.11)
|
||
- `mq:load`, `mq:players/login`, `mq:commands/start`, `mq:commands/stop`,
|
||
`mq:quiz/start`, `mq:quiz/end`, `mq:repeat/buttons/btn` 등에 남아 있던
|
||
`# say ...` / `# stopsound` 사문화 디버그 주석 제거
|
||
|
||
### 2026-05-13 — 무대 의존 제거 + 최적화 (이번 커밋)
|
||
|
||
특정 맵 좌표에만 동작하던 장식 로직을 들어내고, 매 틱 부하와 정적
|
||
데이터 관리를 정비했다.
|
||
|
||
#### 삭제 (특정 맵 한정)
|
||
|
||
- `repeat/map/` 디렉터리 통째 — 무대 트리/조명 애니메이션 (`tree`, `lamp1`)
|
||
- `images/image_custom.mcfunction` — 파이브가이즈 간판 (호출자 없음)
|
||
- `data/mq/수정.txt` — 작가용 TODO 메모
|
||
- `load.mcfunction`의 `map` 스코어보드 초기화, `tree map -1`, `lamp1 map -1`,
|
||
`fill 94 78 -279 194 78 -279 minecraft:red_wool`
|
||
- `tick.mcfunction`의 `function mq:repeat/map/tree`, `function mq:repeat/map/lamp1`
|
||
|
||
#### 매 틱 부하 감소
|
||
|
||
- `tick.mcfunction` 디스패치를 `init` 상태로 게이팅:
|
||
- `repeat/timer` 는 `init >= 2` 일 때만
|
||
- `repeat/check_answer` 는 `init in {5, 6}` 일 때만
|
||
- `check_server` / `players` / `buttons/handler` / `triggers/handler` 는
|
||
항상 호출 (게임 비활성 상태에서도 시작 버튼·로그인 처리 필요)
|
||
- idle 틱(`init=0`)에서 함수 호출 8개 → 4개. 정답 스캔과 카운트다운 reset
|
||
로직이 빠진다.
|
||
|
||
#### 곡 관리 단순화
|
||
|
||
기존: `quiz/setanswer.mcfunction` 안에 50곡의 `function mq:quiz/macro/setanswer { index:N, title:..., author:..., alias:... }` 가
|
||
나열돼 있어, 매번 50개 매크로 호출 × 5라인 (`if score index matches N`
|
||
가드 포함) = 약 250 명령이 평가됐다.
|
||
|
||
변경:
|
||
|
||
- 곡 데이터를 `mq:init/songs` 에서 `mq:main.songs` 리스트로 한 번 적재
|
||
- `quiz/setanswer.mcfunction` 은 `index - 1` 을 계산해 `mq:tmp.idx` 에 저장
|
||
- `quiz/macro/setanswer.mcfunction` 은 매크로 한 줄로 룩업:
|
||
`data modify storage mq:main answer set from storage mq:main songs[$(idx)]`
|
||
- 호출당 3 라인의 storage 복사로 끝남 (50× → O(1))
|
||
|
||
#### 버튼/트리거 정의 분리
|
||
|
||
- 버튼 6개의 좌표·실행 명령을 `mq:init/buttons` 에서 `mq:main.button_defs`
|
||
리스트로 적재. `repeat/buttons/handler` 는 인덱스로 매크로 호출만 한다.
|
||
- 트리거 4개(stop/skip/hint/replay)도 동일하게 `mq:init/triggers` →
|
||
`mq:main.trigger_defs` 로 분리.
|
||
- 버튼/트리거를 추가·제거할 때 `repeat/*/handler` 가 아닌 `init/*` 만
|
||
편집하면 된다.
|
||
|
||
#### 다수결 산술 dedup
|
||
|
||
`repeat/triggers/trigger.mcfunction` 에서 투표 갱신 전후로 `real_max_player`,
|
||
`rest_player`, `max_player` 를 두 번 계산했다. 이 값들은 (참가자 수에만
|
||
의존하므로) 한 틱 안에서 바뀌지 않는다. 갱신되는 것은 `$(n)_player`
|
||
(투표 수)뿐이라 — 후처리 블록은 `$(n)_player` 재계산 한 줄로 축약.
|
||
트리거당 5 라인 절감 × 4 트리거.
|
||
|
||
#### 다듬지 않은 부분 (의도된 동작)
|
||
|
||
- `mq:commands/test` 및 `mq:repeat/timer` 의 3중 `playsound` — 음량 강조용
|
||
스택 호출.
|
||
- `mq:quiz/play` 의 `auto 1b` → `0b` 토글 두 줄 — 명령 블록 강제 재발화
|
||
패턴.
|
||
- `mq:repeat/buttons/btn` 내부 상태머신 — 상태 -2/-1/0/1/2 전이 의미가
|
||
엮여 있어 라인 단축 시 디바운스가 깨질 위험이 있어 유지.
|
||
- `func:` 헬퍼는 `mq:commands/hint` 가 사용 중이므로 유지.
|
||
|
||
---
|
||
|
||
## 원격 저장소
|
||
|
||
- `https://git.tkrmagid.kr/tkrmagid/mc_datapack.git`
|