7 Commits

Author SHA1 Message Date
Claude (owner)
28f1012294 music_quiz: 버튼 interaction 오프셋을 facing 으로부터 자동 계산
button_defs 항목은 이제 {n, x, y, z, f, c} 만 가진다 (ox/oy/oz/w/h 제거).
사용자가 facing 을 바꿀 때마다 손으로 오프셋 표를 옮겨 적을 필요가 없도록
repeat/buttons/btn.mcfunction 한 곳에 facing → 소환 오프셋을 고정해두고
mq:tmp.btn{f:"..."} 분기로 디스패치한다. width/height 도 stone_button
hitbox 에 맞춰 0.375/0.25 로 박제.

handler 는 각 entry 를 mq:tmp.btn 으로 복사한 뒤 btn 을 호출해 분기
predicate 에 사용할 수 있게 한다.
2026-05-17 03:56:41 +09:00
Claude (owner)
4349fddc25 music_quiz: dialog tag entry 에 required:false 추가 — /reload 시 datapack 깨짐 방지
증상:
- /reload 후 다음 에러:
  - Couldn't load tag minecraft:quick_actions as it is missing following references: mq:answer
  - Failed to load function mq:commands/start (Can't find element 'mq:page1' in registry 'minecraft:dialog')
  - Failed to load function mq:answer/open (Can't find element 'mq:answer' ...)

원인:
- MC 의 minecraft:dialog 레지스트리는 hot-reload 불가. /reload 로는 dialog 파일이
  registry 에 등록되지 않음 — 서버 완전 재시작이 필요.
- 그 상태에서 tag 가 `["mq:answer"]` 같은 짧은 형식으로 dialog 를 참조하면
  MC 는 누락된 reference 로 보고 태그 로드 실패 → 같은 reload 의 function 들이
  dialog registry 를 못 찾아 연쇄 실패.

수정:
- quick_actions.json: `"mq:answer"` → `{ "id": "mq:answer", "required": false }`.
  required:false 는 reference 가 없을 때 silently 무시하라는 지시. /reload 직후
  잠시 dialog registry 가 비어있어도 datapack 자체가 깨지지 않음.

주의 (운영):
- 데이터팩 업데이트 후에는 반드시 서버를 완전 재시작 해야 dialog show 호출이
  정상 동작함. /reload 만으로는 dialog 파일이 registry 에 들어가지 않음 — 이건
  MC 26.1 시점의 dialog 시스템 자체 제약 (Smithed/Mojang 문서 기준).
2026-05-17 00:10:51 +09:00
Claude (owner)
a8d09ece02 music_quiz: marker 엔티티 제거 + 노래 재생 채널을 player 로 변경
## marker 제거
모든 marker 소환 코드는 write-only — 어디에서도 @e[type=marker] / tag=default
selector 로 읽거나 죽이지 않았음. interaction 엔티티가 클릭 UI 를 대체한 이후
완전히 쓸모없는 잔존물.

- 삭제: quiz/macro/summon.mcfunction, quiz/macro/summon2.mcfunction (소비처 없음)
- commands/stop.mcfunction: marker_call 빌드 + macro 호출 제거.
  기존 월드에 누적된 legacy marker 청소를 위해 `kill @e[type=minecraft:marker,tag=mq]`
  한 줄 추가 (tag=mq 스코프라 외부 마커는 건드리지 않음).
- quiz/setanswer.mcfunction: 정답 marker 소환 블록 제거.
- init/config.mcfunction: marker 좌표 템플릿 (mq:main marker) 제거.

`answer.title="음악퀴즈"` 대기상태 sentinel 은 marker 외에 reader 가 없지만
다른 reset 의미를 가질 가능성을 고려해 보수적으로 유지.

## 노래 재생 채널을 player 로
init/config.mcfunction: mq:main audio.source 를 "weather" → "player".
play_sound / stop_sound 매크로 모두 동일 source 값을 읽으므로 한 곳 변경으로
모든 노래 재생/정지 채널이 player 채널로 이동. 음악/마스터/플레이어 슬라이더
중 "player" (음성) 슬라이더로 노래 음량 제어 가능.

타이머 비프 / UI 클릭음 등은 "노래 재생" 이 아니므로 weather 채널 그대로 유지.
2026-05-16 23:44:00 +09:00
Claude (owner)
b43d120e66 music_quiz: pack.mcmeta 를 min_format/max_format [101,1] 로 갱신 + normalize/step.mcfunction set string from 파싱 에러 수정
- pack.mcmeta: 25w31a 이후 도입된 min_format/max_format 배열 스펙으로 교체. min_format >= 82 이므로 pack_format 키는 생략.
- step.mcfunction L8, L45: `data modify ... set string from <source>` 는 잘못된 문법. 올바른 형태는 `set string <source> [start] [end]` (from 없음). 이로 인해 8행 부근 파싱이 멈추던 문제 해결.
2026-05-16 23:36:03 +09:00
claude
f0a2e4fb6b music_quiz: [정답 입력] 채팅 메시지 제거 + 외부 모드 설치 검증 가드 추가
- quiz/setanswer.mcfunction: 클릭형 tellraw 제거. dialog/`/trigger input` 인프라는 유지하여 모드 없는 환경 fallback.
- 외부 모드 검증 (load/login/start):
  - mq_chat_mod: 서버 전용 모드 (mc_chat_answer_mod) — `#server mq_chat_mod` fake holder 로 서버 presence 검증.
  - mq_video_mod: 클라이언트 렌더링 모드 (mc_video_player_mod) — 같은 objective 안에 `#server` (서버 컴포넌트 매 tick 갱신) + `<player>` (클라 payload 수신 시 갱신) 두 holder 로 server/client 부재 안내 분리.
  - start.mcfunction: server presence 우선 검사 → per-player client presence 검사. unset 매치 안 되는 selector 이슈는 `add @a ... 0` 으로 materialize.
  - login.mcfunction: 플레이어 join 시 `mq_video_mod=0` 초기화 (stale 1 방지).
- docs/mc_video_player_mod_integration.md: video mod 측 구현 사양 (서버 컴포넌트 매 tick presence pulse + client payload handshake, 주기 재전송 필수 명시).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 22:29:13 +09:00
Claude (owner)
2f6dc17092 music_quiz: interaction hitbox 를 stone_button 면 크기에 정합
interaction entity 의 위치/크기를 facing 별 오프셋 + 버튼 hitbox
치수 (6/16 × 4/16) 에 맞춰 분리.

- button_defs 각 항목에 ox/oy/oz/w/h 추가. facing 별 보정값으로
  interaction 의 "튀어나온 쪽 면 = 버튼 visible face" 가 되게 함.
  반대편은 벽 블록 속으로 들어가 invisible.
- width=0.375f, height=0.25f → 가로/세로 버튼 face 와 정합.
  horizontal hitbox 가 square 강제라 두께는 0.375 까지 가지만,
  벽 쪽 0.25 가 wall 블록 안에 묻혀 시각적으로는 버튼 두께 0.125 만 튀어나옴.
- btn.mcfunction 의 summon 라인이 매크로 변수 (~$(ox)/$(oy)/$(oz)) 와
  $(w)f / $(h)f 사용으로 변경됨.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-16 21:23:30 +09:00
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
110 changed files with 347 additions and 1330 deletions

236
README.md
View File

@@ -16,91 +16,26 @@
### 호환 버전
- **Minecraft 26.1.2** (`pack.mcmeta``min_format`/`max_format` 모두
`[101, 1]`).
- **Minecraft 26.1.2** (pack_format `75`) 기준.
- 1.21.6에서 도입된 `dialog` 시스템, 1.21+의 단수형 `function/` 태그 폴더,
매크로 함수(`function ... with storage`) 기능을 사용한다.
- 텍스트 컴포넌트는 JSON 표기로 작성돼 있으며, 1.21.5 이후의 SNBT 파서와도
호환된다 (JSON은 SNBT의 부분집합).
### 외부 모드 의존성 (서버/클라)
모드는 **필수 1 + 선택 1** 구성이다.
- **`mc_chat_answer_mod` v1.3.7+ (필수)** — 서버 전용. 채팅으로 정답 입력을
받는다. 정답 판정의 핵심이라 없으면 `/start` 가 차단된다. presence 는
`#server mq_chat_mod` 점수로 매 tick 표시되며, `SERVER_STARTED` /
`END_DATA_PACK_RELOAD` / `PlayerJoin` / `ServerTick` 네 지점에서 갱신된다.
https://git.tkrmagid.kr/tkrmagid/mc_chat_answer_mod/releases/tag/v1.3.7
- **`mc_video_player_mod` (선택)** — 정답 영상 재생용. **없어도 게임은
정상 진행되며 영상만 표시되지 않는다.** `/start` 는 차단하지 않고 노란
경고만 출력한다 (서버 부재 시 전체 안내, 특정 플레이어 클라 부재 시 본인
에게만 안내). presence 는 `#server mq_video_mod` (서버 컴포넌트가 매 tick
1 로 갱신) + `<player> mq_video_mod` (클라 join handshake 수신 시 1 로 set)
로 나눠 표시한다. 모드 측 연동 사양(presence handshake)은
[docs/mc_video_player_mod_integration.md](docs/mc_video_player_mod_integration.md) 참고.
https://git.tkrmagid.kr/tkrmagid/mc_video_player_mod
> 데이터팩은 모드 명령(`videoCache`/`videoPlace`/`videoDelete`)을 mcfunction
> 안에서 직접 실행하지 않고 **NBT 문자열 큐 → 커맨드블럭** 으로만 내보낸다.
> 덕분에 영상 모드가 없어도 데이터팩 로드 시 미등록 명령 파싱 에러가 나지
> 않는다 (자세한 건 아래 "정답 영상" 절).
### 100% 바닐라 — 의존 플러그인 없음 (모드 외)
### 100% 바닐라 — 의존 플러그인 없음
음원 재생과 정답 이미지 표시는 모두 바닐라 명령으로 처리한다. 음원과
페인팅 텍스처는 [minecraft_launcher](https://git.tkrmagid.kr/tkrmagid/minecraft_launcher)
가 만들어주는 리소스팩(`musicquiz` 네임스페이스)에서 가져온다.
- 음원: `/playsound musicquiz:track_NN <source> @s ~ ~ ~ <volume> <pitch>`
(예: `musicquiz:track_01`). `init/config.mcfunction` `audio.source`
채널을 결정하며, **곡 재생은 기본 `player` 채널** (음성/플레이어 볼륨
슬라이더로 음량 제어). `stopsound` 도 같은 채널로 묶여 있다. 카운트다운
비프와 종료 클릭 같은 UI 사운드는 별도로 `weather` 채널을 사용한다.
(예: `musicquiz:track_01`). 채널은 기본 `weather` `stopsound` 와 함께 묶여 있다.
- 정답 이미지: `painting_variant musicquiz:cover_NN``/summon painting` 으로
벽에 띄우고, 다음 곡 직전 `kill @e[type=painting,tag=mq_cover]` 로 제거.
- gif 페인팅: `images/gif.mcfunction` 을 실행하면 실행한 플레이어에게
`mq:gif` variant 페인팅 아이템을 지급한다. 벽에 설치하면 랜덤 선택 없이
`mq:gif` 로 고정 배치되고, 리소스팩의 애니메이션 텍스처
(`musicquiz:gif` `.png` + `.png.mcmeta`) 덕분에 gif 처럼 움직인다.
variant 정의는 `painting_variant/gif.json` (`asset_id: musicquiz:gif`).
리소스팩 트랙 번호와 `mq:init/songs` 리스트의 순서가 1:1로 일치해야 한다.
1번째 곡 → `track_01` / `cover_01`, … (2자리 zero-pad).
### 정답 영상 (선택)
`mc_video_player_mod` 가 설치돼 있으면 정답 공개 시 곡별 영상을 벽면에
띄운다. 모드가 없어도 게임은 정상 진행되며 영상만 표시되지 않는다.
핵심은 **모드 명령을 mcfunction 안에서 직접 실행하지 않는다**는 점이다.
모드 명령(`videoCache add/remove/clear`, `videoPlace`, `videoDelete`)은
미설치 시 데이터팩 로드 단계에서 미등록 명령 파싱 에러를 일으키므로,
NBT 문자열로만 만들어 큐에 적재하고 **커맨드블럭**으로 실행한다.
- **큐 적재** — `mq:videos/macro/cmd` 가 명령을 문자열로 조립해
`mq:main video.cmdq` 리스트(`{c:"<명령 문자열>"}`)에 push 한다.
문자열은 mcfunction 파서를 거치지 않으므로 모드가 없어도 로드된다.
- **드레인** — `mq:videos/drain` (매 tick, `mq:tick` 에서 호출) 이 큐를
한 tick 에 한 개씩 꺼내 `cmd_x/cmd_y/cmd_z` 좌표에 커맨드블럭을
setblock 하고 `auto` 를 켜 1회 실행한다. 커맨드블럭은 `auto:1b` 면 매
tick 재실행되므로, `video.armed` 플래그로 실행한 다음 tick 에
`mq:videos/macro/drain_off``auto:0b` 로 꺼 **정확히 1회만** 실행되게
한다 (모드의 `videoCache add` 가 중복 이름을 실패 처리해 스팸나는 것 방지).
- **캐시(preload / FIFO keep)** — 게임 시작 시 `videos/cache/preload`
1..`preload` 번 영상을 미리 받아두고, 라운드마다 `videos/cache/add`
N개 앞까지 캐시를 채운다. 캐시 개수가 `keep` 을 넘으면
`videos/cache/evict` 가 가장 먼저 받은 것부터 FIFO 로 축출한다
(`keep<=0` 이면 무제한). 데이터팩이 `mq:main video.cached`
(`{i:N}` 리스트) 로 캐시 상태를 1:1 추적해 중복 등록을 막고 src 를 고른다.
- **동적 src** — 재생 시 해당 번호가 캐시에 있으면 등록 이름 `video_N` 으로,
없으면 전체 URL `$(namespace)/N` 으로 `videoPlace` 한다.
`videos/normalize``namespace` 끝의 `/` 를 떼어 경로 중복 `//` 를 막는다.
모드 측이 구현해야 하는 presence handshake(서버 tick + 클라 payload) 사양은
[docs/mc_video_player_mod_integration.md](docs/mc_video_player_mod_integration.md)
참고.
### 게임 흐름과 상태 (`init` 스코어보드)
게임 전체 상태는 `main` 스코어보드의 가상 플레이어 `init` 값으로 관리한다.
@@ -136,44 +71,29 @@ NBT 문자열로만 만들어 큐에 적재하고 **커맨드블럭**으로 실
### 입력 버튼
관리자가 사용하는 6개의 물리 스톤 버튼. 좌표·표면 방향·실행 명령·라벨
관리자가 사용하는 6개의 물리 스톤 버튼. 좌표·표면 방향·실행 명령은
`mq:init/buttons`에서 storage 리스트(`mq:main button_defs`)로 관리되며,
`mq:repeat/buttons/handler`가 매 틱 storage 인덱스로 `btn_prep`
`btn` 매크로 체인을 호출한다.
`mq:repeat/buttons/handler`가 매 틱 storage 인덱스로 `btn` 매크로를 호출한다.
- `start` / `stop` / `skip` / `hint` / `replay` / `test`
버튼 본체는 보이는 `stone_button` 블록 + 클릭을 받는 `interaction` 엔티티
3 타일 (블록 면 바깥, 플레이어 쪽으로 살짝 튀어나오게) + 버튼 바로 아래
벽면에 부착되는 `text_display` 라벨 1 개로 구성된다. interaction 폭이
`width × width` 정사각형으로 강제되기 때문에 stone_button hitbox 의 가로
0.375 를 0.125 폭 × 3 타일로 덮고, 깊이는 두께(0.125) 만큼 밖으로 밀어
vanilla stone_button 클릭이 동시에 발화되지 않도록 한다. 라벨은 `bold`
적용 text component 로 직접 렌더링된다.
버튼 본체는 보이는 `stone_button` 블록 + 그 좌표에 덮인 `interaction`
엔티티로 구성된다. 클릭 처리는 항상 `interaction` 경로로 흐르므로
`on target as @s` 로 누른 플레이어가 식별되고, 다수결(`trigger $(n)`)
투표가 성립한다.
`button_defs` 항목의 필드:
- 필수: `n` (이름·태그), `x,y,z`, `f` (facing), `c` (실행 명령)
- 옵션: `label`, `label_color` (기본 `black`), `label_font` (기본
`minecraft:default`), `label_scale` (기본 `1.0`). `btn_prep` 에서
defaults + `merge from` 패턴으로 기본값이 자동 채워진다.
클릭 처리는 항상 `interaction` 경로로 흐르므로 `on target as @s` 로 누른
플레이어가 식별되고, 다수결(`trigger $(n)`) 투표가 성립한다.
`interaction` / `text_display` 는 데이터팩이 직접 소환·관리한다 —
`buttons` 점수가 `-1` (초기화) 일 때마다 같은 태그의 기존 entity 를
정리하고 정확한 개수만 (재)소환한다. `/reload``commands/stop`
호출해 `buttons` 점수를 `-1` 로 재설정하므로 리로드 시 자동 보장된다.
`/kill @e` 로 지워졌어도 다음 `/reload` 한 번으로 복구. 월드 회로
(커맨드블럭) 의존은 없다.
`interaction` 은 데이터팩이 직접 소환·관리한다 — `buttons` 점수가
`-1` (초기화) 일 때마다 같은 태그의 기존 entity 를 정리하고 정확히
1개를 (재)소환한다. `/reload``commands/stop` 을 호출해 `buttons`
점수를 `-1` 로 재설정하므로, 리로드 시 자동 보장된다. `/kill @e`
지워졌어도 다음 `/reload` 한 번으로 복구. 월드 회로(커맨드블럭) 의존은
다.
### 파일 구조
```
music_quiz/
├── pack.mcmeta # min_format/max_format [101, 1]
├── pack.png
├── pack.mcmeta # pack_format 75
└── data/
├── minecraft/tags/function/
│ ├── load.json # → mq:load
@@ -186,9 +106,9 @@ music_quiz/
│ ├── tick.mcfunction # 매 틱 서브함수 디스패치 (init 게이팅)
│ ├── tellraw.mcfunction # 매크로 prefix 메시지 헬퍼
│ ├── init/ # 사용자 설정·정적 데이터 (수정 포인트)
│ │ ├── config.mcfunction # 주제·스폰·오디오·페인팅 설정
│ │ ├── config.mcfunction # 주제·스폰·오디오·페인팅·marker 설정
│ │ ├── songs.mcfunction # 곡 목록 + max_index 자동계산
│ │ ├── buttons.mcfunction # 버튼 좌표·실행 명령·라벨
│ │ ├── buttons.mcfunction # 버튼 좌표·실행 명령
│ │ └── triggers.mcfunction # 다수결 트리거 정의
│ ├── commands/ # start·stop·skip·hint·replay·test
│ ├── quiz/ # 게임 진행 로직
@@ -197,31 +117,18 @@ music_quiz/
│ │ └── macro/ # 매크로 진입점
│ │ ├── setanswer.mcfunction # songs[$(idx)] → answer + track/cover id
│ │ ├── play_sound.mcfunction # $playsound 매크로
│ │ ── stop_sound.mcfunction # $stopsound 매크로
│ │ ── stop_sound.mcfunction # $stopsound 매크로
│ │ └── summon{,2}.mcfunction # 정답 marker 엔티티 + alias 체인
│ ├── images/ # 정답 페인팅 표시·제거
│ │ ├── show.mcfunction # cover painting 소환
│ │ ├── clear.mcfunction # cover painting 일괄 제거
│ │ ├── gif.mcfunction # 실행 플레이어에게 mq:gif 페인팅 아이템 지급
│ │ └── macro/show.mcfunction # $summon painting 매크로
│ ├── videos/ # 정답 영상 (선택 모드, 커맨드블럭 큐 경로)
│ │ ├── show.mcfunction # "play" 큐 적재
│ │ ├── clear.mcfunction # "delete" 큐 적재
│ │ ├── normalize.mcfunction # namespace 끝 / 제거 + 캐시 상태 초기화
│ │ ├── drain.mcfunction # 매 tick 큐 1개씩 커맨드블럭 실행 (mq:tick)
│ │ ├── cache/{fill,add,preload,evict,clear}.mcfunction # preload/FIFO 캐시 관리
│ │ └── macro/ # 매크로 진입점
│ │ ├── cmd.mcfunction # 모드 명령 문자열 조립 → cmdq push
│ │ ├── drain.mcfunction # $setblock command_block + auto 1b
│ │ └── drain_off.mcfunction # $auto 0b (다음 tick 재실행 방지)
│ ├── repeat/ # tick에서 호출되는 매 틱 처리
│ │ ├── players·check_answer·timer.mcfunction
│ │ ├── timers/{init2,init6,init10}.mcfunction # init 단계별 timer 디스패치 분할
│ │ ├── buttons/{handler,btn_prep,btn}.mcfunction
│ │ ├── buttons/{handler,btn}.mcfunction
│ │ └── triggers/{handler,trigger}.mcfunction
── answer/ # 채팅 정답 입력 정규화/판정 (chat_answer 모드 경로)
│ └── players/{login,mod_active_notice}.mcfunction
── players/login.mcfunction
├── dialog/page{1,2,3}.json
├── painting_variant/{cover_01..50,gif}.json
└── advancement/player/login.json
```
@@ -235,10 +142,6 @@ music_quiz/
- `func.temp` — 산술용 임시 상수 (`two=2` 등)
- `leave_game``custom:leave_game` 통계 (재접속 감지)
- `score` — 사이드바 표시용 점수
- `mq_chat_mod``mc_chat_answer_mod` presence. `#server` 점수 1 이면 설치됨
(없으면 `/start` 차단)
- `mq_video_mod``mc_video_player_mod` presence. `#server` = 서버 측 모드
존재, `<player>` = 클라 측 모드 존재 (없으면 경고만, 차단 X)
- `ready` / `cancel` / `stop` / `skip` / `hint` / `replay` — 플레이어 트리거
스토리지:
@@ -247,33 +150,21 @@ music_quiz/
- `title`, `max_index`, `spawn` — 설정
- `audio` = `{namespace, source, volume, pitch}``/playsound` 파라미터
- `image` = `{namespace, x, y, z, facing}` — 정답 페인팅 좌표
- `video` = `{namespace, cmd_x/y/z, x/y/z, facing, w, h, sound, preload, keep}`
— 정답 영상 설정 + 런타임 캐시 상태. `cmd_*` 는 큐 실행용 커맨드블럭
좌표, `x/y/z`·`facing`·`w/h` 는 영상 표시 위치/크기. 런타임에
`cached`(`{i:N}` 리스트), `cmdq`(명령 큐), `armed`(커맨드블럭 1회 실행
플래그) 가 이 컴파운드에 추가된다
- `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, btn, btn_default)
- `mq:input` — 채팅 정답 입력 큐 (chat_answer 모드 경로)
- `mq:tmp` — setanswer·play_sound·페인팅 호출용 임시 페이로드 (idx, pad, num, playsound, painting, marker_call)
- `func:temp``func:` 헬퍼 함수용 임시 NBT
> 참고: 과거 `marker` 스토리지와 `minecraft:marker` 정답 입력 엔티티는
> 폐기됨. `commands/stop` 의 `kill @e[type=marker,tag=mq]` 한 줄만 이전
> 월드에 남아 있을 수 있는 legacy entity 청소 목적으로 유지된다.
### 설정 (한 곳에서 수정)
세계마다 다른 값은 모두 `data/mq/function/init/` 폴더에서 편집한다.
`/reload` 후 반영된다.
- **`init/config.mcfunction`** — 주제, 스폰 위치, 오디오 설정(`audio`),
정답 페인팅 좌표(`image`), 정답 영상 설정(`video`). `title``[ … ]`
채팅 접두사로도 사용된다. `video``namespace` 는 영상 URL 베이스,
`cmd_x/y/z` 는 모드 명령 실행용 커맨드블럭 좌표, `x/y/z`·`facing`·`w/h`
는 영상 표시 위치/크기, `sound` 는 음량(-1 음소거), `preload` 는 미리
받아둘 캐시 개수(0 이면 끔), `keep` 은 FIFO 유지 개수(0 이하 무제한).
정답 페인팅 좌표(`image`), marker 엔티티 좌표. `title``[ … ]`
채팅 접두사로도 사용된다.
- **`init/songs.mcfunction`** — 곡 목록 (한 줄에 한 곡씩 append). `alias`
배열의 문자열은 정답 판정 시 동의어로 인정된다. **곡의 순서가 리소스팩
트랙 번호와 1:1 매칭** 되므로 순서 변경 시 리소스팩도 함께 재생성해야
@@ -292,84 +183,31 @@ JSON 텍스트 컴포넌트가 storage 참조를 일관되게 지원하지 않
### 설치
1. 서버 월드 폴더 `datapacks/``music_quiz/` 디렉터리째 복사.
2. 서버 mods 폴더에 `mc_chat_answer_mod` v1.3.7+ (필수) 설치.
`mc_video_player_mod` (선택) 는 정답 영상을 쓸 때만 서버 + 클라이언트
양쪽에 설치한다. 없어도 게임은 정상 동작하며 영상만 표시되지 않는다.
3. minecraft_launcher 에서 생성한 `musicquiz` 리소스팩을 클라이언트에 적용
2. minecraft_launcher 에서 생성한 `musicquiz` 리소스팩을 클라이언트에 적용
(런처가 자동 처리).
4. 서버 `/reload` — 리로드 성공 메시지가 채팅에 표시되면 정상.
5. `mq:init/buttons` 에 정의된 좌표 부근에 6개 버튼이 자동 배치된다.
6. `start` 버튼을 눌러 게임 시작`mc_chat_answer_mod` (필수) 미설치 시
사유와 함께 차단된다. `mc_video_player_mod` (선택) 미설치 시에는 노란
경고만 출력하고 영상 없이 진행된다.
3. 서버 `/reload` — 리로드 성공 메시지가 채팅에 표시되면 정상.
4. 좌표 `144, 62, -225` 부근에 6개 버튼이 자동 배치된다.
5. `start` 버튼을 눌러 게임 시작.
### 좌표 의존성 (주의)
다음 좌표 데이터팩 안에 박혀 있어 다른 월드에서 쓰려면 직접 바꿔야
한다. 현재 박혀 있는 좌표는 본인 월드 기준이므로 그대로 옮겨가면 동작
안 한다.
다음 좌표 데이터팩 안에 박혀 있어, 다른 월드에서 그대로 사용하려면
`init/config.mcfunction` 의 값을 바꿔야 한다:
- 정답 페인팅 / 플레이어 스폰 — `init/config.mcfunction` (`image`, `spawn`)
- 버튼 좌표·facing — `init/buttons.mcfunction` (`button_defs``x,y,z,f`)
- 정답 입력 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-06-05 — 정답 영상 시스템 + 영상 모드 선택화 + gif 페인팅
- **정답 영상 추가** — `mc_video_player_mod` 연동으로 정답 공개 시 곡별
영상을 벽면에 표시. `videos/` 트리 신설 (show/clear/normalize/drain +
cache/{fill,add,preload,evict,clear} + macro/{cmd,drain,drain_off}).
- **모드 명령 커맨드블럭 큐 경로** — 모드 명령(`videoCache`/`videoPlace`/
`videoDelete`)을 mcfunction 에서 직접 실행하지 않고 NBT 문자열로 큐
(`mq:main video.cmdq`) 에 적재 → `videos/drain` 이 매 tick 한 개씩
커맨드블럭으로 실행. 미설치 모드여도 데이터팩 로드 시 파싱 에러 없음.
`video.armed` 플래그 + `drain_off` (auto:0b) 로 정확히 1회 실행 보장.
- **영상 모드 선택화** — `mc_video_player_mod` 없어도 `/start` 차단 안 함
(노란 경고만, 영상 없이 진행). 채팅 모드(`mc_chat_answer_mod`) 만 필수.
- **캐시 (preload / FIFO keep)** — `config.video``preload`/`keep` 으로
미리받기 개수와 FIFO 유지 개수 제어. `video.cached` 로 캐시 상태 자체
추적, 동적 src (`video_N` vs 전체 URL), `normalize` 가 namespace 끝 `/`
제거.
- **gif 페인팅** — `images/gif.mcfunction`: 실행 플레이어에게 `mq:gif`
variant 페인팅 아이템 지급. 벽 설치 시 리소스팩 애니메이션 텍스처로
움직임. `painting_variant/gif.json` (`asset_id: musicquiz:gif`) 신설.
- `init/config.mcfunction``video` 컴파운드 추가, `load.mcfunction`
`mq_chat_mod`/`mq_video_mod` objective 추가.
### 2026-05-19 — v1.0.25: 버튼 hitbox/라벨 미세조정 + 곡목록·좌표 갱신
- `repeat/buttons/btn.mcfunction`: interaction hitbox 미세조정 (`width`
0.13 가운데 타일로 micro-gap 보정, `height` 0.26, 깊이 오프셋
0.07/0.93, text_display Y `~-0.5` 로 라벨 위치 조정).
- 셀렉터 정렬 `[type=...,tag=mq,tag=$(n)]` → `[distance=0..,tag=mq,
tag=$(n),type=...]`.
- `init/songs.mcfunction`: 아이유 17 곡 셋으로 교체 (alias 빈 배열).
- `init/buttons.mcfunction`: 버튼 좌표 본인 월드 기준으로 갱신,
`label` 필드 추가 ("게임시작" / "정지" / "넘기기" / "힌트" /
"다시듣기" / "소리 테스트").
- `repeat/timer.mcfunction` 분할 → `repeat/timers/{init2,init6,init10}`.
- `commands/start.mcfunction` 에 `mq_video_mod` 게이트 추가 (서버 부재
단일 차단 + 플레이어별 부재 안내). `load.mcfunction` 에 `mq_video_mod`
objective + `#server` 0 materialize 추가.
### 2026-05-18 ~ 19 — v1.0.19 ~ v1.0.24: 버튼 인프라 안정화
- v1.0.19/20/21: `btn_prep` defaults+merge 패턴, `positioned $(x).0`
로 +0.5 보정 회피, interaction 3 타일 분할, `text_display` 도입.
- v1.0.23: 채팅정답 모드 false negative 의 진짜 fix — 데이터팩 게이트는
유지하고 모드 (`mc_chat_answer_mod` v1.3.7) 의 presence pulse 를
4 지점으로 확장.
- v1.0.24: `text_display` Y 보정 (`~-1` → `~-0.25`) + 라벨 bold + v1.0.21
의 interaction 깊이 부호 반전 수정.
### 2026-05-13 — 26.1.2 호환 + 1차 정리 (`b1babad`)
이전 푸시본(`6841b7a 이전퀴즈 데이터팩`)을 26.1.2 기준으로 정비.
- `pack_format` 69 → 75 (MC 26.1.2 / 1.21.11). 이후 `min_format`/
`max_format` 가 `[101, 1]` 로 갱신됨 (현재).
- `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` 사문화 디버그 주석 제거

View File

@@ -10,16 +10,12 @@
| `<player> mq_video_mod` | 해당 플레이어 클라에 모드 존재 | 서버 컴포넌트가 client→server payload 수신 시 1 |
둘 다 같은 objective `mq_video_mod` (dummy) 를 쓰고 holder 이름으로 의미를
구분한다. **영상재생 모드는 선택 모드라 미설치여도 `/start` 를 차단하지
않는다.** `mq:commands/start` 는 두 점수를 보고 **경고만** 출력한다:
서버 부재 → 전원에게 "영상 없이 진행됩니다" / 일부 플레이어 클라 부재 →
본인에게만 "본인 화면에는 영상이 표시되지 않습니다". 게임은 그대로 진행된다.
구분한다. 데이터팩 `mq:commands/start` 가드는 두 단계로 검사:
서버 부재 → 전원 안내 후 차단 / 일부 플레이어 클라 부재 → 본인 안내 + 차단.
> 참고: 자매 모드 `mc_chat_answer_mod` 는 채팅 가로채기가 본질적으로 서버
> 측 동작이라 `#server mq_chat_mod` 한 가지만 쓴다 (per-player handshake 무의미).
> 그쪽은 정답 판정의 핵심이라 **없으면 `/start` 가 실제로 차단된다.** 본
> 모드는 클라이언트가 직접 영상 렌더링하므로 per-player handshake 가 필요하고,
> 그 점수는 차단이 아니라 **플레이어별 경고**에만 쓰인다.
> 본 모드는 클라이언트가 직접 영상 렌더링하므로 per-player handshake 도 필요함.
## 데이터팩 측이 이미 제공하는 것
@@ -36,24 +32,25 @@ scoreboard objectives add mq_video_mod dummy
scoreboard players set @s mq_video_mod 0
```
`mq:commands/start.mcfunction` 경고 (차단 아님):
`mq:commands/start.mcfunction` 가드:
```mcfunction
# 서버 부재 우선 차단
execute unless score #server mq_video_mod matches 1 run return run function mq:tellraw {"text":"영상재생 모드가 서버에 미설치 — 서버 관리자에게 문의해주세요.","color":"red","msg":""}
# unset 매치 안 되므로 materialize
scoreboard players add @a mq_video_mod 0
# 서버 부재 — 전원에게 경고만, 그대로 진행
execute unless score #server mq_video_mod matches 1 run function mq:tellraw {"text":"영상재생 모드가 서버에 미설치 — 영상 없이 진행됩니다.","color":"yellow","msg":""}
# 서버는 있는데 본인 클라만 부재 — 본인에게만 경고
execute if score #server mq_video_mod matches 1 as @a[scores={mq_video_mod=..0}] run tellraw @s ["",{"text":"영상재생 모드 미설치 — 본인 화면에는 영상이 표시되지 않습니다.","color":"yellow"}]
# 본인 안내 (tellraw @s 직접 — mq:tellraw 는 @a broadcast 라 부적합)
execute as @a[scores={mq_video_mod=..0}] run tellraw @s ["",{"text":"영상재생 모드 미설치 — 모드 적용 후 다시 입장해주세요.","color":"red"}]
# 한 명이라도 누락이면 시작 차단
execute if entity @a[scores={mq_video_mod=..0}] run return run function mq:tellraw {"text":"필수 모드 미설치 플레이어가 있어 시작할 수 없습니다.","color":"red","msg":""}
```
`load.mcfunction` 에서 `#server mq_video_mod = 0` 으로 미리 깔아둠 → 서버
컴포넌트가 한 tick 도 안 돌면 0 유지 → 서버 부재 경고 출력.
컴포넌트가 한 tick 도 안 돌면 0 유지 → 가드 차단.
즉, **서버 컴포넌트가 (a) 매 tick `#server mq_video_mod=1` 갱신,
(b) client payload 수신 시 송신 플레이어 점수=1 갱신**, 이 두 가지만 하면
나머지는 데이터팩이 알아서 한다. (점수가 0 이어도 게임은 진행되며, 경고
출력과 영상 표시 여부에만 영향을 준다.)
나머지는 데이터팩이 알아서 한다.
## 모드가 구현해야 하는 동작
@@ -138,13 +135,12 @@ ServerPlayNetworking.registerGlobalReceiver(HelloPayload.TYPE, (payload, context
- 데이터팩 `mq:players/login` 이 spawn dialog 통과 시점에 점수를 0 으로
리셋하므로, **클라 payload 주기 재전송은 필수**. login 이후 다음 재전송
까지의 짧은 공백에 호스트가 start 를 누르면 (차단은 아니지만) 설치된
플레이어에게도 잘못된 미설치 경고가 뜰 수 있으니, 재전송 간격은 5 초
이하 권장.
까지의 짧은 공백에 호스트가 start 를 누르면 가드가 차단되니, 재전송
간격은 5 초 이하 권장.
- 서버가 set 한 점수는 다음 join 시 login 에서 다시 0 으로 리셋됨 → 모드를
빼고 재접속한 플레이어가 stale 1 을 가질 일 없음.
- payload 송신 → 서버 처리는 ms 단위 → 주기 재전송이 살아 있는 한 경고
오탐 race 없음.
- payload 송신 → 서버 처리는 ms 단위 → 주기 재전송이 살아 있는 한 start
타이밍 race 없음.
### 대안: `/trigger` 방식 (별도 서버 컴포넌트 불필요)
@@ -166,21 +162,18 @@ ServerPlayNetworking.registerGlobalReceiver(HelloPayload.TYPE, (payload, context
4. 플레이어 spawn dialog 통과 → `mq:players/login` 이 점수를 0 으로 리셋.
5. **클라 모드가 주기 재전송 (5 초 이하 권장, 필수)** → 늦어도 다음 주기에
서버 모드가 다시 `mq_video_mod = 1` 로 갱신.
6. 호스트 start → `@a[scores={mq_video_mod=..0}]` 검사 → 클라 모드 미설치
플레이어 본인에게만 경고. 차단 없이 게임 진행 (해당 플레이어 화면에만
영상 미표시).
6. 호스트 start → 가드가 `@a[scores={mq_video_mod=..0}]` 검사 → 클라 모드
미설치 플레이어 있으면 시작 차단.
## 테스트
1. **서버 미설치**: 서버에 모드 jar 가 없는 상태에서 데이터팩만 적용 →
호스트 start → "영상재생 모드가 서버에 미설치 — 영상 없이 진행됩니다."
노란 경고 출력 후 **그대로 게임 진행** (영상만 안 나옴).
(`#server mq_video_mod` 가 갱신되지 않음.)
호스트 start → "영상재생 모드가 서버에 미설치 — 서버 관리자에게..." 한
출력 후 차단. (`#server mq_video_mod` 가 갱신되지 않음.)
2. **서버 설치 + 일부 클라 미설치**: 모드를 클라에 설치한 플레이어와 안
한 플레이어 혼재 → 호스트 start → 클라 미설치 본인에게 "영상재생 모드
미설치 — 본인 화면에는 영상이 표시되지 않습니다." 노란 경고. 차단 없이
게임 진행.
3. **서버 + 모든 클라 설치**: 모두 정상 → start 정상 진행 + 영상 표시.
한 플레이어 혼재 → 호스트 start → 클라 미설치 본인에게 "영상재생 모드
미설치" + 전원에게 "필수 모드 미설치 플레이어가 있어..." 후 차단.
3. **서버 + 모든 클라 설치**: 모두 정상 → start 정상 진행.
4. `/scoreboard players list #server` 로 server presence 점수 확인,
`/scoreboard players list <player>` 로 client presence 점수 확인.

View File

@@ -3,7 +3,6 @@ data modify storage func:temp zero set value 0
$data modify storage func:temp length set value $(length)
execute store result score result func.temp run function func:comp_num {n1:"zero",n2:"length"}
# warn-off execute-group
execute if score result func.temp matches 1 run tellraw @s {"text":"length는 1이상 이어야 합니다.","color":"red"}
execute if score result func.temp matches 1 run return 0

View File

@@ -5,7 +5,7 @@ execute if score length func.temp matches 0 run return 1
execute store result score random func.temp run random value 0..2147483646
scoreboard players operation random func.temp %= length func.temp
function func:shuffle/f2 with storage func:temp {index:0}
execute run function func:shuffle/f2 with storage func:temp {index:0}
execute store result storage func:temp shuffle.index int 1 run scoreboard players get random func.temp
function func:shuffle/f2 with storage func:temp shuffle

View File

@@ -3,7 +3,6 @@ data modify storage func:temp zero set value 0
$data modify storage func:temp length set value $(length)
execute store result score result func.temp run function func:comp_num {n1:"zero",n2:"length"}
# warn-off execute-group
execute if score result func.temp matches 1 run tellraw @s {"text":"length는 1이상 이어야 합니다.","color":"red"}
execute if score result func.temp matches 1 run return 0

View File

@@ -3,7 +3,6 @@ execute store result score result func.temp run function func:is_space with stor
$execute store result score result2 func.temp run function func:is_index {l1:"half",l2:"result",index:$(index)}
# warn-off execute-group
execute if score result2 func.temp matches 0 if score result func.temp matches 0 run data modify storage func:temp text_list append value ""
execute if score result2 func.temp matches 0 if score result func.temp matches 1 run data modify storage func:temp text_list append from storage func:temp space.text
execute if score result2 func.temp matches 1 run data modify storage func:temp text_list append from storage func:temp space.text

View File

@@ -15,7 +15,7 @@
{
"type": "minecraft:plain_message",
"contents": {
"text": "\n1. 정답입력시에 채팅으로 입력해주시면 됩니다.\n[ 띄어쓰기, 영어 대소문자 ]\n상관없이 입력하셔도 인식 됩니다.\n\n2. 모든 소리는 주크박스/소리 블록 채널로 조절할수 있습니다.\n\n3. 게임시작후 버튼들은 과반수(절반이상)가 눌러야 작동합니다.\n\n4. 힌트는 특수문자 제외 정답의 절반이 가려져서 나옵니다.\n힌트는 여러번 받을수 있고,\n받을때마다 가려지는 부분이 달라집니다."
"text": "\n1. 정답입력시에 채팅으로 입력해주시면 됩니다.\n[ 띄어쓰기, 영어 대소문자, 특수문자 ]\n상관없이 입력하셔도 인식 됩니다.\n\n2. 모든 소리는 날씨 소리로 조절할수 있습니다.\n\n3. 게임시작후 버튼들은 과반수(절반이상)가 눌러야 작동합니다.\n\n4. 힌트는 특수문자 제외 정답의 절반이 가려져서 나옵니다.\n힌트는 여러번 받을수 있고,\n받을때마다 가려지는 부분이 달라집니다."
},
"width": 300
}

View File

@@ -9,7 +9,6 @@ data modify storage mq:tmp judge.answer set from storage mq:tmp norm.acc
function mq:answer/macro/match with storage mq:tmp judge
# 2) 제목 매치 실패 시 alias 들과 순차 비교 (조기 종료)
# warn-off execute-group
execute unless score @s answer matches 1 run data modify storage mq:tmp aliases set from storage mq:main answer.alias
execute unless score @s answer matches 1 run function mq:answer/iter_aliases

View File

@@ -5,7 +5,6 @@ execute if score init main matches 1..4 run return run function mq:tellraw {"tex
execute if score init main matches 6.. run return run function mq:tellraw {"text":"아직 다음노래가 재생되지 않았습니다.","color":"red",msg:'""'}
execute if score init main matches 10 run return run function mq:tellraw {"text":"퀴즈가 종료되었습니다.","color":"red",msg:'""'}
# warn-off execute-group
execute if score init main matches 5 run data modify storage mq:main hint.text set from storage mq:main answer.title
execute if score init main matches 5 run data modify storage mq:main hint.hint set value ""
execute if score init main matches 5 run function func:hint with storage mq:main hint

View File

@@ -5,7 +5,5 @@ execute if score init main matches 1..4 run return run function mq:tellraw {"tex
execute if score init main matches 6.. run return run function mq:tellraw {"text":"아직 다음노래가 재생되지 않았습니다.","color":"red",msg:'""'}
execute if score init main matches 10 run return run function mq:tellraw {"text":"퀴즈가 종료되었습니다.","color":"red",msg:'""'}
# warn-off execute-group
execute if score init main matches 5 run function mq:quiz/stop_sound
execute if score init main matches 5 run function mq:quiz/play_sound

View File

@@ -5,6 +5,5 @@ execute if score init main matches 1..4 run return run function mq:tellraw {"tex
execute if score init main matches 6.. run return run function mq:tellraw {"text":"아직 다음노래가 재생되지 않았습니다.","color":"red",msg:'""'}
execute if score init main matches 10 run return run function mq:tellraw {"text":"퀴즈가 종료되었습니다.","color":"red",msg:'""'}
# warn-off execute-group
execute if score init main matches 5 run scoreboard players set skip buttons -2
execute if score init main matches 5 run function mq:quiz/correct

View File

@@ -1,35 +1,41 @@
execute if score init main matches 10 run return run function mq:tellraw {"text":"퀴즈가 완전히 종료된후 시작해주세요.","color":"red","msg":""}
# ---- 외부 모드 확인 ----
# * mq_chat_mod : mc_chat_answer_mod = 채팅 정답 판정에 필수인 서버 전용
# 모드. 서버에서 채팅을 가로채야 퀴즈가 성립하므로 없으면 시작 차단.
# 모드가 매 server tick 마다 fake player `#server` 점수를 1 로 set.
# ---- 외부 모드 설치 검증 ----
# 두 모드는 성격이 달라서 검증 방식이 다름:
#
# * mq_video_mod : mc_video_player_mod = 정답 영상 재생용 선택 모드. 없어도
# 게임은 정상 진행되며 영상만 표시되지 않는다 → 차단하지 않고 경고만 출력.
# - `#server mq_video_mod` : 서버 컴포넌트가 매 tick 1 로 갱신. 없으면 0.
# - `<player> mq_video_mod` : 클라 join handshake 수신 시 1 로 set. 클라
# * mq_chat_mod : mc_chat_answer_mod = 서버 전용 모드 (채팅 가로채기는
# 서버에서 일어남, 클라 설치 불필요). 따라서 fake player `#server`
# 점수를 모드가 매 server tick 마다 1 로 set. 서버에 모드가 없으면
# 이 점수가 갱신되지 않음.
#
# * mq_video_mod : mc_video_player_mod = 클라이언트 측 렌더링 + 서버 측
# 컴포넌트. 같은 objective 안에 holder 두 종류 사용:
# - `#server mq_video_mod` : 서버 컴포넌트가 매 tick 1 로 갱신 (server
# presence). 없으면 0 → 서버에 모드 미설치.
# - `<player> mq_video_mod` : 클라 join 시 payload 가 서버로 오면 서버
# 컴포넌트가 해당 플레이어 점수를 1 로 set (client presence). 클라
# 미설치면 0 유지.
# 1) 채팅정답 모드(필수) 서버 부재 — 전원 차단.
# 이렇게 분리해야 "서버 미설치"와 "특정 플레이어 클라 미설치"가 안내에서
# 구분된다.
#
# 1) 서버 측 모드 부재 — 전원 차단, 단일 안내. 서버 부재는 클라 검사보다
# 우선해야 — 클라가 다 설치되어 있어도 서버가 없으면 동작 안 한다.
execute unless score #server mq_chat_mod matches 1 run return run function mq:tellraw {"text":"채팅정답 모드가 서버에 미설치 — 서버 관리자에게 문의해주세요.","color":"red","msg":""}
execute unless score #server mq_video_mod matches 1 run return run function mq:tellraw {"text":"영상재생 모드가 서버에 미설치 — 서버 관리자에게 문의해주세요.","color":"red","msg":""}
# 2) 영상 모드(선택) 부재 — 경고만 출력하고 그대로 진행. 영상은 안 나옴.
# 2) 클라이언트 측 모드 (mc_video_player_mod) 부재 — 본인 누락 안내 + 차단.
# selector `scores={X=..0}` 는 점수 미존재를 매치하지 않으므로 직전에
# `add @a ... 0` 으로 materialize.
# `add @a ... 0` 으로 materialize. 개인 안내는 tellraw @s 직접 (mq:tellraw
# 는 내부 @a broadcast 라 부적합).
scoreboard players add @a mq_video_mod 0
execute unless score #server mq_video_mod matches 1 run function mq:tellraw {"text":"영상재생 모드가 서버에 미설치 — 영상 없이 진행됩니다.","color":"yellow","msg":""}
execute if score #server mq_video_mod matches 1 as @a[scores={mq_video_mod=..0}] run tellraw @s ["",{"text":"영상재생 모드 미설치 — 본인 화면에는 영상이 표시되지 않습니다.","color":"yellow"}]
execute as @a[scores={mq_video_mod=..0}] run tellraw @s ["",{"text":"영상재생 모드 미설치 — 모드 적용 후 다시 입장해주세요.","color":"red"}]
execute if entity @a[scores={mq_video_mod=..0}] run return run function mq:tellraw {"text":"필수 모드 미설치 플레이어가 있어 시작할 수 없습니다.","color":"red","msg":""}
scoreboard players set start buttons -3
setblock ~ ~ ~ minecraft:air
function mq:quiz/stop_sound
$scoreboard players set max_index main $(max_index)
scoreboard players set init main 1
# 시작 시 1..preload 번 영상 캐시 미리받기(큐 적재). schedule 로 다음 tick 에
# 실행해 max_index 설정 이후 동작 보장. 실제 다운로드는 drain 이 tick 마다 처리.
schedule function mq:videos/cache/preload 1t
dialog show @a mq:page1

View File

@@ -51,8 +51,6 @@ scoreboard objectives add score dummy {"text":"점수","bold":true}
scoreboard objectives setdisplay sidebar score
dialog clear @a
# 정답 video URL actionbar 잔상 제거 (전체 리셋의 일부)
title @a actionbar {"text":""}
bossbar set mq:process name [{"text":"진행도: ","color": "yellow","bold": true},{"score":{"name":"index","objective": "main"},"color": "yellow","bold": true},{"text":"/","color": "yellow","bold": true},{"score":{"name":"max_index","objective": "main"},"color": "yellow","bold": true}]
$bossbar set mq:process max $(max_index)
@@ -66,15 +64,7 @@ bossbar set mq:process players @a
data modify storage mq:main answer set value {title:"음악퀴즈", alias:[]}
# 이전 버전이 남긴 legacy marker 정리 (현재는 marker 사용 안 함)
kill @e[distance=0..,tag=mq,type=minecraft:marker]
# 이전 버전이 남긴 잔존 text_display 정리.
# 현재 버튼은 -1 init 단계에서 같은 tag interaction 만 kill 하므로 (n 태그
# 가 일치할 때만), 옛 버튼 정의에 있던 이름의 text_display 가 남으면 안
# 지워짐. 여기서 mq 태그 전체를 한 번에 정리해 stale 제거.
kill @e[distance=0..,tag=mq,type=minecraft:text_display]
kill @e[type=minecraft:marker,tag=mq]
function mq:quiz/stop_sound
function mq:images/clear
function mq:videos/clear
function mq:videos/cache/clear

View File

@@ -1,8 +1,5 @@
stopsound @a block minecraft:block.stone_button.click_on
function mq:tellraw {"text":"띵!!!","color":"white","msg":'""'}
$stopsound @a $(source) $(namespace):bell
# warn-off execute-group
$execute as @a at @s run playsound $(namespace):bell $(source) @s ~ ~ ~ 1 0.9
$execute as @a at @s run playsound $(namespace):bell $(source) @s ~ ~ ~ 1 0.9
$execute as @a at @s run playsound $(namespace):bell $(source) @s ~ ~ ~ 1 0.9
execute as @a at @s run playsound minecraft:block.note_block.bell weather @s ~ ~ ~ 1 0.9
execute as @a at @s run playsound minecraft:block.note_block.bell weather @s ~ ~ ~ 1 0.9
execute as @a at @s run playsound minecraft:block.note_block.bell weather @s ~ ~ ~ 1 0.9

View File

@@ -1 +1 @@
kill @e[distance=0..,tag=mq_cover,type=minecraft:painting]
kill @e[type=minecraft:painting,tag=mq_cover]

View File

@@ -1,4 +0,0 @@
# 실행한 플레이어에게 gif 페인팅 아이템을 지급한다.
# 벽에 설치하면 mq:gif variant 로 고정 배치되고(랜덤 선택 X), 리소스팩의
# musicquiz:gif 애니메이션 텍스처(.png + .png.mcmeta) 덕분에 gif 처럼 움직인다.
give @s minecraft:painting[minecraft:painting/variant="mq:gif"]

View File

@@ -1,7 +1 @@
# "Block-attached entity at invalid position" 로그 제거(MC-252934).
# /summon 은 NBT 를 먼저 읽어(Entity.load → readAdditionalSaveData 가 block_pos 검사)
# 그 다음에 명령 좌표로 엔티티를 이동시킨다. 따라서 block_pos 만 주면 검사 시점에
# 엔티티가 기본 위치라 16블록 거리 검사를 실패해 로그가 계속 남는다.
# Pos 를 함께 넣으면 load 단계에서 위치가 먼저 설정돼 block_pos 검사를 통과한다.
# (1.20.5+ block_pos:[I; x,y,z] 정수배열 = 페인팅 자신의 블록 좌표)
$summon minecraft:painting $(x) $(y) $(z) {Pos:[$(x)d,$(y)d,$(z)d],variant:"$(namespace):$(cover)",facing:$(facing)b,block_pos:[I;$(x),$(y),$(z)],Tags:["mq","mq_cover"]}
$summon minecraft:painting $(x) $(y) $(z) {variant:"$(namespace):$(cover)",facing:$(facing)b,Tags:["mq","mq_cover"]}

View File

@@ -4,21 +4,14 @@
# f : facing (south / north / east / west)
# c : 클릭 시 실행 명령 (init=0 직접, 그 외 trigger $(n) 투표)
#
# optional 필드 (버튼 아래 y-1 위치에 같은 벽면 라벨 부착):
# label : 표시할 텍스트. 생략하면 라벨 없음.
# label_color : 텍스트 색 (예 "black", "red", "#FFAA00"). 기본 "black".
# label_font : 텍스트 폰트 (예 "minecraft:default"). 기본 "minecraft:default".
# label_scale : 텍스트 크기 (Vector3f 한 축, 3축 동일). 기본 "1.0".
# label_addY : 추가할 높이. 기본 "-0.1".
#
# interaction entity 의 위치/크기와 text_display 의 위치/회전은 facing 만
# 보면 결정됨 — 매번 손으로 ox/oy/oz 를 적지 않는다. 실제 오프셋 테이블은
# repeat/buttons/btn 안에서 한 곳에만 정의되어 있다.
# interaction entity 의 위치/크기는 facing 만 보면 결정됨 — 매번 손으로
# ox/oy/oz/w/h 를 적지 않는다. 실제 오프셋 테이블은 repeat/buttons/btn
# 안에서 한 곳에만 정의되어 있다 (stone_button[face=wall] 면 정합용).
data modify storage mq:main button_defs set value []
data modify storage mq:main button_defs append value {n:"start", x:2773, y:86, z:5968, f:"north", c:"function mq:commands/start with storage mq:main", label:"게임시작"}
data modify storage mq:main button_defs append value {n:"stop", x:2771, y:86, z:5968, f:"north", c:"function mq:commands/stop with storage mq:main", label:"정지"}
data modify storage mq:main button_defs append value {n:"skip", x:2769, y:86, z:5968, f:"north", c:"function mq:commands/skip", label:"넘기기"}
data modify storage mq:main button_defs append value {n:"hint", x:2767, y:86, z:5968, f:"north", c:"function mq:commands/hint", label:"힌트"}
data modify storage mq:main button_defs append value {n:"replay", x:2765, y:86, z:5968, f:"north", c:"function mq:commands/replay", label:"다시듣기"}
data modify storage mq:main button_defs append value {n:"test", x:2769, y:87, z:5957, f:"south", c:"function mq:commands/test with storage mq:main audio", label:"소리\n테스트", label_addY: "-0.2"}
data modify storage mq:main button_defs append value {n:"start", x:364, y:146, z:-263, f:"east", c:"function mq:commands/start with storage mq:main"}
data modify storage mq:main button_defs append value {n:"stop", x:364, y:146, z:-265, f:"east", c:"function mq:commands/stop with storage mq:main"}
data modify storage mq:main button_defs append value {n:"skip", x:364, y:146, z:-267, f:"east", c:"function mq:commands/skip"}
data modify storage mq:main button_defs append value {n:"hint", x:364, y:146, z:-269, f:"east", c:"function mq:commands/hint"}
data modify storage mq:main button_defs append value {n:"replay", x:364, y:146, z:-271, f:"east", c:"function mq:commands/replay"}
data modify storage mq:main button_defs append value {n:"test", x:144, y:62, z:-213, f:"north", c:"function mq:commands/test"}

View File

@@ -2,39 +2,21 @@
data modify storage mq:main title set value "음악퀴즈"
# 플레이어 접속 시 텔레포트 위치 (x y z, r=yaw, f=pitch)
data modify storage mq:main spawn set value {x: 2769, y: 85, z: 5963, r: 0, f: 0}
data modify storage mq:main spawn set value {x: 144, y: 61, z: -219, r: 180, f: 0}
# 음원 재생 — minecraft_launcher 리소스팩의 musicquiz:track_NN 사운드 이벤트
# namespace — 리소스팩 네임스페이스 (기본 "musicquiz")
# source — /playsound 채널. stopsound 와 동일해야 함. 노래는 "record" 채널로
# 재생 (주크박스/소리 블록 채널 슬라이더로 음량 제어).
# source — /playsound 채널. stopsound 와 동일해야 함. 노래는 "player" 채널로
# 재생 (음성/플레이어 채널 슬라이더로 음량 제어). 타이머/UI 비프는
# 별도로 weather 채널 사용.
# volume — 기본 음량. 곡별 override 는 init/songs.mcfunction 의 volume 필드 사용
# pitch — 1.0 = 원본 속도
data modify storage mq:main audio set value {namespace: "musicquiz", source: "record", volume: 1.0, pitch: 1.0}
data modify storage mq:main audio set value {namespace: "musicquiz", source: "player", volume: 1.0, pitch: 1.0}
# 정답 페인팅 — 데이터팩의 mq:cover_NN painting_variant (텍스처는 리소스팩 musicquiz:cover_NN)
# namespace — painting_variant 네임스페이스 (기본 "mq")
# x,y,z — 페인팅 entity 좌표 (벽면 앞쪽 블록 위치)
# facing — 페인팅이 바라보는 방향: south=0 / west=1 / north=2 / east=3
data modify storage mq:main image set value {namespace: "mq", x: 2782, y: 88, z: 5979, facing: 2b}
# 정답 영상 — 모드의 영상 재생 관련
# namespace — 영상 아이디를 제외한 주소 (마지막 / 는 자동으로 제거됨)
# x,y,z — 영상 블럭 좌표 (벽면 앞쪽 블록 위치)
# w,h — 영상 가로세로 크키 (왼쪽아래 기준)
# facing — 영상이 바라보는 방향: south / west / north / east
# sound — 영상 소리 설정 (0~100, 음소거: -1)
# preload — 게임 시작 시 미리 받아둘 영상 캐시 갯수 (1번부터 N번까지).
# 재생 중에도 항상 N개 앞까지 미리 받아둠. 0 이면 미리받기 끔(매번 URL 재생)
# keep — 캐시 유지 갯수(FIFO). 이 값을 넘기면 가장 먼저 받은 캐시부터 삭제.
# 메모리 관리용이므로 preload 보다 크거나 같게 설정 권장
data modify storage mq:main video set value { \
namespace: "https://video.tkrmagid.kr/api/video//s3-2", \
cmd_x: 2769, cmd_y: 73, cmd_z: 5965, \
x: 2731, y: 96, z: 6034, facing: "north", w: 25,h: 14, \
sound: -1, \
preload: 5, \
keep: 10 \
}
data modify storage mq:main image set value {namespace: "mq", x: 144, y: 84, z: -261, facing: 0b}
# 곡 개수 max_index 는 init/songs.mcfunction 의 길이로 자동 계산됨

View File

@@ -1,62 +1,59 @@
# 곡 한 개 = 한 줄.
# 필수 — title, author, alias, description
# 선택 — volume (이 곡만의 /playsound 음량. 미지정시 init/config.mcfunction
# 의 audio.volume 사용)
# video (정답 공개 시 actionbar 에 흰색으로 표시되는 영상 URL. 미지정시
# 표시 안 함)
# 필수 — title, author, alias, volume
# volume: /playsound 음량. 1.0 = 기본. 곡마다 음량 조절 가능.
# 곡 순서가 리소스팩의 track_NN / cover_NN 인덱스와 1:1 매칭된다.
# 예) {volume:0.5, title:"Quiet Song", author:"...", alias:[...], description:"...", video:"..."}
# 예) {title:"Quiet Song", author:"...", alias:[...], volume:2.0}
data modify storage mq:main songs set value []
data modify storage mq:main songs append value {volume:0.7, title:"GTA V", author:"Welcome to Los Santos Intro/Theme", alias:["Grand Theft Auto","Grand Theft Auto 5","Grand Theft Auto V","GTA","GTA 5"], description:"GTA V의 인트로 테마곡이다. 로스 산토스에 입성하는 순간의 어두우면서도 힙한 분위기를 형성하는 시그니처 사운드트랙이다.", video:"https://youtu.be/QkkoHAzjnUs"}
data modify storage mq:main songs append value {volume:0.7, title:"FIFA 2000", author:"It's Only Us", alias:["FIFA"," 2000",""], description:"영국 가수 Robbie Williams의 곡 \"It's Only Us\". EA에서 FIFA 2000의 사운드트랙으로 라이선스해 인트로 곡으로 삽입한 노래이다. 피파가 큰 인기를 끌게 되며 게임에 삽입곡으로 분위기를 만드는 전통을 이어간 초기 사례.", video:"https://youtu.be/YznY5_PQZ3Y"}
data modify storage mq:main songs append value {volume:1.27, title:"테일즈런너", author:"트레이닝 시리즈 BGM", alias:[], description:"이 곡은 테일즈런너의 트레이닝 시리즈 BGM으로, 가벼운 리듬감의 곡이 기본적인 맵인 트레이닝 시리즈의 분위기와 어울리며 많은 이들의 뇌리에 각인되었다.", video:"https://youtu.be/gfswv3n6sLw"}
data modify storage mq:main songs append value {volume:0.5, title:"ARK : Survival Evolved", author:"Main Theme", alias:["ARK Survival Evolved"," "], description:"작곡가 Gareth Coker가 작곡하고 Abbey Road 스튜디오에서 93인조 필하모니아 오케스트라로 녹음된 곡으로, 광활한 공룡 생존 세계의 웅장함과 신비로움을 담은 합창과 오케스트라가 인상적이다.", video:"https://youtu.be/FW9vsrPWujI"}
data modify storage mq:main songs append value {volume:0.6, title:"League of Legends", author:"Challengers", alias:[" "], description:"2015년에 라이엇 게임즈가 발매한 앨범 \"The Music of League of Legends Vol. 1\"의 수록곡이다. 라이엇 게임즈가 LoL의 세계관과 e스포츠 문화를 음악으로 표현하기 위해 모은 정식 게임 OST 컴필레이션 중 한 트랙이며, LoL의 본질을 담은 곡이다. 과거에는 인 게임에 재생되어 익숙한 곡이었으나, 현재는 재생되지 않는다.", video:"https://youtu.be/zF5Ddo9JdpY"}
data modify storage mq:main songs append value {volume:0.5, title:"카트라이더", author:"상점 BGM", alias:["Kart Rider"], description:"넥슨의 국민 레이싱 게임이었던 카트라이더의 상점 BGM이다. 게임을 해봤던 게이머라면 모를 수가 없을 정도의 경쾌하고 친숙한 멜로디로 한국 게이머들에게 추억의 곡으로 자리 잡았다.", video:"https://youtu.be/Ie4tEpNuWps"}
data modify storage mq:main songs append value {volume:1.54, title:"The Legend of Zelda", author:"Main Theme", alias:[" "], description:"젤다의 전설: 신들의 트라이포스의 메인 테마. 콘도 코지가 작곡한 젤다 시리즈의 영원한 클래식 명곡이다.", video:"https://youtu.be/a9WLI25xb8Q"}
data modify storage mq:main songs append value {volume:0.53, title:"크레이지 아케이드", author:"Camp BGM", alias:[], description:"크레이지 아케이드 맵 캠프 시리즈의 BGM이다. 빠른 템포와 단순한 반복 멜로디가 인상적이다.", video:"https://youtu.be/_Gj9ef-Jdd8"}
data modify storage mq:main songs append value {volume:0.54, title:"메이플스토리", author:"에레브 수련의 숲 BGM", alias:["Maplestory"], description:"메이플스토리 에레브 지역의 BGM이다. 시그너스 기사단의 시작 지역으로, 빗방울이 떨어지는 듯한 서정적이고 신비로운 분위기의 명곡으로 자리 잡았다.", video:"https://youtu.be/syNANSJN_xc"}
data modify storage mq:main songs append value {volume:1.72, title:"Minecraft", author:"C418 - Mice on Venus", alias:[""], description:"마인크래프트 C418의 대표곡으로, 게임을 플레이하지 않은 사람도 알 정도로 유명한 명상적이고 서정적인 피아노 곡. 이 곡을 들으면 괜히 추억에 잠길 수도 있게 된다.", video:"https://youtu.be/MmB9b5njVbA"}
data modify storage mq:main songs append value {volume:0.35, title:"던전 앤 파이터", author:"Purgatorium", alias:[], description:"퍼가토리엄 지역을 통과하는 데 사용된 테마곡이며, 던전앤파이터 하면 이 곡이 떠오를 정도로 많은 던전앤파이터 유저들에게 추억의 곡으로 알려져 있다. 특히 타 게임 \"레바의 모험\"에서도 주로 사용되며 던전앤파이터를 해보지 않은 게이머들에게도 익숙하게 느껴질 수 있다.", video:"https://youtu.be/B8_VeJLH80I"}
data modify storage mq:main songs append value {volume:0.55, title:"DJMAX RESPECT V", author:"Lobby Main Theme", alias:["DJMAX RESPECT 5","DJMAX RESPECT"," V"," 5"," "], description:"DJMAX RESPECT V의 메인 메뉴 BGM이다. 시리즈 특유의 세련된 일렉트로닉 사운드가 매력적이다.", video:"https://youtu.be/hZ0jXmDv3Ac"}
data modify storage mq:main songs append value {volume:0.52, title:"소닉", author:"Green Hill Zone", alias:["Sonic"], description:"소닉 더 헤지혹의 상징과도 같은 BGM이다. 1991년 첫 작품부터 시작된 SEGA의 대표적인 BGM으로, 게임 음악 역사에 한 획을 그었다.", video:"https://youtu.be/K7-kl5DHLpU"}
data modify storage mq:main songs append value {volume:0.77, title:"Apex 레전드", author:"Main Theme", alias:[" ","Apex Legends"], description:"에이펙스 레전드의 메인 테마곡. 박진감 넘치는 배틀로얄의 긴장감과 히어로물 같은 웅장함을 결합한 곡이다. 특히 곡 중간 부분의 특유의 멜로디가 인상적이다.", video:"https://youtu.be/7M4Vds3fI9E"}
data modify storage mq:main songs append value {volume:0.5, title:"디아블로 2", author:"트리스트럼 BGM", alias:["","DIABLO 2","DIABLO"], description:"디아블로 2의 트리스트럼 마을 BGM이다. 시리즈를 대표하는 기타 선율로, 디아블로 1로부터 이어진 황량하고 우울한 분위기의 명곡이다.", video:"https://youtu.be/84Tr5rCVRgM"}
data modify storage mq:main songs append value {volume:0.5, title:"포켓몬스터", author:"등화도시 BGM", alias:[" "," ORAS"], description:"포켓몬스터 오메가루비/알파사파이어의 등화도시 BGM이다. 도시의 활기와 화려함을 담은 경쾌한 리메이크 버전이다. 쇼츠에도 많이 사용되며 포켓몬스터 BGM 중에 인기 있는 곡에 속한다.", video:"https://youtu.be/bpu5ofLNt0w"}
data modify storage mq:main songs append value {volume:0.65, title:"슈퍼 마리오 64", author:"Main Theme", alias:[" ","Super Mario 64","Super Mario"], description:"닌텐도의 명작 슈퍼마리오 64의 메인 테마곡이다. 3D 마리오 시리즈의 역사적 시작을 알린 곡이다. 닌텐도 게임 음악사에서 빼놓을 수 없는 명곡 중 하나이다.", video:"https://youtu.be/Ujir2X1D_yw"}
data modify storage mq:main songs append value {volume:0.65, title:"Elden Ring", author:"Malenia, Blade of Miquella", alias:[" "], description:"엘든 링의 최강 보스 중 하나인 말레니아, 미켈라의 칼날 전투 BGM이다. 소프라노 보컬과 비장한 오케스트라가 어우러진 보스 BGM의 정점으로, 엘든 링을 클리어해본 유저라면 이 곡이 친숙하게 느껴질 수 있다.", video:"https://youtu.be/8McaLMSQRBk"}
data modify storage mq:main songs append value {volume:0.7, title:"별의 커비", author:"Green Greens", alias:["Kirby Super Star"], description:"커비 시리즈를 대표하는 Green Greens BGM. 첫 스테이지 BGM으로 시작해 시리즈 전반에 걸쳐 다양하게 편곡되는 커비의 시그니처 테마이다.", video:"https://youtu.be/aYjFa0dVAjo"}
data modify storage mq:main songs append value {volume:0.51, title:"원신", author:"Genshin Impact Main Theme", alias:["Genshin Impact"], description:"원신의 메인 테마곡이다. HOYO-Mix가 작곡한 웅장하고 환상적인 오케스트라 곡으로, 원신을 가장 잘 표현한 곡으로 평가받고 있다.", video:"https://youtu.be/poaXbXgTWx0"}
data modify storage mq:main songs append value {volume:0.86, title:"Slay The spire 2", author:"Main Menu Theme", alias:[" 2"," "], description:"인기를 끌고 있는 인디 덱빌딩 로그라이크 게임 슬레이 더 스파이어 2의 메인 메뉴 테마곡이다. 신비로운 탑을 오르는 여정의 분위기를 담았다.", video:"https://youtu.be/caEYvo6c8oU"}
data modify storage mq:main songs append value {volume:0.83, title:"Arc Raiders", author:"Lobby Music - Track 01", alias:[" "], description:"익스트랙션 슈터 장르인 아크 레이더스의 로비 음악이다. 아크 레이더스는 출시 초기 참신한 컨셉과 완성도 높은 게임성으로 큰 인기를 끌었으며 현재도 꾸준한 인기를 유지하고 있다. 게임을 켜자마자 나오는 포스트 아포칼립스 분위기의 긴장감 있는 사운드가 특징이다.", video:"https://youtu.be/xuftkDxjGT4"}
data modify storage mq:main songs append value {volume:0.48, title:"FIFA 온라인 3", author:"Yeah Yeah Yeah", alias:["FIFA ","FC 3","FC "," 3"," "], description:"영국 DJ Jax Jones가 2015년 7월 발매한 댄스/하우스 싱글로, 게임 OST로 작곡된 곡은 아니지만 클럽/스포츠 게임 등에서 BGM이나 라이선스 음악으로 자주 활용된다. 경쾌한 비트와 멜로디가 인상적이다.", video:"https://youtu.be/Pbnh06kCgVk"}
data modify storage mq:main songs append value {volume:0.55, title:"Terraria", author:"Underground BGM", alias:[""], description:"샌드박스 게임인 테라리아의 지하 BGM이다. 지하 탐험의 신비로운 분위기를 담았다.", video:"https://youtu.be/HHNLfxBxcvo"}
data modify storage mq:main songs append value {volume:0.65, title:"Project Zomboid", author:"Main Menu Theme", alias:[" "], description:"좀비 생존 게임인 프로젝트 좀보이드의 메인 메뉴 BGM. 게임을 켜게 되면 이 곡이 재생되며 게임에 황량하고 외로운 분위기를 더해준다.", video:"https://youtu.be/pU150792vtY"}
data modify storage mq:main songs append value {volume:0.53, title:"Undertale", author:"His Theme", alias:[""], description:"언더테일의 명곡 중 하나. 게임 후반부의 감동적인 순간을 책임지는 Toby Fox의 대표곡이다.", video:"https://youtu.be/1Hojv0m3TqA"}
data modify storage mq:main songs append value {volume:0.71, title:"Battlefield 4", author:"Main Theme", alias:["Battlefield"," 4",""], description:"배틀필드 4의 공식 메인 테마곡이다. DICE의 시그니처 시리즈 사운드로, 전쟁 영화 같은 웅장함을 담았다. 노래의 웅장함 덕분에 게임 BGM뿐만 아니라 여러 웅장하고 전투적인 영상에 자주 쓰이는 곡이다.", video:"https://youtu.be/vupPwLJ1zkE"}
data modify storage mq:main songs append value {volume:0.73, title:"Stardew Valley", author:"Stardew Valley Overture", alias:[" "], description:"스타듀밸리의 오프닝 테마이다. 따뜻하고 향수 어린 멜로디로, 시골 농장 생활의 시작을 알린다.", video:"https://youtu.be/ot7uXNQskhs"}
data modify storage mq:main songs append value {volume:0.7, title:"Rainbow Six Siege", author:"영사관 지하 라디오", alias:[" "], description:"레인보우 식스 시즈의 영사관 맵 지하에서 재생되는 라디오 BGM이다. 이 게임을 플레이해본 게이머라면 영사관 맵 지하에 들어서는 순간 들리는 이 익숙한 라디오 BGM을 잊을 수 없을 것이다.", video:"https://youtu.be/yX6F6764sMQ"}
data modify storage mq:main songs append value {volume:0.63, title:"StarCraft", author:"Terran Theme 3", alias:[""], description:"대한민국 전통 민속놀이 스타크래프트의 테란 종족 3번째 트랙 곡이다. 우주적이고 전투적인 분위기를 잘 담은 곡으로 한국 e스포츠와 함께 자란 세대에게 정말 깊은 향수를 자극한다. 특히 테란 종족 1번째 트랙이 대표적이다.", video:"https://youtu.be/Cqh37RlJ32Y"}
data modify storage mq:main songs append value {volume:0.81, title:"모두의 마블", author:"월드맵 BGM", alias:[], description:"넷마블의 모바일 게임 모두의 마블의 월드맵 BGM. 신나는 분위기로 여행 콘셉트를 표현한다.", video:"https://youtu.be/FMJe3L4amNE"}
data modify storage mq:main songs append value {volume:0.7, title:"Blue Archive", author:"Constant Moderato", alias:[" "], description:"블루 아카이브의 메인 테마곡이다. 잔잔하고 청량하고 산뜻한 분위기로 인트로를 알리며 중반부 신나는 멜로디와 비트가 인상 깊다. 학원 도시 키보토스의 일상을 잘 표현하는 시리즈의 시그니처 곡이다.", video:"https://youtu.be/YHy8JmWX3kw"}
data modify storage mq:main songs append value {volume:0.51, title:"튀어나와요 동물의 숲", author:"In Kapp'n's Bus", alias:[" "], description:"동물의 숲 시리즈의 캅파 보트 투어 BGM. 무인도 여행의 즐거움을 담은 곡이다.", video:"https://youtu.be/3fKRexrC76c"}
data modify storage mq:main songs append value {volume:0.62, title:"VALORANT", author:"WATCH", alias:[""], description:"라이엇 게임즈의 FPS 게임인 발로란트의 공식 음악이다. 챔피언스 투어 등 e스포츠 이벤트와 함께 공개된 곡이지만, 인 게임 스킨 테마곡으로 쓰이며 많은 게이머들에게 친숙하게 되었다.", video:"https://youtu.be/RCUI9q99qus"}
data modify storage mq:main songs append value {volume:0.66, title:"Civilization VI", author:"Sogno di Volare", alias:["Civilization 6"," VI"," 6"], description:"문명 6의 메인 테마곡이다. 아카데미상 후보에 오른 작곡가 Christopher Tin이 작곡한 만큼, 이탈리아어 합창곡의 분위기가 문명 시리즈 음악의 정점을 알린다.", video:"https://youtu.be/uULVgaeHPqg"}
data modify storage mq:main songs append value {volume:0.67, title:"배틀그라운드", author:"Old Main Theme", alias:[], description:"배틀그라운드의 구 메인 로비 BGM이다. 배틀그라운드가 막 출시한 시점 게임을 켜게 되면 항상 듣게 되는 곡이었으며, 배틀로얄 장르를 대중화시킨 작품의 긴장감 있는 테마가 인상 깊다.", video:"https://youtu.be/kp3YL3QoPEE"}
data modify storage mq:main songs append value {volume:0.5, title:"버블파이터", author:"대기방 BGM", alias:["Bubble Fighter"], description:"넥슨의 캐주얼 슈팅 게임 버블파이터의 대기방 BGM이다. 친숙하고 귀여운 분위기의 곡이 인상적이다.", video:"https://youtu.be/xpPKJMpRkY8"}
data modify storage mq:main songs append value {volume:1.25, title:"Bubble Bobble", author:"Main Theme", alias:[""], description:"추억의 클래식 아케이드 게임 버블보블의 메인 테마곡이다. 짧지만 강렬한 중독성으로 80~90년대 오락실 세대의 추억을 자극하는 명곡이다.", video:"https://youtu.be/Hhh_yY3fTb8"}
data modify storage mq:main songs append value {volume:0.74, title:"냥코 대전쟁", author:"전투 테마 1", alias:[], description:"모바일 게임 냥코 대전쟁의 전투 BGM이다. 귀엽지만 중독성 있는 멜로디가 특징이며, 게임을 플레이해봤다면 익숙할 것이다.", video:"https://youtu.be/2ay66PWuw6o"}
data modify storage mq:main songs append value {volume:0.55, title:"SEPHIRIA", author:"토끼마을 BGM", alias:[""], description:"인디 RPG 세피리아의 토끼마을 BGM이다. 평화로운 마을의 분위기를 담은 곡.", video:"https://youtu.be/Ls9h1MZ0mFE"}
data modify storage mq:main songs append value {volume:0.8, title:"산나비", author:"끝났다는 것은 다시 시작된다는 것을", alias:["SANABI"], description:"한국 인디 게임인 산나비의 엔딩곡이다. 감동적인 스토리의 여운을 그대로 전달하는 명곡으로 평가받는다. 산나비를 플레이해본 유저라면 이 노래를 듣고 감동의 눈물을 흘릴 수밖에 없다.", video:"https://youtu.be/wZ6wx0rSL8M"}
data modify storage mq:main songs append value {volume:1.9, title:"쿠키런", author:"에피소드 1 Main Theme", alias:[], description:"모바일 게임 쿠키런의 첫 에피소드 BGM이다. 경쾌하고 귀여운 분위기로 쿠키들의 오븐 탈출을 잘 표현한 곡이다.", video:"https://youtu.be/_m6SEk_yK8A"}
data modify storage mq:main songs append value {volume:0.89, title:"끄투", author:"로비 BGM", alias:[], description:"한국 끝말잇기 게임 끄투의 구 로비 BGM이다. 한국 웹 게임 문화를 대표하는 곡 중 하나.", video:"https://youtu.be/1vTkng-RMCk"}
data modify storage mq:main songs append value {volume:0.71, title:"Geometry Dash", author:"Stay Inside Me", alias:[" "], description:"지오메트리 대시의 사운드트랙이다. 지오메트리 대시는 모르는 맵을 클리어하기 위해서 연습 모드를 플레이할 수밖에 없는데, 이 곡이 연습 모드에서 반복 재생되며 많은 이들의 뇌리에 집어넣어졌다.", video:"https://youtu.be/WkkBkBcVzK8"}
data modify storage mq:main songs append value {volume:0.98, title:"레이튼 교수와 이상한 마을", author:"수수께끼", alias:[], description:"닌텐도 DS의 명작 추리/퍼즐 게임 레이튼 교수와 이상한 마을의 OST이다. 아코디언 선율의 특징적인 유럽풍 분위기의 곡.", video:"https://youtu.be/MZjBpjmMccY"}
data modify storage mq:main songs append value {volume:0.62, title:"TEKKEN 7", author:"Infinite Azure - Round 1", alias:["TEKKEN"," 7",""], description:"철권 7의 인피니트 아주르 스테이지 BGM. 시리즈 전통의 일본풍 도시 야경 스테이지의 세련된 EDM 트랙으로, 주로 인 게임에서 콤보 연습을 할 때 자주 사용하는 맵이므로 플레이해본 게이머들에겐 익숙한 곡이다.", video:"https://youtu.be/5Lw3tg40DG8"}
data modify storage mq:main songs append value {volume:0.6, title:"엘소드", author:"Login Screen 1", alias:[], description:"넥슨 게임 엘소드의 로그인 화면 BGM. 중세 판타지 전투의 분위기를 가볍게 표현했다.", video:"https://youtu.be/B-M0f1lIB5g"}
data modify storage mq:main songs append value {volume:0.77, title:"Clash Royale", author:"Menu A", alias:[" "], description:"슈퍼셀의 모바일 게임 클래시 로얄의 메뉴 BGM이다. 게임을 해봤다면 정말 정말 익숙한 곡이다.", video:"https://youtu.be/_hNxfiXmeAE"}
data modify storage mq:main songs append value {volume:0.58, title:"OverWatch", author:"환상의 겨울나라 BGM", alias:[""], description:"오버워치 환상의 겨울나라 이벤트 BGM으로, 크리스마스 시즌 한정 이벤트의 따뜻한 분위기를 담았다. 그 시절 겨울에도 오버워치를 플레이해본 유저라면 추억이 떠오를 수밖에 없는 향수 같은 곡이다.", video:"https://youtu.be/66h40e_0_Q8"}
data modify storage mq:main songs append value {volume:0.77, title:"Cyberpunk 2077", author:"I Really Want to Stay at Your House", alias:["Cyberpunk"," 2077",""], description:"사이버 펑크 2077의 OST이자 애니메이션 사이버펑크: 엣지러너의 엔딩곡인 이 곡은 게임 OST이긴 하나, 애니메이션에서 감성적인 마무리를 장식하며 전 세계적으로 폭발적인 인기를 끌게된 곡이다. 그와 자연스레 사이버펑크 2077의 정서를 대표하는 노래가 되었다. 죽음이란게 가볍게 여겨지는 차가운 사이버펑크 세상속에서 순수한 순애 분위기의 곡으로, 큰 여운이 남는 것이 특징.", video:"https://youtu.be/ecO_xfm8JD4"}
data modify storage mq:main songs append value {title:"Lose My Mind", author:"Don Toliver", alias:[" "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"The Chase", author:"Hearts2Hearts", alias:[" "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"HOT SAUCE", author:"BABYMONSTER", alias:[" "," "], volume:1.0}
data modify storage mq:main songs append value {title:"Golden", author:"HUNTR/X", alias:[""," "], volume:1.0}
data modify storage mq:main songs append value {title:"돌림판", author:"머쉬베놈", alias:["Spin the wheel"], volume:1.0}
data modify storage mq:main songs append value {title:"OVERDRIVE", author:"TWS", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"눈물참기", author:"QWER", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"깨어", author:"tripleS", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"like JENNIE", author:"제니", alias:[" "," "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"Rich Man", author:"aespa", alias:[" "," "], volume:1.0}
data modify storage mq:main songs append value {title:"I DO ME", author:"KiiiKiii", alias:[" "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"SIGN", author:"izna", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"WICKED", author:"ALLDAY PROJECT", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"Good Thing", author:"i-dle", alias:["굿 "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"0+0", author:"한로로", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"HANDS UP", author:"MEOVV", alias:[" "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"Blue Valentine", author:"NMIXX", alias:[" "," "], volume:1.0}
data modify storage mq:main songs append value {title:"Flower", author:"오반", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"Soda Pop", author:"Saja Boys", alias:[" "," "], volume:1.0}
data modify storage mq:main songs append value {title:"REBEL HEART", author:"IVE", alias:[" "," "], volume:1.0}
data modify storage mq:main songs append value {title:"GO!", author:"CORTIS", alias:["","!","GO","","!"], volume:1.0}
data modify storage mq:main songs append value {title:"BEEP", author:"izna", alias:["","",""], volume:1.0}
data modify storage mq:main songs append value {title:"Pookie", author:"FIFTY FIFTY", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"DAISIES", author:"Justin Bieber", alias:["","",""], volume:1.0}
data modify storage mq:main songs append value {title:"빌려온 고양이", author:"ILLIT", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"TOO BAD", author:"OfficialGDRAGON", alias:[" "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"시작의 아이", author:"마크툽", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"STYLE", author:"Hearts2Hearts", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"Good Goodbye", author:"화사", alias:["굿 굿"," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"너에게 닿기를", author:"10CM", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"IRIS OUT", author:"Kenshi Yonezu", alias:[" "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"Sugar On My Tongue", author:"Tyler, The Creator", alias:[" "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"Hollywood Action", author:"BOYNEXTDOOR", alias:[" "," "], volume:1.0}
data modify storage mq:main songs append value {title:"SPAGHETTI", author:"LE SSERAFIM", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"Gabriela", author:"KATSEYE", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"LIKE YOU BETTER", author:"프로미스나인", alias:[" "," "," "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"뛰어", author:"BLACKPINK", alias:["jump"], volume:1.0}
data modify storage mq:main songs append value {title:"CHANEL", author:"Tyla", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"오늘만 I LOVE YOU", author:"BOYNEXTDOOR", alias:[" "," "], volume:1.0}
data modify storage mq:main songs append value {title:"earthquake", author:"지수", alias:["","",""," "], volume:1.0}
data modify storage mq:main songs append value {title:"윽!", author:"염따", alias:[""], volume:1.0}
data modify storage mq:main songs append value {title:"Abracadabra", author:"Lady Gaga", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"멸종위기사랑", author:"이찬혁", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"Dirty Work", author:"aespa", alias:[" "," "," "," "," "], volume:1.0}
data modify storage mq:main songs append value {title:"HOT", author:"LE SSERAFIM", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"FAMOUS", author:"ALLDAY PROJECT", alias:["",""], volume:1.0}
data modify storage mq:main songs append value {title:"XOXZ", author:"IVE", alias:[" "," ",""], volume:1.0}
data modify storage mq:main songs append value {title:"여름이었다", author:"H1-KEY", alias:[], volume:1.0}
data modify storage mq:main songs append value {title:"LOV3", author:"식케이", alias:["3","3"], volume:1.0}
data modify storage mq:main songs append value {title:"Drive", author:"Ed Sheeran", alias:["",""], volume:1.0}
# 곡 개수는 songs 배열 길이에서 자동 계산됨
execute store result storage mq:main max_index int 1 run data get storage mq:main songs

View File

@@ -1,9 +1,8 @@
data modify storage mq:main answer set value {title:"", author:"", alias:[], description:""}
data modify storage mq:main answer set value {title:"", author:"", alias:[]}
data merge storage func:temp {}
data merge storage mq:tmp {}
function mq:init/config
function mq:videos/normalize
function mq:init/songs
function mq:init/buttons
function mq:init/triggers
@@ -23,11 +22,9 @@ scoreboard objectives add answer dummy
scoreboard objectives add leave_game custom:leave_game
# 외부 모드 존재 확인용 점수.
# mq_chat_mod : 채팅 정답 판정 필수 모드(mc_chat_answer_mod, 서버 전용).
# 모드가 매 server tick 마다 fake player `#server` 점수를 1 로 set. 서버에
# 없으면 0 유지 → start 차단.
# mq_video_mod : 정답 영상 재생용 선택 모드(mc_video_player_mod). 없어도
# 게임은 진행되고 영상만 안 나온다(start 는 경고만). 클라 join 시 서버로
# mq_chat_mod : 서버 전용 모드(mc_chat_answer_mod). 모드가 매 server tick
# 마다 fake player `#server` 점수를 1 로 set. 모드가 서버에 없으면 0 유지.
# mq_video_mod : 클라이언트 모드(mc_video_player_mod). 클라 join 시 서버로
# handshake payload 전송 → 서버 측 모드가 해당 플레이어 점수를 1 로 set.
# 클라에 모드가 없으면 0 유지. (login.mcfunction 에서 플레이어별 0 초기화.)
scoreboard objectives remove mq_chat_mod
@@ -48,5 +45,3 @@ bossbar add mq:process [{"text":"진행도: ","color": "yellow","bold": true},{"
function mq:commands/stop with storage mq:main
function mq:players/login with storage mq:main spawn
function mq:videos/cache/clear

View File

@@ -1,13 +1,12 @@
tag @s add player
scoreboard players reset @s leave_game
# 외부 모드 확인 점수 초기화 (per-player 대상만).
# mq_video_mod : 영상 모드(mc_video_player_mod, 선택)는 join 시 handshake
# 외부 모드 검증 점수 초기화 (per-player 검증 대상만).
# mq_video_mod : 클라이언트 모드(mc_video_player_mod) 가 join 시 handshake
# payload 를 서버로 보내면 서버 모드가 해당 플레이어 점수를 1 로 set 한다.
# 여기서 0 으로 미리 깔아 두면 handshake 가 없는 플레이어는 0 유지 →
# start 에서 "영상 안 보임" 경고 대상이 된다(차단은 아님). handshake 가
# 오면 곧바로 1 로 갱신됨.
# mq_chat_mod 는 서버 전용 모드라 fake player(#server) 로 확인 — per-player
# start 가드 차단. handshake 가 오면 곧바로 1 로 갱신됨.
# mq_chat_mod 는 서버 전용 모드라 fake player(#server) 로 검증 — per-player
# 초기화 불필요.
scoreboard players set @s mq_video_mod 0

View File

@@ -4,8 +4,7 @@ scoreboard players set @s answer 2
function mq:tellraw {"text":"","color":"black","msg":""}
function mq:tellraw {"text":"","color":"black",msg:[{"text":"정답: ","color": "aqua"},{"storage":"mq:main","nbt":"answer.title","color": "yellow","bold": true}]}
function mq:tellraw {"text":"","color":"black",msg:[{"text":"제목: ","color":"aqua"},{"storage":"mq:main","nbt":"answer.author","color": "yellow","bold": true}]}
function mq:tellraw {"text":"","color":"black",msg:[{"text":"설명: ","color":"aqua"},{"storage":"mq:main","nbt":"answer.description","color": "yellow","bold": false}]}
function mq:tellraw {"text":"","color":"black",msg:[{"text":"가수: ","color":"aqua"},{"storage":"mq:main","nbt":"answer.author","color": "yellow","bold": true}]}
execute if score skip buttons matches -2 run function mq:tellraw {"text":"","color":"black",msg:[{"text":"정답자: ","color": "aqua"},{"text":"스킵","color": "yellow","bold": true}]}
execute unless score skip buttons matches -2 run function mq:tellraw {"text":"","color":"black",msg:[{"text":"정답자: ","color": "aqua"},{"selector":"@s","color": "yellow","bold": true}]}
function mq:tellraw {"text":"","color":"black",msg:[{"text": "( 15초뒤 다음문제로 넘어갑니다. )","color": "gray"}]}
@@ -31,4 +30,3 @@ scoreboard players set replay buttons -3
scoreboard players set timer main 1
function mq:images/show
function mq:videos/show

View File

@@ -1,2 +1 @@
$stopsound @a $(source)
$execute as @a at @s run playsound $(namespace):$(track) $(source) @s ~ ~ ~ $(volume) $(pitch)

View File

@@ -1,9 +0,0 @@
$execute unless data storage mq:main {answer:{title:"음악퀴즈"}} run summon minecraft:marker $(x) $(y) $(z) {Tags:["mq","default"],CustomName:"정답입력시작"}
$summon minecraft:marker $(x) $(y) $(z) {Tags:["mq","default"],CustomName:"$(name)"}
execute store result score length func.temp run data get storage mq:tmp marker_call.alias
execute if score length func.temp matches 1.. run data modify storage mq:tmp marker_call.name set from storage mq:tmp marker_call.alias[0]
execute if score length func.temp matches 1.. run data remove storage mq:tmp marker_call.alias[0]
execute if score length func.temp matches 1.. run function mq:quiz/macro/summon2 with storage mq:tmp marker_call
$execute unless data storage mq:main {answer:{title:"음악퀴즈"}} run summon minecraft:marker $(x) $(y) $(z) {Tags:["mq","default"],CustomName:"정답입력종료"}

View File

@@ -1,6 +0,0 @@
$summon minecraft:marker $(x) $(y) $(z) {Tags:["mq","default"],CustomName:"$(name)"}
execute store result score length func.temp run data get storage mq:tmp marker_call.alias
execute if score length func.temp matches 1.. run data modify storage mq:tmp marker_call.name set from storage mq:tmp marker_call.alias[0]
execute if score length func.temp matches 1.. run data remove storage mq:tmp marker_call.alias[0]
execute if score length func.temp matches 1.. run function mq:quiz/macro/summon2 with storage mq:tmp marker_call

View File

@@ -2,9 +2,4 @@ data modify storage mq:tmp playsound set from storage mq:main audio
data modify storage mq:tmp playsound.track set from storage mq:main answer.track
# 곡 단위 volume override — songs[i].volume 가 없으면 audio.volume 그대로 유지 (no-op)
data modify storage mq:tmp playsound.volume set from storage mq:main answer.volume
function mq:quiz/macro/play_sound with storage mq:tmp playsound
# volume 테스트 (replay 이용)
# data modify storage mq:main answer.volume set value 0.5
# function mq:quiz/play_sound

View File

@@ -18,4 +18,3 @@ execute if score index main matches 1..9 run data modify storage mq:tmp pad set
execute unless score index main matches 1..9 run data modify storage mq:tmp pad set value ""
function mq:quiz/setanswer
function mq:videos/cache/add

View File

@@ -1,124 +1,55 @@
# warn-off-file always-pass-condition
# 버튼 1개에 대한 매 tick 처리.
# 매크로 인자(mq:tmp.btn): n, x, y, z, f, c, label, label_color, label_font, label_scale, label_addY
# 매크로 인자(mq:tmp.btn): n, x, y, z, f, c
# buttons 점수 상태:
# ..-2 : 비활성 (버튼 블록 제거, interaction 응답 차단)
# -1 : 초기화 단계 (버튼 블록 + interaction × 3 + text_display 보장 후 0)
# -1 : 초기화 단계 (버튼 블록 배치 + interaction entity 보장 후 0 으로)
# 0 : 정상 (interaction 클릭 대기)
#
# interaction/text_display entity 는 데이터팩이 직접 summon — /reload 시
# commands/stop 에서 buttons 가 -1 로 재설정되어 다음 tick 에 ensure 로직
# 실행. -1 단계에서 같은 태그 entity 를 모두 kill 후 정확한 개수만 다시
# summon → 항상 idempotent (dup 누적 없음, 좌표/라벨 갱신 자동 반영).
# interaction entity 는 데이터팩이 직접 summon — /reload 시 commands/stop
# 에서 buttons 가 -1 로 재설정되어 다음 tick 에 ensure 로직이 실행됨.
# -1 단계에서 같은 태그 entity 를 모두 kill 후 정확히 1개 summon → dup
# 누적 없이 항상 "버튼당 1개, 올바른 좌표" 상태로 수렴 (idempotent).
#
# ---- facing → 머리 hitbox 위치 (이 파일 한 곳에서만 정의) ----
# stone_button[face=wall, facing=X] AABB (블록 상대 좌표):
# facing 의 의미 = "버튼 머리 visible 면의 normal 방향". 머리는 그 방향
# 쪽 face 에 붙어 있고 hitbox 는 그 face 에서 안쪽(1/8) 만큼 들어감.
# south : z ∈ [0, 0.125] 가로 x ∈ [0.3125, 0.6875]
# north : z ∈ [0.875, 1] 가로 x ∈ [0.3125, 0.6875]
# east : x ∈ [0, 0.125] 가로 z ∈ [0.3125, 0.6875]
# west : x ∈ [0.875, 1] 가로 z ∈ [0.3125, 0.6875]
# 세로 y ∈ [0.375, 0.625] 공통.
# ---- facing → 소환 오프셋 (이 파일 한 곳에서만 정의) ----
# stone_button[face=wall] hitbox: 가로 6/16 (0.375), 세로 4/16 (0.25),
# 두께 2/16 (0.125). 벽 블록의 한 면에서만 튀어나오고 반대편은 안쪽으로
# 들어감. interaction 의 horizontal hitbox 는 width × width 정사각형
# 강제 → width 를 가로(0.375) 에 맞추고 위치를 튀어나온 쪽 면에 정렬.
#
# interaction entity 의 horizontal hitbox 는 width × width 정사각형 강제라
# 단일 entity 로는 "가로 0.375 × 두께 0.125" 직사각형 불가. → 두께(0.125)
# 와 같은 width=0.125 인 interaction 을 가로축으로 3 개 타일링 (gap 없이
# 인접: 중심 0.375 / 0.5 / 0.625, 각 폭 0.125 → 합 [0.3125, 0.6875]).
# interaction Y 는 hitbox 바닥 → 소환 y = block y + 0.375, height = 0.25.
#
# ---- 깊이축: 블록 면 바로 바깥, 플레이어 쪽 (이중 트리거 방지) ----
# interaction 박스가 stone_button hitbox 와 겹치면 한 번 클릭에 interaction
# 도 발화하고 stone_button 도 vanilla 클릭으로 인식되어 powered=true 애니
# 메이션이 같이 일어남. → interaction 박스를 버튼 머리 바깥쪽 (플레이어
# 측) 으로 한 두께 (0.125) 만큼 밀어 ray 가 stone_button 에 닿기 전에
# interaction 에서 멈추게.
#
# 주의: facing 은 "버튼 머리 normal 방향" = 플레이어가 보는 방향.
# south 면 머리 +z 향함, 벽은 -z 쪽. 따라서 플레이어 쪽 = +z = interaction
# 을 z > 버튼 머리 (0.125) 영역으로. (v1.0.21 에서 한 두께만큼 뺀다는
# 의도였는데 부호를 반대로 잡아 interaction 이 벽 안으로 들어가 있었음.)
# south : 깊이 z 중심 = 0.1875 (interaction z ∈ [0.125, 0.25], 버튼 z ∈ [0, 0.125])
# north : 깊이 z 중심 = 0.8125 (interaction z ∈ [0.75, 0.875], 버튼 z ∈ [0.875, 1])
# east : 깊이 x 중심 = 0.1875
# west : 깊이 x 중심 = 0.8125
#
# ---- positioned 의 .5 보정 회피 ----
# MC 의 vec3 인자는 정수만 쓰면 자동으로 +0.5 보정됨 (블록 중심으로 잡힘).
# positioned 2773 86 5968 → 실제로는 (2773.5, 86, 5968.5). 거기서 ~ 오프셋
# 을 더하면 박스 전체가 0.5 칸 어긋남. $(x).0 $(y).0 $(z).0 처럼 decimal
# 형태로 넘기면 보정 없이 정확한 블록 origin (minimal corner) 이 됨.
#
# ---- text_display 위치 (버튼 바로 아래 같은 벽면에 가운데 정렬) ----
# 버튼 아래 블록의 같은 벽면 (visible 면, 플레이어 쪽) 에 살짝 띄워 부착.
# 가로축: ~0.5 (block 가로 중심, alignment=center 기본값과 합쳐져서 라벨
# 자체도 수평 중앙).
# 세로축: text_display 의 entity Y 는 텍스트 윗변 — 아래로 자람. ~-0.25
# 로 두면 텍스트 윗변이 Y-0.25 (버튼 바로 아래), 한 줄(기본 ~0.5 블록 높이)
# 이 Y-0.75 까지 내려와 버튼 아래 한 칸 벽면 [Y-1, Y] 의 위쪽 절반에
# 자리잡음 — 시각적으로 버튼 바로 밑 가운데 라벨.
# south : ~0.5 ~-0.25 ~0.01 yaw 0 (head 가 +z → 벽면 z=0 에서 +0.01 띄움)
# north : ~0.5 ~-0.25 ~0.99 yaw 180
# east : ~0.01 ~-0.25 ~0.5 yaw -90
# west : ~0.99 ~-0.25 ~0.5 yaw 90
# facing 의미: "플레이어가 그 방향을 보면서 누른다". 즉 button 머리는
# facing 의 반대쪽 면에 붙어 있다.
# south : 머리는 블록의 -z 쪽 면(north face) → 소환 z = -0.0625
# north : 머리는 블록의 +z 쪽 면(south face) → 소환 z = 1.0625
# east : 머리는 블록의 -x 쪽 면(west face) → 소환 x = -0.0625
# west : 머리는 블록의 +x 쪽 면(east face) → 소환 x = 1.0625
# interaction entity Y 는 hitbox 의 바닥(=발 위치) — 버튼 머리가 블록의
# 세로 중앙(y+0.5) 에 있고 높이 0.25 라 바닥은 y+0.375.
# ---- 비활성: 블록 + interaction × 3 + text_display 전부 제거 후 종료 ----
# data modify entity @e[...] 는 대상 1개 강제 → interaction 3개 모드에선
# 못 쓰므로 그냥 kill. 어차피 버튼 블록도 air 로 바꾸므로 라벨도 같이 제거.
# ---- 비활성: 버튼 제거 + interaction 응답 차단 후 종료 ----
$execute if score $(n) buttons matches ..-2 run setblock $(x) $(y) $(z) minecraft:air
$execute if score $(n) buttons matches ..-2 run kill @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction]
# $execute if score $(n) buttons matches ..-2 run kill @e[type=minecraft:text_display,tag=mq,tag=$(n)]
$execute if score $(n) buttons matches ..-2 run data modify entity @e[type=minecraft:interaction,tag=mq,tag=$(n),limit=1] response set value 0b
$execute if score $(n) buttons matches ..-2 run return 0
# ---- 초기화: 블록 + interaction × 3 + text_display 보장 ----
# ---- 초기화: 버튼 블록 배치 + interaction entity 보장 ----
# 기존 mq/$(n) interaction 을 전부 제거 후 정확히 1개 소환.
# 옛 월드 cmd block 으로 누적 소환된 dup 이나 엉뚱한 좌표에 남은 잔존
# entity 까지 정리 → "정상 상태(버튼당 정확히 1개, 올바른 좌표)" 가 보장됨.
$execute unless score $(n) buttons matches -1.. run scoreboard players set $(n) buttons -1
$execute if score $(n) buttons matches -1 run setblock $(x) $(y) $(z) minecraft:stone_button[face=wall,facing=$(f),powered=false]
$execute if score $(n) buttons matches -1 run kill @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction]
$execute if score $(n) buttons matches -1 run kill @e[distance=0..,tag=mq,tag=$(n),type=minecraft:text_display]
# south: 깊이축=z(+0.1875, 플레이어 쪽), 가로축=x, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.37 ~0.37 ~0.07 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.5 ~0.37 ~0.07 {Tags:["mq","$(n)"],width:0.13f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.63 ~0.37 ~0.07 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"south"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.5 ~-0.5 ~0.01 {Tags:["mq","$(n)"],Rotation:[0f,0f],background:0,text:{text:"$(label)",color:"$(label_color)",font:"$(label_font)",bold:true},transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,$(label_addY)f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
# north: 깊이축=z(+0.8125, 플레이어 쪽), 가로축=x, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.37 ~0.37 ~0.93 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.5 ~0.37 ~0.93 {Tags:["mq","$(n)"],width:0.13f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.63 ~0.37 ~0.93 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"north"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.5 ~-0.5 ~0.99 {Tags:["mq","$(n)"],Rotation:[180f,0f],background:0,text:{text:"$(label)",color:"$(label_color)",font:"$(label_font)",bold:true},transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,$(label_addY)f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
# east: 깊이축=x(+0.1875, 플레이어 쪽), 가로축=z, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.07 ~0.37 ~0.37 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.07 ~0.37 ~0.5 {Tags:["mq","$(n)"],width:0.13f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.07 ~0.37 ~0.63 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"east"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.01 ~-0.5 ~0.5 {Tags:["mq","$(n)"],Rotation:[-90f,0f],background:0,text:{text:"$(label)",color:"$(label_color)",font:"$(label_font)",bold:true},transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,$(label_addY)f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
# west: 깊이축=x(+0.8125, 플레이어 쪽), 가로축=z, 3 타일 + 라벨
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.93 ~0.37 ~0.37 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.93 ~0.37 ~0.5 {Tags:["mq","$(n)"],width:0.13f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:interaction ~0.93 ~0.37 ~0.63 {Tags:["mq","$(n)"],width:0.125f,height:0.26f,response:1b}
$execute if score $(n) buttons matches -1 unless data storage mq:tmp btn{label:""} if data storage mq:tmp btn{f:"west"} positioned $(x).0 $(y).0 $(z).0 run summon minecraft:text_display ~0.99 ~-0.5 ~0.5 {Tags:["mq","$(n)"],Rotation:[90f,0f],background:0,text:{text:"$(label)",color:"$(label_color)",font:"$(label_font)",bold:true},transformation:{scale:[$(label_scale)f,$(label_scale)f,$(label_scale)f],translation:[0f,$(label_addY)f,0f],left_rotation:[0f,0f,0f,1f],right_rotation:[0f,0f,0f,1f]}}
$execute if score $(n) buttons matches -1 run kill @e[type=minecraft:interaction,tag=mq,tag=$(n)]
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"south"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.5 ~0.375 ~-0.0625 {Tags:["mq","$(n)"],width:0.375f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"north"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~0.5 ~0.375 ~1.0625 {Tags:["mq","$(n)"],width:0.375f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"east"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~-0.0625 ~0.375 ~0.5 {Tags:["mq","$(n)"],width:0.375f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 if data storage mq:tmp btn{f:"west"} positioned $(x) $(y) $(z) run summon minecraft:interaction ~1.0625 ~0.375 ~0.5 {Tags:["mq","$(n)"],width:0.375f,height:0.25f,response:0b}
$execute if score $(n) buttons matches -1 run scoreboard players set $(n) buttons 0
# ---- 상시: interaction 클릭/타격 → playsound + 명령/투표 실행 ----
# init main = 0 (퀴즈 시작 전 설정 단계) : 명령 직접 실행
# init main >= 10 (종료 상태) + 정지 버튼 : 명령 직접 실행 (재시작용, 투표 없음)
# 그 외 : trigger 투표 경로
# 한 버튼에 interaction 3개지만 `on target` 은 클릭된 1개만 통과
# (나머지는 target 부재로 체인 중단). limit=1 을 두면 MC 가 임의로 1개를
# 골라 잘못된 entity 만 검사하므로 limit 두지 않음.
$execute as @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction] on target as @s positioned $(x).0 $(y).0 $(z).0 run playsound minecraft:block.stone_button.click_on block @s ~ ~ ~ 1 1
$execute as @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction] on target if data storage mq:tmp btn{n:"test"} run $(c)
$execute as @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction] on target unless data storage mq:tmp btn{n:"test"} as @s positioned $(x).0 $(y).0 $(z).0 if score init main matches 0 run $(c)
# 종료 상태(init 10/11)에서 정지 버튼 단일 클릭 → 즉시 commands/stop 으로 재시작 대기(init 0).
# 투표 집계는 repeat/triggers/trigger 가 init==5 에서만 하므로, 이 분기 없이는 종료 안내문
# ("종료를 눌러주세요")대로 눌러도 종료가 안 된다. commands/stop 이 init 을 0 으로 만들어
# 아래 투표 줄(unless init==0)은 같은 tick 에 자동으로 건너뛰어진다(중복 실행 없음).
$execute as @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction] on target if data storage mq:tmp btn{n:"stop"} as @s positioned $(x).0 $(y).0 $(z).0 if score init main matches 10.. run $(c)
$execute as @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction] on target unless data storage mq:tmp btn{n:"test"} as @s positioned $(x).0 $(y).0 $(z).0 unless score init main matches 0 run trigger $(n)
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n),limit=1] on target as @s positioned $(x) $(y) $(z) run playsound minecraft:block.stone_button.click_on block @s ~ ~ ~ 1 1
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n),limit=1] on target as @s positioned $(x) $(y) $(z) if score init main matches 0 run $(c)
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n),limit=1] on target as @s positioned $(x) $(y) $(z) unless score init main matches 0 run trigger $(n)
# ---- 처리 후 attack/interaction NBT 클리어 (다음 tick 중복 발화 방지) ----
$execute as @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction] at @s run data remove entity @s attack
$execute as @e[distance=0..,tag=mq,tag=$(n),type=minecraft:interaction] at @s run data remove entity @s interaction
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] at @s run data remove entity @s attack
$execute as @e[type=minecraft:interaction,tag=mq,tag=$(n)] at @s run data remove entity @s interaction

View File

@@ -1,22 +0,0 @@
# 한 button entry 의 optional 필드 기본값을 채워 macro 호출 시 $(arg) 미존재
# 에러를 방지한다. handler 에서 entry 복사 직후 호출.
#
# label : 없으면 "" (빈 문자열) -> btn 안의 text_display 분기는 label
# 이 "" 이면 스킵.
# label_color : 기본 "black"
# label_font : 기본 "minecraft:default"
# label_scale : 기본 "1.0" (Vector3f 의 한 축, 3축 동일하게 사용됨)
# label_addY : 기본 "-0.1"
#
# 구현: defaults 컴파운드를 먼저 만들고 entry (mq:tmp.btn) 를 그 위에 merge
# 한 뒤 다시 mq:tmp.btn 으로 되돌린다. data modify ... merge from 은 source
# compound 의 키로 target compound 를 덮어쓰므로 entry 에 있는 값은 보존되고
# entry 에 없는 키만 default 값으로 채워진다.
#
# (이전에 `execute unless data storage mq:tmp btn.label run data modify ...`
# 방식이었으나 MC 26.1.2 parser 가 해당 라인을 거부했음. merge 방식은 문제
# 난 execute-unless-data 구문 자체를 제거.)
data modify storage mq:tmp btn_default set value {label:"",label_color:"black",label_font:"minecraft:default",label_scale:"1.0",label_addY:"-0.1"}
data modify storage mq:tmp btn_default merge from storage mq:tmp btn
data modify storage mq:tmp btn set from storage mq:tmp btn_default

View File

@@ -1,27 +1,16 @@
# 각 button_defs 항목을 mq:tmp.btn 으로 복사 → optional 필드 기본값 채움
# → btn 호출. btn 안에서 facing 별 분기 (if data storage mq:tmp btn{f:"..."})
# 와 macro arg ($(label) 등) 둘 다 사용 가능하게 같은 storage 에 노출시킨다.
# 각 button_defs 항목을 mq:tmp.btn 으로 복사 후 btn 호출.
# btn 안에서 facing 별 분기 (if data storage mq:tmp btn{f:"..."})
# 사용하기 위해 현재 항목을 같은 storage 에 노출시킨다.
data modify storage mq:tmp btn set from storage mq:main button_defs[0]
function mq:repeat/buttons/btn_prep
function mq:repeat/buttons/btn with storage mq:tmp btn
data modify storage mq:tmp btn set from storage mq:main button_defs[1]
function mq:repeat/buttons/btn_prep
function mq:repeat/buttons/btn with storage mq:tmp btn
data modify storage mq:tmp btn set from storage mq:main button_defs[2]
function mq:repeat/buttons/btn_prep
function mq:repeat/buttons/btn with storage mq:tmp btn
data modify storage mq:tmp btn set from storage mq:main button_defs[3]
function mq:repeat/buttons/btn_prep
function mq:repeat/buttons/btn with storage mq:tmp btn
data modify storage mq:tmp btn set from storage mq:main button_defs[4]
function mq:repeat/buttons/btn_prep
function mq:repeat/buttons/btn with storage mq:tmp btn
data modify storage mq:tmp btn set from storage mq:main button_defs[5]
function mq:repeat/buttons/btn_prep
function mq:repeat/buttons/btn with storage mq:tmp btn

View File

@@ -6,10 +6,44 @@ execute unless score init main matches 2 \
run scoreboard players set timer main 0
# start title timer
execute if score init main matches 2 run function mq:repeat/timers/init2 with storage mq:main audio
execute if score init main matches 2 if score timer main matches 20 run title @a title {"text":"3"}
execute if score init main matches 2 if score timer main matches 20 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 20 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 20 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 40 run title @a title {"text":"2"}
execute if score init main matches 2 if score timer main matches 40 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 40 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 40 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 60 run title @a title {"text":"1"}
execute if score init main matches 2 if score timer main matches 60 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 60 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 60 as @a at @s run playsound minecraft:block.note_block.iron_xylophone weather @s ~ ~ ~ 1 1
execute if score init main matches 2 if score timer main matches 100 run title @a title {"text":""}
execute if score init main matches 2 if score timer main matches 100.. run function mq:quiz/select with storage mq:main
# next song timer
execute if score init main matches 6 run function mq:repeat/timers/init6
execute if score init main matches 6 if score timer main matches 300 run title @a title {"text":""}
execute if score init main matches 6 if score timer main matches 290 run function mq:images/clear
execute if score init main matches 6 if score timer main matches 300.. run function mq:quiz/select with storage mq:main
# endding timer
execute if score init main matches 10 run function mq:repeat/timers/init10 with storage mq:main audio
execute if score init main matches 10 if score timer main matches 60 run function mq:tellraw {"text":"퀴즈가 종료되었습니다.","color":"white","msg":""}
execute if score init main matches 10 if score timer main matches 60 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 60 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 60 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 180 run function mq:tellraw {"text":"퀴즈를 다시 시작하시려면 종료를 눌러주세요.","color":"white","msg":""}
execute if score init main matches 10 if score timer main matches 120 as @a at @s run scoreboard players set stop buttons -1
execute if score init main matches 10 if score timer main matches 120 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 120 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 120 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 120 run function mq:tellraw {"text":"플레이 해주셔서 감사합니다.","color":"white","msg":""}
execute if score init main matches 10 if score timer main matches 180 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 180 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 180 as @a at @s run playsound minecraft:ui.button.click weather @s ~ ~ ~ 1 1
execute if score init main matches 10 if score timer main matches 200.. run scoreboard players set init main 11

View File

@@ -1,15 +0,0 @@
# warn-off-file execute-group
execute if score timer main matches 60 run function mq:tellraw {"text":"퀴즈가 종료되었습니다.","color":"white","msg":""}
$execute if score timer main matches 60 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 60 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 60 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
execute if score timer main matches 180 run function mq:tellraw {"text":"퀴즈를 다시 시작하시려면 종료를 눌러주세요.","color":"white","msg":""}
execute if score timer main matches 120 as @a at @s run scoreboard players set stop buttons -1
$execute if score timer main matches 120 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 120 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 120 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
execute if score timer main matches 120 run function mq:tellraw {"text":"플레이 해주셔서 감사합니다.","color":"white","msg":""}
$execute if score timer main matches 180 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 180 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 180 as @a at @s run playsound minecraft:ui.button.click $(source) @s ~ ~ ~ 1 1
execute if score timer main matches 200.. run scoreboard players set init main 11

View File

@@ -1,15 +0,0 @@
# warn-off-file execute-group
execute if score timer main matches 20 run title @a title {"text":"3"}
$execute if score timer main matches 20 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 20 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 20 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
execute if score timer main matches 40 run title @a title {"text":"2"}
$execute if score timer main matches 40 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 40 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 40 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
execute if score timer main matches 60 run title @a title {"text":"1"}
$execute if score timer main matches 60 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 60 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
$execute if score timer main matches 60 as @a at @s run playsound minecraft:block.note_block.iron_xylophone $(source) @s ~ ~ ~ 1 1
execute if score timer main matches 100 run title @a title {"text":""}
execute if score timer main matches 100.. run function mq:quiz/select with storage mq:main

View File

@@ -1,13 +0,0 @@
# 정답 공개 동안(init=6) 곡의 video URL 을 actionbar 에 흰색으로 계속 표시.
# 매 tick 항상 재전송한다. video 가 "" 이거나 없는 곡은 빈 문자열이 그대로 출력돼
# 아무것도 안 보이고, 매 tick 빈 actionbar 로 덮어써 잔상도 남지 않는다.
title @a actionbar [{"storage":"mq:main","nbt":"answer.video","color":"white"}]
execute if score timer main matches 300 run title @a title {"text":""}
# 다음 상태로 넘어가기 전 actionbar 비우기 — 안 비우면 마지막 URL 이 다음 곡(init=5)
# 초반까지 ~3초 잔상으로 남는다. select 보다 먼저 실행되어 깔끔히 지워진다.
execute if score timer main matches 300 run title @a actionbar {"text":""}
# warn-off execute-group
execute if score timer main matches 290 run function mq:images/clear
execute if score timer main matches 290 run function mq:videos/clear
execute if score timer main matches 300.. run function mq:quiz/select with storage mq:main

View File

@@ -1,4 +1,3 @@
# warn-off execute-group
execute if score init main matches 0..1 run scoreboard players enable @a ready
execute if score init main matches 0..1 as @a if score @s ready matches 1 run function mq:tellraw {"text":"","color":"black",msg:[{"selector":"@s","color": "yellow","bold": true},{"text":" : ","color":"gray"},{"text":"준비완료","color":"white"}]}
execute if score init main matches 0..1 as @a if score @s ready matches 1 run scoreboard players set @s ready 2

View File

@@ -2,13 +2,9 @@ function mq:repeat/players
function mq:repeat/buttons/handler
function mq:repeat/triggers/handler
# 영상 모드 명령 큐를 매 tick 한 개씩 command_block 으로 실행
function mq:videos/drain
execute if score init main matches 2.. run function mq:repeat/timer
execute if score init main matches 5..6 run function mq:repeat/check_answer
# 정답 입력 다이얼로그: init=5 (곡 재생 중) 일 때만 열림 / 제출 처리
# warn-off execute-group
execute if score init main matches 5 as @a[scores={input=1..}] run function mq:answer/open
execute if score init main matches 5 run function mq:answer/process

View File

@@ -1,8 +0,0 @@
# 라운드 진행 시 preload 윈도우의 맨 앞(index+preload-1) 한 개를 캐시에 추가.
# 시작 preload 와 합쳐 항상 preload 개 버퍼가 굴러간다.
execute store result score cache_lo func.temp run data get storage mq:main video.preload
execute if score cache_lo func.temp matches ..0 run return 0
scoreboard players operation cache_lo func.temp += index main
scoreboard players remove cache_lo func.temp 1
scoreboard players operation cache_hi func.temp = cache_lo func.temp
function mq:videos/cache/fill

View File

@@ -1,6 +0,0 @@
# 추적 목록을 비우고 전체 캐시 wipe 를 큐에 적재 (command_block 으로 실행).
data modify storage mq:main video.cached set value []
data modify storage mq:tmp video set from storage mq:main video
execute store result storage mq:tmp video.num int 1 run scoreboard players get index main
data modify storage mq:tmp video.cmd set value "cache_clear"
function mq:videos/macro/cmd with storage mq:tmp video

View File

@@ -1,12 +0,0 @@
# 추적 캐시 갯수가 keep 을 넘으면 가장 먼저 받은 것부터(FIFO) 제거 큐에 적재.
# keep<=0 이면 무제한으로 보고 축출하지 않는다.
execute store result score cache_keep func.temp run data get storage mq:main video.keep
execute if score cache_keep func.temp matches ..0 run return 0
execute store result score cache_len func.temp run data get storage mq:main video.cached
execute if score cache_len func.temp <= cache_keep func.temp run return 0
data modify storage mq:tmp video set from storage mq:main video
data modify storage mq:tmp video.num set from storage mq:main video.cached[0].i
data modify storage mq:tmp video.cmd set value "cache_remove"
function mq:videos/macro/cmd with storage mq:tmp video
data remove storage mq:main video.cached[0]
function mq:videos/cache/evict

View File

@@ -1,11 +0,0 @@
# cache_lo..cache_hi 범위의 영상을 캐시 큐에 등록한다 (max_index 상한).
# 등록 후 keep 초과분은 FIFO 로 축출. 실제 videoCache add/remove 는 큐를 통해
# command_block 으로 실행되므로 모드 없이도 로드된다.
execute if score cache_lo func.temp > cache_hi func.temp run return run function mq:videos/cache/evict
execute if score cache_lo func.temp > max_index main run return run function mq:videos/cache/evict
data modify storage mq:tmp video set from storage mq:main video
execute store result storage mq:tmp video.num int 1 run scoreboard players get cache_lo func.temp
data modify storage mq:tmp video.cmd set value "cache_add"
function mq:videos/macro/cmd with storage mq:tmp video
scoreboard players add cache_lo func.temp 1
function mq:videos/cache/fill

View File

@@ -1,6 +0,0 @@
# 게임 시작 시 1번부터 preload 번까지 캐시를 미리 등록한다.
execute store result score cache_lo func.temp run data get storage mq:main video.preload
execute if score cache_lo func.temp matches ..0 run return 0
scoreboard players operation cache_hi func.temp = cache_lo func.temp
scoreboard players set cache_lo func.temp 1
function mq:videos/cache/fill

View File

@@ -1,4 +0,0 @@
data modify storage mq:tmp video set from storage mq:main video
execute store result storage mq:tmp video.num int 1 run scoreboard players get index main
data modify storage mq:tmp video.cmd set value "delete"
function mq:videos/macro/cmd with storage mq:tmp video

View File

@@ -1,18 +0,0 @@
# warn-off-file execute-group
# 매 tick: 큐(mq:main video.cmdq)에 대기 중인 모드 명령을 한 개씩 command_block 으로
# 실행한다. command_block 은 auto:1b 면 매 tick 재실행되므로(=videoCache add 중복
# 실패 스팸) 명령을 실행한 다음 tick 에 auto:0b 로 꺼서 1회만 실행되게 한다.
# 한 좌표 블록은 한 tick 에 1회만 켜지므로 큐를 1개씩 소진해 preload·축출 다건도
# 순서대로 처리된다.
data remove storage mq:tmp fire
execute if data storage mq:main video.cmdq[0] run data modify storage mq:tmp fire set from storage mq:main video
execute if data storage mq:main video.cmdq[0] run data modify storage mq:tmp fire.c set from storage mq:main video.cmdq[0].c
# 큐에 명령이 있으면 적재·실행하고 armed 표시 후 pop.
execute if data storage mq:tmp fire.c run function mq:videos/macro/drain with storage mq:tmp fire
execute if data storage mq:tmp fire.c run data modify storage mq:main video.armed set value 1b
execute if data storage mq:tmp fire.c run data remove storage mq:main video.cmdq[0]
# 큐가 비었고 직전에 켠 블록이 있으면 한 번만 꺼서 재실행을 막는다.
execute unless data storage mq:tmp fire.c if data storage mq:main {video:{armed:1b}} run function mq:videos/macro/drain_off with storage mq:main video
execute unless data storage mq:tmp fire.c run data modify storage mq:main video.armed set value 0b

View File

@@ -1,16 +0,0 @@
# 모드 명령을 "큐(mq:main video.cmdq)"에 문자열로 적재한다. 실제 실행은 매 tick
# videos/drain 이 command_block 으로 하나씩 처리한다. 데이터팩 로드 시 모드 명령을
# 직접 파싱하지 않으므로 영상 모드가 없어도 게임이 동작한다.
# delete : videoDelete
# play : videoPlace (캐시 추적중이면 video_N, 아니면 전체 URL)
# cache_add : videoCache add (이미 추적중이면 skip, 아니면 추적목록 append)
# cache_remove : videoCache remove
# cache_clear : videoCache clear
$execute if data storage mq:tmp {video:{cmd:"delete"}} run data modify storage mq:main video.cmdq append value {c:"videoDelete $(x) $(y) $(z)"}
$execute if data storage mq:tmp {video:{cmd:"play"}} if data storage mq:main video.cached[{i:$(num)}] run data modify storage mq:main video.cmdq append value {c:"videoPlace $(x) $(y) $(z) $(facing) $(w) $(h) $(sound) video_$(num)"}
$execute if data storage mq:tmp {video:{cmd:"play"}} unless data storage mq:main video.cached[{i:$(num)}] run data modify storage mq:main video.cmdq append value {c:"videoPlace $(x) $(y) $(z) $(facing) $(w) $(h) $(sound) $(namespace)/$(num)"}
$execute if data storage mq:tmp {video:{cmd:"cache_add"}} if data storage mq:main video.cached[{i:$(num)}] run return 0
$execute if data storage mq:tmp {video:{cmd:"cache_add"}} run data modify storage mq:main video.cmdq append value {c:"videoCache add video_$(num) $(namespace)/$(num)"}
$execute if data storage mq:tmp {video:{cmd:"cache_add"}} run data modify storage mq:main video.cached append value {i:$(num)}
$execute if data storage mq:tmp {video:{cmd:"cache_remove"}} run data modify storage mq:main video.cmdq append value {c:"videoCache remove video_$(num)"}
execute if data storage mq:tmp {video:{cmd:"cache_clear"}} run data modify storage mq:main video.cmdq append value {c:"videoCache clear"}

View File

@@ -1,3 +0,0 @@
# 큐에서 꺼낸 명령 1개를 공용 command_block 에 적재 후 auto 0→1 로 실행시킨다.
$setblock $(cmd_x) $(cmd_y) $(cmd_z) minecraft:command_block[conditional=false,facing=up]{Command:"$(c)",auto:0b}
$data modify block $(cmd_x) $(cmd_y) $(cmd_z) auto set value 1b

View File

@@ -1,2 +0,0 @@
# 직전 tick 에 켠 명령 블록을 끈다 (armed 일 때만 호출되므로 블록이 존재함).
$data modify block $(cmd_x) $(cmd_y) $(cmd_z) auto set value 0b

View File

@@ -1,9 +0,0 @@
# namespace 끝에 / 가 붙어 있으면 제거 (config 오타 방어).
# string 소스 마지막 글자만 떼서 비교 후, 맞으면 마지막 글자를 잘라낸다.
data modify storage mq:tmp ns_last set string storage mq:main video.namespace -1
execute if data storage mq:tmp {ns_last:"/"} run data modify storage mq:main video.namespace set string storage mq:main video.namespace 0 -1
# 캐시 추적 목록({i:N}), 모드 명령 큐, command_block armed 플래그 초기화.
data modify storage mq:main video.cached set value []
data modify storage mq:main video.cmdq set value []
data modify storage mq:main video.armed set value 0b

View File

@@ -1,6 +0,0 @@
# 정답 영상 재생을 큐에 적재. videoPlace 는 같은 앵커 좌표를 덮어쓰므로 별도
# clear 불필요. 캐시 추적중이면 video_N, 아니면 전체 URL 로 macro/cmd 가 결정.
data modify storage mq:tmp video set from storage mq:main video
execute store result storage mq:tmp video.num int 1 run scoreboard players get index main
data modify storage mq:tmp video.cmd set value "play"
function mq:videos/macro/cmd with storage mq:tmp video

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_01",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_01"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_02",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_02"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_03",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_03"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_04",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_04"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_05",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_05"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_06",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_06"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_07",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_07"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_08",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_08"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_09",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_09"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_10",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_10"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_11",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_11"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_12",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_12"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_13",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_13"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_14",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_14"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_15",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_15"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_16",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_16"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_17",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_17"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_18",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_18"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_19",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_19"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_20",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_20"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_21",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_21"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_22",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_22"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_23",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_23"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_24",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_24"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_25",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_25"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_26",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_26"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_27",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_27"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_28",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_28"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_29",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_29"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_30",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_30"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_31",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_31"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_32",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_32"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_33",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_33"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_34",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_34"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_35",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_35"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_36",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_36"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_37",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_37"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_38",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_38"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_39",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_39"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_40",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_40"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_41",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_41"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_42",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_42"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_43",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_43"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_44",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_44"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_45",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_45"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_46",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_46"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_47",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_47"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_48",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_48"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_49",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_49"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:cover_50",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "cover_50"
"width": 1,
"height": 1
}

View File

@@ -1,7 +1,5 @@
{
"asset_id": "musicquiz:gif",
"width": 5,
"height": 5,
"author": "musicquiz",
"title": "gif"
"width": 1,
"height": 1
}

Some files were not shown because too many files have changed in this diff Show More