월드 cmd block 의존 (redstone_block/red_wool 펄스) 을 제거하고 btn.mcfunction 이 직접 summon 하도록 변경. - buttons=-1 초기화 단계에서 기존 mq/<버튼명> interaction 을 모두 kill 후 정확히 1개를 (x+0.5, y, z+0.5) 에 1f×1f 로 재소환. /reload 마다 dup 누적 없이 "버튼당 1개, 올바른 좌표" 로 수렴. - /reload → load → commands/stop 이 buttons 점수를 -1 로 재설정 → 다음 tick 에 ensure 로직 실행. /kill @e 후에도 /reload 한 번으로 복구. - stone_button 직접 감지 fallback 및 잉여 state machine (1→2→0) 제거. 클릭 경로는 interaction 단일화 → trigger 투표 흐름 보존. - README 의 버튼 본체 설명을 새 구조로 갱신. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
296 lines
14 KiB
Markdown
296 lines
14 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의 부분집합).
|
||
|
||
### 100% 바닐라 — 의존 플러그인 없음
|
||
|
||
음원 재생과 정답 이미지 표시는 모두 바닐라 명령으로 처리한다. 음원과
|
||
페인팅 텍스처는 [minecraft_launcher](https://git.tkrmagid.kr/tkrmagid/minecraft_launcher)
|
||
가 만들어주는 리소스팩(`musicquiz` 네임스페이스)에서 가져온다.
|
||
|
||
- 음원: `/playsound musicquiz:track_NN <source> @s ~ ~ ~ <volume> <pitch>`
|
||
(예: `musicquiz:track_01`). 채널은 기본 `weather` — `stopsound` 와 함께 묶여 있다.
|
||
- 정답 이미지: `painting_variant musicquiz:cover_NN` 을 `/summon painting` 으로
|
||
벽에 띄우고, 다음 곡 직전 `kill @e[type=painting,tag=mq_cover]` 로 제거.
|
||
|
||
리소스팩 트랙 번호와 `mq:init/songs` 리스트의 순서가 1:1로 일치해야 한다.
|
||
1번째 곡 → `track_01` / `cover_01`, … (2자리 zero-pad).
|
||
|
||
### 게임 흐름과 상태 (`init` 스코어보드)
|
||
|
||
게임 전체 상태는 `main` 스코어보드의 가상 플레이어 `init` 값으로 관리한다.
|
||
|
||
| `init` | 단계 |
|
||
|--------|------|
|
||
| `0` | 정지/대기 (기본 상태) |
|
||
| `1` | 게임 시작 — page1 대화창에서 `ready` 트리거 대기 |
|
||
| `2` | 카운트다운 (3·2·1) |
|
||
| `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`
|
||
|
||
버튼 본체는 보이는 `stone_button` 블록 + 그 좌표에 덮인 `interaction`
|
||
엔티티로 구성된다. 클릭 처리는 항상 `interaction` 경로로 흐르므로
|
||
`on target as @s` 로 누른 플레이어가 식별되고, 다수결(`trigger $(n)`)
|
||
투표가 성립한다.
|
||
|
||
`interaction` 은 데이터팩이 직접 소환·관리한다 — `buttons` 점수가
|
||
`-1` (초기화) 일 때마다 같은 태그의 기존 entity 를 정리하고 정확히
|
||
1개를 (재)소환한다. `/reload` 가 `commands/stop` 을 호출해 `buttons`
|
||
점수를 `-1` 로 재설정하므로, 리로드 시 자동 보장된다. `/kill @e` 로
|
||
지워졌어도 다음 `/reload` 한 번으로 복구. 월드 회로(커맨드블럭) 의존은
|
||
없다.
|
||
|
||
### 파일 구조
|
||
|
||
```
|
||
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 # 주제·스폰·오디오·페인팅·marker 설정
|
||
│ │ ├── songs.mcfunction # 곡 목록 + max_index 자동계산
|
||
│ │ ├── buttons.mcfunction # 버튼 좌표·실행 명령
|
||
│ │ └── triggers.mcfunction # 다수결 트리거 정의
|
||
│ ├── commands/ # start·stop·skip·hint·replay·test
|
||
│ ├── quiz/ # 게임 진행 로직
|
||
│ │ ├── start·select·setanswer·correct·end.mcfunction
|
||
│ │ ├── play_sound·stop_sound.mcfunction # /playsound · /stopsound 래퍼
|
||
│ │ └── macro/ # 매크로 진입점
|
||
│ │ ├── setanswer.mcfunction # songs[$(idx)] → answer + track/cover id
|
||
│ │ ├── play_sound.mcfunction # $playsound 매크로
|
||
│ │ ├── stop_sound.mcfunction # $stopsound 매크로
|
||
│ │ └── summon{,2}.mcfunction # 정답 marker 엔티티 + alias 체인
|
||
│ ├── images/ # 정답 페인팅 표시·제거
|
||
│ │ ├── show.mcfunction # cover painting 소환
|
||
│ │ ├── clear.mcfunction # cover painting 일괄 제거
|
||
│ │ └── macro/show.mcfunction # $summon painting 매크로
|
||
│ ├── repeat/ # tick에서 호출되는 매 틱 처리
|
||
│ │ ├── players·check_answer·timer.mcfunction
|
||
│ │ ├── buttons/{handler,btn}.mcfunction
|
||
│ │ └── triggers/{handler,trigger}.mcfunction
|
||
│ └── players/login.mcfunction
|
||
├── dialog/page{1,2,3}.json
|
||
└── advancement/player/login.json
|
||
```
|
||
|
||
### 사용 스코어보드 / 스토리지
|
||
|
||
스코어보드:
|
||
|
||
- `main` — 게임 핵심 상태 (`init`, `index`, `max_index`, `timer`, `score`, `song_idx`)
|
||
- `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` — 설정
|
||
- `audio` = `{namespace, source, volume, pitch}` — `/playsound` 파라미터
|
||
- `image` = `{namespace, x, y, z, facing}` — 정답 페인팅 좌표
|
||
- `marker` = `{x, y, z}` — 정답 입력 marker 엔티티 위치
|
||
- `answer` = `{title, author, alias, track, cover}` — 현재 곡 정답
|
||
- `songs` — 곡 목록 (`mq:init/songs` 가 채움)
|
||
- `button_defs` / `trigger_defs` — 버튼·트리거 정의
|
||
- `mq:tmp` — setanswer·play_sound·페인팅 호출용 임시 페이로드 (idx, pad, num, playsound, painting, marker_call)
|
||
- `func:temp` — `func:` 헬퍼 함수용 임시 NBT
|
||
|
||
### 설정 (한 곳에서 수정)
|
||
|
||
세계마다 다른 값은 모두 `data/mq/function/init/` 폴더에서 편집한다.
|
||
`/reload` 후 반영된다.
|
||
|
||
- **`init/config.mcfunction`** — 주제, 스폰 위치, 오디오 설정(`audio`),
|
||
정답 페인팅 좌표(`image`), marker 엔티티 좌표. `title` 은 `[ … ]`
|
||
채팅 접두사로도 사용된다.
|
||
- **`init/songs.mcfunction`** — 곡 목록 (한 줄에 한 곡씩 append). `alias`
|
||
배열의 문자열은 정답 판정 시 동의어로 인정된다. **곡의 순서가 리소스팩
|
||
트랙 번호와 1:1 매칭** 되므로 순서 변경 시 리소스팩도 함께 재생성해야
|
||
한다. `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. minecraft_launcher 에서 생성한 `musicquiz` 리소스팩을 클라이언트에 적용
|
||
(런처가 자동 처리).
|
||
3. 서버 `/reload` — 리로드 성공 메시지가 채팅에 표시되면 정상.
|
||
4. 좌표 `144, 62, -225` 부근에 6개 버튼이 자동 배치된다.
|
||
5. `start` 버튼을 눌러 게임 시작.
|
||
|
||
### 좌표 의존성 (주의)
|
||
|
||
다음 좌표가 데이터팩 안에 박혀 있어, 다른 월드에서 그대로 사용하려면
|
||
`init/config.mcfunction` 의 값을 바꿔야 한다:
|
||
|
||
- 정답 입력 marker: `144 59 -219` — `marker.{x,y,z}`
|
||
- 정답 페인팅: `144 84 -261` (facing south) — `image.{x,y,z,facing}`
|
||
- 플레이어 스폰: `144 61 -219` (yaw 180) — `spawn`
|
||
- 버튼 좌표: `140..148, 62, -225` / `144, 62, -213` — `mq:init/buttons`
|
||
|
||
---
|
||
|
||
## 변경 이력
|
||
|
||
### 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 — 무대 의존 제거 + 최적화 (`2b61af2`)
|
||
|
||
특정 맵 좌표에만 동작하던 장식 로직을 들어내고, 매 틱 부하와 정적
|
||
데이터 관리를 정비했다.
|
||
|
||
- `repeat/map/` (무대 트리/조명 애니메이션) 삭제
|
||
- `images/image_custom.mcfunction` (호출자 없는 사문화 코드) 삭제
|
||
- `tick.mcfunction` 디스패치를 `init` 상태로 게이팅 (idle 틱 함수 호출 8→4)
|
||
- 곡 데이터를 `mq:init/songs` 의 `mq:main.songs` 리스트로 한 번 적재.
|
||
매크로 룩업 한 줄로 50× → O(1) 복사
|
||
- 버튼/트리거 정의를 `mq:init/buttons` / `mq:init/triggers` 의 storage
|
||
리스트로 분리. handler 는 인덱스로 매크로 호출만 한다.
|
||
- 다수결 산술 dedup (트리거당 5 라인 절감 × 4 트리거)
|
||
|
||
### 2026-05-13 — 설정 통합 (`b236118`)
|
||
|
||
세계별 수정 포인트를 한 파일로 모았다.
|
||
|
||
- `init/config.mcfunction` 신설 — 주제·곡 개수·스폰·명령 블록 좌표를 한곳에
|
||
- `mq:tellraw` 가 `mq:main.title` 을 읽도록 변경 ([ 주제 ] 접두사 동기화)
|
||
|
||
### 2026-05-13 — 바닐라 마이그레이션 (이번 커밋)
|
||
|
||
YP / TS Bukkit 플러그인 의존을 제거하고, 음원과 정답 이미지를 바닐라
|
||
명령으로 재구현했다. [minecraft_launcher](https://git.tkrmagid.kr/tkrmagid/minecraft_launcher)
|
||
가 생성하는 `musicquiz` 리소스팩과 한 쌍으로 동작한다.
|
||
|
||
#### 삭제
|
||
|
||
- `mq:check/server` + `mq:repeat/check_server` — 플러그인 설치 확인 로직
|
||
- `mq:quiz/macro/command_block` — `data modify block … Command` + `auto`
|
||
토글 패턴 (YP `playall` 호출용)
|
||
- `mq:quiz/play` — 명령 블록 강제 재발화 함수
|
||
- `mq:images/image{,_set,_delete}` — TS `placeloc` / `removeall` 호출
|
||
- `status` 스코어보드 (`yp`, `ts`, `skript`, `timer`) — 플러그인 hello 패킷
|
||
대기에만 쓰이던 객체
|
||
|
||
#### 신설
|
||
|
||
- **음원** — `mq:quiz/play_sound` → `mq:quiz/macro/play_sound`:
|
||
`$execute as @a at @s run playsound $(namespace):$(track) $(source) @s ~ ~ ~ $(volume) $(pitch)`
|
||
- **음원 정지** — `mq:quiz/stop_sound` → `mq:quiz/macro/stop_sound`:
|
||
`$stopsound @a $(source)` (모든 stopsound 호출 합쳐서 매크로화)
|
||
- **정답 페인팅** — `mq:images/show` → `mq:images/macro/show`:
|
||
`$summon minecraft:painting $(x) $(y) $(z) {variant:"$(namespace):$(cover)",facing:$(facing)b,Tags:["mq","mq_cover"]}`
|
||
- **정답 페인팅 제거** — `mq:images/clear`: `kill @e[type=painting,tag=mq_cover]`
|
||
|
||
#### 스토리지 재구성
|
||
|
||
- `command_block` 컴파운드(좌표·볼륨·이미지 영역·alias 사본을 한데 묶었던
|
||
것) 폐기
|
||
- `audio` = `{namespace, source, volume, pitch}` 신설 — `/playsound` 인자
|
||
- `image` = `{namespace, x, y, z, facing}` 신설 — `/summon painting` 좌표
|
||
- `marker` = `{x, y, z}` 신설 — 정답 입력 marker 위치
|
||
- `answer.track` / `answer.cover` 필드 추가 — 라운드별 리소스팩 ID
|
||
(`track_NN` / `cover_NN`)
|
||
- `max_index` 는 `mq:init/songs` 끝에서 `songs` 배열 길이로 자동 계산
|
||
(수동 동기화 필요 없음)
|
||
|
||
#### 트랙 번호 padding
|
||
|
||
mcfunction 은 문자열 zero-pad 가 없으므로, `quiz/select` 에서
|
||
`index ∈ 1..9` 일 때만 `mq:tmp.pad="0"` 로 분기시키고, 매크로 안에서
|
||
`"track_$(pad)$(num)"` 로 조립한다.
|
||
|
||
#### 기타
|
||
|
||
- `tick.mcfunction` 에서 `mq:repeat/check_server` 제거 (매 틱 함수 호출
|
||
4→3 으로 추가 감소)
|
||
- `commands/start` / `commands/stop` / `commands/replay` 의 하드코딩된
|
||
`stopsound @a weather` 를 모두 `function mq:quiz/stop_sound` 로 교체
|
||
— 채널 변경 시 `audio.source` 한 곳만 수정하면 됨
|
||
- `setanswer` 호출 경로 단축: 기존 `select → macro/command_block →
|
||
play(auto-toggle) → setanswer → macro/setanswer + macro/summon` →
|
||
현재 `select → setanswer → macro/setanswer → macro/summon → play_sound`
|
||
|
||
---
|
||
|
||
## 원격 저장소
|
||
|
||
- `https://git.tkrmagid.kr/tkrmagid/mc_datapack.git`
|