diff --git a/music_quiz/data/mq/function/commands/start.mcfunction b/music_quiz/data/mq/function/commands/start.mcfunction index 8e08a05..ae5cdc1 100644 --- a/music_quiz/data/mq/function/commands/start.mcfunction +++ b/music_quiz/data/mq/function/commands/start.mcfunction @@ -38,4 +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 설정 이후 동작 보장. +schedule function mq:videos/cache/preload 1t + dialog show @a mq:page1 diff --git a/music_quiz/data/mq/function/init/config.mcfunction b/music_quiz/data/mq/function/init/config.mcfunction index 4a5a6bd..a461b42 100644 --- a/music_quiz/data/mq/function/init/config.mcfunction +++ b/music_quiz/data/mq/function/init/config.mcfunction @@ -19,16 +19,22 @@ data modify storage mq:main audio set value {namespace: "musicquiz", source: "re data modify storage mq:main image set value {namespace: "mq", x: 2782, y: 88, z: 5979, facing: 2b} # 정답 영상 — 모드의 영상 재생 관련 -# namespace — 영상 아이디를 제외한 주소 (마지막에 /는 없어야함) +# 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/폴더1/폴더2", \ cmd_x: 2769, cmd_y: 73, cmd_z: 5965, \ x: 2731, y: 96, z: 6034, facing: "north", w: 25,h: 14, \ - sound: -1 \ + sound: -1, \ + preload: 5, \ + keep: 10 \ } # 곡 개수 max_index 는 init/songs.mcfunction 의 길이로 자동 계산됨 diff --git a/music_quiz/data/mq/function/load.mcfunction b/music_quiz/data/mq/function/load.mcfunction index ad233a9..7831eb6 100644 --- a/music_quiz/data/mq/function/load.mcfunction +++ b/music_quiz/data/mq/function/load.mcfunction @@ -3,6 +3,7 @@ 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 diff --git a/music_quiz/data/mq/function/videos/cache/add.mcfunction b/music_quiz/data/mq/function/videos/cache/add.mcfunction index 8d6e0c7..b3ace99 100644 --- a/music_quiz/data/mq/function/videos/cache/add.mcfunction +++ b/music_quiz/data/mq/function/videos/cache/add.mcfunction @@ -1,9 +1,12 @@ -data modify storage mq:tmp video set from storage mq:main video +# 재생 진행에 맞춰 항상 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_idx func.temp = index main -scoreboard players add cache_idx func.temp 1 -execute store result storage mq:tmp video.cache_num int 1 run scoreboard players get cache_idx func.temp +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 -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" -function mq:videos/macro/cmd with storage mq:tmp video +function mq:videos/cache/add_one diff --git a/music_quiz/data/mq/function/videos/cache/add_one.mcfunction b/music_quiz/data/mq/function/videos/cache/add_one.mcfunction new file mode 100644 index 0000000..08b7d4d --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/add_one.mcfunction @@ -0,0 +1,4 @@ +# 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 diff --git a/music_quiz/data/mq/function/videos/cache/clear.mcfunction b/music_quiz/data/mq/function/videos/cache/clear.mcfunction index 4fda891..4edbb2f 100644 --- a/music_quiz/data/mq/function/videos/cache/clear.mcfunction +++ b/music_quiz/data/mq/function/videos/cache/clear.mcfunction @@ -1 +1,5 @@ -function mq:videos/macro/custom {cmd: "videoCache clear"} +# 추적 목록을 비우고, 다음 tick 에 서버 소스로 전체 캐시 wipe. +# (videoCache clear 를 호출자 소스에서 바로 실행하면 player/비OP 소스일 때 +# 권한 문제가 날 수 있어 schedule 로 서버 소스 보장.) +data modify storage mq:main video.cached set value [] +schedule function mq:videos/cache/clear_run 1t diff --git a/music_quiz/data/mq/function/videos/cache/clear_run.mcfunction b/music_quiz/data/mq/function/videos/cache/clear_run.mcfunction new file mode 100644 index 0000000..37bb540 --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/clear_run.mcfunction @@ -0,0 +1 @@ +videoCache clear diff --git a/music_quiz/data/mq/function/videos/cache/evict.mcfunction b/music_quiz/data/mq/function/videos/cache/evict.mcfunction new file mode 100644 index 0000000..d6a245f --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/evict.mcfunction @@ -0,0 +1,4 @@ +# 캐시 갯수가 keep 을 넘으면 가장 먼저 받은 것부터(FIFO) 삭제. +execute store result score cache_len func.temp run data get storage mq:main video.cached +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 diff --git a/music_quiz/data/mq/function/videos/cache/evict_one.mcfunction b/music_quiz/data/mq/function/videos/cache/evict_one.mcfunction new file mode 100644 index 0000000..8ba4d5d --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/evict_one.mcfunction @@ -0,0 +1,5 @@ +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 diff --git a/music_quiz/data/mq/function/videos/cache/macro/add_one.mcfunction b/music_quiz/data/mq/function/videos/cache/macro/add_one.mcfunction new file mode 100644 index 0000000..4cc7a55 --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/macro/add_one.mcfunction @@ -0,0 +1,7 @@ +# 이미 받은 영상이면(추적 목록에 {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 diff --git a/music_quiz/data/mq/function/videos/cache/macro/evict_one.mcfunction b/music_quiz/data/mq/function/videos/cache/macro/evict_one.mcfunction new file mode 100644 index 0000000..a5fb796 --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/macro/evict_one.mcfunction @@ -0,0 +1 @@ +$videoCache remove video_$(evict_num) diff --git a/music_quiz/data/mq/function/videos/cache/preload.mcfunction b/music_quiz/data/mq/function/videos/cache/preload.mcfunction new file mode 100644 index 0000000..5b9b5bc --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/preload.mcfunction @@ -0,0 +1,5 @@ +# 게임 시작 시 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 diff --git a/music_quiz/data/mq/function/videos/cache/preload_loop.mcfunction b/music_quiz/data/mq/function/videos/cache/preload_loop.mcfunction new file mode 100644 index 0000000..72e2bc7 --- /dev/null +++ b/music_quiz/data/mq/function/videos/cache/preload_loop.mcfunction @@ -0,0 +1,5 @@ +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 diff --git a/music_quiz/data/mq/function/videos/macro/cmd.mcfunction b/music_quiz/data/mq/function/videos/macro/cmd.mcfunction index 743e18e..3110718 100644 --- a/music_quiz/data/mq/function/videos/macro/cmd.mcfunction +++ b/music_quiz/data/mq/function/videos/macro/cmd.mcfunction @@ -2,15 +2,11 @@ $execute if data storage mq:tmp {video:{cmd:"delete"}} run setblock $(cmd_x) $(c Command:"videoDelete $(x) $(y) $(z)", \ auto:0b \ } -$execute if data storage mq:tmp {video:{cmd:"num"}} run setblock $(cmd_x) $(cmd_y) $(cmd_z) minecraft:command_block[conditional=false,facing=up]{ \ - Command:"videoPlace $(x) $(y) $(z) $(facing) $(w) $(h) $(sound) video_$(num)", \ +$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:"cache"}} run setblock $(cmd_x) $(cmd_y) $(cmd_z) minecraft:command_block[conditional=false,facing=up]{ \ - Command:"videoCache add video_$(num) $(namespace)/$(cache_num)", \ - auto:0b \ -} -$execute if data storage mq:tmp {video:{cmd:"cmd"}} run setblock $(cmd_x) $(cmd_y) $(cmd_z) minecraft:command_block[conditional=false,facing=up]{ \ +$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 \ } diff --git a/music_quiz/data/mq/function/videos/macro/custom.mcfunction b/music_quiz/data/mq/function/videos/macro/custom.mcfunction deleted file mode 100644 index c17805c..0000000 --- a/music_quiz/data/mq/function/videos/macro/custom.mcfunction +++ /dev/null @@ -1,6 +0,0 @@ -function mq:videos/clear -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 "custom" -$data modify storage mq:tmp video.cmd_value set value "$(cmd)" -function mq:videos/macro/cmd with storage mq:tmp video diff --git a/music_quiz/data/mq/function/videos/macro/resolve_src.mcfunction b/music_quiz/data/mq/function/videos/macro/resolve_src.mcfunction new file mode 100644 index 0000000..bad9340 --- /dev/null +++ b/music_quiz/data/mq/function/videos/macro/resolve_src.mcfunction @@ -0,0 +1,8 @@ +# mq:tmp video.src 를 결정한다. +# - 기본: 전체 URL "/" +# - mq:main video.cached 목록에 {i:num} 이 있으면(=videoCache add 로 등록됨) +# 등록 이름 "video_" 사용 → 모드가 서버 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)" diff --git a/music_quiz/data/mq/function/videos/normalize.mcfunction b/music_quiz/data/mq/function/videos/normalize.mcfunction new file mode 100644 index 0000000..b9a47b1 --- /dev/null +++ b/music_quiz/data/mq/function/videos/normalize.mcfunction @@ -0,0 +1,7 @@ +# 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 + +# 캐시 추적 목록 초기화 (videoCache add/remove 와 1:1 로 관리되는 {i:N} 리스트) +data modify storage mq:main video.cached set value [] diff --git a/music_quiz/data/mq/function/videos/show.mcfunction b/music_quiz/data/mq/function/videos/show.mcfunction index e52e228..d52af10 100644 --- a/music_quiz/data/mq/function/videos/show.mcfunction +++ b/music_quiz/data/mq/function/videos/show.mcfunction @@ -1,5 +1,10 @@ -function mq:videos/clear +# 정답 영상 재생. videoPlace 는 같은 앵커 좌표를 덮어쓰므로 별도 clear 불필요 +# (clear + place 를 한 tick 에 하면 command_block 이 한 번만 켜져 place 가 씹힘). 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 "num" + +# 캐시가 있으면 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