Skip to content

Commit c56e975

Browse files
committed
✨ Support Minecraft 26.3
- advancement entity conditions are now loot conditions - loot functions use `type` instead of `function`
1 parent e5c82bf commit c56e975

3 files changed

Lines changed: 365 additions & 5 deletions

File tree

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
# global.mcb — Minecraft 26.3. Shared `animated_java:global/*` functions: load/tick
2+
# dispatch, the entity-UUID helper (gu), the storage-backed per-entity data manager,
3+
# and the interaction/attack advancement plumbing.
4+
#
5+
# Changes from 26.2/global.mcb:
6+
# - Advancement entity conditions are a loot condition instead of a bare entity
7+
# predicate.
8+
# - Loot functions name their kind with `type` instead of `function`.
9+
10+
import ./global.mcbt
11+
12+
dir global {
13+
function on_load minecraft:load {
14+
# Initialize Scoreboards
15+
scoreboard objectives add <%OBJECTIVES.I()%> dummy
16+
scoreboard objectives add <%OBJECTIVES.ID()%> dummy
17+
scoreboard objectives add <%OBJECTIVES.IS_RIG_LOADED()%> dummy
18+
scoreboard objectives add <%OBJECTIVES.TWEEN_DURATION()%> dummy
19+
20+
scoreboard players add aj.last_id <%OBJECTIVES.ID()%> 0
21+
22+
# Enforce loading order.
23+
function *global/gu/on_load
24+
function *global/data_manager/on_load
25+
function #*global/on_load
26+
27+
IF (debug_mode) {
28+
scoreboard players reset * <%OBJECTIVES.IS_RIG_LOADED()%>
29+
execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_load
30+
}
31+
}
32+
33+
function on_tick minecraft:tick {
34+
execute as @e[type=item_display,tag=<%TAGS.GLOBAL_ROOT()%>] at @s run function *global/root/on_tick
35+
}
36+
37+
tag functions on_load {
38+
<%blueprint_id%>/on_load
39+
}
40+
41+
dir root {
42+
function on_tick {
43+
data_manager prep read
44+
IF (debug_mode) {
45+
execute unless score @s <%OBJECTIVES.IS_RIG_LOADED()%> matches 1 run function *global/root/on_load
46+
}
47+
function *global/root/run_project_tick with storage <%temp_storage%> entry.data
48+
}
49+
50+
function run_project_tick {
51+
$function $(blueprint_id)/root/on_tick
52+
}
53+
54+
IF (debug_mode) {
55+
function on_load {
56+
# Check if the rig is outdated by comparing the function's rig_hash of the rig to the rig_hash stored in the entity data.
57+
execute if function animated_java:global/util/is_rig_outdated run block outdated_warning/modify_rig {
58+
data remove storage <%temp_storage%> args
59+
function *global/gu/get_entity_uuid_string
60+
data modify storage <%temp_storage%> args.uuid set from storage <%gu_storage%> out
61+
# Get position
62+
execute store result storage <%temp_storage%> args.x double 1 run data get entity @s Pos[0] 1
63+
execute store result storage <%temp_storage%> args.y double 1 run data get entity @s Pos[1] 1
64+
execute store result storage <%temp_storage%> args.z double 1 run data get entity @s Pos[2] 1
65+
66+
# No need to read here, since is_rig_outdated already reads the data.
67+
# data_manager prep read
68+
69+
# Get export namespace
70+
data modify storage <%temp_storage%> args.blueprint_id set from storage <%temp_storage%> entry.data.blueprint_id
71+
# Overwrite the rig hash so the warning doesn't show again.
72+
data modify storage <%temp_storage%> entry.data.rig_hash set value '<%rig_hash%>'
73+
data_manager write
74+
block run_print { with storage <%temp_storage%> entry.data
75+
$function $(blueprint_id)/zzz/outdated_rig_warning with storage <%temp_storage%> args
76+
}
77+
78+
# Loop over rig entities and glow them red.
79+
data modify storage <%temp_storage%> list set from storage <%temp_storage%> entry.data.uuids
80+
block loop {
81+
data modify storage <%temp_storage%> args.uuid set from storage <%temp_storage%> list[-1]
82+
data remove storage <%temp_storage%> list[-1]
83+
block as_uuid { with storage <%temp_storage%> args
84+
$data merge entity $(uuid) {Glowing: 1b, glow_color_override: <%0xff0000%>}
85+
}
86+
87+
execute store result score #length <%OBJECTIVES.I()%> run data get storage <%temp_storage%> list
88+
execute if score #length <%OBJECTIVES.I()%> matches 1.. run function ^0
89+
}
90+
91+
summon minecraft:text_display ~ ~ ~ { \
92+
Tags:[ \
93+
'<%TAGS.GLOBAL_ENTITY()%>', \
94+
'<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>', \
95+
'<%TAGS.NEW()%>' \
96+
], \
97+
text:<%TELLRAW.RIG_OUTDATED_TEXT_DISPLAY()%>, \
98+
alignment: 'center', \
99+
billboard: 'vertical', \
100+
shadow: true, \
101+
transformation:{ \
102+
translation:[0f,<%boundingBox[1]/16%>f,0f], \
103+
left_rotation:[0f,0f,0f,1f], \
104+
right_rotation:[0f,0f,0f,1f], \
105+
scale:[1f,1f,1f] \
106+
} \
107+
}
108+
109+
ride @n[ \
110+
type=minecraft:text_display, \
111+
tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \
112+
distance=..1 \
113+
] mount @s
114+
115+
tag @n[ \
116+
type=minecraft:text_display, \
117+
tag=<%TAGS.OUTDATED_RIG_TEXT_DISPLAY()%>, tag=<%TAGS.NEW()%>, \
118+
distance=..1 \
119+
] remove <%TAGS.NEW()%>
120+
}
121+
scoreboard players set @s <%OBJECTIVES.IS_RIG_LOADED()%> 1
122+
}
123+
}
124+
}
125+
126+
dir remove {
127+
# Removes every entity related to Animated Java.
128+
function everything {
129+
kill @e[tag=<%TAGS.GLOBAL_ENTITY()%>]
130+
}
131+
132+
function entity_stack {
133+
execute on passengers if entity @s[type=!player] run function ^0
134+
kill @s
135+
}
136+
137+
function entity_stack_by_uuid {
138+
#ARGS: {uuid: string}
139+
$execute as $(uuid) run function ./entity_stack
140+
}
141+
142+
# Removes locators and cameras owned by the rig, even if they're not included in the currently loaded export.
143+
function outdated_rig {
144+
data_manager prep read
145+
146+
data remove storage <%temp_storage%> args
147+
data remove storage <%temp_storage%> uuids
148+
data modify storage <%temp_storage%> uuids set from entity @s data.uuids
149+
execute store result score #aj.length <%OBJECTIVES.I()%> run data get storage <%temp_storage%> uuids
150+
151+
execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run block loop_over_uuids {
152+
data modify storage <%temp_storage%> args.current_uuid set from storage <%temp_storage%> uuids[-1].uuid
153+
data remove storage <%temp_storage%> uuids[-1]
154+
function ./entity_stack_by_uuid with storage <%temp_storage%> args
155+
156+
scoreboard players remove #aj.length <%OBJECTIVES.I()%> 1
157+
execute if score #aj.length <%OBJECTIVES.I()%> matches 1.. run function ^0
158+
}
159+
160+
function ./entity_stack
161+
}
162+
}
163+
164+
dir util {
165+
# Recurses through the passengers of the context entity and appends their UUIDs to `storage <%temp_storage%> uuids`.
166+
function get_entity_stack_uuids {
167+
data remove storage <%temp_storage%> uuids
168+
execute on passengers run {
169+
function *global/gu/get_entity_uuid_string
170+
data modify storage <%temp_storage%> uuids append from storage <%gu_storage%> out
171+
execute on passengers run function ^0
172+
}
173+
}
174+
# Returns 1 if the rig is outdated, else returns 0.
175+
function is_rig_outdated {
176+
data_manager prep read
177+
return run block { with storage <%temp_storage%> entry.data
178+
# REVIEW - The replace here is a bit hacky.
179+
# Because this is a core function, I need to create a generic version of
180+
# project_storage that uses a macro to get the blueprint ID instead of hardcoding it.
181+
$execute if data storage <%project_storage.replace(blueprint_id, '$(blueprint_id)')%> {rig_hash:'$(rig_hash)'} run return 0
182+
return 1
183+
}
184+
}
185+
}
186+
187+
# Thanks Gibbsly for the original code! https://github.com/gibbsly/gu
188+
dir gu {
189+
function on_load {
190+
scoreboard players set 256 <%OBJECTIVES.I()%> 256
191+
data modify storage <%gu_storage%> hex_chars set value \
192+
<%JSON.stringify([...Array(0x100).keys()].map(v => {const x = v.toString(16); return x.length > 1 ? x : '0' + x}))%>
193+
}
194+
195+
item_modifier entity_uuid {
196+
"type": "minecraft:set_name",
197+
"entity": "this",
198+
"target": "item_name",
199+
"name": {"selector": "@s"}
200+
}
201+
202+
function get_entity_uuid_string {
203+
execute if entity @s[type=!minecraft:player] run return run {
204+
summon minecraft:item_display ~ ~ ~ {UUID:uuid('21e0f4fa-2970-4cba-be91-6a50cd9dd0eb'),item:{id:'minecraft:pufferfish'}}
205+
item modify entity 21e0f4fa-2970-4cba-be91-6a50cd9dd0eb container.0 animated_java:global/gu/entity_uuid
206+
data modify storage <%gu_storage%> out set from entity 21e0f4fa-2970-4cba-be91-6a50cd9dd0eb item.components.minecraft:item_name.insertion
207+
kill 21e0f4fa-2970-4cba-be91-6a50cd9dd0eb
208+
}
209+
210+
data modify storage <%gu_storage%> temp set value {0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,a:0,b:0,c:0,d:0,e:0,f:0}
211+
data modify storage <%gu_storage%> in set from entity @s UUID
212+
213+
execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[0]
214+
execute store result storage <%gu_storage%> temp.0 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
215+
execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
216+
execute store result storage <%gu_storage%> temp.1 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
217+
execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
218+
execute store result storage <%gu_storage%> temp.2 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
219+
execute store result storage <%gu_storage%> temp.3 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
220+
221+
execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[1]
222+
execute store result storage <%gu_storage%> temp.4 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
223+
execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
224+
execute store result storage <%gu_storage%> temp.5 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
225+
execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
226+
execute store result storage <%gu_storage%> temp.6 int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
227+
execute store result storage <%gu_storage%> temp.7 int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
228+
229+
execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[2]
230+
execute store result storage <%gu_storage%> temp.8 int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
231+
execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
232+
execute store result storage <%gu_storage%> temp.9 int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
233+
execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
234+
execute store result storage <%gu_storage%> temp.a int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
235+
execute store result storage <%gu_storage%> temp.b int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
236+
237+
execute store result score 0= <%OBJECTIVES.I()%> store result score 1= <%OBJECTIVES.I()%> run data get storage <%gu_storage%> in[3]
238+
execute store result storage <%gu_storage%> temp.c int 1 run scoreboard players operation 0= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
239+
execute store result score 2= <%OBJECTIVES.I()%> run scoreboard players operation 1= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
240+
execute store result storage <%gu_storage%> temp.d int 1 run scoreboard players operation 1= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
241+
execute store result score 3= <%OBJECTIVES.I()%> run scoreboard players operation 2= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
242+
execute store result storage <%gu_storage%> temp.e int 1 run scoreboard players operation 2= <%OBJECTIVES.I()%> %= 256 <%OBJECTIVES.I()%>
243+
execute store result storage <%gu_storage%> temp.f int 1 run scoreboard players operation 3= <%OBJECTIVES.I()%> /= 256 <%OBJECTIVES.I()%>
244+
245+
block { with storage <%gu_storage%> temp
246+
REPEAT (0, 15) as i {
247+
$data modify storage <%gu_storage%> temp.<%i.toString(16)%> set from storage <%gu_storage%> hex_chars[$(<%i.toString(16)%>)]
248+
}
249+
}
250+
251+
block { with storage <%gu_storage%> temp
252+
$data modify storage <%gu_storage%> out set value "$(3)$(2)$(1)$(0)-$(7)$(6)-$(5)$(4)-$(b)$(a)-$(9)$(8)$(f)$(e)$(d)$(c)"
253+
}
254+
}
255+
}
256+
257+
# Storage-based per-entity NBT storage. Thanks Xanbelor for the concept!
258+
dir data_manager {
259+
function on_load {
260+
scoreboard players add #aj.data_manager.index <%OBJECTIVES.I()%> 0
261+
}
262+
263+
function on_tick minecraft:tick {
264+
execute store result storage <%temp_storage%> args.id int 1 run scoreboard players get #aj.data_manager.index <%OBJECTIVES.I()%>
265+
block { with storage <%temp_storage%> args
266+
$data modify storage <%temp_storage%> args.uuid set from storage <%data_storage%> "$(id)".uuid
267+
scoreboard players set #success <%OBJECTIVES.I()%> 0
268+
execute if data storage <%temp_storage%> args.uuid run { with storage <%temp_storage%> args
269+
$execute store success score #success <%OBJECTIVES.I()%> if score $(uuid) <%OBJECTIVES.ID()%> matches 1..
270+
$execute if score #success <%OBJECTIVES.I()%> matches 0 run data remove storage <%data_storage%> "$(id)"
271+
}
272+
}
273+
scoreboard players add #aj.data_manager.index <%OBJECTIVES.I()%> 1
274+
execute \
275+
if score #aj.data_manager.index <%OBJECTIVES.I()%> > aj.last_id <%OBJECTIVES.ID()%> \
276+
run scoreboard players set #aj.data_manager.index <%OBJECTIVES.I()%> 0
277+
}
278+
279+
function create_entry {
280+
$data modify storage <%data_storage%> "$(id)" set value {uuid: "$(uuid)", data: {}}
281+
}
282+
283+
function read {
284+
data remove storage <%temp_storage%> entry
285+
$data modify storage <%temp_storage%> entry set from storage <%data_storage%> "$(id)"
286+
}
287+
288+
function write {
289+
$data modify storage <%data_storage%> "$(id)" set from storage <%temp_storage%> entry
290+
}
291+
}
292+
293+
dir interactions {
294+
REPEAT (['interaction', 'attack']) as direction {
295+
dir <%direction%> {
296+
advancement trigger {
297+
"criteria": {
298+
"player_<%direction%>_clicked_interaction_entity": {
299+
"trigger": "<%direction === 'interaction' ? 'minecraft:player_interacted_with_entity' : 'minecraft:player_hurt_entity'%>",
300+
"conditions": {
301+
"entity": {
302+
"type": "minecraft:entity_properties",
303+
"entity": "this",
304+
"predicate": {
305+
"minecraft:entity_type": "minecraft:interaction"
306+
}
307+
}
308+
}
309+
}
310+
},
311+
"rewards": {
312+
"function": "<%context.namespace + ':' + context.path.join('/')%>/on"
313+
}
314+
}
315+
316+
function on {
317+
advancement revoke @s only <%context.namespace + ':' + context.path.join('/')%>/trigger
318+
tag @s add <%TAGS.INTERACTING_PLAYER()%>
319+
execute \
320+
as @e[type=interaction, tag=<%TAGS.GLOBAL_INTERACTION()%>, distance=..8] \
321+
if data entity @s <%direction%> \
322+
run function ./check
323+
tag @s remove <%TAGS.INTERACTING_PLAYER()%>
324+
}
325+
326+
function check {
327+
execute store result score #gametime <%OBJECTIVES.I()%> run time query gametime
328+
execute \
329+
store result score #timestamp <%OBJECTIVES.I()%> \
330+
run data get entity @s <%direction%>.timestamp
331+
332+
# Check that the interaction has a command to run, and
333+
# if this interaction is from the player being processed.
334+
scoreboard players set #check <%OBJECTIVES.I()%> 0
335+
execute \
336+
if score #timestamp <%OBJECTIVES.I()%> = #gametime <%OBJECTIVES.I()%> \
337+
if data entity @s data.animated_java.on_<%direction === 'interaction' ? 'interact' : 'attack'%>_function \
338+
store success score #check <%OBJECTIVES.I()%> \
339+
on <%direction === 'interaction' ? 'target' : 'attacker'%> \
340+
if entity @s[tag=<%TAGS.INTERACTING_PLAYER()%>]
341+
342+
execute \
343+
if score #check <%OBJECTIVES.I()%> matches 1 \
344+
run function ./do
345+
}
346+
347+
function do {
348+
scoreboard players set #check <%OBJECTIVES.I()%> 0
349+
block execute { with entity @s data.animated_java
350+
$$(on_<%direction === 'interaction' ? 'interact' : 'attack'%>_function)
351+
scoreboard players set #check <%OBJECTIVES.I()%> 1
352+
}
353+
execute if score #check <%OBJECTIVES.I()%> matches 0 run tellraw @a <%TELLRAW.INTERACTION_FUNCTION_FAILED(direction)%>
354+
# Only remove the interaction if it's consumed.
355+
data remove entity @s <%direction%>
356+
}
357+
}
358+
}
359+
}
360+
}

