Files
mc_datapack/README.md
Claude (owner) c39a0516bc music_quiz: interaction 엔티티 소환을 데이터팩 내부로 흡수
월드 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>
2026-05-16 21:07:31 +09:00

296 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`