영상 모드 명령을 전부 command_block 큐로 우회 — 모드 없이도 게임 동작

videoCache add/remove/clear 까지 모든 모드 명령을 mcfunction 에서 직접
실행하지 않고 큐(mq:main video.cmdq)에 문자열로 적재한 뒤, 매 tick
videos/drain 이 공용 command_block 으로 하나씩 실행(auto 0→1, 1회 실행 후
off)한다. 데이터팩 로드 시 모드 명령을 파싱하지 않으므로 영상 모드 미설치
상태에서도 데이터팩이 정상 로드/플레이된다.

- preload/FIFO 축출 다건도 큐를 tick 당 1개씩 소진해 순서 보장
- cache 캐시 추적/dedup/src 결정(video_N vs 전체 URL)을 macro/cmd 로 통합
- 캐시 관련 파일 정리: add_one/preload_loop/evict_one/clear_run/
  macro/add_one/macro/evict_one/resolve_src 제거, fill/drain/drain_off 도입
  (videos 15→12 파일, cache/macro 폴더 제거)

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
Claude (owner)
2026-06-05 02:45:55 +09:00
parent f98dd9952b
commit d0e11e12ce
21 changed files with 88 additions and 79 deletions

View File

@@ -38,8 +38,8 @@ function mq:quiz/stop_sound
$scoreboard players set max_index main $(max_index)
scoreboard players set init main 1
# 시작 시 1..preload 번 영상 캐시 미리받기. schedule 로 다음 tick(서버 소스)
# 실행해 videoCache add 권한 보장 + max_index 설정 이후 동작 보장.
# 시작 시 1..preload 번 영상 캐시 미리받기(큐 적재). schedule 로 다음 tick
# 실행해 max_index 설정 이후 동작 보장. 실제 다운로드는 drain 이 tick 마다 처리.
schedule function mq:videos/cache/preload 1t
dialog show @a mq:page1

View File

@@ -2,6 +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

View File

@@ -1,12 +1,8 @@
# 재생 진행에 맞춰 항상 preload 개 앞까지 캐시를 채운다.
# 현재 index 에서 (preload-1) 만큼 앞선 영상을 받아두면 시작 시 preload(1..N)
# 와 합쳐져 N개 버퍼가 굴러간다.
execute store result score cache_preload func.temp run data get storage mq:main video.preload
execute if score cache_preload func.temp matches ..0 run return 0
scoreboard players operation cache_target func.temp = index main
scoreboard players operation cache_target func.temp += cache_preload func.temp
scoreboard players remove cache_target func.temp 1
execute if score cache_target func.temp > max_index main run return 0
function mq:videos/cache/add_one
# 라운드 진행 시 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,4 +0,0 @@
# cache_target(func.temp) 번 영상을 캐시에 추가. 매크로로 넘겨 등록/추적/축출.
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_target func.temp
function mq:videos/cache/macro/add_one with storage mq:tmp video

View File

@@ -1,5 +1,5 @@
# 추적 목록을 비우고, 다음 tick 에 서버 소스로 전체 캐시 wipe.
# (videoCache clear 를 호출자 소스에서 바로 실행하면 player/비OP 소스일 때
# 권한 문제가 날 수 있어 schedule 로 서버 소스 보장.)
# 추적 목록을 비우고 전체 캐시 wipe 를 큐에 적재 (command_block 으로 실행).
data modify storage mq:main video.cached set value []
schedule function mq:videos/cache/clear_run 1t
data modify storage mq:tmp video set from storage mq:main video
data modify storage mq:tmp video.cmd set value "cache_clear"
function mq:videos/macro/cmd with storage mq:tmp video

View File

@@ -1 +0,0 @@
videoCache clear

View File

@@ -1,4 +1,12 @@
# 캐시 갯수가 keep 을 넘으면 가장 먼저 받은 것부터(FIFO) 제.
execute store result score cache_len func.temp run data get storage mq:main video.cached
# 추적 캐시 갯수가 keep 을 넘으면 가장 먼저 받은 것부터(FIFO) 제거 큐에 적재.
# keep<=0 이면 무제한으로 보고 축출하지 않는다.
execute store result score cache_keep func.temp run data get storage mq:main video.keep
execute if score cache_len func.temp > cache_keep func.temp run function mq:videos/cache/evict_one
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,5 +0,0 @@
execute store result storage mq:tmp evict_num int 1 run data get storage mq:main video.cached[0].i
function mq:videos/cache/macro/evict_one with storage mq:tmp
data remove storage mq:main video.cached[0]
# keep 보다 여러 개 초과해 있을 수 있으니 재귀로 다시 검사.
function mq:videos/cache/evict