‎src/tests/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ can reach them: `renderRig`, `renderProjectAnimations`, `hashRig`,
4646
- `rig-renderer.test.ts` — outliner → `IRenderedRig`: node-type mapping, `includes_custom_models`, texture collection, `hashRig` change detection
4747
- `animation-renderer.test.ts` — keyframe baking to `IRenderedAnimation`: per-tick frame count, per-node transforms, animated-channel change, `hashAnimations`
4848
- `molang-baking.test.ts` — a Molang expression keyframe bakes identically to its literal; `animation_variable_placeholders` round-trip
49-
- `export-pipeline.test.ts` — `exportProject` writes a real data pack + resource pack; runs across three target versions (1.20.4 / 1.21.5 / 26.2, one per `getMCBFilesByVersion` branch)
49+
- `export-pipeline.test.ts` — `exportProject` writes a real data pack + resource pack; runs across five target versions (1.20.4 / 1.21.2 / 1.21.5 / 26.2 / 26.3, one per `getMCBFilesByVersion` branch)
5050
- `export-contents.test.ts` — inspects a real `player` export: `pack.mcmeta` pack_format, `summon` function body, a function per animation, scoreboard setup, block atlas, bone model JSON (geometry + textures), item model definitions, texture PNGs
5151
- `ajmeta-incremental.test.ts` — delete a bone from an exported project, re-export, assert the stale model file + ajmeta entry are removed
5252
- `plugin-mode-export.test.ts` — plugin-mode single-JSON export (`pluginCompiler`) shape

‎src/tests/export-pipeline.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ describe('Export pipeline', () => {
4949
}, 120_000)
5050

5151
// One version per distinct `getMCBFilesByVersion` layering result: 1.20.4
52-
// (base), 1.21.2 (its own global, older main), 1.21.5 (full set), and 26.2
53-
// (26.2 global over the 1.21.5 main). First run downloads each version's
54-
// assets into `~/.envbench`.
55-
it.each(['1.20.4', '1.21.2', '1.21.5', '26.2'])(
52+
// (base), 1.21.2 (1.21.0 global under the 1.21.2 main), 1.21.5 (full set), 26.2
53+
// (26.2 global over the 1.21.5 main), and 26.3 (26.3 global). First run downloads
54+
// each version's assets into `~/.envbench`.
55+
it.each(['1.20.4', '1.21.2', '1.21.5', '26.2', '26.3'])(
5656
'compiles a working data pack for target %s',
5757
async version => {
5858
const run = await runFixtureExport({ fixture: FIXTURE, targetVersion: version })

0 commit comments

Comments
 (0)