View File

@@ -0,0 +1,11 @@
# 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,7 +0,0 @@
# 이미 받은 영상이면(추적 목록에 {i:num} 존재) 건너뜀.
$execute if data storage mq:main video.cached[{i:$(num)}] run return 0
# videoCache add 는 서버 소스(또는 OP)에서 직접 실행. preload/select 모두 서버
# 소스이므로 command_block 우회 없이 바로 호출 가능.
$videoCache add video_$(num) $(namespace)/$(num)
$data modify storage mq:main video.cached append value {i:$(num)}
function mq:videos/cache/evict

View File

@@ -1 +0,0 @@
$videoCache remove video_$(evict_num)

View File

@@ -1,5 +1,6 @@
# 게임 시작 시 1번부터 preload 번까지 캐시를 미리 받는다 (max_index 로 상한).
scoreboard players set cache_i func.temp 1
execute store result score cache_max func.temp run data get storage mq:main video.preload
execute if score cache_max func.temp > max_index main run scoreboard players operation cache_max func.temp = max_index main
function mq:videos/cache/preload_loop
# 게임 시작 시 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,5 +0,0 @@
execute if score cache_i func.temp > cache_max func.temp run return 0
scoreboard players operation cache_target func.temp = cache_i func.temp
function mq:videos/cache/add_one
scoreboard players add cache_i func.temp 1
function mq:videos/cache/preload_loop

View File

@@ -1,4 +1,3 @@
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

@@ -0,0 +1,17 @@
# 매 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,14 +1,16 @@
$execute if data storage mq:tmp {video:{cmd:"delete"}} run setblock $(cmd_x) $(cmd_y) $(cmd_z) minecraft:command_block[conditional=false,facing=up]{ \
Command:"videoDelete $(x) $(y) $(z)", \
auto:0b \
}
$execute if data storage mq:tmp {video:{cmd:"play"}} run setblock $(cmd_x) $(cmd_y) $(cmd_z) minecraft:command_block[conditional=false,facing=up]{ \
Command:"videoPlace $(x) $(y) $(z) $(facing) $(w) $(h) $(sound) $(src)", \
auto:0b \
}
$execute if data storage mq:tmp {video:{cmd:"custom"}} run setblock $(cmd_x) $(cmd_y) $(cmd_z) minecraft:command_block[conditional=false,facing=up]{ \
Command:"$(cmd_value)", \
auto:0b \
}
$data modify block $(cmd_x) $(cmd_y) $(cmd_z) auto set value 1b
# 모드 명령을 "큐(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

@@ -0,0 +1,3 @@
# 큐에서 꺼낸 명령 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

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

View File

@@ -1,8 +0,0 @@
# mq:tmp video.src 를 결정한다.
# - 기본: 전체 URL "<namespace>/<num>"
# - mq:main video.cached 목록에 {i:num} 이 있으면(=videoCache add 로 등록됨)
# 등록 이름 "video_<num>" 사용 → 모드가 서버 config 에서 URL 로 resolve.
# cached 목록은 데이터팩이 videoCache add/remove 와 1:1 로 직접 추적하므로
# 모드에 캐시 존재 여부를 묻지 않고도 정확하다.
$data modify storage mq:tmp video.src set value "$(namespace)/$(num)"
$execute if data storage mq:main video.cached[{i:$(num)}] run data modify storage mq:tmp video.src set value "video_$(num)"

View File

@@ -3,5 +3,7 @@
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
# 캐시 추적 목록 초기화 (videoCache add/remove 와 1:1 로 관리되는 {i:N} 리스트)
# 캐시 추적 목록({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,10 +1,6 @@
# 정답 영상 재생. videoPlace 는 같은 앵커 좌표를 덮어쓰므로 별도 clear 불필요
# (clear + place 를 한 tick 에 하면 command_block 이 한 번만 켜져 place 가 씹힘).
# 정답 영상 재생을 큐에 적재. 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
# 캐시가 있으면 video_N(등록된 이름), 없으면 전체 URL 로 src 결정
function mq:videos/macro/resolve_src with storage mq:tmp video
data modify storage mq:tmp video.cmd set value "play"
function mq:videos/macro/cmd with storage mq:tmp video