From 9b0ff43c46a97e73f72f3e1804b1f7aece213db7 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 8 May 2025 17:49:39 -0400 Subject: [PATCH 001/182] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20adding=20cameras?= =?UTF-8?q?=20to=20a=20rig=20causing=20the=20data=20entity=20to=20dismount?= =?UTF-8?q?=20root.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datapackCompiler/1.21.5/animation.mcb | 7 +- .../1.21.5/blueprints/armor_stand.ajblueprint | 469 +++++++++++++++++- 2 files changed, 470 insertions(+), 6 deletions(-) diff --git a/src/systems/datapackCompiler/1.21.5/animation.mcb b/src/systems/datapackCompiler/1.21.5/animation.mcb index a58f981d..237b9e88 100644 --- a/src/systems/datapackCompiler/1.21.5/animation.mcb +++ b/src/systems/datapackCompiler/1.21.5/animation.mcb @@ -548,7 +548,7 @@ dir <%export_namespace%> { type=<%locator.config.entity_type%>, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ + distance=..<%Math.ceil(locator.max_distance)%> \ ] \ run block as_locator/<%locator.path_name%> { # run block ../as_locator/<%locator.path_name%> { @@ -582,9 +582,10 @@ dir <%export_namespace%> { type=minecraft:item_display, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ + distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.path_name%> { + # run block ../as_camera/<%camera.path_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ diff --git a/test-packs/1.21.5/blueprints/armor_stand.ajblueprint b/test-packs/1.21.5/blueprints/armor_stand.ajblueprint index b2fce9be..aa74a774 100644 --- a/test-packs/1.21.5/blueprints/armor_stand.ajblueprint +++ b/test-packs/1.21.5/blueprints/armor_stand.ajblueprint @@ -1,7 +1,7 @@ { "meta": { "format": "animated_java_blueprint", - "format_version": "1.7.0", + "format_version": "1.7.2", "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", "save_location": "D:\\github-repos\\animated-java\\animated-java\\test-packs\\1.21.5\\blueprints\\armor_stand.ajblueprint", "last_used_export_namespace": "armor_stand" @@ -1183,7 +1183,7 @@ "locked": false, "visibility": true, "autouv": 0, - "selected": false, + "selected": true, "children": [ "55829f3d-26dc-8f49-39b8-5ba2c0159a24", "9d16df18-0574-58bb-e0f9-f799946257c9", @@ -1604,7 +1604,470 @@ } ] }, - "animations": [], + "animations": [ + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": false, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "commands", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "commands": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "commands", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "commands": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "commands", + "data_points": [ + { + "commands": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "commands", + "data_points": [ + { + "commands": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": false, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "variant", + "data_points": [ + { + "variant": "5417306e-2c69-3f36-1e3c-edd904034a36", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "1baa095b-e941-62df-a215-698b50c93524", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], "animation_controllers": [], "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" } \ No newline at end of file From b9b123e2015a70b6f21e7563a4efaf2ad4111993 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 8 May 2025 17:53:23 -0400 Subject: [PATCH 002/182] =?UTF-8?q?=F0=9F=A9=B9=20Make=20variant=20keyfram?= =?UTF-8?q?es=20reset=20to=20default=20if=20their=20variant=20doesn't=20ex?= =?UTF-8?q?ist=20anymore.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../keyframePanels/variantKeyframePanel.svelte | 7 +++++++ src/variants.ts | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/components/keyframePanels/variantKeyframePanel.svelte b/src/components/keyframePanels/variantKeyframePanel.svelte index 193a3cdc..ab3e6e00 100644 --- a/src/components/keyframePanels/variantKeyframePanel.svelte +++ b/src/components/keyframePanels/variantKeyframePanel.svelte @@ -7,6 +7,13 @@ const keyframeValue = new Valuable(getKeyframeVariant(selectedKeyframe) as string) let selectContainer: HTMLDivElement + if (!Variant.all.find(v => v.uuid === keyframeValue.get())) { + console.warn('Keyframe variant not found. Resetting to default.') + const uuid = Variant.getDefault().uuid + setKeyframeVariant(selectedKeyframe, uuid) + keyframeValue.set(uuid) + } + keyframeValue.subscribe(value => { setKeyframeVariant(selectedKeyframe, value) }) diff --git a/src/variants.ts b/src/variants.ts index 4352a971..97d3f28c 100644 --- a/src/variants.ts +++ b/src/variants.ts @@ -1,4 +1,5 @@ import { IBlueprintVariantJSON } from './blueprintFormat' +import { getKeyframeVariant, setKeyframeVariant } from './mods/customKeyframesMod' import { events } from './util/events' import { sanitizePathName } from './util/minecraftUtil' @@ -116,6 +117,17 @@ export class Variant { // Cannot delete default variant if (this.isDefault) return + for (const animation of Blockbench.Animation.all) { + for (const animator of Object.values(animation.animators)) { + for (const keyframe of animator.keyframes) { + const uuid = getKeyframeVariant(keyframe) + if (uuid != undefined && this.uuid === uuid) { + setKeyframeVariant(keyframe, Variant.getDefault().uuid) + } + } + } + } + const index = Variant.all.indexOf(this) if (index > -1) { Variant.all.splice(index, 1) From fb5875cc4ec463bbfe98233dca3e796fa8eca00d Mon Sep 17 00:00:00 2001 From: Titus Evans Date: Thu, 8 May 2025 15:01:11 -0700 Subject: [PATCH 003/182] =?UTF-8?q?=F0=9F=94=96=20v1.7.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf8ef866..08e53c6e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "title": "Animated Java", "icon": "icon.svg", "description": "A Blockbench plugin that makes complex animation a breeze in Minecraft: Java Edition.", - "version": "1.7.2", + "version": "1.7.3", "min_blockbench_version": "4.12.0", "variant": "desktop", "tags": [ From c351afb54720b0bc06a310e6b8b69cfb8cf314ed Mon Sep 17 00:00:00 2001 From: Titus Evans Date: Thu, 8 May 2025 15:15:24 -0700 Subject: [PATCH 004/182] Update changelog.json --- src/pluginPackage/changelog.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/pluginPackage/changelog.json b/src/pluginPackage/changelog.json index c987e8ec..8cdb6360 100644 --- a/src/pluginPackage/changelog.json +++ b/src/pluginPackage/changelog.json @@ -192,4 +192,18 @@ } ] } + "1.7.3": { + "title": "v1.7.3", + "author": "Titus Evans (SnaveSutit)", + "date": "2025-05-08", + "categories": [ + { + "title": "Fixes", + "list": [ + "Fixed adding cameras to a rig causing the data entity to dismount root.", + "Make variant keyframes reset to default if their variant doesn't exist anymore" + ] + } + ] + } } From bffe15dda16b3d4506e1c85930eb796e77183d63 Mon Sep 17 00:00:00 2001 From: Titus Evans Date: Thu, 8 May 2025 15:16:03 -0700 Subject: [PATCH 005/182] Update changelog.json --- src/pluginPackage/changelog.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pluginPackage/changelog.json b/src/pluginPackage/changelog.json index 8cdb6360..334016c8 100644 --- a/src/pluginPackage/changelog.json +++ b/src/pluginPackage/changelog.json @@ -191,7 +191,7 @@ ] } ] - } + }, "1.7.3": { "title": "v1.7.3", "author": "Titus Evans (SnaveSutit)", From 756ace606b81891cd91c4c35e25a2a618bf1f8cb Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 15 May 2025 11:26:12 -0400 Subject: [PATCH 006/182] =?UTF-8?q?=F0=9F=A7=AA=20Add=20test=20pack=20for?= =?UTF-8?q?=201.20.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ajbooth_witch_broom.ajblueprint | 2728 +++++++++++++++++ .../1.20.4/blueprints/armor_stand.ajblueprint | 2073 +++++++++++++ .../datapacks/animated_java/pack.mcmeta | 6 + .../datapacks/test-framework/mcb.config.js | 8 + .../datapacks/test-framework/pack.mcmeta | 6 + .../datapacks/test-framework/package.json | 3 + .../datapacks/test-framework/src/test.mcb | 9 + test-packs/1.20.4/resources/pack.mcmeta | 6 + 8 files changed, 4839 insertions(+) create mode 100644 test-packs/1.20.4/blueprints/ajbooth_witch_broom.ajblueprint create mode 100644 test-packs/1.20.4/blueprints/armor_stand.ajblueprint create mode 100644 test-packs/1.20.4/datapacks/animated_java/pack.mcmeta create mode 100644 test-packs/1.20.4/datapacks/test-framework/mcb.config.js create mode 100644 test-packs/1.20.4/datapacks/test-framework/pack.mcmeta create mode 100644 test-packs/1.20.4/datapacks/test-framework/package.json create mode 100644 test-packs/1.20.4/datapacks/test-framework/src/test.mcb create mode 100644 test-packs/1.20.4/resources/pack.mcmeta diff --git a/test-packs/1.20.4/blueprints/ajbooth_witch_broom.ajblueprint b/test-packs/1.20.4/blueprints/ajbooth_witch_broom.ajblueprint new file mode 100644 index 00000000..1c979ad7 --- /dev/null +++ b/test-packs/1.20.4/blueprints/ajbooth_witch_broom.ajblueprint @@ -0,0 +1,2728 @@ +{ + "meta": { + "format": "animated_java_blueprint", + "format_version": "1.7.3", + "uuid": "5603b13d-d160-8027-d740-d40383fe7c83", + "save_location": "D:\\github-repos\\animated-java\\animated-java\\test-packs\\1.20.4\\blueprints\\ajbooth_witch_broom.ajblueprint", + "last_used_export_namespace": "ajbooth_witch_broom" + }, + "blueprint_settings": { + "export_namespace": "ajbooth_witch_broom", + "show_bounding_box": false, + "auto_bounding_box": true, + "bounding_box": [48, 48], + "enable_plugin_mode": false, + "resource_pack_export_mode": "raw", + "data_pack_export_mode": "raw", + "target_minecraft_versions": ["1.20.4"], + "display_item": "minecraft:white_dye", + "custom_model_data_offset": 0, + "enable_advanced_resource_pack_settings": false, + "resource_pack": "../resources", + "enable_advanced_data_pack_settings": false, + "data_pack": "../datapacks/animated_java", + "summon_commands": "", + "remove_commands": "", + "ticking_commands": "", + "interpolation_duration": 1, + "teleportation_duration": 1, + "use_storage_for_animation": false, + "show_function_errors": false, + "show_outdated_warning": false, + "baked_animations": true, + "json_file": "", + "target_minecraft_version": "1.21.4", + "enable_advanced_resource_pack_folders": false, + "display_item_path": "", + "model_folder": "", + "texture_folder": "" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "elements": [ + { + "name": "Body", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -2], + "to": [4, 24, 2], + "autouv": 0, + "color": 0, + "origin": [0, 0, 0], + "uv_offset": [16, 16], + "faces": { + "north": { + "uv": [20, 20, 28, 32], + "texture": 0 + }, + "east": { + "uv": [16, 20, 20, 32], + "texture": 0 + }, + "south": { + "uv": [32, 20, 40, 32], + "texture": 0 + }, + "west": { + "uv": [28, 20, 32, 32], + "texture": 0 + }, + "up": { + "uv": [28, 20, 20, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 28, 20], + "texture": 0 + } + }, + "type": "cube", + "uuid": "45d82418-2321-4f24-9a63-b5bc2057e800" + }, + { + "name": "Body Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -2], + "to": [4, 24, 2], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, 0], + "uv_offset": [16, 32], + "faces": { + "north": { + "uv": [20, 36, 28, 48], + "texture": 0 + }, + "east": { + "uv": [16, 36, 20, 48], + "texture": 0 + }, + "south": { + "uv": [32, 36, 40, 48], + "texture": 0 + }, + "west": { + "uv": [28, 36, 32, 48], + "texture": 0 + }, + "up": { + "uv": [28, 36, 20, 32], + "texture": 0 + }, + "down": { + "uv": [36, 32, 28, 36], + "texture": 0 + } + }, + "type": "cube", + "uuid": "37dca7a9-789b-3984-4d16-fdaa3b292048" + }, + { + "name": "Body Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 24, 2], + "to": [-4, 12, -2], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [8, 12, 4], + "uv_offset": [16, 32], + "faces": { + "north": { + "uv": [32, 48, 40, 36], + "texture": 0 + }, + "east": { + "uv": [28, 48, 32, 36], + "texture": 0 + }, + "south": { + "uv": [20, 48, 28, 36], + "texture": 0 + }, + "west": { + "uv": [16, 48, 20, 36], + "texture": 0 + }, + "up": { + "uv": [28, 32, 36, 36], + "texture": 0 + }, + "down": { + "uv": [20, 36, 28, 32], + "texture": 0 + } + }, + "type": "cube", + "uuid": "53130d68-3a4f-7a65-51bc-882312407cb7" + }, + { + "name": "Right Arm", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 12, -2.05], + "to": [7, 24, 1.95], + "autouv": 0, + "color": 0, + "origin": [0, 0, -0.05], + "uv_offset": [40, 16], + "faces": { + "north": { + "uv": [44, 20, 47, 32], + "texture": 0 + }, + "east": { + "uv": [40, 20, 44, 32], + "texture": 0 + }, + "south": { + "uv": [51, 20, 54, 32], + "texture": 0 + }, + "west": { + "uv": [47, 20, 51, 32], + "texture": 0 + }, + "up": { + "uv": [47, 20, 44, 16], + "texture": 0 + }, + "down": { + "uv": [50, 16, 47, 20], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a22f9634-063d-97f7-86c5-24256821bb96" + }, + { + "name": "Right Arm Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 12, -2.05], + "to": [7, 24, 1.95], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, -0.05], + "uv_offset": [40, 32], + "faces": { + "north": { + "uv": [44, 36, 47, 48], + "texture": 0 + }, + "east": { + "uv": [40, 36, 44, 48], + "texture": 0 + }, + "south": { + "uv": [51, 36, 54, 48], + "texture": 0 + }, + "west": { + "uv": [47, 36, 51, 48], + "texture": 0 + }, + "up": { + "uv": [47, 36, 44, 32], + "texture": 0 + }, + "down": { + "uv": [50, 32, 47, 36], + "texture": 0 + } + }, + "type": "cube", + "uuid": "6b4537cd-19f5-52e2-fa6a-4b8cd831ff30" + }, + { + "name": "Right Arm Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [7, 24, 1.95], + "to": [4, 12, -2.05], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 3.95], + "uv_offset": [40, 32], + "faces": { + "north": { + "uv": [51, 48, 54, 36], + "texture": 0 + }, + "east": { + "uv": [47, 48, 51, 36], + "texture": 0 + }, + "south": { + "uv": [44, 48, 47, 36], + "texture": 0 + }, + "west": { + "uv": [40, 48, 44, 36], + "texture": 0 + }, + "up": { + "uv": [47, 32, 50, 36], + "texture": 0 + }, + "down": { + "uv": [44, 36, 47, 32], + "texture": 0 + } + }, + "type": "cube", + "uuid": "db64cc71-2153-401a-dd3c-b1cea98adebe" + }, + { + "name": "Left Arm", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -2.05], + "to": [-4, 24, 1.95], + "autouv": 0, + "color": 0, + "origin": [0, 0, -0.05], + "uv_offset": [32, 48], + "faces": { + "north": { + "uv": [36, 52, 39, 64], + "texture": 0 + }, + "east": { + "uv": [32, 52, 36, 64], + "texture": 0 + }, + "south": { + "uv": [43, 52, 46, 64], + "texture": 0 + }, + "west": { + "uv": [39, 52, 43, 64], + "texture": 0 + }, + "up": { + "uv": [39, 52, 36, 48], + "texture": 0 + }, + "down": { + "uv": [42, 48, 39, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "7ff42713-1443-789a-5165-dbef83209ad8" + }, + { + "name": "Left Arm Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -2.05], + "to": [-4, 24, 1.95], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, -0.05], + "uv_offset": [48, 48], + "faces": { + "north": { + "uv": [52, 52, 55, 64], + "texture": 0 + }, + "east": { + "uv": [48, 52, 52, 64], + "texture": 0 + }, + "south": { + "uv": [59, 52, 62, 64], + "texture": 0 + }, + "west": { + "uv": [55, 52, 59, 64], + "texture": 0 + }, + "up": { + "uv": [55, 52, 52, 48], + "texture": 0 + }, + "down": { + "uv": [59, 48, 55, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "693f4ac0-3774-4a83-c93b-2a2951987a65" + }, + { + "name": "Left Arm Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 24, 1.95], + "to": [-7, 12, -2.05], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 3.95], + "uv_offset": [48, 48], + "faces": { + "north": { + "uv": [59, 64, 62, 52], + "texture": 0 + }, + "east": { + "uv": [55, 64, 59, 52], + "texture": 0 + }, + "south": { + "uv": [52, 64, 55, 52], + "texture": 0 + }, + "west": { + "uv": [48, 64, 52, 52], + "texture": 0 + }, + "up": { + "uv": [55, 48, 58, 52], + "texture": 0 + }, + "down": { + "uv": [52, 52, 55, 48], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55a3b67f-8f28-db5a-8b77-62ce1e10fcc4" + }, + { + "name": "Head", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 24, -4], + "to": [4, 32, 4], + "autouv": 0, + "color": 0, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [8, 8, 16, 16], + "texture": 0 + }, + "east": { + "uv": [0, 8, 8, 16], + "texture": 0 + }, + "south": { + "uv": [24, 8, 32, 16], + "texture": 0 + }, + "west": { + "uv": [16, 8, 24, 16], + "texture": 0 + }, + "up": { + "uv": [16, 8, 8, 0], + "texture": 0 + }, + "down": { + "uv": [24, 0, 16, 8], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9a20434d-4a80-a43d-b8f4-c747f469e12b" + }, + { + "name": "Hat Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 24, -4], + "to": [4, 32, 4], + "autouv": 0, + "color": 0, + "inflate": 0.5, + "origin": [0, 0.5, 0], + "uv_offset": [32, 0], + "faces": { + "north": { + "uv": [40, 8, 48, 16], + "texture": 0 + }, + "east": { + "uv": [32, 8, 40, 16], + "texture": 0 + }, + "south": { + "uv": [56, 8, 64, 16], + "texture": 0 + }, + "west": { + "uv": [48, 8, 56, 16], + "texture": 0 + }, + "up": { + "uv": [48, 8, 40, 0], + "texture": 0 + }, + "down": { + "uv": [56, 0, 48, 8], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f9c88722-d723-f7de-ce1d-40d630c00049" + }, + { + "name": "Hat Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [4, 32, 4], + "to": [-4, 24, -4], + "autouv": 0, + "color": 0, + "inflate": -0.5, + "origin": [8, 8.5, 8], + "uv_offset": [32, 0], + "faces": { + "north": { + "uv": [56, 16, 64, 8], + "texture": 0 + }, + "east": { + "uv": [48, 16, 56, 8], + "texture": 0 + }, + "south": { + "uv": [40, 16, 48, 8], + "texture": 0 + }, + "west": { + "uv": [32, 16, 40, 8], + "texture": 0 + }, + "up": { + "uv": [48, 0, 56, 8], + "texture": 0 + }, + "down": { + "uv": [40, 8, 48, 0], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55fb5425-1275-3a16-70a7-4b6276ac5999" + }, + { + "name": "Right Leg", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.1, 0, -2], + "to": [3.9, 12, 2], + "autouv": 0, + "color": 0, + "origin": [0, 0, 0], + "uv_offset": [0, 16], + "faces": { + "north": { + "uv": [4, 20, 8, 32], + "texture": 0 + }, + "east": { + "uv": [0, 20, 4, 32], + "texture": 0 + }, + "south": { + "uv": [12, 20, 16, 32], + "texture": 0 + }, + "west": { + "uv": [8, 20, 12, 32], + "texture": 0 + }, + "up": { + "uv": [8, 20, 4, 16], + "texture": 0 + }, + "down": { + "uv": [12, 16, 8, 20], + "texture": 0 + } + }, + "type": "cube", + "uuid": "73b9fdbc-c2f9-e43c-ef6d-bc159d183193" + }, + { + "name": "Right Leg Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.1, 0, -2], + "to": [3.9, 12, 2], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, 0], + "uv_offset": [0, 32], + "faces": { + "north": { + "uv": [4, 36, 8, 48], + "texture": 0 + }, + "east": { + "uv": [0, 36, 4, 48], + "texture": 0 + }, + "south": { + "uv": [12, 36, 16, 48], + "texture": 0 + }, + "west": { + "uv": [8, 36, 12, 48], + "texture": 0 + }, + "up": { + "uv": [8, 36, 4, 32], + "texture": 0 + }, + "down": { + "uv": [12, 32, 8, 36], + "texture": 0 + } + }, + "type": "cube", + "uuid": "ac495b61-fa9f-44f0-7d4c-30799a96a4c8" + }, + { + "name": "Right Leg Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3.9, 12, 2], + "to": [-0.1, 0, -2], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 4], + "uv_offset": [0, 32], + "faces": { + "north": { + "uv": [12, 48, 16, 36], + "texture": 0 + }, + "east": { + "uv": [8, 48, 12, 36], + "texture": 0 + }, + "south": { + "uv": [4, 48, 8, 36], + "texture": 0 + }, + "west": { + "uv": [0, 48, 4, 36], + "texture": 0 + }, + "up": { + "uv": [8, 32, 12, 36], + "texture": 0 + }, + "down": { + "uv": [4, 36, 8, 32], + "texture": 0 + } + }, + "type": "cube", + "uuid": "426f5735-a6d5-8fdd-78de-06675b178e88" + }, + { + "name": "Left Leg", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.9, 0, -2.05], + "to": [0.1, 12, 1.95], + "autouv": 0, + "color": 0, + "origin": [0, 0, -0.05], + "uv_offset": [16, 48], + "faces": { + "north": { + "uv": [20, 52, 24, 64], + "texture": 0 + }, + "east": { + "uv": [16, 52, 20, 64], + "texture": 0 + }, + "south": { + "uv": [28, 52, 32, 64], + "texture": 0 + }, + "west": { + "uv": [24, 52, 28, 64], + "texture": 0 + }, + "up": { + "uv": [24, 52, 20, 48], + "texture": 0 + }, + "down": { + "uv": [28, 48, 24, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "de1c2115-133b-e2de-0275-6d784aee8ecc" + }, + { + "name": "Left Leg Layer", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.9, 0, -2.05], + "to": [0.1, 12, 1.95], + "autouv": 0, + "color": 0, + "inflate": 0.25, + "origin": [0, 0, -0.05], + "uv_offset": [0, 48], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 0 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 0 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 0 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 0 + }, + "up": { + "uv": [8, 52, 4, 48], + "texture": 0 + }, + "down": { + "uv": [12, 48, 8, 52], + "texture": 0 + } + }, + "type": "cube", + "uuid": "0185a3b1-67bf-38b5-373f-6a6facb044bc" + }, + { + "name": "Left Leg Layer Inverted", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0.1, 12, 1.95], + "to": [-3.9, 0, -2.05], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [4, 12, 3.95], + "uv_offset": [0, 48], + "faces": { + "north": { + "uv": [12, 64, 16, 52], + "texture": 0 + }, + "east": { + "uv": [8, 64, 12, 52], + "texture": 0 + }, + "south": { + "uv": [4, 64, 8, 52], + "texture": 0 + }, + "west": { + "uv": [0, 64, 4, 52], + "texture": 0 + }, + "up": { + "uv": [8, 48, 12, 52], + "texture": 0 + }, + "down": { + "uv": [4, 52, 8, 48], + "texture": 0 + } + }, + "type": "cube", + "uuid": "da1792fe-8974-f1bb-88bd-262e759f1175" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 10, -16], + "to": [1, 12, 22], + "autouv": 0, + "color": 3, + "origin": [0, 10, -3], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [4, 4, 42, 6], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [42, 2, 4, 0], + "texture": 2 + }, + "up": { + "uv": [42, 4, 4, 2], + "rotation": 90, + "texture": 2 + }, + "down": { + "uv": [4, 6, 42, 8], + "rotation": 90, + "texture": 2 + } + }, + "type": "cube", + "uuid": "63947e2c-8407-1963-bf31-8eee08aee500" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.5, 7.5, 22], + "to": [3.5, 14.5, 32], + "autouv": 0, + "color": 3, + "origin": [-0.5, 10.5, 37], + "uv_offset": [28, 16], + "faces": { + "north": { + "uv": [38, 26, 45, 33], + "texture": 2 + }, + "east": { + "uv": [28, 26, 38, 33], + "texture": 2 + }, + "south": { + "uv": [55, 26, 62, 33], + "texture": 2 + }, + "west": { + "uv": [45, 26, 55, 33], + "texture": 2 + }, + "up": { + "uv": [45, 26, 38, 16], + "texture": 2 + }, + "down": { + "uv": [52, 16, 45, 26], + "texture": 2 + } + }, + "type": "cube", + "uuid": "53f25d61-a8ce-e2c9-4ccb-32f0b688f827" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 13, -8.05], + "to": [6, 14, 3.95], + "autouv": 0, + "color": 1, + "origin": [5, 12, -2.05], + "faces": { + "north": { + "uv": [0, 0, 1, 1], + "texture": 2 + }, + "east": { + "uv": [0, 0, 1, 12], + "rotation": 90, + "texture": 2 + }, + "south": { + "uv": [0, 0, 1, 1], + "texture": 2 + }, + "west": { + "uv": [0, 12, 1, 0], + "rotation": 90, + "texture": 2 + }, + "up": { + "uv": [0, 0, 1, 12], + "texture": 2 + }, + "down": { + "uv": [0, 12, 1, 0], + "texture": 2 + } + }, + "type": "cube", + "uuid": "164667fe-1cad-e25a-efd1-1a3716dfbbf9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.474, 14.95148, -12.73926], + "to": [5.524, 15.95148, -7.73926], + "autouv": 0, + "color": 1, + "rotation": [-22.5, 0, 0], + "origin": [5.5, 14.95148, -7.73926], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [0, 4, 20, 8], + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [20, 4, 0, 8], + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "9b9e03f6-1324-ab71-11bc-b08061b7712a" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.399, 13, -12.55], + "to": [5.599, 14, -7.55], + "autouv": 0, + "color": 1, + "rotation": [22.5, 0, 0], + "origin": [5.5, 13.5, -12.55], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [36, 4, 16, 8], + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [16, 4, 36, 8], + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "e39f2736-2736-e6fb-c569-4a6f55c55aaa" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.449, 11.12464, -8.12194], + "to": [5.549, 17.12464, -7.12194], + "autouv": 0, + "color": 1, + "rotation": [-22.5, 0, 0], + "origin": [5.5, 11.12464, -8.12194], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [32, 4, 56, 8], + "rotation": 270, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [32, 4, 56, 8], + "rotation": 270, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "c7c670d3-c3e7-f1c1-a857-0bfeed6be33d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.424, 9.87536, -8.12194], + "to": [5.574, 15.87536, -7.12194], + "autouv": 0, + "color": 1, + "rotation": [22.5, 0, 0], + "origin": [5.5, 15.87536, -8.12194], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [48, 0, 24, 4], + "rotation": 90, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [48, 0, 24, 4], + "rotation": 90, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + } + }, + "type": "cube", + "uuid": "935a8c82-6b9e-cf70-7360-5ad0197622f7" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.499, 13.0506, -10.49417], + "to": [5.499, 17.0506, -9.49417], + "autouv": 0, + "color": 1, + "origin": [5.5, 20.5506, -9.49417], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [16, 0, 0, 4], + "rotation": 270, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [16, 0, 0, 4], + "rotation": 270, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "43115d9e-1e6c-7684-030b-05e980fb6498" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [6, 10, -6.05], + "to": [6, 13, 3.95], + "autouv": 0, + "color": 1, + "origin": [5, 11, -0.05], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [1, 2, 4, 12], + "rotation": 90, + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [1, 12, 4, 2], + "rotation": 90, + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "94f914ad-eb20-d7bf-0e51-4253cf38d09a" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1.5, 9.5, -19.25], + "to": [1.5, 12.5, -16.25], + "autouv": 0, + "color": 0, + "origin": [-0.5, 9.5, -17.25], + "uv_offset": [42, 0], + "faces": { + "north": { + "uv": [45, 3, 48, 6], + "texture": 2 + }, + "east": { + "uv": [42, 3, 45, 6], + "texture": 2 + }, + "south": { + "uv": [51, 3, 54, 6], + "texture": 2 + }, + "west": { + "uv": [48, 3, 51, 6], + "texture": 2 + }, + "up": { + "uv": [48, 3, 45, 0], + "texture": 2 + }, + "down": { + "uv": [51, 0, 48, 3], + "texture": 2 + } + }, + "type": "cube", + "uuid": "0bb3b56e-f47f-5c47-14fe-f6dd3de3a639" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 6, -16], + "to": [-1, 10, 22], + "autouv": 0, + "color": 3, + "origin": [0, 10, -3], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [4, 8, 42, 12], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [42, 8, 4, 12], + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + } + }, + "type": "cube", + "uuid": "c0c36880-9cd7-a3e3-749b-2ef00dbd1051" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 6, -16], + "to": [1, 10, 22], + "autouv": 0, + "color": 3, + "origin": [2, 10, -3], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [4, 12, 42, 16], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [42, 12, 4, 16], + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "rotation": 90, + "texture": null + } + }, + "type": "cube", + "uuid": "0cb22105-c1b9-086d-f0d9-e087c1a1139f" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1.5, 12.5, -16.25], + "to": [-1.5, 9.5, -19.25], + "autouv": 0, + "color": 0, + "inflate": -0.25, + "origin": [2.5, 12.5, -14.25], + "uv_offset": [54, 6], + "faces": { + "north": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "east": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "south": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "west": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "up": { + "uv": [54, 0, 57, 3], + "texture": 2 + }, + "down": { + "uv": [54, 0, 57, 3], + "texture": 2 + } + }, + "type": "cube", + "uuid": "7bee31f4-0f08-299a-aaa0-8177d8078dff" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0, 11, 11], + "to": [2, 13, 13], + "autouv": 0, + "color": 7, + "origin": [1, 12, 12], + "uv_offset": [12, 19], + "faces": { + "north": { + "uv": [14, 21, 16, 23], + "texture": 2 + }, + "east": { + "uv": [12, 21, 14, 23], + "texture": 2 + }, + "south": { + "uv": [18, 21, 20, 23], + "texture": 2 + }, + "west": { + "uv": [16, 21, 18, 23], + "texture": 2 + }, + "up": { + "uv": [16, 21, 14, 19], + "texture": 2 + }, + "down": { + "uv": [18, 19, 16, 21], + "texture": 2 + } + }, + "type": "cube", + "uuid": "6fef4f90-1eea-a2f3-84ac-19fa1edeba34" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2.5, 12, 12], + "to": [2.5, 16, 17], + "autouv": 0, + "color": 7, + "origin": [0.5, 12, 13], + "uv_offset": [0, 30], + "faces": { + "north": { + "uv": [5, 35, 10, 39], + "texture": 2 + }, + "east": { + "uv": [0, 35, 5, 39], + "texture": 2 + }, + "south": { + "uv": [15, 35, 20, 39], + "texture": 2 + }, + "west": { + "uv": [10, 35, 15, 39], + "texture": 2 + }, + "up": { + "uv": [10, 35, 5, 30], + "texture": 2 + }, + "down": { + "uv": [15, 30, 10, 35], + "texture": 2 + } + }, + "type": "cube", + "uuid": "ea6d3eea-4586-0816-0a8a-bfc509ba028f" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2, 15, 11], + "to": [2, 18, 15], + "autouv": 0, + "color": 7, + "origin": [1, 14, 11], + "uv_offset": [0, 23], + "faces": { + "north": { + "uv": [4, 27, 8, 30], + "texture": 2 + }, + "east": { + "uv": [0, 27, 4, 30], + "texture": 2 + }, + "south": { + "uv": [12, 27, 16, 30], + "texture": 2 + }, + "west": { + "uv": [8, 27, 12, 30], + "texture": 2 + }, + "up": { + "uv": [8, 27, 4, 23], + "texture": 2 + }, + "down": { + "uv": [12, 23, 8, 27], + "texture": 2 + } + }, + "type": "cube", + "uuid": "f9cc9927-9caa-17b1-7172-3391bc9967c5" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1.5, 18, 10], + "to": [1.5, 20, 13], + "autouv": 0, + "color": 7, + "origin": [0.5, 18, 9], + "uv_offset": [0, 16], + "faces": { + "north": { + "uv": [3, 19, 6, 21], + "texture": 2 + }, + "east": { + "uv": [0, 19, 3, 21], + "texture": 2 + }, + "south": { + "uv": [9, 19, 12, 21], + "texture": 2 + }, + "west": { + "uv": [6, 19, 9, 21], + "texture": 2 + }, + "up": { + "uv": [6, 19, 3, 16], + "texture": 2 + }, + "down": { + "uv": [9, 16, 6, 19], + "texture": 2 + } + }, + "type": "cube", + "uuid": "938e8b16-7a21-e402-d481-6035bb73cd17" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3.5, 19, 12], + "to": [-0.5, 22, 12], + "autouv": 0, + "color": 7, + "origin": [-1.5, 20, 9], + "faces": { + "north": { + "uv": [17, 16, 20, 19], + "texture": 2 + }, + "east": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "south": { + "uv": [20, 16, 23, 19], + "texture": 2 + }, + "west": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "dd3cb301-f072-0447-26bb-aa239d5864ba" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1.5, 18, 9], + "to": [1.5, 19, 10], + "autouv": 0, + "color": 7, + "origin": [0.5, 17, 6], + "uv_offset": [0, 21], + "faces": { + "north": { + "uv": [1, 22, 4, 23], + "texture": 2 + }, + "east": { + "uv": [0, 22, 1, 23], + "texture": 2 + }, + "south": { + "uv": [5, 22, 8, 23], + "texture": 2 + }, + "west": { + "uv": [4, 22, 5, 23], + "texture": 2 + }, + "up": { + "uv": [4, 22, 1, 21], + "texture": 2 + }, + "down": { + "uv": [7, 21, 4, 22], + "texture": 2 + } + }, + "type": "cube", + "uuid": "ade1c9ce-a3f2-b6ae-44fa-55a993b0749e" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2.5, 17, 9], + "to": [2.5, 20, 9], + "autouv": 0, + "color": 7, + "origin": [1.5, 17, 6], + "uv_offset": [1, 22], + "faces": { + "north": { + "uv": [12, 16, 17, 19], + "texture": 2 + }, + "east": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "south": { + "uv": [12, 16, 17, 19], + "texture": 2 + }, + "west": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "bbbdefdc-5fb6-fa1f-4b13-39e8c045e0e3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0.5, 19, 12], + "to": [3.5, 22, 12], + "autouv": 0, + "color": 7, + "origin": [1.5, 20, 9], + "faces": { + "north": { + "uv": [20, 16, 17, 19], + "texture": 2 + }, + "east": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "south": { + "uv": [23, 16, 20, 19], + "texture": 2 + }, + "west": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "0947e07a-fbce-bd81-536a-514bb9ac87aa" + }, + { + "name": "cube", + "box_uv": true, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-2, 11, 11], + "to": [0, 13, 13], + "autouv": 0, + "color": 7, + "mirror_uv": true, + "origin": [-1, 12, 12], + "uv_offset": [12, 19], + "faces": { + "north": { + "uv": [16, 21, 14, 23], + "texture": 2 + }, + "east": { + "uv": [18, 21, 16, 23], + "texture": 2 + }, + "south": { + "uv": [20, 21, 18, 23], + "texture": 2 + }, + "west": { + "uv": [14, 21, 12, 23], + "texture": 2 + }, + "up": { + "uv": [14, 21, 16, 19], + "texture": 2 + }, + "down": { + "uv": [16, 19, 18, 21], + "texture": 2 + } + }, + "type": "cube", + "uuid": "28d5f17e-1718-c16e-d620-2eae46e6ad48" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0, 12, 16], + "to": [0, 24, 24], + "autouv": 0, + "color": 7, + "origin": [0.5, 13, 17], + "uv_offset": [20, 11], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [28, 19, 20, 31], + "texture": 2 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [20, 19, 28, 31], + "texture": 2 + }, + "up": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "23229772-9322-7e23-686b-e5fda951e8f3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3.5, 14.5, 32], + "to": [-3.5, 7.5, 22], + "autouv": 0, + "color": 3, + "origin": [6.5, 17.5, 47], + "uv_offset": [28, 16], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "east": { + "uv": [45, 33, 55, 26], + "texture": 2 + }, + "south": { + "uv": [38, 33, 45, 26], + "texture": 2 + }, + "west": { + "uv": [28, 33, 38, 26], + "texture": 2 + }, + "up": { + "uv": [45, 16, 52, 26], + "texture": 2 + }, + "down": { + "uv": [38, 26, 45, 16], + "texture": 2 + } + }, + "type": "cube", + "uuid": "2175040c-9503-bcb8-9bcc-52cc5ec35660" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5.374, 9.9506, -10.49417], + "to": [5.624, 13.7006, -9.49417], + "autouv": 0, + "color": 1, + "origin": [5.5, 17.0506, -9.49417], + "faces": { + "north": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "east": { + "uv": [28, 0, 13, 4], + "rotation": 270, + "texture": 1 + }, + "south": { + "uv": [0, 0, 0, 0], + "texture": null + }, + "west": { + "uv": [28, 0, 13, 4], + "rotation": 270, + "texture": 1 + }, + "up": { + "uv": [0, 0, 0, 0], + "rotation": 180, + "texture": null + }, + "down": { + "uv": [0, 0, 0, 0], + "texture": null + } + }, + "type": "cube", + "uuid": "9d9f4cc7-cb0a-1515-b9cb-28a8dcc176cb" + }, + { + "name": "wand_particles", + "position": [5.5, 13.5, -9.5], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": null, + "uuid": "c7ae7054-1365-0f5b-e777-ff9284ac230e", + "type": "locator" + }, + { + "name": "broom_particles", + "position": [0, 11, 26], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": null, + "uuid": "3857dee2-7a29-d026-bab3-73868390263b", + "type": "locator" + } + ], + "outliner": [ + { + "name": "broom", + "origin": [0, 11, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "e24cb11a-42ef-7cb8-56d8-5eec84e57093", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "63947e2c-8407-1963-bf31-8eee08aee500", + "53f25d61-a8ce-e2c9-4ccb-32f0b688f827", + "0bb3b56e-f47f-5c47-14fe-f6dd3de3a639", + "c0c36880-9cd7-a3e3-749b-2ef00dbd1051", + "0cb22105-c1b9-086d-f0d9-e087c1a1139f", + "7bee31f4-0f08-299a-aaa0-8177d8078dff", + "2175040c-9503-bcb8-9bcc-52cc5ec35660", + { + "name": "witch", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "e54ffebf-c16f-05d6-6c9d-81e55a97dac8", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "86662d66-4b1e-be0a-bbaa-6a6dec088fef", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "45d82418-2321-4f24-9a63-b5bc2057e800", + "37dca7a9-789b-3984-4d16-fdaa3b292048", + "53130d68-3a4f-7a65-51bc-882312407cb7", + { + "name": "rightarm", + "origin": [5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "d8d52ffc-92a4-36dc-eadc-b32e43e95516", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "rightarm_slim", + "origin": [5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "3647609e-a900-8b00-b39e-8c8a909cc6bc", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "a22f9634-063d-97f7-86c5-24256821bb96", + "6b4537cd-19f5-52e2-fa6a-4b8cd831ff30", + "db64cc71-2153-401a-dd3c-b1cea98adebe", + { + "name": "wand", + "origin": [5.5, 13.5, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "899dfa49-6f29-4fc0-db9a-b0a8e710d2ca", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "164667fe-1cad-e25a-efd1-1a3716dfbbf9", + "94f914ad-eb20-d7bf-0e51-4253cf38d09a", + "9b9e03f6-1324-ab71-11bc-b08061b7712a", + "935a8c82-6b9e-cf70-7360-5ad0197622f7", + "e39f2736-2736-e6fb-c569-4a6f55c55aaa", + "9d9f4cc7-cb0a-1515-b9cb-28a8dcc176cb", + "43115d9e-1e6c-7684-030b-05e980fb6498", + "c7c670d3-c3e7-f1c1-a857-0bfeed6be33d", + "c7ae7054-1365-0f5b-e777-ff9284ac230e" + ] + } + ] + } + ] + }, + { + "name": "leftarm", + "origin": [-5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "519e38af-3987-5abc-fc4b-35258cf082d1", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "leftarm_slim", + "origin": [-5, 22, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "2e329e12-5f7f-dfe2-5977-83fbbc53cf25", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["7ff42713-1443-789a-5165-dbef83209ad8", "693f4ac0-3774-4a83-c93b-2a2951987a65", "55a3b67f-8f28-db5a-8b77-62ce1e10fcc4"] + } + ] + }, + { + "name": "head", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "b8bb5ce4-8554-85f7-8a9a-74a944beacda", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["9a20434d-4a80-a43d-b8f4-c747f469e12b", "f9c88722-d723-f7de-ce1d-40d630c00049", "55fb5425-1275-3a16-70a7-4b6276ac5999"] + } + ] + }, + { + "name": "rightleg", + "origin": [1.9, 12, 0], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "16a71e7b-efea-4d7e-2197-b83f1c54966e", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["73b9fdbc-c2f9-e43c-ef6d-bc159d183193", "ac495b61-fa9f-44f0-7d4c-30799a96a4c8", "426f5735-a6d5-8fdd-78de-06675b178e88"] + }, + { + "name": "leftleg", + "origin": [-1.9, 12, -0.05], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "c71134bb-9430-596b-6372-4eefaf8083f5", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["de1c2115-133b-e2de-0275-6d784aee8ecc", "0185a3b1-67bf-38b5-373f-6a6facb044bc", "da1792fe-8974-f1bb-88bd-262e759f1175"] + } + ] + }, + { + "name": "cat", + "origin": [0, 12, 14.5], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "600a0134-73f2-0ef4-f7f1-6ba4007e0acf", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "28d5f17e-1718-c16e-d620-2eae46e6ad48", + "6fef4f90-1eea-a2f3-84ac-19fa1edeba34", + "ea6d3eea-4586-0816-0a8a-bfc509ba028f", + { + "name": "upper_body", + "origin": [0, 16, 14], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "53ae45ad-72b3-918c-5af2-1ac9b48a7ef8", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "f9cc9927-9caa-17b1-7172-3391bc9967c5", + { + "name": "cat_head", + "origin": [0, 18, 12], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "5b9afb46-cbe4-e283-eeae-50b4b9dc488e", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "938e8b16-7a21-e402-d481-6035bb73cd17", + "dd3cb301-f072-0447-26bb-aa239d5864ba", + "bbbdefdc-5fb6-fa1f-4b13-39e8c045e0e3", + "0947e07a-fbce-bd81-536a-514bb9ac87aa", + "ade1c9ce-a3f2-b6ae-44fa-55a993b0749e" + ] + } + ] + }, + { + "name": "tail", + "origin": [0, 14, 17], + "color": 0, + "configs": { + "variants": {} + }, + "uuid": "09c04eec-1969-9dda-61c6-e7ef781985e1", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["23229772-9322-7e23-686b-e5fda951e8f3"] + } + ] + }, + "3857dee2-7a29-d026-bab3-73868390263b" + ] + } + ], + "textures": [ + { + "path": "C:\\Users\\SnaveSutit\\AppData\\Roaming\\.modrinth\\profiles\\Animated Java Dev\\resourcepacks\\animated_java_booth_rp\\assets\\animated_java_booth\\textures\\item\\witch_skin.png", + "name": "witch_skin.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 2, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "ce16ff1e-f58a-dbf7-24bb-22dbd43317ec", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAACsJJREFUeF7VW32MVcUVnxcVDPK17AK6u4girNKqRQFbTWmobTFCQqVJa2zZxmINoW3arsbQ1KpRq6k1kaZpS0gLGteEoEm1JGDE2pralCioW/uxCnQRWbYCu3yupKD2tb/z9nc597yZO+/tfQvt/efdO3PmzJzfnHPmnJl5BRd5zmwYVQTJ8PHD3PH9J4Ta944y/YAWZe919hVifeSpP2d6vYwP/Xzhmx+V91///G8FXZ7FPzo4AKCFo2Bgyvdj3cfciOYRqX5YNtQAcII+6D1auOC6cwWAt597t6DLcwEAJMeff1bCo/vV91zzzHPke/8777uvDq/LnMCVHTuiIOfRgJCgNQNAm4CedZrDrc0TTysAecBD2+jsVKoBjRPGpsbSs++QfA+1BpxyAKD2NAm8f2/ShY7C2sEAlLs2bY2CnFeIPO2jg6M31X6A9o9fAJD1/N8DoH2Annn6AAh/z5UXu5H1Jx0lyvr73nf3vvaWg3fOM0ND3TZZLri22yWPA0B5CADfMqkBAo9QHHG6ARIAKJxd4ig813sLgBacdeQB/6BjhpAJVRMw6UAnphkwXR242XiE9RIxacF87xDKpwE6HgjxwEC/dPYEGW/77m75bZ3ULL9P/mtfIkcsYMI4r7v2XKF/7nfvRiNM+i7QT9tXcB19vSlTnFHfUNw+oegkerpyar17bUefs7NHAW8plgZsn9WFkkAaIPIgT7bBt37QHx5EbaHZ1JEdxwlatEU7XZ+lEaCz/bAsBcDdUy5P8bmv6w2360+HpeyM4WeKs8MD5/bh8Q/kffI1Y5yvnQYgS4M4sDXL5kkYi2fJyk0CSkxAW+/joQXy8UtQBCFmGt58W+d+hzX8R7t3OsT0Kx7rdI+0zndtnx8j/Fb85rC7rX2ja7t5uuQAjAVapo8X70/NYOcxEyJYAGPFTVcX29ZuFgBiCY2tZ1ut1RwDNQZ9IVkiGEnWBELE9RACD5cxzPoNa37r7px1i5t9+S6p2/LGZPfA1tXumSWfFW3QyyDAe/z4wZQmZTlR7TvoB+64ZGnx4TdXRRMaHe+zDYHTThcO2ZctgiYBQCc1DGshHJ6sdZ71+NURoQZBrwZExmaVKK/VkmjzF/AOpctlDmjOVVcV9+7d7w4cPOSmXzLNLR72ORnzjt5et37P6+6to69kBjbLZkwVW352Yn9KC67fO1K+/zpsnOt8c7sbVzfWTZxY0jbd30uvZPOHmlNDmy4al/RBv5Hq1LnEr5hyfhbKhGkYXZdqVF+clmpbKQDQAK3e1LCnukreP/T0HjmYCbAGQEef9BuKb0x4IS3rrGXyhdIQGoBZKhwozRKfSgEICfjCwQ8T3pqG/W3btbNiACIaIHLMKX5Dunmp8Av5td9RE9jXmRalUgBC6fEpNIEyACi8BqQAm8+ySWuj+Nb0l5444J3sEAAhzQj5DK5Kth1WKd9Ds2hbuzk145qW2iAmcP+8WUFbycrzNUPS3firp6W4sanR9ezpSY1Pl637+qISndpEsbEDo0+t5prhnn+UgGff5EX6JSs3eQHQwgcBIIpY0/XDTliPWZjz4KPu5S0dQrbohvmhCU6VP/3MRvn++OwZKcAWtM6V7w3tLyYgdq2+3cvz8KGjUs4xUlPGjB0l5QRg5FNrytr3f3FJUlbgsgXhfDm9Rtk3krZnX3YUSAPg0wC299Gj7L6ffldI7v72T1Jgghc0iI/mTfAJpmhWU6NrOa+UexAACK3BIAhiAlZwdkQ7s6ZAsFA/bNF3Kpr1LCIMHoK8vepeIbtg6T2iHSgDqADHahdBsAAQKB8AFhB8iwZQtbU9hexfEB6w3SyawdBR2zR/+BUCYYUOgQrw5s66rKKJyQ0ABgsgtEOLaZQVlPQ/6Ngmg/7hjBb5hYbRqcHmad94t/6J0sIXgM46wWAcsOqTd8oqgFCXz4InviWvDy9c7kVx4ZQrkvKO/nXy7gOAJqSdZmLHA9vm+KYDswBAyNlXT/GOYcvmLm856SsGYEHjfAHgI6MnBQFg3d+P7C7rdPtZJlL671kAYnzE9zrWZ5lv1KzbM+KIVDcdG53wWHZNaRdIPwA2a4kG4FlxAHhRIwpII8mcAn7qrpuk6A/3r/WirMF6tOdJL02tCn/8ldleVowDbKWNA1CvI0DSJwDABNZ3vZ7SABDZ2d5x9KSJLGy6wk1taBBeT5x4viy7yxNZgqfODr82c6zT9o/6kPpTOJgBTcACpJdF1BUAwCN/fl7oINitv39Q3rn2ts1sle//JQCsUBYg1GcFQqhP4gDLDLkBcnLuC9w+6ctCsvSPD1R9wOHbxgZfscGBPnzvNTKfxLRtNJiKBEMAMC2OpafVDlYDoNuGyqvlr+gHtx9gO6STpE/Y0LOxak0ICTEEQuuuUumwrkhlgz4N0GrJLTGUwVnmBeBUm4BvBRD5BjZIxNatHfrsEibBdd3abw41TTWt5uirgj4r2xGis4OtEwwsQ/jWfoDv6Jh1AERvYm77Z5902nJefQHv+BX6gXddZgWg8CzH3n0FQmaRBE0gpQE5O6lZc9/RV07mAkB0PyBnJ6nmL279i3SqUtJEA1CO2GLurMuqmlmtPeyMGkZto5YZ7RPy6H5AXgAgNIV6aGW7AMDNCZYTGKS1y5e1JgCwHHQ+DdC8BzHOlAZw7S+LBAfBeEiaDJUJxAYroXCISKfImoZ5AMoGEyGG+oudBseEMfVF3xaYLRMAICiECgnDlJkdIBskCLUEoEoBY+QCgA57dQPWJfao02IKpzUAp7WxHvPW0wxqsARiKEUEQXYbnGNkXQqAwQiJ+ADHWjjT08dq9owPZ444ahOPvWtnQX+zPa/BAADW58hFBAA+dksM5ShLAKApoIJAaNUPhcC+QEqYB7I91unIUtPvrntHrr/UIE+oLBmC4LBjmIB2bhgUYn8+FgA6T2yIWIErAYACJjM0NOlxDITy43HODGeQoS4Fjjk9e9aIw1W9gxRrr88puAsMkALn/3ndTvh4nLlB7MKCHUHe+wWhgxrP+X9u4cEg6tntrnHMUdIp0uFVe7/gtGuA3hIDQqO7G5w+B4ipsM8E9FTF7hdYAG6+sdk9tq576EzA58W1B/c5OFtmd4Gr2RWeN/bfZaocOlkiIc4FanUL3Zvz63zft2zp/YLP1J1RJoC9HBEyVh6phc74Q+1wJlAzAPJ6EqisPaXRdw19/PVRGS9mgs6e8YfGhm3wWjnFqBOMAcT7BaCzFyi0yoaOsnj6C1p7xs/2+n4AT4iZatudJwFyYEeK7e3+gW5zSgDQQOAds87zPR8A+jIE6O39gMFsrIQmsqYA6E54bK41w9o6vnEZwvfo6zMAgN+8EKE3VmJamlWfOhwloQ2JQwyQLfaf/YK3ulIAfI15xo86e+zFuwG1+jdaoZKER+8ZcMAQ3ndcjvo71j8kZBsW/yyRzwcqco3zJ5QuRegndCeANDgcrRkAZDrY/QAAaO8P+AAIgeUDwHdhS/sRmE7NAciyE30V3dLZ3SLU2/sF+j6Bbg9QPnHpyb/NsC50N5D1QxIH+EyBWpG1XXbxqNLJ0tRRpfsCfKzQEFaH1Lx9fu1F/pum2gy0H+DdgJprgAZADxQCYbBIgvQGKnMCC8CKV9uTuwVo+8tPf1+u2eO57WOlq/fkifLli8svQDD+J23g/D/3Cgb+/wGfkeY4ThdFjwAAAABJRU5ErkJggg==", + "relative_path": "C:/Users/SnaveSutit/AppData/Roaming/.modrinth/profiles/Animated Java Dev/resourcepacks/animated_java_booth_rp/assets/animated_java_booth/textures/item/witch_skin.png" + }, + { + "path": "C:\\Users\\SnaveSutit\\AppData\\Roaming\\.modrinth\\profiles\\Animated Java Dev\\resourcepacks\\animated_java_booth_rp\\assets\\animated_java_booth\\textures\\item\\witch_animation.png", + "name": "witch_animation.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 16, + "height": 96, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 4, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "c3e57243-93f3-dfd7-c4d5-aaa585e74dc6", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAABgCAYAAAAU0fKgAAAAAXNSR0IArs4c6QAAATtJREFUWEftmDFLxEAQhV/ATrARbEYQS7HSnzaWetVp6fw0rcRSBKew8qorbEaSzZ3JcnfJsSlEXiBsYJch7O775s1U/qIBB5r3A8DXIQABcNqM3zjpTd+YVOg81aN6eL02rU/jwWcbLUU+xrKZWk0fLQC5tCZQL1o38thvBuAm8iIltZRr4cE1cqnu4oPYVZ8Hrs+RQ2MMH26FPFghr/wYx8Jz2zr+wRRi+genoP4UyRhsT+VdPsgsyXh9lX2uMWQqNvHB5Jr+oN1FqpFqnCa9FxNJzWMfq3/RpvU1D15do/b/+xQdNR9MU+FBLXAPeA+msvvFPHiry/9O+2DI6pvc9/2B+l1s6g8M8eG8bUSQB+QBefBneOA6i139w5wPdpb1E/XdY0z/MOeD2Jz+gPXCb+FZnNoYoNxs/wB+FYpheqpQPwAAAABJRU5ErkJggg==", + "relative_path": "C:/Users/SnaveSutit/AppData/Roaming/.modrinth/profiles/Animated Java Dev/resourcepacks/animated_java_booth_rp/assets/animated_java_booth/textures/item/witch_animation.png" + }, + { + "path": "C:\\Users\\SnaveSutit\\AppData\\Roaming\\.modrinth\\profiles\\Animated Java Dev\\resourcepacks\\animated_java_booth_rp\\assets\\animated_java_booth\\textures\\item\\witch_assets.png", + "name": "witch_assets.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "05e98464-7aec-b7f7-3e91-b36b122bef63", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB1NJREFUeF7tWGtsFUUUnlVBLhpoe4tAA1WE8BAJVImKoQYENAISE2zsHyDyCPUVJSKgBA0iCopB46uGhxFjgin8qfhEHhFU1GprFAqmiLS1FOltKSoXIbDmm3o250537s7eu725N3T+7M7u2Zn5vvOdc2bWyuk+yG4+XWOJ/9uocK79c8tp2et6aTdxe69bxHU9+osDp+qc67RrC0T5b5VO/9PG3dIW7ez5MzRU3OvAsixRV9xm2/OuZaL3olHi+ItVovWTFXLOnSf2OWONyO4uqiJNzhrnlQ+7gO/GTcoSJTNqnHn+2nLiEurURtdLG7T80Fznubooa1Lfmfb2Y5ucwe8vGGZv/uW4CHXJkbZjsgYLAK5pahKDcnMlcLWPxZI9TRA916wlALZ3fxkSH94WbSNg8VOix4QB4tSOI6J19fNiTv4EsaF2h3yHcYqv7y3eqax21vh+dMyF3dtPOuPv/a5F3lc/1+ifgFkVI+xvjkbEr9Mb5ASZQAB5d4/YIIgIkJAQAV4KgDd0niclvH64LCkFdJ34kBMCZ794Q7xww33iyR8/iFHAZcuFDckXijmO50EAGkjAO7rHdd20aq3suTStkXt32XgA+R15ZralKiBdCNg3pcUee1O2A5SD4Cqg51wN2lgUQlihLjl29Fxz3CSoJj1KivQ86CT48MAiAVVRQkUSPDzesotn5jpYunbpJnZ+dczp1x88H4OTJ8SMICB0Y08R/aFVVgE3AngViAfI7zsjBaRbGfQL0pcCdMZT8ibbRMRHDR87IZPoYgZvzbOp8qhjYK4g5jBZWzsFxCOAYr6jF/fE0Pn2SwffTppkIwIwGcocGjY7uolpUalYXCrmIHIsdSusYy2VBJh4LigbT5npYj/InNBn2lqbzgKN5Qs81xQUeIzjORklJDUxoR9UTkhrAnTSp9wRL2+YemrA8o02HYawGzX9Lgi7hCcLMieAAACnaxDATMeIIaBy6WP2Nfl54vfahpjvC1a+4th9/tpS+45HVrbrn96yTp4pqHW/d17C5MZb/NriyXKeBZuT34u0ywEA8W+kRVwezhaHKvc76+AEuC2uuXS1/X3VAVE4cax8faap7V9ATsnihEkAUA6S+htLiiQBs0vLLJ2NqfddCbAsS9i2LUAEtez5ixwgax6dYS989b2Yfs4fbbb3TCyMmTsZAgCUg6T+rjULJAHjF661dDa+CIDshxQMl9/kLnlaRFavEOHFy0TTqmflM1IChQapAd/9yUi6s/TdhL3ttmAA3bbtazF8aL70NvWnTr1VmuMd7omI/Qdrnb4vAj4rmWVfFc4WV/fvK7+D96ECtKN1xwQHiWcENFUEACSBrao4JEaNHiLXRvf0jhPiiwAYR7eutxG33XLb/gPSfWj63HZeBXCaQCUHRHrlC9PFweMEkl97Zl0hh2g9+Y8kQ7WBIkznkDkA4OH1uk0Vov/M0Y73dSRE3lrlEFDxU3XMXKNHDhPhB5b4WoBusYhvFeSRmnoJHA1EDBjUL4YAPEO4+CJANQbAUK+wfBw9ERG19Y0iv18f2cd9UB72WiSVO4AiInQEkA3G9Fse27Gl1nOQwJuphzmRiewJHh93s92vT1h6GgkOyRAN92i8j3uQVN8YES/v/jZ5BcDTaPA8qYFIMAUDImGLK0g0JY7mIQLQBzCQQfe48j6/T5oAnuQwEZVIvwRw1UANnADIm2KY7EjeAEtNB1J9rxvLJBwskiqSHsqeW0OJRIWgcOA5wsSzKgHcy9ybbnODkIbjEZHXO+x4HXZUDXBPiTGhEOAx7wYQE6hhwBeqhgQpiJIlVQ2VKJ0KVEDoEwGcLNXrqpJMvI9vLCzYLdZVb1BFoNyAq1tV4CFE1SOeSnis8xgn4Oo6oAQ1BOg7v/EvCXCTHd8e4z22w7wUmgBTlcDncfO+6kFOhk4BsMFeIFHvawngJQxG8D7CgO8L0DetCG4k4xn3vltMEwluOUAdk5KnXxW4KkDdC9BkQRAQrwKoIWBSBZKpAFoF6Ajg/whQHpNVgE4ZqXxurAAOnhaYqm1xRxJiXAX4InhF4BslkJRppMitqpro0Oe1n+8PKCHqvNIBYRHzrzFoNcidIAZFTefnexxtqfFjL878VAaJKHgeSkhkzx8HUIcCp3mdrTA9cIt1esflzk+J/MhssjU28GJKwGurgMECO9rkoiYgZeDTVQGdBLjF15VlG13D7u+i2THPTe2cJNjRwZzA+K4KMAVmapexBKge9yKYCNF95+sHotdkAb2Pq4CLloBC+0HJ7x7rTV88e32XMQrwAqJjxeu7jCNABaoqggB72WVcEjQFZmqXcQT4CnwfxhkTAj4w+TLtJMAXXakx7jwLEM9qCUu0z/2mVo20DgEOmGd3AmFKSLySmBEE6DzoVfK8CEr7/wEqQLdtsBsJqkIyVgFuR9t4hyH15GfyfVqHAAfEwRAJXmd/r+/TPgR0HuUqUElwe6cSxm3SWgGp2HZ0EpAKln3OkdKd4H9dQJRwK05NdwAAAABJRU5ErkJggg==", + "relative_path": "C:/Users/SnaveSutit/AppData/Roaming/.modrinth/profiles/Animated Java Dev/resourcepacks/animated_java_booth_rp/assets/animated_java_booth/textures/item/witch_assets.png" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "ceca2b6f-acbe-23b5-4e39-b2f2148ed716", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "animations": [ + { + "uuid": "55cac467-ea1e-c70f-63e8-6a496a57fbe4", + "name": "pose", + "loop": "loop", + "override": false, + "length": 4, + "snapping": 20, + "selected": true, + "saved": false, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "86662d66-4b1e-be0a-bbaa-6a6dec088fef": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "21.5326 + math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "6.94082", + "z": "-10.14915" + } + ], + "uuid": "7375b3e2-8bb8-96fe-7d0a-995799b9b124", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1" + } + ], + "uuid": "9bcbb128-fa90-a43c-6715-ca4b3d1287b8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3647609e-a900-8b00-b39e-8c8a909cc6bc": { + "name": "rightarm_slim", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "50.6959 - math.cos(q.life_time * 180 + 45) * 22.5 - 12.25", + "y": "15.6806", + "z": "55.0931 - math.cos(q.life_time * 180 - 45) * 22.5" + } + ], + "uuid": "43a3a5bf-eb57-71a6-38f8-a6908ec75014", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "2e329e12-5f7f-dfe2-5977-83fbbc53cf25": { + "name": "leftarm_slim", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-53.8268 - math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "23.61535", + "z": "7.1579" + } + ], + "uuid": "6b045717-0fac-aceb-f4a2-8421cea86682", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1 + math.cos(q.life_time * 180 - 22.5)", + "z": "math.cos(q.life_time * 180 - 22.5) * 2" + } + ], + "uuid": "153aa669-4c98-c128-e66f-c02e04c24f6b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b8bb5ce4-8554-85f7-8a9a-74a944beacda": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-14.4966 - math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "-6.6848", + "z": "9.10914" + } + ], + "uuid": "2f449be4-da35-4144-e82b-3626a040cda0", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "16a71e7b-efea-4d7e-2197-b83f1c54966e": { + "name": "rightleg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-33.735 + math.cos(q.life_time * 180 + 45) * 12.25", + "y": "9.93191", + "z": "14.4817 - math.cos(q.life_time * 180 - 45) * 12.25" + } + ], + "uuid": "2242ad81-4ea6-c374-29ce-7dd39ea7732d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "c71134bb-9430-596b-6372-4eefaf8083f5": { + "name": "leftleg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-27.3779 + math.cos(q.life_time * 180 - 45) * 12.25", + "y": "-1.9293", + "z": "-9.74852" + } + ], + "uuid": "0e06f514-d90a-8e60-eff7-54f12916d179", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "09c04eec-1969-9dda-61c6-e7ef781985e1": { + "name": "tail", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "14.21301", + "y": "-65.79957", + "z": "237.13131" + } + ], + "uuid": "8e858d72-fc56-9bca-b792-6903609981f6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "600a0134-73f2-0ef4-f7f1-6ba4007e0acf": { + "name": "cat", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "12.5", + "z": "-12.5" + } + ], + "uuid": "e994ff64-e40d-173a-16f4-1eb77fdb6512", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "scale", + "data_points": [ + { + "x": "1 + math.cos(q.life_time * 180 - 22.5) * 0.1", + "y": "1 - math.cos(q.life_time * 180 - 22.5) * 0.1", + "z": "1 + math.cos(q.life_time * 180 - 22.5) * 0.1" + } + ], + "uuid": "1d7b63da-2172-2b07-33c0-8d587ccdf851", + "time": 0, + "color": -1, + "uniform": false, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "5b9afb46-cbe4-e283-eeae-50b4b9dc488e": { + "name": "cat_head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "14.8737 + math.cos(q.life_time * 180 - 22.5) * 12.25", + "y": "9.53055", + "z": "12.3725" + } + ], + "uuid": "28f38076-27d5-ec21-7fae-bfab14b87a42", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "-1.25", + "y": "-0.75", + "z": "0" + } + ], + "uuid": "3129fa1a-35d1-1d11-6b11-7c6a33595618", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "53ae45ad-72b3-918c-5af2-1ac9b48a7ef8": { + "name": "upper_body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "-6", + "z": "-6.5" + } + ], + "uuid": "dbcda364-7b28-fe3f-4265-c8b1825ab332", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "-1.25", + "y": "-0.75 - math.cos(q.life_time * 180 - 45) * 1", + "z": "0" + } + ], + "uuid": "0285b4b5-9258-832f-ac2c-6616e28c7090", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "e24cb11a-42ef-7cb8-56d8-5eec84e57093": { + "name": "broom", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "7.5283 - math.cos(q.life_time * 180) * 12.25", + "y": "4.95712", + "z": "0.65426" + } + ], + "uuid": "39c88ccc-2963-635b-7765-4220f69c5931", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "5 + math.sin(q.life_time * 180) * 4", + "z": "0" + } + ], + "uuid": "1aaa4431-a52a-28b5-eedb-b98102928609", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "e54ffebf-c16f-05d6-6c9d-81e55a97dac8": { + "name": "witch", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.cos(q.life_time * 180) * 8", + "y": "0", + "z": "0" + } + ], + "uuid": "3bc3480f-7f1f-6893-006e-d969bfbb5af8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_controllers": [] +} \ No newline at end of file diff --git a/test-packs/1.20.4/blueprints/armor_stand.ajblueprint b/test-packs/1.20.4/blueprints/armor_stand.ajblueprint new file mode 100644 index 00000000..06eff3f5 --- /dev/null +++ b/test-packs/1.20.4/blueprints/armor_stand.ajblueprint @@ -0,0 +1,2073 @@ +{ + "meta": { + "format": "animated_java_blueprint", + "format_version": "1.7.3", + "uuid": "167b27cd-b559-3f13-a97c-0841fe21f1d1", + "save_location": "D:\\github-repos\\animated-java\\animated-java\\test-packs\\1.20.4\\blueprints\\armor_stand.ajblueprint", + "last_used_export_namespace": "armor_stand" + }, + "blueprint_settings": { + "export_namespace": "armor_stand", + "show_bounding_box": false, + "auto_bounding_box": true, + "bounding_box": [95, 32], + "enable_plugin_mode": false, + "resource_pack_export_mode": "raw", + "data_pack_export_mode": "raw", + "target_minecraft_versions": ["1.20.4"], + "display_item": "minecraft:white_dye", + "custom_model_data_offset": 0, + "enable_advanced_resource_pack_settings": false, + "resource_pack": "../resources", + "enable_advanced_data_pack_settings": false, + "data_pack": "../datapacks/animated_java", + "summon_commands": "say On-Summon!", + "remove_commands": "say On-Remove!", + "ticking_commands": "", + "interpolation_duration": 1, + "teleportation_duration": 1, + "use_storage_for_animation": false, + "show_function_errors": true, + "show_outdated_warning": true, + "baked_animations": false, + "json_file": "../testPluginExport.json", + "target_minecraft_version": "1.21.4", + "enable_advanced_resource_pack_folders": false, + "display_item_path": "", + "model_folder": "", + "texture_folder": "", + "customModelDataOffset": 0, + "custom_summon_commands": "" + }, + "resolution": { + "width": 64, + "height": 64 + }, + "elements": [ + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 0, -6], + "to": [6, 1, 6], + "autouv": 0, + "color": 2, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [12, 44, 24, 45], + "texture": 0 + }, + "east": { + "uv": [0, 44, 12, 45], + "texture": 0 + }, + "south": { + "uv": [36, 44, 48, 45], + "texture": 0 + }, + "west": { + "uv": [24, 44, 36, 45], + "texture": 0 + }, + "up": { + "uv": [12, 32, 24, 44], + "texture": 0 + }, + "down": { + "uv": [24, 32, 36, 44], + "texture": 0 + } + }, + "type": "cube", + "uuid": "46624743-e0ef-5e97-e6d8-ba5dc100baf4" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4, 12, -1], + "to": [4, 14, 1], + "autouv": 0, + "color": 2, + "origin": [0, 13, 0], + "faces": { + "north": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "east": { + "uv": [0, 50, 2, 52], + "texture": 0 + }, + "south": { + "uv": [12, 50, 20, 52], + "texture": 0 + }, + "west": { + "uv": [10, 50, 12, 52], + "texture": 0 + }, + "up": { + "uv": [2, 50, 10, 52], + "texture": 0 + }, + "down": { + "uv": [10, 48, 18, 50], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9b73ebc3-aca6-b85c-d022-b0a811ab38f8" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 1, -1], + "to": [3, 12, 1], + "autouv": 0, + "color": 2, + "origin": [2, 6.5, 0], + "faces": { + "north": { + "uv": [10, 2, 12, 13], + "texture": 0 + }, + "east": { + "uv": [12, 2, 14, 13], + "texture": 0 + }, + "south": { + "uv": [14, 2, 16, 13], + "texture": 0 + }, + "west": { + "uv": [8, 2, 10, 13], + "texture": 0 + }, + "up": { + "uv": [10, 0, 12, 2], + "texture": 0 + }, + "down": { + "uv": [12, 0, 14, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "d29efa52-def5-a311-7438-1fc73aac804c" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 1, -1], + "to": [-1, 12, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 6.5, 0], + "faces": { + "north": { + "uv": [44, 18, 42, 29], + "texture": 0 + }, + "east": { + "uv": [46, 18, 44, 29], + "texture": 0 + }, + "south": { + "uv": [48, 18, 46, 29], + "texture": 0 + }, + "west": { + "uv": [42, 18, 40, 29], + "texture": 0 + }, + "up": { + "uv": [42, 16, 44, 18], + "texture": 0 + }, + "down": { + "uv": [44, 16, 46, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "60766231-bd45-cb3f-0a4f-c6c066e86f54" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-6, 21, -1.5], + "to": [6, 24, 1.5], + "autouv": 0, + "color": 2, + "origin": [0, 22.5, 0], + "faces": { + "north": { + "uv": [3, 29, 15, 32], + "texture": 0 + }, + "east": { + "uv": [0, 29, 3, 32], + "texture": 0 + }, + "south": { + "uv": [18, 29, 30, 32], + "texture": 0 + }, + "west": { + "uv": [15, 29, 18, 32], + "texture": 0 + }, + "up": { + "uv": [3, 26, 15, 29], + "texture": 0 + }, + "down": { + "uv": [15, 26, 27, 29], + "texture": 0 + } + }, + "type": "cube", + "uuid": "55829f3d-26dc-8f49-39b8-5ba2c0159a24" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [1, 14, -1], + "to": [3, 21, 1], + "autouv": 0, + "color": 2, + "origin": [2, 17.5, 0], + "faces": { + "north": { + "uv": [18, 2, 20, 9], + "texture": 0 + }, + "east": { + "uv": [16, 2, 18, 9], + "texture": 0 + }, + "south": { + "uv": [22, 2, 24, 9], + "texture": 0 + }, + "west": { + "uv": [20, 2, 22, 9], + "texture": 0 + }, + "up": { + "uv": [18, 0, 20, 2], + "texture": 0 + }, + "down": { + "uv": [20, 0, 22, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "9d16df18-0574-58bb-e0f9-f799946257c9" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-3, 14, -1], + "to": [-1, 21, 1], + "autouv": 0, + "color": 2, + "origin": [-2, 17.5, 0], + "faces": { + "north": { + "uv": [50, 18, 52, 25], + "texture": 0 + }, + "east": { + "uv": [48, 18, 50, 25], + "texture": 0 + }, + "south": { + "uv": [54, 18, 56, 25], + "texture": 0 + }, + "west": { + "uv": [52, 18, 54, 25], + "texture": 0 + }, + "up": { + "uv": [50, 16, 52, 18], + "texture": 0 + }, + "down": { + "uv": [52, 16, 54, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "f26e6144-8dab-547a-16e8-adaa6fa2c45d" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-7, 12, -1], + "to": [-5, 24, 1], + "autouv": 0, + "color": 2, + "origin": [-6, 18, 0], + "faces": { + "north": { + "uv": [36, 18, 34, 30], + "texture": 0 + }, + "east": { + "uv": [34, 18, 32, 30], + "texture": 0 + }, + "south": { + "uv": [40, 18, 38, 30], + "texture": 0 + }, + "west": { + "uv": [38, 18, 36, 30], + "texture": 0 + }, + "up": { + "uv": [34, 18, 36, 16], + "texture": 0 + }, + "down": { + "uv": [36, 16, 38, 18], + "texture": 0 + } + }, + "type": "cube", + "uuid": "c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [5, 12, -1], + "to": [7, 24, 1], + "autouv": 0, + "color": 2, + "origin": [6, 18, 0], + "faces": { + "north": { + "uv": [26, 2, 28, 14], + "texture": 0 + }, + "east": { + "uv": [24, 2, 26, 14], + "texture": 0 + }, + "south": { + "uv": [30, 2, 32, 14], + "texture": 0 + }, + "west": { + "uv": [28, 2, 30, 14], + "texture": 0 + }, + "up": { + "uv": [26, 0, 28, 2], + "texture": 0 + }, + "down": { + "uv": [30, 0, 28, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "13293700-7f42-2279-6a1f-4e0a6a4d89b3" + }, + { + "name": "cube", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, 23, -1], + "to": [1, 30, 1], + "autouv": 0, + "color": 2, + "origin": [0, 26.5, 0], + "faces": { + "north": { + "uv": [2, 2, 4, 9], + "texture": 0 + }, + "east": { + "uv": [0, 2, 2, 9], + "texture": 0 + }, + "south": { + "uv": [6, 2, 8, 9], + "texture": 0 + }, + "west": { + "uv": [4, 2, 6, 9], + "texture": 0 + }, + "up": { + "uv": [2, 0, 4, 2], + "texture": 0 + }, + "down": { + "uv": [4, 0, 6, 2], + "texture": 0 + } + }, + "type": "cube", + "uuid": "a23d9259-0d9a-4e16-1b42-47e5bedba00f" + }, + { + "name": "helmet", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 22, -5], + "to": [5, 32, 5], + "autouv": 0, + "color": 9, + "origin": [0, 27, 0], + "faces": { + "north": { + "uv": [8, 16, 16, 32], + "texture": 1 + }, + "east": { + "uv": [0, 16, 8, 32], + "texture": 1 + }, + "south": { + "uv": [24, 16, 32, 32], + "texture": 1 + }, + "west": { + "uv": [16, 16, 24, 32], + "texture": 1 + }, + "up": { + "uv": [8, 0, 16, 16], + "texture": 1 + }, + "down": { + "uv": [16, 0, 24, 16], + "texture": 1 + } + }, + "type": "cube", + "uuid": "a302b268-1699-4013-011f-211bd85806d4" + }, + { + "name": "chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-5, 11.25, -3], + "to": [5, 23.75, 3], + "autouv": 0, + "color": 9, + "origin": [0, 17.5, 0], + "faces": { + "north": { + "uv": [20, 40, 28, 64], + "texture": 1 + }, + "east": { + "uv": [16, 40, 20, 64], + "texture": 1 + }, + "south": { + "uv": [32, 40, 40, 64], + "texture": 1 + }, + "west": { + "uv": [28, 40, 32, 64], + "texture": 1 + }, + "up": { + "uv": [20, 32, 28, 40], + "texture": 1 + }, + "down": { + "uv": [28, 32, 36, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "6dd668dd-27bd-631d-5e44-a94f7a35ddbb" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-9, 19.25, -3], + "to": [-3, 25, 3], + "autouv": 0, + "color": 9, + "origin": [-6, 22.125, 0], + "faces": { + "north": { + "uv": [48, 40, 44, 49.75], + "texture": 1 + }, + "east": { + "uv": [44, 40, 40, 49.75], + "texture": 1 + }, + "south": { + "uv": [56, 40, 52, 49.75], + "texture": 1 + }, + "west": { + "uv": [52, 40, 48, 49.75], + "texture": 1 + }, + "up": { + "uv": [44, 39.75, 48, 32], + "texture": 1 + }, + "down": { + "uv": [52, 32, 48, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "967c77d0-2607-0ecb-8093-416d237cdb85" + }, + { + "name": "left_chestplate", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [3, 19.25, -3], + "to": [9, 25, 3], + "autouv": 0, + "color": 9, + "origin": [6, 22.125, 0], + "faces": { + "north": { + "uv": [44, 40, 48, 49.75], + "texture": 1 + }, + "east": { + "uv": [48, 40, 52, 49.75], + "texture": 1 + }, + "south": { + "uv": [52, 40, 56, 49.75], + "texture": 1 + }, + "west": { + "uv": [40, 40, 44, 49.75], + "texture": 1 + }, + "up": { + "uv": [48, 39.75, 44, 32], + "texture": 1 + }, + "down": { + "uv": [48, 32, 52, 39.75], + "texture": 1 + } + }, + "type": "cube", + "uuid": "98475898-cfc8-f67b-3145-abaf659deb3e" + }, + { + "name": "pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.5, 11.5, -2.5], + "to": [4.5, 17, 2.5], + "autouv": 0, + "color": 9, + "origin": [0, 14.25, 0], + "faces": { + "north": { + "uv": [20, 54, 28, 64], + "texture": 2 + }, + "east": { + "uv": [16, 54, 20, 64], + "texture": 2 + }, + "south": { + "uv": [32, 54, 40, 64], + "texture": 2 + }, + "west": { + "uv": [28, 54, 32, 64], + "texture": 2 + }, + "up": { + "uv": [20, 48, 28, 54], + "texture": 2 + }, + "down": { + "uv": [28, 48, 36, 54], + "texture": 2 + } + }, + "type": "cube", + "uuid": "d5e974ae-a732-db7a-59a1-c16ff717e426" + }, + { + "name": "right_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-0.5, 2.75, -2.375], + "to": [4.25, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [1.875, 7.375, 0], + "faces": { + "north": { + "uv": [4, 40, 8, 58], + "texture": 2 + }, + "east": { + "uv": [0, 40, 4, 58], + "texture": 2 + }, + "south": { + "uv": [12, 40, 16, 58], + "texture": 2 + }, + "west": { + "uv": [8, 40, 12, 58], + "texture": 2 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 2 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "71bfec99-5014-21d3-739b-6512074212db" + }, + { + "name": "left_pants", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.25, 2.75, -2.375], + "to": [0.5, 12, 2.375], + "autouv": 0, + "color": 9, + "origin": [-1.875, 7.375, 0], + "faces": { + "north": { + "uv": [8, 40, 4, 58], + "texture": 2 + }, + "east": { + "uv": [12, 40, 8, 58], + "texture": 2 + }, + "south": { + "uv": [16, 40, 12, 58], + "texture": 2 + }, + "west": { + "uv": [4, 40, 0, 58], + "texture": 2 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 2 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 2 + } + }, + "type": "cube", + "uuid": "4dd0c5f1-828f-d559-6496-16e2fbe6276d" + }, + { + "name": "right_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-1, -1, -3], + "to": [4.75, 6, 3], + "autouv": 0, + "color": 9, + "origin": [1.875, 2.5, 0], + "faces": { + "north": { + "uv": [4, 52, 8, 64], + "texture": 1 + }, + "east": { + "uv": [0, 52, 4, 64], + "texture": 1 + }, + "south": { + "uv": [12, 52, 16, 64], + "texture": 1 + }, + "west": { + "uv": [8, 52, 12, 64], + "texture": 1 + }, + "up": { + "uv": [4, 32, 8, 40], + "texture": 1 + }, + "down": { + "uv": [8, 32, 12, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "41413d23-edf5-3fdc-2ddb-5fd45ea99a89" + }, + { + "name": "left_shoe", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [-4.75, -1, -3], + "to": [1, 6, 3], + "autouv": 0, + "color": 9, + "origin": [-1.875, 2.5, 0], + "faces": { + "north": { + "uv": [8, 52, 4, 64], + "texture": 1 + }, + "east": { + "uv": [12, 52, 8, 64], + "texture": 1 + }, + "south": { + "uv": [16, 52, 12, 64], + "texture": 1 + }, + "west": { + "uv": [4, 52, 0, 64], + "texture": 1 + }, + "up": { + "uv": [8, 32, 4, 40], + "texture": 1 + }, + "down": { + "uv": [12, 32, 8, 40], + "texture": 1 + } + }, + "type": "cube", + "uuid": "11976d8b-3994-df35-0fb9-b92ac5012141" + }, + { + "name": "left_hand", + "position": [-6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": true, + "summon_commands": "", + "ticking_commands": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test3,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7de415a2-9fdd-e4b2-3455-c3bed72eb896", + "type": "locator" + }, + { + "name": "right_hand", + "position": [6, 11, 0], + "rotation": [0, 0, 0], + "ignore_inherited_scale": false, + "visibility": true, + "locked": false, + "config": { + "use_entity": true, + "entity_type": "minecraft:item_display", + "sync_passenger_rotation": false, + "summon_commands": "", + "ticking_commands": "particle minecraft:bubble ^ ^ ^1\ntp @e[tag=test2,limit=1] ~ ~ ~ ~ ~" + }, + "uuid": "7747736f-85e2-338f-207e-53f8d3d2fa39", + "type": "locator" + }, + { + "name": "text_display", + "position": [0, 38, 0], + "rotation": [45, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "block": "minecraft:stone", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "{\n\t\"text\": \"Hello, World!\",\n\t\"color\": \"green\"\n}\n", + "lineWidth": 200, + "backgroundColor": "#ffffff", + "backgroundAlpha": 0.25098, + "align": "center", + "shadow": true, + "seeThrough": true, + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display" + }, + { + "name": "camera", + "box_uv": false, + "rescale": false, + "locked": false, + "light_emission": 0, + "render_order": "default", + "allow_mirror_modeling": true, + "from": [0, 0, 0], + "to": [1, 1, 1], + "autouv": 0, + "color": 0, + "origin": [0, 0, 0], + "faces": { + "north": { + "uv": [0, 0, 1, 1], + "texture": 0 + }, + "east": { + "uv": [0, 0, 1, 1], + "texture": 0 + }, + "south": { + "uv": [0, 0, 1, 1], + "texture": 0 + }, + "west": { + "uv": [0, 0, 1, 1], + "texture": 0 + }, + "up": { + "uv": [0, 0, 1, 1], + "texture": 0 + }, + "down": { + "uv": [0, 0, 1, 1], + "texture": 0 + } + }, + "type": "cube", + "uuid": "3f916352-b0e1-f1e4-0e09-308cc68f96e6" + } + ], + "outliner": [ + { + "name": "root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "98795982-9c43-6e3e-eed9-2c776a957938", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_root", + "origin": [0, 0, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "export": true, + "mirror_uv": false, + "isOpen": false, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "baseplate_pivot_a", + "origin": [0, 1, 6], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["46624743-e0ef-5e97-e6d8-ba5dc100baf4"] + } + ] + }, + { + "name": "armor_stand_root", + "origin": [0, 1, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": {} + }, + "uuid": "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "98a28100-07e8-849a-38b7-f56abc2c2e56", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body_waist_pivot", + "origin": [0, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": {} + } + }, + "uuid": "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + { + "name": "body", + "origin": [0, 24, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "5417306e-2c69-3f36-1e3c-edd904034a36": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": false, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff06", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": [ + "55829f3d-26dc-8f49-39b8-5ba2c0159a24", + "9d16df18-0574-58bb-e0f9-f799946257c9", + "f26e6144-8dab-547a-16e8-adaa6fa2c45d", + "9b73ebc3-aca6-b85c-d022-b0a811ab38f8", + "6dd668dd-27bd-631d-5e44-a94f7a35ddbb", + "d5e974ae-a732-db7a-59a1-c16ff717e426", + { + "name": "head", + "origin": [0, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#c400ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "808e3c26-7285-af3f-a079-d8b899176dd3", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["a23d9259-0d9a-4e16-1b42-47e5bedba00f", "a302b268-1699-4013-011f-211bd85806d4"] + }, + { + "name": "left_arm", + "origin": [-6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#00ff9f", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "b5b52d2c-097c-08df-6457-372fbae12445", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["c219cb9b-53a7-6a5b-7b47-dce0a1e6ec14", "967c77d0-2607-0ecb-8093-416d237cdb85", "7de415a2-9fdd-e4b2-3455-c3bed72eb896"] + }, + { + "name": "right_arm", + "origin": [6, 23, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": false, + "override_glow_color": true, + "glow_color": "#ffffff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ff0000", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["13293700-7f42-2279-6a1f-4e0a6a4d89b3", "98475898-cfc8-f67b-3145-abaf659deb3e", "7747736f-85e2-338f-207e-53f8d3d2fa39"] + } + ] + } + ] + }, + { + "name": "left_leg", + "origin": [2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#ffee00", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["d29efa52-def5-a311-7438-1fc73aac804c", "71bfec99-5014-21d3-739b-6512074212db", "41413d23-edf5-3fdc-2ddb-5fd45ea99a89"] + }, + { + "name": "right_leg", + "origin": [-2, 12, 0], + "color": 0, + "configs": { + "default": { + "billboard": "fixed", + "brightness_override": 0, + "enchanted": false, + "glow_color": "#ffffff", + "glowing": false, + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + }, + "variants": { + "49521f4b-b970-e4bd-b594-2e05d2d46027": { + "billboard": "fixed", + "override_brightness": false, + "brightness_override": 0, + "enchanted": false, + "glowing": true, + "override_glow_color": true, + "glow_color": "#6e00ff", + "inherit_settings": true, + "invisible": false, + "nbt": "{}", + "shadow_radius": 0, + "shadow_strength": 1, + "use_nbt": false + } + } + }, + "uuid": "60d50d30-a018-429d-ba78-c5fb5804dd8a", + "export": true, + "mirror_uv": false, + "isOpen": true, + "locked": false, + "visibility": true, + "autouv": 0, + "selected": false, + "children": ["60766231-bd45-cb3f-0a4f-c6c066e86f54", "4dd0c5f1-828f-d559-6496-16e2fbe6276d", "11976d8b-3994-df35-0fb9-b92ac5012141"] + } + ] + } + ] + }, + "3f916352-b0e1-f1e4-0e09-308cc68f96e6" + ] + }, + "c2e217f1-b50a-5c9a-b342-71a35e984046" + ], + "textures": [ + { + "path": "", + "name": "wood.png", + "folder": "", + "namespace": "", + "id": "0", + "group": "", + "width": 64, + "height": 64, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAB6xJREFUeF7tW01oXUUUnmebkjZNCU1bjSnVtAUNVks0VK1gEbLowmK6EIOKP3VRKBYNBldqiLoMRIkUurAq+PPEhUpduAiIgkVLMDy1qGCtDY3RmsRH07SxSb3yzfW7OW9y75u5uS/v3WBnc+fnzN8353wzc+a9jPovHHtzv4foRH5S59z79LsZluH7xev7vJOjf6otDesVvo8/dzR2+a8jf6nLV83qNu5+4khBfdlXOePBIGwAPPPInd59u5rVx5//oPA1J2Ar/+SVB72pv/9Rx0+cDq1fzknLvjKfvvaoXnmEizMXVH5qOiivq6nW8dGRMbWybrVCemXVqqAc8kivWrlMawVWVgYz78LFy7oPBPTTULdW7X7yrYpqQubDvvs9OWkMjhNHHBPkRE2AKEtQWI76aPNi/nwAHNuSAEF+b+cHlQXgUNc9WgN+n7ig/piYCsZ39doadc3audWW5ShDgPwdrU06Tu3gClNzNl+/qUBjuPrMNLmk3KaQAbmhU6inHnR+Yp4qF1Prd44e18UEBaCZYMpyxKXM4Y++r6wGgAMwaa4izQH2ieCi1uAAgkgNqF9TW3QxATj6rbgGgANo57Rbqq20fc4GhCjVmiBJcpTAsR55gV8tkz+vDvR+VlkN2N++TZtAlM0zP0qt2+66SZuPZHe59JJQpf1Dw1KhATbSgYkUU2vJASTNKEJFPoIk24pzgAsAlAk7J9i2MZIs2uC5ABpD3qj0idBqf7Zzgg2AN17e40lCJd8QVFt92wIlLbcCYDsn2EiMJ82wQxQGX/FdICmCtvoAkJwAQpX2j7qp5wDbBJd6udUElvoEbeMvAACEZatQrDyJPbv4G4r5E2z1o8adGgCS+hNs9VMLAA9a9CuYAx0/Nxn4IMLuJdua/duoGVzvGvo2iANKqQKcIujc1dEht0mMwXS+yIuaeYagLOot9K5RUgB4vo/j6eFlTJ425eXsq8FTuijqur17V3PB2snbrMtdI/AHlEIL6AGKAwDPCZhF2F1ib9ttwbFZXrp43cZdRF7Y0E6cu0bFt0GYQDGfIsDktVwuNflA+iEXctfQALyw73avafMG3T5tmLZ36pezOl+WI02S4aAgBxl8XzzytTOw0gSiSK7YdRv9J7lrZDh5NMKV4GTYOG9uYRNn5wBsISDw7BHlU8z97BO09FfI6zZMBGGhdw3nlSrVLmG2Ix0y5AE5wYf27NDXaC4QF0HvGPkJ7VVKcteY97rDAbr4+Sm7fFmV+unMb+qGjdeq2cszCmkE5lFu52OH5wFODuDETOcLzgEIdLmZ/sskp0+0WzAgvA5hArRvqjcngM6ZJx2hXI0oecgClCgAipGc9CFKM6H/0nYdt2nuPABYQa4eV1Wustmwi8aEeX9wECtGcjgHyHcIkw8SAyDfBdasrtYqjEC7m52dUcuX+yoNDVi/2nd3r6mt1pqCcpkmGUGO9WRd5EEjCEaYU1ZywGL7CwreBjEwTur4tydVy82bCs7hQ98NB3mQPTc5HUzSrLvjli0aGEwYoKDuzltv1IABvEr7AqnBGXkF5lEWhVA9PntRWOZJWZTHret6V7DZcNLy2Ntgd3e3NzU1pWpq/PdBxBEaGxvVyMiIjqOsrq5O5fP5QMZVvqenJ/aYkoAQu7Ouri6vtbVVVVf7T+dmGB8fD8AZHR3VoMSR7+zsjD2msgPQ3NysVqxYofu9dOmSjldV+USJAHCmp6fV4OCgTseR7+3tTTcAMIGWlhY9QQaYAQGRqzE2NqbNII586jWgr6/Pa2ryvTCTk/4pDaG2dv5r8NDQkOaCOPKp5wBowNatWwtUfmZmJkgjTtMYHh7W8TjyqQdAkiA1wFx9mgc5gCToIr8kOACkxiBXX5Ig4jABkqCrfOo1ABywbt06yXWRcZAgQhz51JMgdwGoM1Sf+z53Am6H2AqpAdgFXOVTrwHkAKwsCU/uArB/5udyOa0B4ABX+SXBAfIojG0OAfs9j8XFjsI2+bJrQDab1e+BcjWlUfOkZ+73UP36+nqt2uYpUNaXZwWZjzoSSPaPfJiPuWMgzbHIXae9vT3RyTFRZScmTLnQFQBSvkCLPrxAA+ibk45PxuntoUfWHFWcp7BFn1HMDq4AwD9K0Bk68OUJhV9/Isg4ff02ubT4+lwVIXCKUt2PffOjdl4iMB5mFlFySR8qXAdeKrkru0AYkngwNf88EefFt1SrU452QjWAvIABwObD/iVWjsGVo49QAOTvezEIl5+alGOwi9FHKADyXyTsdKmRmytYoQBIDuDD5P+KA1zRK5Ncol+v2sZIDWAnSMsOF5JWqr7bb2O8J6Pj5pejcinHeOq71cjbPbpW48NzcabxRTnKGJdlZh7bQr40AQyaE174d0O/p84enGuXaZkfFo8qV8rLvafU9qf6g8XMvXowNM38qC8bkPVLD0BD1lOjHXPtIs3AfCnDeFieX89TDVk18HyHanspqzMQRzDTzAsrhyzbkOVRK80hxzeBjQOeOtM2BwDTMt+MszfWK2zDUxsHtET2QJvqODQQfFnNzHdN0wTCbD4MGPZHUzHTc/nX5fw2T2/PKMTNL2u6lCvl9T/rVzj4vu9k7X9ge6BUyEO62Jd1wur/C4S4j3y2CFhdAAAAAElFTkSuQmCC" + }, + { + "path": "", + "name": "iron_layer_1.png", + "folder": "", + "namespace": "", + "id": "1", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "240844f3-f615-fe51-0ada-d208791fbdc0", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAA6VJREFUaEPlmb9qMkEUxVcIKCikUBCSTot0yROYwkqf2FQK8Qm0U0g6hYAWAYUEhP34DZzlOqzORqNu/KZJZufuOPfMvef+2UIUGK+vr/F6vXZS5XI5kdazbrdbCO2R5/Xg4QGg1Wo5Hebzufu7Wq3c34+Pj+j5+Tm4x1UAMBgMomq1muhSKpX+DwB6vV78+PgYLRaLaLlcJgAAxufn5/VbgA/A/f19AsL7+3t09RwAAI1GI/r6+nIWIAAqlUr09vZ2/RYACdbrdXfrgIDvozxjPB5fvwUAAMruCoV/3gV2KYjCNu7L8XcBIdm0dRsG7b78f2kAC4rzxHjFd5k7LM8hO51ONJ1OnQtowAff398OpNvbW+caGrjI3d1dNBwO3SObRxBNJJuHPKKQFuYIcbVazfm4MkCAgAxns1miuG7bD5P2fQACAO2lUAqZ5gYAbksHu7m5cUpbU1a2F8dxTELUbrdd9gd4unW5wGazcY8AQZkjAE0mk62EUOu5cAGr8L4D9ft9p7AASEtxBYrlBAFsM0ksLA9h9OR5vIopQLAhFOVzQYLnKlSwDGsVlzZ96X1yCzgXwIf+ThCA0WgUiyDx4aenp+A7hx4my3vKW8Rbx1pSUJnJZBKrDsgLAIRVIsxvkGgQgNlsFitBAohLW4DyFsIqIfvYhkwQAJsqQ2LH/mAWM98nY3MP5E7uAqED76olfKCs7/qJFvN98n79oOw0bZ+f1hpBC8gCAD5JrUBqy2g2m1v+qZ6C1tPkSZVtmKT+eHl5cc+sz2t/v9aAE7SH5LOU60cDYGsJWzzpQBxYxRQAcEOqHZBn6MDiGoopiib2AAAAtXPS7YeHh0RhijGG5JkTubJwxNEAYNr+AXTDHAplpBgtNFWPOrAFSNamqIMCAMZtMwSwPy8Wi6728NcBJMQRvwKAOkbWBbgxO9RF5llIXq03WQygqTiz/r+PA7J+tzgaAFxAxY5ujLI5bUghK28B8d85R7ns+gE/YVXdhiUsNU1lgpqnWYD6Cpi5b7LI26wziwmHSDq0vtURUoUGaSnTsiSVxrLyab9DxHvq/KiT5JMUHSWGtRjJnuvDiwMAn9SNyCSZ68B23b9BIWzjLyxta3/drJot8l1ZkUj0Eh9enAvAorSoLEsrjIiFdSPMJb/LR7WnQJB17GLktGTqXL2CxAL0oYPbIe4SukRamKhdxyKY70s0fjtlDfnyoevOAtJa2jbsiCTTQtGla4NDFdd7/wAIfE5OhlGtmAAAAABJRU5ErkJggg==" + }, + { + "path": "", + "name": "iron_layer_2.png", + "folder": "", + "namespace": "", + "id": "2", + "group": "", + "width": 64, + "height": 32, + "uv_width": 64, + "uv_height": 64, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "34528a28-3a31-8b47-65bb-ef2b60b02401", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAYAAACinX6EAAAAAXNSR0IArs4c6QAAAZZJREFUaEPtVruqhDAQjZ1gYSlY+gH+gfZ+sb2fYWEpCDaCgt1eTrhHhlldLsgVNkkajZldmJPzmMh4viLP+zcBgMAAzxEIEvCcAMEEgwSCBDxHIEjAcwKEFAgSCBJQCHRd98KnbdtMkiSmrmunWfLWHACoqsr0fW+GYTBN0/gFQNu2r6IobPNYXgIgVeE8ANQ8dY8ntC994OrMBYZEAACGl+e5GcfRNp+mqQVA75dlsd9xjjMnAIDm0QgAYINZltnmpmmyaQBPwKIvYL/vu63/9pSwDOCNoyGy4YoBEhAA5AQAvHHcMCVA+pPycRxbRnCPpxMMkBJg07xlSkD6g/QAFwalQwK6eeqdjAADyBBpmt8ek28eoCMPzWqT1CnxKSZ1pHLE5v/yXMYuz55ImSMGz7L/L42hhqMzPQJ7mui6rvadqSLrySjW6988YbK353w5OgNE3F5Zlmae5wMEMogyg+lKOV3VP2GytwE4G6TYIG8eJsrU0JKSfqLrAdh/e8xtAJgiB89/hyrdsDzX72cAoeaJlPkBm2JOMHlI974AAAAASUVORK5CYII=" + }, + { + "path": "", + "name": "transparent.png", + "folder": "", + "namespace": "", + "id": "3", + "group": "", + "width": 16, + "height": 16, + "uv_width": 16, + "uv_height": 16, + "particle": false, + "use_as_default": false, + "layers_enabled": false, + "sync_to_project": "", + "render_mode": "default", + "render_sides": "auto", + "pbr_channel": "color", + "frame_time": 1, + "frame_order_type": "loop", + "frame_order": "", + "frame_interpolate": false, + "visible": true, + "internal": true, + "saved": false, + "uuid": "797174ae-5c58-4a83-a630-eefd51007c80", + "source": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABgWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIA0EURY+JokjEwhQiFltEK21UxFKiKIKBkETwV7i7MVHIrmE3YmMp2AYs/DRGLWystbWwFQTBD4i1hZWijcj6ZhNIEOPAMIc7cy9v3oNAKWdabuMoWHbBSUxGtdm5ea35hSaCQIAR3XTzseREirrr844Gdd72q6z67/5cbell14QGTXjUzDsF4SXh4Y1CXvGecNhc0dPCZ8J9jhQo/KB0o8yvirM+B1Rm2EklxoTDwlq2ho0aNlccS3hIOJK2bMkPzJY5rXhTsZVbNyt1qh+Glu2ZpNJldzPJFDHiaBiss0qOAv1y2qK4JOQ+Wsff5fvj4jLEtYopjnHWsNB9P2oGv3vrZgYHykmhKDQ9e957DzTvwHfR876OPO/7GIJPcGlX/WslGPkQvVjVIofQvgXnV1XN2IWLbeh8zOuO7kv+/DMZeDuVMc1Bxw20LpT7Vrnn5B5S0qvpa9g/gN6sZC/W+XdLbd/+fVPp3w9iNHKgdXImZAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+gFAwAFKkpaLkIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAEklEQVQ4y2NgGAWjYBSMAggAAAQQAAGFP6pyAAAAAElFTkSuQmCC" + } + ], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "aac00620-0822-f892-856c-635a4439bbd4", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [ + { + "name": "no_armor", + "display_name": "no_armor", + "uuid": "5417306e-2c69-3f36-1e3c-edd904034a36", + "texture_map": { + "240844f3-f615-fe51-0ada-d208791fbdc0": "797174ae-5c58-4a83-a630-eefd51007c80", + "34528a28-3a31-8b47-65bb-ef2b60b02401": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [] + }, + { + "name": "no_baseplate", + "display_name": "no_baseplate", + "uuid": "bf2a6915-ec63-7ccf-3844-ea8be00b97e6", + "texture_map": { + "eb6c49b8-8e8e-a039-9a6f-ef6a06bc7924": "797174ae-5c58-4a83-a630-eefd51007c80" + }, + "excluded_nodes": [ + "98795982-9c43-6e3e-eed9-2c776a957938", + "102db36a-eeda-0e6f-d5cc-c186ecda8165", + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824", + "98a28100-07e8-849a-38b7-f56abc2c2e56", + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0", + "6c53311f-ffc8-e567-ff1b-65840698e9ba", + "808e3c26-7285-af3f-a079-d8b899176dd3", + "b5b52d2c-097c-08df-6457-372fbae12445", + "215d1b02-0e64-a794-1b88-a9c5a6d7541c", + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69", + "60d50d30-a018-429d-ba78-c5fb5804dd8a" + ] + }, + { + "name": "colors", + "display_name": "colors", + "uuid": "49521f4b-b970-e4bd-b594-2e05d2d46027", + "texture_map": {}, + "excluded_nodes": [] + } + ] + }, + "animations": [ + { + "uuid": "baae8403-f2ab-6a7a-acb5-97d80bf8e31a", + "name": "wizard", + "loop": "loop", + "override": false, + "length": 2, + "snapping": 20, + "selected": false, + "saved": false, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [], + "animators": { + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "-22.5\n+ math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "df1fbae1-9b41-d0f4-5f6c-473441c294ff", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-90\n+ math.sin(q.life_time * 360) * 30", + "y": "22.5\n- math.cos(q.life_time * 360) * 30", + "z": "0" + } + ], + "uuid": "53b169b5-69f4-45a9-288a-de94ad2b3b90", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7de415a2-9fdd-e4b2-3455-c3bed72eb896": { + "name": "left_hand", + "type": "locator", + "keyframes": [ + { + "channel": "commands", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "commands": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 5 + } + ], + "uuid": "923a41b4-3218-552c-06cb-582ead7ce8e8", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "7747736f-85e2-338f-207e-53f8d3d2fa39": { + "name": "right_hand", + "type": "locator", + "keyframes": [ + { + "channel": "commands", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "0", + "commands": "particle minecraft:flame ^ ^ ^", + "execute_condition": "", + "repeat": true, + "repeat_frequency": 1 + } + ], + "uuid": "fe357d53-5802-876b-c5d0-9eb8a19f573b", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "3b35d1a9-befe-066e-60df-e1f3a5ec5a78": { + "name": "pig", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "16 - math.cos(q.life_time * 180) * 16", + "y": "16 + math.sin(q.life_time * 360) * 8", + "z": "-32" + } + ], + "uuid": "08083fab-35c8-d8f0-44b0-000f720cf533", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "commands", + "data_points": [ + { + "commands": "say hi", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "e8054bec-122c-6f34-6781-a4a58d46a5a6", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "commands", + "data_points": [ + { + "commands": "say bye", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "523673ac-dff1-1729-8064-4d0258e2de2a", + "time": 2, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + }, + { + "uuid": "bd510bbf-d10c-d05a-44cb-7f116627ad9f", + "name": "walk", + "loop": "loop", + "override": false, + "length": 1, + "snapping": 20, + "selected": true, + "saved": false, + "path": "", + "anim_time_update": "", + "blend_weight": "", + "start_delay": "", + "loop_delay": "0", + "excluded_nodes": [ + { + "name": "baseplate_pivot_a", + "value": "4f000ffc-11d6-3a4b-7c8d-6b5ba483228c" + } + ], + "animators": { + "5e08acae-d6ca-0dfd-9360-8cdb13c7a824": { + "name": "armor_stand_root", + "type": "bone", + "keyframes": [ + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "-1", + "z": "0" + } + ], + "uuid": "fe4ec2e7-5246-ebf1-b38a-521a5f3d9907", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "6c53311f-ffc8-e567-ff1b-65840698e9ba": { + "name": "body", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 - math.sin(q.life_time * v.walk_speed + 90) * 12.25\n", + "z": "0" + } + ], + "uuid": "6bbb9d3d-e9c7-4ee6-79fc-ca26899b722c", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed * 2) * 0.5", + "z": "0" + } + ], + "uuid": "15fe5d50-bd56-ade8-1de5-c1e234e7d019", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "808e3c26-7285-af3f-a079-d8b899176dd3": { + "name": "head", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "1 + math.sin(q.life_time * v.walk_speed + 90) * 12.25", + "z": "0" + } + ], + "uuid": "f62415e9-43b1-de7f-8c6a-ab713dafa65e", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "b5b52d2c-097c-08df-6457-372fbae12445": { + "name": "left_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 8", + "y": "0", + "z": "22.5" + } + ], + "uuid": "20a2dfee-e0e7-87aa-13cb-27861c08b166", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "2", + "y": "0", + "z": "-math.sin(q.life_time * v.walk_speed) * 2" + } + ], + "uuid": "2e24c9ea-de91-bd22-2d7c-bb95813a0658", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "215d1b02-0e64-a794-1b88-a9c5a6d7541c": { + "name": "right_arm", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "math.sin(q.life_time * v.walk_speed) * 12.25", + "z": "math.cos(q.life_time * v.walk_speed * 2) * 6 + 6" + } + ], + "uuid": "8eaf4e97-7b14-ac66-b575-4cc50963c99d", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "55b812fe-7e4c-b2a5-da5b-3a8c82f9fc69": { + "name": "left_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "-math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "32f56ab1-fa8f-fa32-0418-45fddbb261a1", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "-math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "95a4ed7e-6530-bf4a-398f-76fe64d52033", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "60d50d30-a018-429d-ba78-c5fb5804dd8a": { + "name": "right_leg", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "math.sin(q.life_time * v.walk_speed) * 22.5", + "y": "0", + "z": "0" + } + ], + "uuid": "151bdae6-6e4a-718c-3317-f5c8bf83d366", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + }, + { + "channel": "position", + "data_points": [ + { + "x": "0", + "y": "math.clamp(-math.cos(q.life_time * v.walk_speed) * 2, 0, 10)", + "z": "math.sin(q.life_time * v.walk_speed) * 1\n" + } + ], + "uuid": "4de6bc86-4ddf-3253-9e00-3d6eb70e0e50", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "49e67c2a-3d1d-792d-9c75-72ecbc0f92e0": { + "name": "body_waist_pivot", + "type": "bone", + "keyframes": [ + { + "channel": "rotation", + "data_points": [ + { + "x": "0", + "y": "0", + "z": "1 - math.sin(q.life_time * v.walk_speed - 90) * 3" + } + ], + "uuid": "d3d81801-bcd6-8e3c-8c8c-1e62874345de", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + }, + "effects": { + "name": "Effects", + "type": "effect", + "keyframes": [ + { + "channel": "variant", + "data_points": [ + { + "variant": "5417306e-2c69-3f36-1e3c-edd904034a36", + "execute_condition": "", + "repeat": false, + "repeat_frequency": 1 + } + ], + "uuid": "1baa095b-e941-62df-a215-698b50c93524", + "time": 0, + "color": -1, + "interpolation": "linear", + "easing": "linear", + "easingArgs": [] + } + ] + } + } + } + ], + "animation_controllers": [], + "animation_variable_placeholders": "v.walk_speed = 360 * 1;\nv.run_speed = 360 * 1.5;\nv.stickbug_speed = 360 * 1.25;\n" +} \ No newline at end of file diff --git a/test-packs/1.20.4/datapacks/animated_java/pack.mcmeta b/test-packs/1.20.4/datapacks/animated_java/pack.mcmeta new file mode 100644 index 00000000..0a063158 --- /dev/null +++ b/test-packs/1.20.4/datapacks/animated_java/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 26, + "description": "AJ Testing DP" + } +} \ No newline at end of file diff --git a/test-packs/1.20.4/datapacks/test-framework/mcb.config.js b/test-packs/1.20.4/datapacks/test-framework/mcb.config.js new file mode 100644 index 00000000..45bc0388 --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/mcb.config.js @@ -0,0 +1,8 @@ +module.exports = { + libDir: null, // default: "null", determine where mcb looks for libraries, default is the bundled install location + generatedDirName: 'zzz', // default: "zzz", the name of the directory where mcb will put generated files + internalScoreboardName: 'aj.i', // default: "mcb.internal", the name of the internal scoreboard + header: null, // default: "#This file was generated by mcb\n", the header to put at the top of supported generated files + ioThreadCount: 1, // default: 1, the number of threads to use for IO operations, 1 is syncronous. + setup: null, // default: null, a function that will be called to allow binding to mcb events. +} diff --git a/test-packs/1.20.4/datapacks/test-framework/pack.mcmeta b/test-packs/1.20.4/datapacks/test-framework/pack.mcmeta new file mode 100644 index 00000000..618a6a2f --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack":{ + "pack_format": 74, + "description": ". Made with MC-Build!" + } +} \ No newline at end of file diff --git a/test-packs/1.20.4/datapacks/test-framework/package.json b/test-packs/1.20.4/datapacks/test-framework/package.json new file mode 100644 index 00000000..a0df0c86 --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/test-packs/1.20.4/datapacks/test-framework/src/test.mcb b/test-packs/1.20.4/datapacks/test-framework/src/test.mcb new file mode 100644 index 00000000..57cbd78c --- /dev/null +++ b/test-packs/1.20.4/datapacks/test-framework/src/test.mcb @@ -0,0 +1,9 @@ +function on_load minecraft:load { + tellraw @a {text:'Test Framework loaded!', color:green} +} + +function summon { + #ARGS: {args:{...}} + function animated_java:armor_stand/remove/all + $execute positioned 0 -63 0 rotated 0 0 run function animated_java:armor_stand/summon {args:$(args)} +} diff --git a/test-packs/1.20.4/resources/pack.mcmeta b/test-packs/1.20.4/resources/pack.mcmeta new file mode 100644 index 00000000..2bd10493 --- /dev/null +++ b/test-packs/1.20.4/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "pack_format": 22, + "description": "AJ Testing RP" + } +} \ No newline at end of file From 308811b68685ad1890de08caf6fb43179ea4e375 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 15 May 2025 11:26:41 -0400 Subject: [PATCH 007/182] =?UTF-8?q?=E2=9C=A8=20Packager=20plugin=20improve?= =?UTF-8?q?ments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/plugins/packagerPlugin.ts | 138 +++++++-- .../discord_release_notes_template | 16 +- .../github_release_notes_template | 8 +- tsconfig.json | 1 + yarn.lock | 267 ++++++++++++++++++ 5 files changed, 392 insertions(+), 38 deletions(-) diff --git a/tools/plugins/packagerPlugin.ts b/tools/plugins/packagerPlugin.ts index e6922460..39a4f1eb 100644 --- a/tools/plugins/packagerPlugin.ts +++ b/tools/plugins/packagerPlugin.ts @@ -1,10 +1,13 @@ import { Plugin } from 'esbuild' import * as fs from 'fs' +import { readFileSync, writeFileSync } from 'fs' +import { Octokit } from 'octokit' import * as pathjs from 'path' +import * as prettier from 'prettier' import * as c from 'svelte/compiler' -import { readFileSync, writeFileSync } from 'fs' import * as svelteInternal from 'svelte/internal' -import * as prettier from 'prettier' + +const octokit = new Octokit({}) const PACKAGE = JSON.parse(fs.readFileSync('./package.json', 'utf-8')) const PLUGIN_PACKAGE_PATH = './src/pluginPackage/' @@ -16,16 +19,30 @@ const PLUGIN_REPO_PATH = 'D:/github-repos/snavesutit/blockbench-plugins/plugins/ const PLUGIN_MANIFEST_PATH = 'D:/github-repos/snavesutit/blockbench-plugins/plugins.json' const CHANGELOG_PATH = './src/pluginPackage/changelog.json' const RELEASE_NOTES_TEMPLATES = './tools/plugins/releaseNoteTemplates/' +const URL_REGEX = + /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/gm function replaceTemplateVars(str: string, items: Record) { - return str.replace(/\{(.+?)\}/g, str => items[str.replace(/[\{\}]/g, '')] || str) + return str.replace(/\{(.+?)\}/g, str => items[str.replace(/[\{\}]/g, '')] ?? str) +} + +const VERSION_REGEX = /(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9]+))?/ + +function getVersionNumbers(version: string) { + const match = VERSION_REGEX.exec(version) + if (!match) return null + const major = parseInt(match[1]) + const minor = parseInt(match[2]) + const patch = parseInt(match[3]) + const preRelease = match[4] ?? null + return { major, minor, patch, preRelease } } function plugin(): Plugin { return { name: 'packagerPlugin', setup(build) { - build.onEnd(() => { + build.onEnd(async () => { console.log('📦 Packaging...') fs.rmSync(DIST_PACKAGE_PATH, { recursive: true, force: true }) fs.cpSync(PLUGIN_PACKAGE_PATH, DIST_PACKAGE_PATH, { recursive: true }) @@ -53,25 +70,100 @@ function plugin(): Plugin { fs.unlinkSync(pathjs.join(DIST_PACKAGE_PATH, 'about.svelte')) if (process.env.NODE_ENV === 'production') { - console.log('📝 Creating changelogs...') - const changelog = JSON.parse(fs.readFileSync(CHANGELOG_PATH, 'utf-8')) - for (const file of fs.readdirSync(RELEASE_NOTES_TEMPLATES)) { - let content = fs.readFileSync( - pathjs.join(RELEASE_NOTES_TEMPLATES, file), - 'utf-8' - ) - content = replaceTemplateVars(content, { - version: PACKAGE.version, - changes: changelog[PACKAGE.version].categories - .find(c => c.title === 'Changes') - ?.list.map(v => '- ' + v) - .join('\n'), - fixes: changelog[PACKAGE.version].categories - .find(c => c.title === 'Fixes') - ?.list.map(v => '- ' + v) - .join('\n'), - }) - fs.writeFileSync(pathjs.join(DIST_PATH, file), content) + try { + console.log('📝 Creating changelogs...') + const rawChangelog = fs.readFileSync(CHANGELOG_PATH, 'utf-8') + const changelog = JSON.parse(rawChangelog) + for (const file of fs.readdirSync(RELEASE_NOTES_TEMPLATES)) { + let content = fs.readFileSync( + pathjs.join(RELEASE_NOTES_TEMPLATES, file), + 'utf-8' + ) + let pings = '' + const version = getVersionNumbers(PACKAGE.version) + const latestRelease = getVersionNumbers( + ( + await octokit.request('GET /repos/{owner}/{repo}/releases', { + owner: 'animated-java', + repo: 'animated-java', + per_page: 1, + headers: { + accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + ).data[0].tag_name + ) + if (!latestRelease) { + throw new Error('No latest release found on github!') + } + if (version.major > latestRelease.major) { + pings += `@Major Release Ping` + } + if (version.minor > latestRelease.minor) { + pings += ` @Minor Release Ping` + } + if (version.patch > latestRelease.patch) { + pings += ` @Patch Release Ping` + } + if (latestRelease.preRelease) { + pings += ` @Pre-Release Ping` + } + if (rawChangelog.includes('[BREAKING]')) { + pings += ` @Breaking Changes Ping` + } + + const versionChangelog = changelog[PACKAGE.version] + if (!versionChangelog) { + throw new Error( + `No changelog found for version ${PACKAGE.version} in ${CHANGELOG_PATH}` + ) + } + + const changeList = versionChangelog.categories.find( + c => c.title === 'Changes' + ) + let changes = '' + if (changeList) { + changes = + '### Changes\n\n' + + changeList.list + .map(v => '- ' + v) + .join('\n') + .replace('[BREAKING]', '⚠️ **BREAKING CHANGE** — ') + } + const fixList = versionChangelog.categories.find( + c => c.title === 'Fixes' + ) + let fixes = '' + if (fixList) { + fixes = + '### Fixes\n\n' + + fixList.list + .map(v => '- ' + v) + .join('\n') + .replace('[BREAKING]', '⚠️ **BREAKING CHANGE** — ') + } + + content = replaceTemplateVars(content, { + version: PACKAGE.version, + changes, + fixes, + pings: pings.trim(), + }) + + if (content.includes('[[ESCAPE_URLS]]')) { + content = content + .replace('[[ESCAPE_URLS]]', '') + // @ts-expect-error + .replaceAll(URL_REGEX, (match: string) => '<' + match + '>') + } + + fs.writeFileSync(pathjs.join(DIST_PATH, file), content) + } + } catch (e) { + console.error('Error creating changelogs:', e) + throw e } if (fs.existsSync(PLUGIN_REPO_PATH)) { diff --git a/tools/plugins/releaseNoteTemplates/discord_release_notes_template b/tools/plugins/releaseNoteTemplates/discord_release_notes_template index e02fda8d..5268f35b 100644 --- a/tools/plugins/releaseNoteTemplates/discord_release_notes_template +++ b/tools/plugins/releaseNoteTemplates/discord_release_notes_template @@ -1,18 +1,16 @@ -# :AnimatedJava: Animated Java Release {version} +[[ESCAPE_URLS]] -### Changes +# :AnimatedJava: Animated Java Release v{version} {changes} -### Fixes - {fixes} ## How to Install -For now, you can download and install the latest build of AJ [here]() or install it via this link: - -Once [this PR]() has been merged, you can install the latest release of AJ from the Blockbench plugin list. -[Follow this tutorial]() if you don't know how to install plugins in Blockbench. +For now, you can download and install the latest build of AJ [here](https://builds.animated-java.dev/latest) or install it via this link: +https://builds.animated-java.dev/latest/download/animated_java.js +Once [this PR](https://github.com/JannisX11/blockbench-plugins/pull/{pr_number}) has been merged, you can install the latest release of AJ from the Blockbench plugin list. +[Follow this tutorial](https://animated-java.github.io/docs/getting-started/installing-animated-java) if you don't know how to install plugins in Blockbench. -{pings} +-# {pings} diff --git a/tools/plugins/releaseNoteTemplates/github_release_notes_template b/tools/plugins/releaseNoteTemplates/github_release_notes_template index 387714a6..198dd399 100644 --- a/tools/plugins/releaseNoteTemplates/github_release_notes_template +++ b/tools/plugins/releaseNoteTemplates/github_release_notes_template @@ -1,16 +1,12 @@ -# Animated Java Release {version} - -### Changes +# Animated Java Release v{version} {changes} -### Fixes - {fixes} ## How to Install For now, you can download and install the latest build of AJ [here](https://builds.animated-java.dev/latest) or install it via this link: - +https://builds.animated-java.dev/latest/download/animated_java.js Once [this PR](https://github.com/JannisX11/blockbench-plugins/pull/{pr_number}) has been merged, you can install the latest release of AJ from the Blockbench plugin list. [Follow this tutorial](https://animated-java.github.io/docs/getting-started/installing-animated-java) if you don't know how to install plugins in Blockbench. diff --git a/tsconfig.json b/tsconfig.json index 4ea32954..49d4a2eb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "noImplicitAny": true, "module": "ES2022", "target": "ES2022", + "lib": ["ES2024"], "moduleResolution": "node", "strict": true, "types": ["node", "./types/blockbench-types", "svelte"], diff --git a/yarn.lock b/yarn.lock index 0a1dddaf..781f845b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,6 +907,222 @@ codejar "^3.5.0" svelte "^3.42.3" +"@octokit/app@^15.1.6": + version "15.1.6" + resolved "https://registry.yarnpkg.com/@octokit/app/-/app-15.1.6.tgz#055f8d59ead26e1bf0a0efe127ff838b49cef7a2" + integrity sha512-WELCamoCJo9SN0lf3SWZccf68CF0sBNPQuLYmZ/n87p5qvBJDe9aBtr5dHkh7T9nxWZ608pizwsUbypSzZAiUw== + dependencies: + "@octokit/auth-app" "^7.2.1" + "@octokit/auth-unauthenticated" "^6.1.3" + "@octokit/core" "^6.1.5" + "@octokit/oauth-app" "^7.1.6" + "@octokit/plugin-paginate-rest" "^12.0.0" + "@octokit/types" "^14.0.0" + "@octokit/webhooks" "^13.6.1" + +"@octokit/auth-app@^7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-7.2.1.tgz#ed3d3ae487c5b42db88b10e95175d1a8a8965cbf" + integrity sha512-4jaopCVOtWN0V8qCx/1s2pkRqC6tcvIQM3kFB99eIpsP53GfsoIKO08D94b83n/V3iGihHmxWR2lXzE0NicUGg== + dependencies: + "@octokit/auth-oauth-app" "^8.1.4" + "@octokit/auth-oauth-user" "^5.1.4" + "@octokit/request" "^9.2.3" + "@octokit/request-error" "^6.1.8" + "@octokit/types" "^14.0.0" + toad-cache "^3.7.0" + universal-github-app-jwt "^2.2.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-oauth-app@^8.1.3", "@octokit/auth-oauth-app@^8.1.4": + version "8.1.4" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-8.1.4.tgz#4a41ed59ae81c36215976e3523a671d5eacb6d52" + integrity sha512-71iBa5SflSXcclk/OL3lJzdt4iFs56OJdpBGEBl1wULp7C58uiswZLV6TdRaiAzHP1LT8ezpbHlKuxADb+4NkQ== + dependencies: + "@octokit/auth-oauth-device" "^7.1.5" + "@octokit/auth-oauth-user" "^5.1.4" + "@octokit/request" "^9.2.3" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-oauth-device@^7.1.5": + version "7.1.5" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-7.1.5.tgz#dd22ed25539c4dadd27bfa3afccd244434fb4c48" + integrity sha512-lR00+k7+N6xeECj0JuXeULQ2TSBB/zjTAmNF2+vyGPDEFx1dgk1hTDmL13MjbSmzusuAmuJD8Pu39rjp9jH6yw== + dependencies: + "@octokit/oauth-methods" "^5.1.5" + "@octokit/request" "^9.2.3" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-oauth-user@^5.1.3", "@octokit/auth-oauth-user@^5.1.4": + version "5.1.4" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-5.1.4.tgz#c8286e2812d0945e737563d94a1046b44078a06d" + integrity sha512-4tJRofMHm6ZCd3O2PVgboBbQ/lNtacREeaihet0+wCATZmvPK+jjg2K6NjBfY69An3yzQdmkcMeiaOOoxOPr7Q== + dependencies: + "@octokit/auth-oauth-device" "^7.1.5" + "@octokit/oauth-methods" "^5.1.5" + "@octokit/request" "^9.2.3" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/auth-token@^5.0.0": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-5.1.2.tgz#68a486714d7a7fd1df56cb9bc89a860a0de866de" + integrity sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw== + +"@octokit/auth-unauthenticated@^6.1.2", "@octokit/auth-unauthenticated@^6.1.3": + version "6.1.3" + resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-6.1.3.tgz#7f6eca87eb5cdfdccacd92399de6fd9607c61256" + integrity sha512-d5gWJla3WdSl1yjbfMpET+hUSFCE15qM0KVSB0H1shyuJihf/RL1KqWoZMIaonHvlNojkL9XtLFp8QeLe+1iwA== + dependencies: + "@octokit/request-error" "^6.1.8" + "@octokit/types" "^14.0.0" + +"@octokit/core@^6.1.4", "@octokit/core@^6.1.5": + version "6.1.5" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-6.1.5.tgz#c2842aae87c2c2130b7dd33e8caa0f642dde2c67" + integrity sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg== + dependencies: + "@octokit/auth-token" "^5.0.0" + "@octokit/graphql" "^8.2.2" + "@octokit/request" "^9.2.3" + "@octokit/request-error" "^6.1.8" + "@octokit/types" "^14.0.0" + before-after-hook "^3.0.2" + universal-user-agent "^7.0.0" + +"@octokit/endpoint@^10.1.4": + version "10.1.4" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-10.1.4.tgz#8783be38a32b95af8bcb6523af20ab4eed7a2adb" + integrity sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA== + dependencies: + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.2" + +"@octokit/graphql@^8.2.2": + version "8.2.2" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-8.2.2.tgz#3db48c4ffdf07f99600cee513baf45e73eced4d1" + integrity sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA== + dependencies: + "@octokit/request" "^9.2.3" + "@octokit/types" "^14.0.0" + universal-user-agent "^7.0.0" + +"@octokit/oauth-app@^7.1.6": + version "7.1.6" + resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-7.1.6.tgz#f55c7ab2381028a71564aa6a1baf3921c620c437" + integrity sha512-OMcMzY2WFARg80oJNFwWbY51TBUfLH4JGTy119cqiDawSFXSIBujxmpXiKbGWQlvfn0CxE6f7/+c6+Kr5hI2YA== + dependencies: + "@octokit/auth-oauth-app" "^8.1.3" + "@octokit/auth-oauth-user" "^5.1.3" + "@octokit/auth-unauthenticated" "^6.1.2" + "@octokit/core" "^6.1.4" + "@octokit/oauth-authorization-url" "^7.1.1" + "@octokit/oauth-methods" "^5.1.4" + "@types/aws-lambda" "^8.10.83" + universal-user-agent "^7.0.0" + +"@octokit/oauth-authorization-url@^7.0.0", "@octokit/oauth-authorization-url@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-7.1.1.tgz#0e17c2225eb66b58ec902d02b6f1315ffe9ff04b" + integrity sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA== + +"@octokit/oauth-methods@^5.1.4", "@octokit/oauth-methods@^5.1.5": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-5.1.5.tgz#647fcd135cedd2371452631ef131497e8037b008" + integrity sha512-Ev7K8bkYrYLhoOSZGVAGsLEscZQyq7XQONCBBAl2JdMg7IT3PQn/y8P0KjloPoYpI5UylqYrLeUcScaYWXwDvw== + dependencies: + "@octokit/oauth-authorization-url" "^7.0.0" + "@octokit/request" "^9.2.3" + "@octokit/request-error" "^6.1.8" + "@octokit/types" "^14.0.0" + +"@octokit/openapi-types@^25.0.0": + version "25.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-25.0.0.tgz#adeead36992abf966e89dcd53518d8b0dc910e0d" + integrity sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw== + +"@octokit/openapi-webhooks-types@11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-11.0.0.tgz#27b170627e48ff8a2f6fffb159263dc20804917c" + integrity sha512-ZBzCFj98v3SuRM7oBas6BHZMJRadlnDoeFfvm1olVxZnYeU6Vh97FhPxyS5aLh5pN51GYv2I51l/hVUAVkGBlA== + +"@octokit/plugin-paginate-graphql@^5.2.4": + version "5.2.4" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-5.2.4.tgz#b6afda7b3f24cb93d2ab822ec8eac664a5d325d0" + integrity sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA== + +"@octokit/plugin-paginate-rest@^12.0.0": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-12.0.0.tgz#4f3c1caefd6f85abb4e95c1c0a38af4568cb4dae" + integrity sha512-MPd6WK1VtZ52lFrgZ0R2FlaoiWllzgqFHaSZxvp72NmoDeZ0m8GeJdg4oB6ctqMTYyrnDYp592Xma21mrgiyDA== + dependencies: + "@octokit/types" "^14.0.0" + +"@octokit/plugin-rest-endpoint-methods@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-14.0.0.tgz#dc4c44b7ac5b1a69596b026ba6d796c5add1668a" + integrity sha512-iQt6ovem4b7zZYZQtdv+PwgbL5VPq37th1m2x2TdkgimIDJpsi2A6Q/OI/23i/hR6z5mL0EgisNR4dcbmckSZQ== + dependencies: + "@octokit/types" "^14.0.0" + +"@octokit/plugin-retry@^7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz#3ec7065ad451c7e6bd64c4fb16c98006a5ff2f66" + integrity sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A== + dependencies: + "@octokit/request-error" "^6.1.8" + "@octokit/types" "^14.0.0" + bottleneck "^2.15.3" + +"@octokit/plugin-throttling@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-10.0.0.tgz#3ca3c2d3b6e1deb263462f5d35ce6727527116ef" + integrity sha512-Kuq5/qs0DVYTHZuBAzCZStCzo2nKvVRo/TDNhCcpC2TKiOGz/DisXMCvjt3/b5kr6SCI1Y8eeeJTHBxxpFvZEg== + dependencies: + "@octokit/types" "^14.0.0" + bottleneck "^2.15.3" + +"@octokit/request-error@^6.1.7", "@octokit/request-error@^6.1.8": + version "6.1.8" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-6.1.8.tgz#3c7ce1ca6721eabd43dbddc76b44860de1fdea75" + integrity sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ== + dependencies: + "@octokit/types" "^14.0.0" + +"@octokit/request@^9.2.3": + version "9.2.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-9.2.3.tgz#00d023ad690903d952e4dd31e3f5804ef98fcd24" + integrity sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w== + dependencies: + "@octokit/endpoint" "^10.1.4" + "@octokit/request-error" "^6.1.8" + "@octokit/types" "^14.0.0" + fast-content-type-parse "^2.0.0" + universal-user-agent "^7.0.2" + +"@octokit/types@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-14.0.0.tgz#bbd1d31e2269940789ef143b1c37918aae09adc4" + integrity sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA== + dependencies: + "@octokit/openapi-types" "^25.0.0" + +"@octokit/webhooks-methods@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-5.1.1.tgz#192f11a1f115702833f033293e2fef8f69f612e4" + integrity sha512-NGlEHZDseJTCj8TMMFehzwa9g7On4KJMPVHDSrHxCQumL6uSQR8wIkP/qesv52fXqV1BPf4pTxwtS31ldAt9Xg== + +"@octokit/webhooks@^13.6.1": + version "13.9.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-13.9.0.tgz#1dd2f1c6b4616bd91735b9aa5a25aa2cdf5fff5c" + integrity sha512-5Kva+/Gi7c+39d0/0MM/v/5RCZuwqm75fUD+t7Es3Iz/adui54GnjfNmJpkkPkXGC+5IWnEvgqwY6gstK/JlUQ== + dependencies: + "@octokit/openapi-webhooks-types" "11.0.0" + "@octokit/request-error" "^6.1.7" + "@octokit/webhooks-methods" "^5.1.1" + "@polka/url@^1.0.0-next.20": version "1.0.0-next.25" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817" @@ -1077,6 +1293,11 @@ dependencies: defer-to-connect "^2.0.0" +"@types/aws-lambda@^8.10.83": + version "8.10.149" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.149.tgz#77c7bde809425546d03626e51bab8181bc5d24c9" + integrity sha512-NXSZIhfJjnXqJgtS7IwutqIF/SOy1Wz5Px4gUY1RWITp3AYTyuJS4xaXr/bIJY1v15XMzrJ5soGnPM+7uigZjA== + "@types/cacheable-request@^6.0.1": version "6.0.3" resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" @@ -1555,6 +1776,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +before-after-hook@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-3.0.2.tgz#d5665a5fa8b62294a5aa0a499f933f4a1016195d" + integrity sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A== + bl@^1.0.0: version "1.2.3" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" @@ -1586,6 +1812,11 @@ boolean@^3.0.1: resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== +bottleneck@^2.15.3: + version "2.19.5" + resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" + integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2352,6 +2583,11 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" +fast-content-type-parse@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz#c236124534ee2cb427c8d8e5ba35a4856947847b" + integrity sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3306,6 +3542,22 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +octokit@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/octokit/-/octokit-4.1.3.tgz#8855681813a83b69a65a4bec8a77075d241f87df" + integrity sha512-PP+EL8h4xPCE9NBo6jXq6I2/EiTXsn1cg9F0IZehHBv/qhuQpyGMFElEB17miWKciuT6vRHiFFiG9+FoXOmg6A== + dependencies: + "@octokit/app" "^15.1.6" + "@octokit/core" "^6.1.5" + "@octokit/oauth-app" "^7.1.6" + "@octokit/plugin-paginate-graphql" "^5.2.4" + "@octokit/plugin-paginate-rest" "^12.0.0" + "@octokit/plugin-rest-endpoint-methods" "^14.0.0" + "@octokit/plugin-retry" "^7.2.1" + "@octokit/plugin-throttling" "^10.0.0" + "@octokit/request-error" "^6.1.8" + "@octokit/types" "^14.0.0" + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4128,6 +4380,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toad-cache@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/toad-cache/-/toad-cache-3.7.0.tgz#b9b63304ea7c45ec34d91f1d2fa513517025c441" + integrity sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw== + totalist@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" @@ -4197,6 +4454,16 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +universal-github-app-jwt@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz#38537e5a7d154085a35f97601a5e30e9e17717df" + integrity sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw== + +universal-user-agent@^7.0.0, universal-user-agent@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz#c05870a58125a2dc00431f2df815a77fe69736be" + integrity sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" From f224b8a33cab772398c919c8b4ce0b76b7404859 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 15 May 2025 11:27:09 -0400 Subject: [PATCH 008/182] =?UTF-8?q?=F0=9F=90=9B=20Fix=20cameras=20in=20ver?= =?UTF-8?q?sions=20below=201.21.5=20using=20outdated=20tech?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/systems/datapackCompiler/1.20.4/animation.mcb | 7 ++++--- src/systems/datapackCompiler/1.20.5/animation.mcb | 7 ++++--- src/systems/datapackCompiler/1.21.2/animation.mcb | 7 ++++--- src/systems/datapackCompiler/1.21.4/animation.mcb | 7 ++++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/systems/datapackCompiler/1.20.4/animation.mcb b/src/systems/datapackCompiler/1.20.4/animation.mcb index f32e3b39..a22298b5 100644 --- a/src/systems/datapackCompiler/1.20.4/animation.mcb +++ b/src/systems/datapackCompiler/1.20.4/animation.mcb @@ -548,7 +548,7 @@ dir <%export_namespace%> { type=<%locator.config.entity_type%>, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ + distance=..<%Math.ceil(locator.max_distance)%> \ ] \ run block as_locator/<%locator.path_name%> { # run block ../as_locator/<%locator.path_name%> { @@ -582,9 +582,10 @@ dir <%export_namespace%> { type=minecraft:item_display, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ + distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.path_name%> { + # run block ../as_camera/<%camera.path_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ diff --git a/src/systems/datapackCompiler/1.20.5/animation.mcb b/src/systems/datapackCompiler/1.20.5/animation.mcb index 737c565c..987d6c35 100644 --- a/src/systems/datapackCompiler/1.20.5/animation.mcb +++ b/src/systems/datapackCompiler/1.20.5/animation.mcb @@ -548,7 +548,7 @@ dir <%export_namespace%> { type=<%locator.config.entity_type%>, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ + distance=..<%Math.ceil(locator.max_distance)%> \ ] \ run block as_locator/<%locator.path_name%> { # run block ../as_locator/<%locator.path_name%> { @@ -582,9 +582,10 @@ dir <%export_namespace%> { type=minecraft:item_display, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ + distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.path_name%> { + # run block ../as_camera/<%camera.path_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ diff --git a/src/systems/datapackCompiler/1.21.2/animation.mcb b/src/systems/datapackCompiler/1.21.2/animation.mcb index 694adb9f..1e89a4fc 100644 --- a/src/systems/datapackCompiler/1.21.2/animation.mcb +++ b/src/systems/datapackCompiler/1.21.2/animation.mcb @@ -548,7 +548,7 @@ dir <%export_namespace%> { type=<%locator.config.entity_type%>, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ + distance=..<%Math.ceil(locator.max_distance)%> \ ] \ run block as_locator/<%locator.path_name%> { # run block ../as_locator/<%locator.path_name%> { @@ -582,9 +582,10 @@ dir <%export_namespace%> { type=minecraft:item_display, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ + distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.path_name%> { + # run block ../as_camera/<%camera.path_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ diff --git a/src/systems/datapackCompiler/1.21.4/animation.mcb b/src/systems/datapackCompiler/1.21.4/animation.mcb index 4ad4b9b6..81b14b60 100644 --- a/src/systems/datapackCompiler/1.21.4/animation.mcb +++ b/src/systems/datapackCompiler/1.21.4/animation.mcb @@ -548,7 +548,7 @@ dir <%export_namespace%> { type=<%locator.config.entity_type%>, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ - distance=..<%locator.max_distance + 1%> \ + distance=..<%Math.ceil(locator.max_distance)%> \ ] \ run block as_locator/<%locator.path_name%> { # run block ../as_locator/<%locator.path_name%> { @@ -582,9 +582,10 @@ dir <%export_namespace%> { type=minecraft:item_display, \ tag=<%TAGS.NEW()%>, \ tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ - distance=..<%camera.max_distance + 1%> \ + distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.path_name%> { + # run block ../as_camera/<%camera.path_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ From fda43bcf6cc432aa7a0ac406b559860b0e466da7 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 15 May 2025 11:28:26 -0400 Subject: [PATCH 009/182] =?UTF-8?q?=F0=9F=A9=B9=20Svelte=20doesn't=20like?= =?UTF-8?q?=20ES2024?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 49d4a2eb..4a0b2a67 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "noImplicitAny": true, "module": "ES2022", "target": "ES2022", - "lib": ["ES2024"], + "lib": ["ES2022"], "moduleResolution": "node", "strict": true, "types": ["node", "./types/blockbench-types", "svelte"], From 0ef9badfc566af14b007c370947b7763708eb374 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 25 Sep 2025 12:04:07 -0400 Subject: [PATCH 010/182] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20minor=20issues=20w?= =?UTF-8?q?ith=20tsconfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/global.d.ts | 2 +- src/systems/minecraft/assetManager.ts | 8 +- tools/esbuild.ts | 3 + tools/plugins/bufferPatchFunction.ts | 22 ++ tsconfig.json | 15 +- yarn.lock | 359 +++++++++++++------------- 7 files changed, 228 insertions(+), 182 deletions(-) create mode 100644 tools/plugins/bufferPatchFunction.ts diff --git a/package.json b/package.json index 08e53c6e..0511ff21 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "eslint": "^8.35.0", "firebase": "^9.19.0", "js-yaml": "^4.1.0", + "octokit": "^5.0.3", "prettier": "^2.5.1", "svelte": "^3.55.1", "svelte-awesome-color-picker": "^3.0.0-beta.7", diff --git a/src/global.d.ts b/src/global.d.ts index e3c0641c..5738cca2 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,4 +1,4 @@ -/// +/// declare module '*.png' { const value: string diff --git a/src/systems/minecraft/assetManager.ts b/src/systems/minecraft/assetManager.ts index 3526dc27..80a7ba6c 100644 --- a/src/systems/minecraft/assetManager.ts +++ b/src/systems/minecraft/assetManager.ts @@ -1,16 +1,16 @@ import { PACKAGE } from '../../constants' -import { getCurrentVersion, getLatestVersion } from './versionManager' import { events } from '../../util/events' +import { getCurrentVersion, getLatestVersion } from './versionManager' -import index from '../../assets/vanillaAssetOverrides/index.json' -import { Unzipped } from 'fflate' -import { unzip } from '../util' import download from 'download' +import type { Unzipped } from 'fflate' +import index from '../../assets/vanillaAssetOverrides/index.json' import { showOfflineError, updateLoadingProgress, updateLoadingProgressLabel, } from '../../interface/popup/animatedJavaLoading' +import { unzip } from '../util' const ASSET_OVERRIDES = index as unknown as Record async function downloadJar(url: string, savePath: string) { diff --git a/tools/esbuild.ts b/tools/esbuild.ts index 34d7728d..374b1437 100644 --- a/tools/esbuild.ts +++ b/tools/esbuild.ts @@ -16,6 +16,7 @@ import { isAbsolute, join } from 'path' import { TextDecoder } from 'util' import svelteConfig from '../svelte.config.js' import assetOverridePlugin from './plugins/assetOverridePlugin' +import bufferPatchPlugin from './plugins/bufferPatchFunction.js' import mcbCompressionPlugin from './plugins/mcbCompressionPlugin' import packagerPlugin from './plugins/packagerPlugin' import sveltePlugin from './plugins/sveltePlugin' @@ -208,6 +209,7 @@ const devConfig: esbuild.BuildOptions = { plugins: [ // @ts-ignore ImportGlobPlugin.default(), + bufferPatchPlugin(), inlineImage({ limit: -1, }), @@ -236,6 +238,7 @@ const prodConfig: esbuild.BuildOptions = { plugins: [ // @ts-ignore ImportGlobPlugin.default(), + bufferPatchPlugin(), inlineImage({ limit: -1, }), diff --git a/tools/plugins/bufferPatchFunction.ts b/tools/plugins/bufferPatchFunction.ts new file mode 100644 index 00000000..22b4017f --- /dev/null +++ b/tools/plugins/bufferPatchFunction.ts @@ -0,0 +1,22 @@ +import { type Plugin } from 'esbuild' + +export default function plugin(): Plugin { + return { + name: 'bufferPatchPlugin', + setup(build) { + build.onResolve({ filter: /^buffer$/ }, args => { + return { path: args.path, namespace: 'buffer-namespace' } + }) + + build.onLoad( + { filter: /^buffer$/, namespace: 'buffer-namespace' }, + async ({ path }) => { + return { + contents: `export const Buffer = globalThis.Buffer;`, + loader: 'js', + } + } + ) + }, + } +} diff --git a/tsconfig.json b/tsconfig.json index 4a0b2a67..1fbdab25 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,23 @@ { - "include": ["./src/**/*"], + "include": ["src/**/*", "tools/**/*"], + "exclude": ["**/node_modules/**"], "compilerOptions": { "outDir": "./dist/", "noImplicitAny": true, "module": "ES2022", "target": "ES2022", - "lib": ["ES2022"], "moduleResolution": "node", "strict": true, - "types": ["node", "./types/blockbench-types", "svelte"], + "types": ["node"], "resolveJsonModule": true, "allowSyntheticDefaultImports": true, - "allowUmdGlobalAccess": true + "allowUmdGlobalAccess": true, + "allowUnreachableCode": true, + "esModuleInterop": true, + "noEmit": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "alwaysStrict": true, + "baseUrl": "." } } diff --git a/yarn.lock b/yarn.lock index 781f845b..45ce48bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -907,221 +907,233 @@ codejar "^3.5.0" svelte "^3.42.3" -"@octokit/app@^15.1.6": - version "15.1.6" - resolved "https://registry.yarnpkg.com/@octokit/app/-/app-15.1.6.tgz#055f8d59ead26e1bf0a0efe127ff838b49cef7a2" - integrity sha512-WELCamoCJo9SN0lf3SWZccf68CF0sBNPQuLYmZ/n87p5qvBJDe9aBtr5dHkh7T9nxWZ608pizwsUbypSzZAiUw== - dependencies: - "@octokit/auth-app" "^7.2.1" - "@octokit/auth-unauthenticated" "^6.1.3" - "@octokit/core" "^6.1.5" - "@octokit/oauth-app" "^7.1.6" - "@octokit/plugin-paginate-rest" "^12.0.0" +"@octokit/app@^16.0.1": + version "16.1.0" + resolved "https://registry.yarnpkg.com/@octokit/app/-/app-16.1.0.tgz#e0d7472fc2e7ae7b0ab3f1e4cca8c2aba5f052ad" + integrity sha512-OdKHnm0CYLk8Setr47CATT4YnRTvWkpTYvE+B/l2B0mjszlfOIit3wqPHVslD2jfc1bD4UbO7Mzh6gjCuMZKsA== + dependencies: + "@octokit/auth-app" "^8.1.0" + "@octokit/auth-unauthenticated" "^7.0.1" + "@octokit/core" "^7.0.2" + "@octokit/oauth-app" "^8.0.1" + "@octokit/plugin-paginate-rest" "^13.0.0" "@octokit/types" "^14.0.0" - "@octokit/webhooks" "^13.6.1" + "@octokit/webhooks" "^14.0.0" -"@octokit/auth-app@^7.2.1": - version "7.2.1" - resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-7.2.1.tgz#ed3d3ae487c5b42db88b10e95175d1a8a8965cbf" - integrity sha512-4jaopCVOtWN0V8qCx/1s2pkRqC6tcvIQM3kFB99eIpsP53GfsoIKO08D94b83n/V3iGihHmxWR2lXzE0NicUGg== +"@octokit/auth-app@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-app/-/auth-app-8.1.0.tgz#19dacfe49ea62a1f40189ac222925e8a757b6a7c" + integrity sha512-6bWhyvLXqCSfHiqlwzn9pScLZ+Qnvh/681GR/UEEPCMIVwfpRDBw0cCzy3/t2Dq8B7W2X/8pBgmw6MOiyE0DXQ== dependencies: - "@octokit/auth-oauth-app" "^8.1.4" - "@octokit/auth-oauth-user" "^5.1.4" - "@octokit/request" "^9.2.3" - "@octokit/request-error" "^6.1.8" + "@octokit/auth-oauth-app" "^9.0.1" + "@octokit/auth-oauth-user" "^6.0.0" + "@octokit/request" "^10.0.2" + "@octokit/request-error" "^7.0.0" "@octokit/types" "^14.0.0" toad-cache "^3.7.0" universal-github-app-jwt "^2.2.0" universal-user-agent "^7.0.0" -"@octokit/auth-oauth-app@^8.1.3", "@octokit/auth-oauth-app@^8.1.4": - version "8.1.4" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-8.1.4.tgz#4a41ed59ae81c36215976e3523a671d5eacb6d52" - integrity sha512-71iBa5SflSXcclk/OL3lJzdt4iFs56OJdpBGEBl1wULp7C58uiswZLV6TdRaiAzHP1LT8ezpbHlKuxADb+4NkQ== +"@octokit/auth-oauth-app@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.1.tgz#d8d2e950c95e9fcbe6f2fb98d4539ee8c9871766" + integrity sha512-TthWzYxuHKLAbmxdFZwFlmwVyvynpyPmjwc+2/cI3cvbT7mHtsAW9b1LvQaNnAuWL+pFnqtxdmrU8QpF633i1g== dependencies: - "@octokit/auth-oauth-device" "^7.1.5" - "@octokit/auth-oauth-user" "^5.1.4" - "@octokit/request" "^9.2.3" + "@octokit/auth-oauth-device" "^8.0.1" + "@octokit/auth-oauth-user" "^6.0.0" + "@octokit/request" "^10.0.2" "@octokit/types" "^14.0.0" universal-user-agent "^7.0.0" -"@octokit/auth-oauth-device@^7.1.5": - version "7.1.5" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-7.1.5.tgz#dd22ed25539c4dadd27bfa3afccd244434fb4c48" - integrity sha512-lR00+k7+N6xeECj0JuXeULQ2TSBB/zjTAmNF2+vyGPDEFx1dgk1hTDmL13MjbSmzusuAmuJD8Pu39rjp9jH6yw== +"@octokit/auth-oauth-device@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.1.tgz#232ec13e299dd6bf199fe237527d04ec12decffb" + integrity sha512-TOqId/+am5yk9zor0RGibmlqn4V0h8vzjxlw/wYr3qzkQxl8aBPur384D1EyHtqvfz0syeXji4OUvKkHvxk/Gw== dependencies: - "@octokit/oauth-methods" "^5.1.5" - "@octokit/request" "^9.2.3" + "@octokit/oauth-methods" "^6.0.0" + "@octokit/request" "^10.0.2" "@octokit/types" "^14.0.0" universal-user-agent "^7.0.0" -"@octokit/auth-oauth-user@^5.1.3", "@octokit/auth-oauth-user@^5.1.4": - version "5.1.4" - resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-5.1.4.tgz#c8286e2812d0945e737563d94a1046b44078a06d" - integrity sha512-4tJRofMHm6ZCd3O2PVgboBbQ/lNtacREeaihet0+wCATZmvPK+jjg2K6NjBfY69An3yzQdmkcMeiaOOoxOPr7Q== +"@octokit/auth-oauth-user@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.0.tgz#ba643060824536cd848c72d835061b1c00007286" + integrity sha512-GV9IW134PHsLhtUad21WIeP9mlJ+QNpFd6V9vuPWmaiN25HEJeEQUcS4y5oRuqCm9iWDLtfIs+9K8uczBXKr6A== dependencies: - "@octokit/auth-oauth-device" "^7.1.5" - "@octokit/oauth-methods" "^5.1.5" - "@octokit/request" "^9.2.3" + "@octokit/auth-oauth-device" "^8.0.1" + "@octokit/oauth-methods" "^6.0.0" + "@octokit/request" "^10.0.2" "@octokit/types" "^14.0.0" universal-user-agent "^7.0.0" -"@octokit/auth-token@^5.0.0": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-5.1.2.tgz#68a486714d7a7fd1df56cb9bc89a860a0de866de" - integrity sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw== +"@octokit/auth-token@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-6.0.0.tgz#b02e9c08a2d8937df09a2a981f226ad219174c53" + integrity sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w== -"@octokit/auth-unauthenticated@^6.1.2", "@octokit/auth-unauthenticated@^6.1.3": - version "6.1.3" - resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-6.1.3.tgz#7f6eca87eb5cdfdccacd92399de6fd9607c61256" - integrity sha512-d5gWJla3WdSl1yjbfMpET+hUSFCE15qM0KVSB0H1shyuJihf/RL1KqWoZMIaonHvlNojkL9XtLFp8QeLe+1iwA== +"@octokit/auth-unauthenticated@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.1.tgz#427b8a52e672318f84757307e766a448c741116b" + integrity sha512-qVq1vdjLLZdE8kH2vDycNNjuJRCD1q2oet1nA/GXWaYlpDxlR7rdVhX/K/oszXslXiQIiqrQf+rdhDlA99JdTQ== dependencies: - "@octokit/request-error" "^6.1.8" + "@octokit/request-error" "^7.0.0" "@octokit/types" "^14.0.0" -"@octokit/core@^6.1.4", "@octokit/core@^6.1.5": - version "6.1.5" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-6.1.5.tgz#c2842aae87c2c2130b7dd33e8caa0f642dde2c67" - integrity sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg== - dependencies: - "@octokit/auth-token" "^5.0.0" - "@octokit/graphql" "^8.2.2" - "@octokit/request" "^9.2.3" - "@octokit/request-error" "^6.1.8" - "@octokit/types" "^14.0.0" - before-after-hook "^3.0.2" +"@octokit/core@^7.0.2": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-7.0.4.tgz#91efe208b52bbb47f997d634cdc95fc839a75e38" + integrity sha512-jOT8V1Ba5BdC79sKrRWDdMT5l1R+XNHTPR6CPWzUP2EcfAcvIHZWF0eAbmRcpOOP5gVIwnqNg0C4nvh6Abc3OA== + dependencies: + "@octokit/auth-token" "^6.0.0" + "@octokit/graphql" "^9.0.1" + "@octokit/request" "^10.0.2" + "@octokit/request-error" "^7.0.0" + "@octokit/types" "^15.0.0" + before-after-hook "^4.0.0" universal-user-agent "^7.0.0" -"@octokit/endpoint@^10.1.4": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-10.1.4.tgz#8783be38a32b95af8bcb6523af20ab4eed7a2adb" - integrity sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA== +"@octokit/endpoint@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-11.0.0.tgz#189fcc022721b4c49d0307eea6be3de1cfb53026" + integrity sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ== dependencies: "@octokit/types" "^14.0.0" universal-user-agent "^7.0.2" -"@octokit/graphql@^8.2.2": - version "8.2.2" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-8.2.2.tgz#3db48c4ffdf07f99600cee513baf45e73eced4d1" - integrity sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA== +"@octokit/graphql@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-9.0.1.tgz#eb258fc9981403d2d751720832652c385b6c1613" + integrity sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg== dependencies: - "@octokit/request" "^9.2.3" + "@octokit/request" "^10.0.2" "@octokit/types" "^14.0.0" universal-user-agent "^7.0.0" -"@octokit/oauth-app@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-7.1.6.tgz#f55c7ab2381028a71564aa6a1baf3921c620c437" - integrity sha512-OMcMzY2WFARg80oJNFwWbY51TBUfLH4JGTy119cqiDawSFXSIBujxmpXiKbGWQlvfn0CxE6f7/+c6+Kr5hI2YA== - dependencies: - "@octokit/auth-oauth-app" "^8.1.3" - "@octokit/auth-oauth-user" "^5.1.3" - "@octokit/auth-unauthenticated" "^6.1.2" - "@octokit/core" "^6.1.4" - "@octokit/oauth-authorization-url" "^7.1.1" - "@octokit/oauth-methods" "^5.1.4" +"@octokit/oauth-app@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@octokit/oauth-app/-/oauth-app-8.0.1.tgz#7cb889945c3ccacfd0ff9aa9f05e9748b439f7f3" + integrity sha512-QnhMYEQpnYbEPn9cae+wXL2LuPMFglmfeuDJXXsyxIXdoORwkLK8y0cHhd/5du9MbO/zdG/BXixzB7EEwU63eQ== + dependencies: + "@octokit/auth-oauth-app" "^9.0.1" + "@octokit/auth-oauth-user" "^6.0.0" + "@octokit/auth-unauthenticated" "^7.0.1" + "@octokit/core" "^7.0.2" + "@octokit/oauth-authorization-url" "^8.0.0" + "@octokit/oauth-methods" "^6.0.0" "@types/aws-lambda" "^8.10.83" universal-user-agent "^7.0.0" -"@octokit/oauth-authorization-url@^7.0.0", "@octokit/oauth-authorization-url@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-7.1.1.tgz#0e17c2225eb66b58ec902d02b6f1315ffe9ff04b" - integrity sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA== +"@octokit/oauth-authorization-url@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz#fdbab39a07d38faaad8621a5fdf04bc0c36d63e7" + integrity sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ== -"@octokit/oauth-methods@^5.1.4", "@octokit/oauth-methods@^5.1.5": - version "5.1.5" - resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-5.1.5.tgz#647fcd135cedd2371452631ef131497e8037b008" - integrity sha512-Ev7K8bkYrYLhoOSZGVAGsLEscZQyq7XQONCBBAl2JdMg7IT3PQn/y8P0KjloPoYpI5UylqYrLeUcScaYWXwDvw== +"@octokit/oauth-methods@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-6.0.0.tgz#a138bbbec6762b52249f7c47d0c548fc1cf6ad7b" + integrity sha512-Q8nFIagNLIZgM2odAraelMcDssapc+lF+y3OlcIPxyAU+knefO8KmozGqfnma1xegRDP4z5M73ABsamn72bOcA== dependencies: - "@octokit/oauth-authorization-url" "^7.0.0" - "@octokit/request" "^9.2.3" - "@octokit/request-error" "^6.1.8" + "@octokit/oauth-authorization-url" "^8.0.0" + "@octokit/request" "^10.0.2" + "@octokit/request-error" "^7.0.0" "@octokit/types" "^14.0.0" -"@octokit/openapi-types@^25.0.0": - version "25.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-25.0.0.tgz#adeead36992abf966e89dcd53518d8b0dc910e0d" - integrity sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw== +"@octokit/openapi-types@^25.1.0": + version "25.1.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-25.1.0.tgz#5a72a9dfaaba72b5b7db375fd05e90ca90dc9682" + integrity sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA== -"@octokit/openapi-webhooks-types@11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-11.0.0.tgz#27b170627e48ff8a2f6fffb159263dc20804917c" - integrity sha512-ZBzCFj98v3SuRM7oBas6BHZMJRadlnDoeFfvm1olVxZnYeU6Vh97FhPxyS5aLh5pN51GYv2I51l/hVUAVkGBlA== +"@octokit/openapi-types@^26.0.0": + version "26.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-26.0.0.tgz#a528b560dbc4f02040dc08d19575498d57fff19d" + integrity sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA== + +"@octokit/openapi-webhooks-types@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.0.3.tgz#fa44fb31fbb4c444c5fd640dbbf537f00c20617c" + integrity sha512-90MF5LVHjBedwoHyJsgmaFhEN1uzXyBDRLEBe7jlTYx/fEhPAk3P3DAJsfZwC54m8hAIryosJOL+UuZHB3K3yA== -"@octokit/plugin-paginate-graphql@^5.2.4": - version "5.2.4" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-5.2.4.tgz#b6afda7b3f24cb93d2ab822ec8eac664a5d325d0" - integrity sha512-pLZES1jWaOynXKHOqdnwZ5ULeVR6tVVCMm+AUbp0htdcyXDU95WbkYdU4R2ej1wKj5Tu94Mee2Ne0PjPO9cCyA== +"@octokit/plugin-paginate-graphql@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz#acdefd7e85ce24716e7ad7352f2df4d29d0e273b" + integrity sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ== -"@octokit/plugin-paginate-rest@^12.0.0": - version "12.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-12.0.0.tgz#4f3c1caefd6f85abb4e95c1c0a38af4568cb4dae" - integrity sha512-MPd6WK1VtZ52lFrgZ0R2FlaoiWllzgqFHaSZxvp72NmoDeZ0m8GeJdg4oB6ctqMTYyrnDYp592Xma21mrgiyDA== +"@octokit/plugin-paginate-rest@^13.0.0": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz#ca5bb1c7b85a583691263c1f788f607e9bcb74b3" + integrity sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw== dependencies: - "@octokit/types" "^14.0.0" + "@octokit/types" "^14.1.0" -"@octokit/plugin-rest-endpoint-methods@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-14.0.0.tgz#dc4c44b7ac5b1a69596b026ba6d796c5add1668a" - integrity sha512-iQt6ovem4b7zZYZQtdv+PwgbL5VPq37th1m2x2TdkgimIDJpsi2A6Q/OI/23i/hR6z5mL0EgisNR4dcbmckSZQ== +"@octokit/plugin-rest-endpoint-methods@^16.0.0": + version "16.1.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.1.0.tgz#eb17ce9e37327dcf7b15f4b31244d7f0b58491d1" + integrity sha512-nCsyiKoGRnhH5LkH8hJEZb9swpqOcsW+VXv1QoyUNQXJeVODG4+xM6UICEqyqe9XFr6LkL8BIiFCPev8zMDXPw== dependencies: - "@octokit/types" "^14.0.0" + "@octokit/types" "^15.0.0" -"@octokit/plugin-retry@^7.2.1": - version "7.2.1" - resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-7.2.1.tgz#3ec7065ad451c7e6bd64c4fb16c98006a5ff2f66" - integrity sha512-wUc3gv0D6vNHpGxSaR3FlqJpTXGWgqmk607N9L3LvPL4QjaxDgX/1nY2mGpT37Khn+nlIXdljczkRnNdTTV3/A== +"@octokit/plugin-retry@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-8.0.1.tgz#ee4a0487d31b97ad3deaf737faad68abeca3c227" + integrity sha512-KUoYR77BjF5O3zcwDQHRRZsUvJwepobeqiSSdCJ8lWt27FZExzb0GgVxrhhfuyF6z2B2zpO0hN5pteni1sqWiw== dependencies: - "@octokit/request-error" "^6.1.8" + "@octokit/request-error" "^7.0.0" "@octokit/types" "^14.0.0" bottleneck "^2.15.3" -"@octokit/plugin-throttling@^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-10.0.0.tgz#3ca3c2d3b6e1deb263462f5d35ce6727527116ef" - integrity sha512-Kuq5/qs0DVYTHZuBAzCZStCzo2nKvVRo/TDNhCcpC2TKiOGz/DisXMCvjt3/b5kr6SCI1Y8eeeJTHBxxpFvZEg== +"@octokit/plugin-throttling@^11.0.1": + version "11.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-throttling/-/plugin-throttling-11.0.1.tgz#31a0b5e759f0313514d9522a4103360f17ffc2e4" + integrity sha512-S+EVhy52D/272L7up58dr3FNSMXWuNZolkL4zMJBNIfIxyZuUcczsQAU4b5w6dewJXnKYVgSHSV5wxitMSW1kw== dependencies: "@octokit/types" "^14.0.0" bottleneck "^2.15.3" -"@octokit/request-error@^6.1.7", "@octokit/request-error@^6.1.8": - version "6.1.8" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-6.1.8.tgz#3c7ce1ca6721eabd43dbddc76b44860de1fdea75" - integrity sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ== +"@octokit/request-error@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-7.0.0.tgz#48ae2cd79008315605d00e83664891a10a5ddb97" + integrity sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg== dependencies: "@octokit/types" "^14.0.0" -"@octokit/request@^9.2.3": - version "9.2.3" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-9.2.3.tgz#00d023ad690903d952e4dd31e3f5804ef98fcd24" - integrity sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w== +"@octokit/request@^10.0.2": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-10.0.3.tgz#2ffdb88105ce20d25dcab8a592a7040ea48306c7" + integrity sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA== dependencies: - "@octokit/endpoint" "^10.1.4" - "@octokit/request-error" "^6.1.8" + "@octokit/endpoint" "^11.0.0" + "@octokit/request-error" "^7.0.0" "@octokit/types" "^14.0.0" - fast-content-type-parse "^2.0.0" + fast-content-type-parse "^3.0.0" universal-user-agent "^7.0.2" -"@octokit/types@^14.0.0": - version "14.0.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-14.0.0.tgz#bbd1d31e2269940789ef143b1c37918aae09adc4" - integrity sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA== +"@octokit/types@^14.0.0", "@octokit/types@^14.1.0": + version "14.1.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-14.1.0.tgz#3bf9b3a3e3b5270964a57cc9d98592ed44f840f2" + integrity sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g== dependencies: - "@octokit/openapi-types" "^25.0.0" + "@octokit/openapi-types" "^25.1.0" -"@octokit/webhooks-methods@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-5.1.1.tgz#192f11a1f115702833f033293e2fef8f69f612e4" - integrity sha512-NGlEHZDseJTCj8TMMFehzwa9g7On4KJMPVHDSrHxCQumL6uSQR8wIkP/qesv52fXqV1BPf4pTxwtS31ldAt9Xg== +"@octokit/types@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-15.0.0.tgz#6afe9b012115284ded9bf779e90d04081e0eadd3" + integrity sha512-8o6yDfmoGJUIeR9OfYU0/TUJTnMPG2r68+1yEdUeG2Fdqpj8Qetg0ziKIgcBm0RW/j29H41WP37CYCEhp6GoHQ== + dependencies: + "@octokit/openapi-types" "^26.0.0" + +"@octokit/webhooks-methods@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz#34abf78aec6f826fe561cfe79d2ebb1950d1d25f" + integrity sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ== -"@octokit/webhooks@^13.6.1": - version "13.9.0" - resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-13.9.0.tgz#1dd2f1c6b4616bd91735b9aa5a25aa2cdf5fff5c" - integrity sha512-5Kva+/Gi7c+39d0/0MM/v/5RCZuwqm75fUD+t7Es3Iz/adui54GnjfNmJpkkPkXGC+5IWnEvgqwY6gstK/JlUQ== +"@octokit/webhooks@^14.0.0": + version "14.1.3" + resolved "https://registry.yarnpkg.com/@octokit/webhooks/-/webhooks-14.1.3.tgz#2d0bed71b07745c0b33363d69e0ae0e440469a18" + integrity sha512-gcK4FNaROM9NjA0mvyfXl0KPusk7a1BeA8ITlYEZVQCXF5gcETTd4yhAU0Kjzd8mXwYHppzJBWgdBVpIR9wUcQ== dependencies: - "@octokit/openapi-webhooks-types" "11.0.0" - "@octokit/request-error" "^6.1.7" - "@octokit/webhooks-methods" "^5.1.1" + "@octokit/openapi-webhooks-types" "12.0.3" + "@octokit/request-error" "^7.0.0" + "@octokit/webhooks-methods" "^6.0.0" "@polka/url@^1.0.0-next.20": version "1.0.0-next.25" @@ -1294,9 +1306,9 @@ defer-to-connect "^2.0.0" "@types/aws-lambda@^8.10.83": - version "8.10.149" - resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.149.tgz#77c7bde809425546d03626e51bab8181bc5d24c9" - integrity sha512-NXSZIhfJjnXqJgtS7IwutqIF/SOy1Wz5Px4gUY1RWITp3AYTyuJS4xaXr/bIJY1v15XMzrJ5soGnPM+7uigZjA== + version "8.10.152" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.152.tgz#f68424a8175f0a54a2a941e65b76c3f51f3bd89d" + integrity sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw== "@types/cacheable-request@^6.0.1": version "6.0.3" @@ -1776,10 +1788,10 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -before-after-hook@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-3.0.2.tgz#d5665a5fa8b62294a5aa0a499f933f4a1016195d" - integrity sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A== +before-after-hook@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-4.0.0.tgz#cf1447ab9160df6a40f3621da64d6ffc36050cb9" + integrity sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ== bl@^1.0.0: version "1.2.3" @@ -2583,10 +2595,10 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -fast-content-type-parse@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz#c236124534ee2cb427c8d8e5ba35a4856947847b" - integrity sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q== +fast-content-type-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz#5590b6c807cc598be125e6740a9fde589d2b7afb" + integrity sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -3542,21 +3554,22 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -octokit@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/octokit/-/octokit-4.1.3.tgz#8855681813a83b69a65a4bec8a77075d241f87df" - integrity sha512-PP+EL8h4xPCE9NBo6jXq6I2/EiTXsn1cg9F0IZehHBv/qhuQpyGMFElEB17miWKciuT6vRHiFFiG9+FoXOmg6A== - dependencies: - "@octokit/app" "^15.1.6" - "@octokit/core" "^6.1.5" - "@octokit/oauth-app" "^7.1.6" - "@octokit/plugin-paginate-graphql" "^5.2.4" - "@octokit/plugin-paginate-rest" "^12.0.0" - "@octokit/plugin-rest-endpoint-methods" "^14.0.0" - "@octokit/plugin-retry" "^7.2.1" - "@octokit/plugin-throttling" "^10.0.0" - "@octokit/request-error" "^6.1.8" +octokit@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/octokit/-/octokit-5.0.3.tgz#1e4f110e28218ab9676c28da5f28ab403fe5b643" + integrity sha512-+bwYsAIRmYv30NTmBysPIlgH23ekVDriB07oRxlPIAH5PI0yTMSxg5i5Xy0OetcnZw+nk/caD4szD7a9YZ3QyQ== + dependencies: + "@octokit/app" "^16.0.1" + "@octokit/core" "^7.0.2" + "@octokit/oauth-app" "^8.0.1" + "@octokit/plugin-paginate-graphql" "^6.0.0" + "@octokit/plugin-paginate-rest" "^13.0.0" + "@octokit/plugin-rest-endpoint-methods" "^16.0.0" + "@octokit/plugin-retry" "^8.0.1" + "@octokit/plugin-throttling" "^11.0.1" + "@octokit/request-error" "^7.0.0" "@octokit/types" "^14.0.0" + "@octokit/webhooks" "^14.0.0" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" From c429b7827449e972b0302276c8256d77942947c4 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 25 Sep 2025 13:20:47 -0400 Subject: [PATCH 011/182] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20Explicitly=20im?= =?UTF-8?q?port=20types=20&=20Fix=20type=20strictness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blueprintFormat.ts | 2 +- src/blueprintSettings.ts | 2 +- src/mods/addLocatorActionMod.ts | 2 +- src/mods/animationPropertiesMod.ts | 2 +- src/mods/customKeyframeEasingsMod.ts | 4 ++-- src/mods/saveAllAnimationsActionMod.ts | 2 +- src/nodeConfigs.ts | 2 +- src/outliner/textDisplay.ts | 2 +- src/outliner/vanillaBlockDisplay.ts | 4 ++-- src/outliner/vanillaItemDisplay.ts | 2 +- src/systems/animationRenderer.ts | 2 +- src/systems/cleaner.ts | 2 +- src/systems/datapackCompiler/compiler.ts | 4 ++-- src/systems/datapackCompiler/index.ts | 17 +++++++++++------ src/systems/datapackCompiler/mcbFiles.ts | 2 +- src/systems/jsonCompiler.ts | 2 +- src/systems/minecraft/assetManager.ts | 6 +++--- src/systems/minecraft/blockModelManager.ts | 6 +++--- src/systems/minecraft/fontManager.ts | 6 +++--- src/systems/minecraft/itemModelManager.ts | 2 +- src/systems/minecraft/textWrapping.ts | 7 ++++++- src/systems/modelDataFixerUpper.ts | 2 +- src/systems/resourcepackCompiler/1.20.4.ts | 4 ++-- src/systems/resourcepackCompiler/1.21.2.ts | 2 +- src/systems/resourcepackCompiler/1.21.4.ts | 4 ++-- src/systems/resourcepackCompiler/index.ts | 11 ++++++++--- src/systems/rigRenderer.ts | 10 +++++----- src/systems/util.ts | 6 +++--- src/util/minecraftUtil.ts | 4 ++-- src/util/misc.ts | 2 +- src/util/moddingTools.ts | 6 +++--- src/util/svelteDialog.ts | 1 + src/util/sveltePanel.ts | 4 ++-- src/variants.ts | 2 +- tools/plugins/assetOverridePlugin.ts | 3 +-- tools/plugins/mcbCompressionPlugin.ts | 6 +++--- tools/plugins/packagerPlugin.ts | 17 +++++++++++------ tools/plugins/sveltePlugin.ts | 4 ++-- 38 files changed, 94 insertions(+), 74 deletions(-) diff --git a/src/blueprintFormat.ts b/src/blueprintFormat.ts index 24515ee3..21c5935e 100644 --- a/src/blueprintFormat.ts +++ b/src/blueprintFormat.ts @@ -2,7 +2,7 @@ import * as blueprintSettings from './blueprintSettings' import FormatPageSvelte from './components/formatPage.svelte' import ProjectTitleSvelte from './components/projectTitle.svelte' import { PACKAGE } from './constants' -import { BillboardMode, BoneConfig, LocatorConfig } from './nodeConfigs' +import { type BillboardMode, BoneConfig, LocatorConfig } from './nodeConfigs' import { process } from './systems/modelDataFixerUpper' import { events } from './util/events' import { injectSvelteCompomponent } from './util/injectSvelteComponent' diff --git a/src/blueprintSettings.ts b/src/blueprintSettings.ts index f9f4e5e6..17132de6 100644 --- a/src/blueprintSettings.ts +++ b/src/blueprintSettings.ts @@ -1,4 +1,4 @@ -import { MinecraftVersion } from './systems/global' +import type { MinecraftVersion } from './systems/global' import { Valuable } from './util/stores' export type ExportMode = 'raw' | 'zip' | 'none' diff --git a/src/mods/addLocatorActionMod.ts b/src/mods/addLocatorActionMod.ts index 269277ad..ddbe4e42 100644 --- a/src/mods/addLocatorActionMod.ts +++ b/src/mods/addLocatorActionMod.ts @@ -11,7 +11,7 @@ createBlockbenchMod( context => { context.action.condition = () => { if (isCurrentFormat()) return true - return !!context.originalCondition?.() + return Condition(context.originalCondition) } Toolbars.outliner.add(context.action, 0) diff --git a/src/mods/animationPropertiesMod.ts b/src/mods/animationPropertiesMod.ts index fd914745..9e2cba7e 100644 --- a/src/mods/animationPropertiesMod.ts +++ b/src/mods/animationPropertiesMod.ts @@ -1,7 +1,7 @@ import { isCurrentFormat } from '../blueprintFormat' import { PACKAGE } from '../constants' import { roundToNth } from '../util/misc' -import { ContextProperty, createBlockbenchMod } from '../util/moddingTools' +import { type ContextProperty, createBlockbenchMod } from '../util/moddingTools' import { translate } from '../util/translation' export const DEFAULT_SNAPPING_VALUE = 20 diff --git a/src/mods/customKeyframeEasingsMod.ts b/src/mods/customKeyframeEasingsMod.ts index 64180946..f73aa1bd 100644 --- a/src/mods/customKeyframeEasingsMod.ts +++ b/src/mods/customKeyframeEasingsMod.ts @@ -1,10 +1,10 @@ -import { ContextProperty, createBlockbenchMod } from '../util/moddingTools' import { isCurrentFormat } from '../blueprintFormat' import { PACKAGE } from '../constants' +import { type ContextProperty, createBlockbenchMod } from '../util/moddingTools' import { EASING_DEFAULT, - EasingKey, + type EasingKey, easingFunctions, getEasingArgDefault, hasArgs, diff --git a/src/mods/saveAllAnimationsActionMod.ts b/src/mods/saveAllAnimationsActionMod.ts index eb0b2c44..10538b99 100644 --- a/src/mods/saveAllAnimationsActionMod.ts +++ b/src/mods/saveAllAnimationsActionMod.ts @@ -13,7 +13,7 @@ createBlockbenchMod( if (Format.id === BLUEPRINT_FORMAT.id) { return false } - return originalCondition.call(this) + return Condition(originalCondition) } return { ...context, originalCondition } }, diff --git a/src/nodeConfigs.ts b/src/nodeConfigs.ts index 06ad0760..73eb7ae6 100644 --- a/src/nodeConfigs.ts +++ b/src/nodeConfigs.ts @@ -1,5 +1,5 @@ import { NbtByte, NbtCompound, NbtFloat, NbtInt, NbtString, NbtTag } from 'deepslate/lib/nbt' -import { +import type { IBlueprintBoneConfigJSON, IBlueprintCameraConfigJSON, IBlueprintLocatorConfigJSON, diff --git a/src/outliner/textDisplay.ts b/src/outliner/textDisplay.ts index d376265b..5d7f1a37 100644 --- a/src/outliner/textDisplay.ts +++ b/src/outliner/textDisplay.ts @@ -1,6 +1,6 @@ import { BLUEPRINT_FORMAT, - IBlueprintTextDisplayConfigJSON, + type IBlueprintTextDisplayConfigJSON, isCurrentFormat, } from '../blueprintFormat' import { PACKAGE } from '../constants' diff --git a/src/outliner/vanillaBlockDisplay.ts b/src/outliner/vanillaBlockDisplay.ts index 254872f1..b5e90d07 100644 --- a/src/outliner/vanillaBlockDisplay.ts +++ b/src/outliner/vanillaBlockDisplay.ts @@ -1,9 +1,9 @@ -import { IBlueprintBoneConfigJSON, isCurrentFormat } from '../blueprintFormat' +import { type IBlueprintBoneConfigJSON, isCurrentFormat } from '../blueprintFormat' import { PACKAGE } from '../constants' import { VANILLA_BLOCK_DISPLAY_CONFIG_ACTION } from '../interface/dialog/vanillaBlockDisplayConfig' import { BoneConfig } from '../nodeConfigs' import { getBlockModel } from '../systems/minecraft/blockModelManager' -import { BlockStateValue, getBlockState } from '../systems/minecraft/blockstateManager' +import { type BlockStateValue, getBlockState } from '../systems/minecraft/blockstateManager' import { MINECRAFT_REGISTRY } from '../systems/minecraft/registryManager' import { getCurrentVersion } from '../systems/minecraft/versionManager' import { events } from '../util/events' diff --git a/src/outliner/vanillaItemDisplay.ts b/src/outliner/vanillaItemDisplay.ts index fcf7fc81..3ae83bc7 100644 --- a/src/outliner/vanillaItemDisplay.ts +++ b/src/outliner/vanillaItemDisplay.ts @@ -1,4 +1,4 @@ -import { IBlueprintBoneConfigJSON, isCurrentFormat } from '../blueprintFormat' +import { type IBlueprintBoneConfigJSON, isCurrentFormat } from '../blueprintFormat' import { PACKAGE } from '../constants' import { VANILLA_ITEM_DISPLAY_CONFIG_ACTION } from '../interface/dialog/vanillaItemDisplayConfig' import { BoneConfig } from '../nodeConfigs' diff --git a/src/systems/animationRenderer.ts b/src/systems/animationRenderer.ts index 10e919d7..d43b412b 100644 --- a/src/systems/animationRenderer.ts +++ b/src/systems/animationRenderer.ts @@ -12,7 +12,7 @@ import { VanillaBlockDisplay } from '../outliner/vanillaBlockDisplay' import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay' import { sanitizePathName, sanitizeStorageKey } from '../util/minecraftUtil' import { eulerFromQuaternion, roundToNth } from '../util/misc' -import { AnyRenderedNode, IRenderedRig } from './rigRenderer' +import type { AnyRenderedNode, IRenderedRig } from './rigRenderer' import { sleepForAnimationFrame } from './util' export function correctSceneAngle() { diff --git a/src/systems/cleaner.ts b/src/systems/cleaner.ts index 5e8692f8..3055d59e 100644 --- a/src/systems/cleaner.ts +++ b/src/systems/cleaner.ts @@ -1,5 +1,5 @@ import { isFunctionTagPath } from '../util/fileUtil' -import { IFunctionTag, parseDataPackPath } from '../util/minecraftUtil' +import { type IFunctionTag, parseDataPackPath } from '../util/minecraftUtil' import { getExportPaths } from './exporter' import { AJMeta } from './global' import { replacePathPart } from './util' diff --git a/src/systems/datapackCompiler/compiler.ts b/src/systems/datapackCompiler/compiler.ts index 4648a4e3..541b3cd1 100644 --- a/src/systems/datapackCompiler/compiler.ts +++ b/src/systems/datapackCompiler/compiler.ts @@ -1,8 +1,8 @@ import { Parser, SyncIo, Tokenizer } from 'mc-build' import { Compiler, VariableMap } from 'mc-build/dist/mcl/Compiler' import { getDataPackFormat } from '../../util/minecraftUtil' -import { MinecraftVersion } from '../global' -import { ExportedFile } from '../util' +import type { MinecraftVersion } from '../global' +import type { ExportedFile } from '../util' interface CompilerOptions { path: string diff --git a/src/systems/datapackCompiler/index.ts b/src/systems/datapackCompiler/index.ts index 7765baa1..112c8430 100644 --- a/src/systems/datapackCompiler/index.ts +++ b/src/systems/datapackCompiler/index.ts @@ -12,7 +12,7 @@ import { BoneConfig, TextDisplayConfig } from '../../nodeConfigs' import { isFunctionTagPath } from '../../util/fileUtil' import { getDataPackFormat, - IFunctionTag, + type IFunctionTag, mergeTag, parseBlock, parseDataPackPath, @@ -21,15 +21,15 @@ import { import { eulerFromQuaternion, floatToHex, roundTo, tinycolorToDecimal } from '../../util/misc' import { MSLimiter } from '../../util/msLimiter' import { Variant } from '../../variants' -import { IRenderedAnimation } from '../animationRenderer' +import type { IRenderedAnimation } from '../animationRenderer' import mcbFiles from '../datapackCompiler/mcbFiles' import { IntentionalExportError } from '../exporter' -import { AJMeta, MinecraftVersion, PackMeta, PackMetaFormats } from '../global' +import { AJMeta, type MinecraftVersion, PackMeta, type PackMetaFormats } from '../global' import { JsonText } from '../minecraft/jsonText' -import { AnyRenderedNode, IRenderedRig, IRenderedVariant } from '../rigRenderer' +import type { AnyRenderedNode, IRenderedRig, IRenderedVariant } from '../rigRenderer' import { arrayToNbtFloatArray, - ExportedFile, + type ExportedFile, matrixToNbtFloatArray, replacePathPart, transformationToNbt, @@ -1323,7 +1323,12 @@ async function writeFiles(exportedFiles: Map, dataPackFold if (file.writeHandler) { await file.writeHandler(path, file.content) } else { - await fs.promises.writeFile(path, file.content) + await fs.promises.writeFile( + path, + new Uint8Array( + Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content) + ) + ) } PROGRESS.set(PROGRESS.get() + 1) } diff --git a/src/systems/datapackCompiler/mcbFiles.ts b/src/systems/datapackCompiler/mcbFiles.ts index 7a351419..abd5901e 100644 --- a/src/systems/datapackCompiler/mcbFiles.ts +++ b/src/systems/datapackCompiler/mcbFiles.ts @@ -1,4 +1,4 @@ -import { MinecraftVersion } from '../global' +import { type MinecraftVersion } from '../global' import animation_1_20_4 from './1.20.4/animation.mcb' import core_1_20_4 from './1.20.4/core.mcb' import static_1_20_4 from './1.20.4/static.mcb' diff --git a/src/systems/jsonCompiler.ts b/src/systems/jsonCompiler.ts index 568d28e3..fee0cd53 100644 --- a/src/systems/jsonCompiler.ts +++ b/src/systems/jsonCompiler.ts @@ -11,7 +11,7 @@ import { getKeyframeRepeatFrequency, getKeyframeVariant, } from '../mods/customKeyframesMod' -import { EasingKey } from '../util/easing' +import type { EasingKey } from '../util/easing' import { resolvePath } from '../util/fileUtil' import { detectCircularReferences, mapObjEntries, scrubUndefined } from '../util/misc' import { Variant } from '../variants' diff --git a/src/systems/minecraft/assetManager.ts b/src/systems/minecraft/assetManager.ts index 80a7ba6c..298efe67 100644 --- a/src/systems/minecraft/assetManager.ts +++ b/src/systems/minecraft/assetManager.ts @@ -29,7 +29,7 @@ async function downloadJar(url: string, savePath: string) { throw new Error('Failed to download Minecraft client after 3 retries.') } - await fs.promises.writeFile(savePath, data) + await fs.promises.writeFile(savePath, new Uint8Array(data)) } export async function getLatestVersionClientDownloadUrl() { @@ -144,12 +144,12 @@ export function getRawAsset(path: string) { export function getPngAssetAsDataUrl(path: string) { const asset = getRawAsset(path) if (!asset) throw new Error(`Asset not found: ${path}`) - return `data:image/png;base64,${Buffer.from(asset).toString('base64')}` + return `data:image/png;base64,${asset.toString('base64')}` } export function getJSONAsset(path: string): any { const asset = getRawAsset(path) if (!asset) throw new Error(`Asset not found: ${path}`) - const json = JSON.parse(Buffer.from(asset).toString('utf-8')) + const json = JSON.parse(asset.toString('utf-8')) return json } diff --git a/src/systems/minecraft/blockModelManager.ts b/src/systems/minecraft/blockModelManager.ts index 705f1a6d..2a7adce9 100644 --- a/src/systems/minecraft/blockModelManager.ts +++ b/src/systems/minecraft/blockModelManager.ts @@ -1,14 +1,14 @@ import { mergeGeometries } from '../../util/bufferGeometryUtils' import { - IParsedBlock, + type IParsedBlock, getPathFromResourceLocation, parseBlock, resolveBlockstateValueType, } from '../../util/minecraftUtil' import { translate } from '../../util/translation' import { assetsLoaded, getJSONAsset, getPngAssetAsDataUrl, hasAsset } from './assetManager' -import { BlockStateValue } from './blockstateManager' -import { +import type { BlockStateValue } from './blockstateManager' +import type { IBlockModel, IBlockState, IBlockStateMultipartCase, diff --git a/src/systems/minecraft/fontManager.ts b/src/systems/minecraft/fontManager.ts index 08df3395..bc7c3ac7 100644 --- a/src/systems/minecraft/fontManager.ts +++ b/src/systems/minecraft/fontManager.ts @@ -10,9 +10,9 @@ import { COLOR_MAP, JsonText } from './jsonText' import { computeTextWrapping, getComponentWords, - IComponentWord, - IStyleSpan, - StyleRecord, + type IComponentWord, + type IStyleSpan, + type StyleRecord, } from './textWrapping' interface IFontProviderBitmap { diff --git a/src/systems/minecraft/itemModelManager.ts b/src/systems/minecraft/itemModelManager.ts index d53be4e7..8c1c3a2c 100644 --- a/src/systems/minecraft/itemModelManager.ts +++ b/src/systems/minecraft/itemModelManager.ts @@ -2,7 +2,7 @@ import { mergeGeometries } from '../../util/bufferGeometryUtils' import { getPathFromResourceLocation, parseResourceLocation } from '../../util/minecraftUtil' import { assetsLoaded, getJSONAsset, getPngAssetAsDataUrl } from './assetManager' import { parseBlockModel } from './blockModelManager' -import { IItemModel } from './model' +import type { IItemModel } from './model' import { TEXTURE_FRAG_SHADER, TEXTURE_VERT_SHADER } from './textureShaders' type ItemModelMesh = { diff --git a/src/systems/minecraft/textWrapping.ts b/src/systems/minecraft/textWrapping.ts index 05a0c100..9643b542 100644 --- a/src/systems/minecraft/textWrapping.ts +++ b/src/systems/minecraft/textWrapping.ts @@ -1,6 +1,11 @@ import { UnicodeString } from '../../util/unicodeString' import { getVanillaFont } from './fontManager' -import { JsonText, JsonTextArray, JsonTextComponent, JsonTextObject } from './jsonText' +import { + JsonText, + type JsonTextArray, + type JsonTextComponent, + type JsonTextObject, +} from './jsonText' // @ts-ignore // import TestWorker from './textWrapping.worker.ts' diff --git a/src/systems/modelDataFixerUpper.ts b/src/systems/modelDataFixerUpper.ts index bd20252c..7a83fb06 100644 --- a/src/systems/modelDataFixerUpper.ts +++ b/src/systems/modelDataFixerUpper.ts @@ -1,6 +1,6 @@ import { NbtCompound, NbtList, NbtString, NbtTag } from 'deepslate/lib/nbt' import TransparentTexture from '../assets/transparent.png' -import { IBlueprintFormatJSON, getDefaultProjectSettings } from '../blueprintFormat' +import { type IBlueprintFormatJSON, getDefaultProjectSettings } from '../blueprintFormat' import { PACKAGE } from '../constants' import { openUnexpectedErrorDialog } from '../interface/dialog/unexpectedError' import { BoneConfig } from '../nodeConfigs' diff --git a/src/systems/resourcepackCompiler/1.20.4.ts b/src/systems/resourcepackCompiler/1.20.4.ts index f8cd7a3d..a045b639 100644 --- a/src/systems/resourcepackCompiler/1.20.4.ts +++ b/src/systems/resourcepackCompiler/1.20.4.ts @@ -1,8 +1,8 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' -import { ITextureAtlas } from '../minecraft/textureAtlas' -import { IRenderedNodes } from '../rigRenderer' +import type { ITextureAtlas } from '../minecraft/textureAtlas' +import type { IRenderedNodes } from '../rigRenderer' import { sortObjectKeys } from '../util' interface IPredicateItemModel { diff --git a/src/systems/resourcepackCompiler/1.21.2.ts b/src/systems/resourcepackCompiler/1.21.2.ts index 9c79cb38..c443cd37 100644 --- a/src/systems/resourcepackCompiler/1.21.2.ts +++ b/src/systems/resourcepackCompiler/1.21.2.ts @@ -3,7 +3,7 @@ import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' import { safeReadSync } from '../../util/fileUtil' import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' import { type ITextureAtlas } from '../minecraft/textureAtlas' -import { IRenderedNodes } from '../rigRenderer' +import type { IRenderedNodes } from '../rigRenderer' const compileResourcePack: ResourcePackCompiler = async ({ coreFiles, diff --git a/src/systems/resourcepackCompiler/1.21.4.ts b/src/systems/resourcepackCompiler/1.21.4.ts index a1e4fb4e..6d72edc6 100644 --- a/src/systems/resourcepackCompiler/1.21.4.ts +++ b/src/systems/resourcepackCompiler/1.21.4.ts @@ -2,9 +2,9 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' import { Variant } from '../../variants' -import { IItemDefinition } from '../minecraft/itemDefinitions' +import type { IItemDefinition } from '../minecraft/itemDefinitions' import { type ITextureAtlas } from '../minecraft/textureAtlas' -import { IRenderedNodes, IRenderedRig, IRenderedVariantModel } from '../rigRenderer' +import type { IRenderedNodes, IRenderedRig, IRenderedVariantModel } from '../rigRenderer' const compileResourcePack: ResourcePackCompiler = async ({ coreFiles, diff --git a/src/systems/resourcepackCompiler/index.ts b/src/systems/resourcepackCompiler/index.ts index a81db639..44cd1761 100644 --- a/src/systems/resourcepackCompiler/index.ts +++ b/src/systems/resourcepackCompiler/index.ts @@ -2,9 +2,9 @@ import { MAX_PROGRESS, PROGRESS, PROGRESS_DESCRIPTION } from '../../interface/di import { getResourcePackFormat } from '../../util/minecraftUtil' import { IntentionalExportError } from '../exporter' import { type IRenderedRig } from '../rigRenderer' -import { ExportedFile } from '../util' +import type { ExportedFile } from '../util' -import { AJMeta, MinecraftVersion, PackMeta, PackMetaFormats } from '../global' +import { AJMeta, type MinecraftVersion, PackMeta, type PackMetaFormats } from '../global' import _1_20_4 from './1.20.4' import _1_21_2 from './1.21.2' import _1_21_4 from './1.21.4' @@ -183,7 +183,12 @@ export default async function compileResourcePack( if (file.writeHandler) { await file.writeHandler(path, file.content) } else { - await fs.promises.writeFile(path, file.content) + await fs.promises.writeFile( + path, + new Uint8Array( + Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content) + ) + ) } PROGRESS.set(PROGRESS.get() + 1) } diff --git a/src/systems/rigRenderer.ts b/src/systems/rigRenderer.ts index 7bdcfbb8..9623d89a 100644 --- a/src/systems/rigRenderer.ts +++ b/src/systems/rigRenderer.ts @@ -1,17 +1,17 @@ import * as crypto from 'crypto' -import { +import type { + IBlueprintBoneConfigJSON, IBlueprintCameraConfigJSON, IBlueprintLocatorConfigJSON, IBlueprintTextDisplayConfigJSON, IBlueprintVariantJSON, - type IBlueprintBoneConfigJSON, } from '../blueprintFormat' import { BoneConfig } from '../nodeConfigs' -import { Alignment, TextDisplay } from '../outliner/textDisplay' +import { type Alignment, TextDisplay } from '../outliner/textDisplay' import { VanillaBlockDisplay } from '../outliner/vanillaBlockDisplay' import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay' import { - IMinecraftResourceLocation, + type IMinecraftResourceLocation, parseResourcePackPath, sanitizePathName, sanitizeStorageKey, @@ -20,9 +20,9 @@ import { Variant } from '../variants' import { correctSceneAngle, getFrame, + type INodeTransform, restoreSceneAngle, updatePreview, - type INodeTransform, } from './animationRenderer' import { IntentionalExportError } from './exporter' import { JsonText } from './minecraft/jsonText' diff --git a/src/systems/util.ts b/src/systems/util.ts index 6cf5e43f..4c19279c 100644 --- a/src/systems/util.ts +++ b/src/systems/util.ts @@ -1,14 +1,14 @@ import { NbtCompound, NbtFloat, NbtList } from 'deepslate/lib/nbt' import { - AsyncZipOptions, - AsyncZippable, unzip as cbUnzip, zip as cbZip, type AsyncUnzipOptions, + type AsyncZipOptions, + type AsyncZippable, type Unzipped, } from 'fflate/browser' import { roundTo } from '../util/misc' -import { INodeTransform } from './animationRenderer' +import type { INodeTransform } from './animationRenderer' export interface ExportedFile { content: string | Buffer diff --git a/src/util/minecraftUtil.ts b/src/util/minecraftUtil.ts index d3702cc9..2b3a6d37 100644 --- a/src/util/minecraftUtil.ts +++ b/src/util/minecraftUtil.ts @@ -1,8 +1,8 @@ import * as pathjs from 'path' -import { MinecraftVersion } from '../systems/global' +import type { MinecraftVersion } from '../systems/global' import { BlockStateRegistryEntry, - BlockStateValue, + type BlockStateValue, getBlockState, } from '../systems/minecraft/blockstateManager' diff --git a/src/util/misc.ts b/src/util/misc.ts index 649ee2f1..7fe8b759 100644 --- a/src/util/misc.ts +++ b/src/util/misc.ts @@ -1,6 +1,6 @@ import { BLUEPRINT_FORMAT } from '../blueprintFormat' -import { ComponentConstructorOptions } from 'svelte' +import { type ComponentConstructorOptions } from 'svelte' export type SvelteComponentConstructor = new ( options: U diff --git a/src/util/moddingTools.ts b/src/util/moddingTools.ts index 13b80950..bbf92660 100644 --- a/src/util/moddingTools.ts +++ b/src/util/moddingTools.ts @@ -137,15 +137,15 @@ export function createMenu(template: MenuItem[], options?: MenuOptions) { * Creates a new Blockbench.BarMenu and automatically handles it's deletion on the plugin unload and uninstall events. * @param id A namespaced ID ('my-plugin-id:my-menu') * @param structure The menu structure. - * @param condition The condition for the menu to be visible. + * @param options The condition for the menu to be visible. * @returns The created menu. */ export function createBarMenu( id: NamespacedString, structure: MenuItem[], - condition: ConditionResolvable + options?: BarMenuOptions ) { - const menu = new BarMenu(id, structure, condition) + const menu = new BarMenu(id, structure, options) // events.EXTRACT_MODS.subscribe(() => { // menu.delete() diff --git a/src/util/svelteDialog.ts b/src/util/svelteDialog.ts index 9d1c94d2..84ff7475 100644 --- a/src/util/svelteDialog.ts +++ b/src/util/svelteDialog.ts @@ -24,6 +24,7 @@ export class SvelteDialog> extends Dialog { const mount = document.createComment(`svelte-dialog-` + guid()) const dialogOptions = { ...options } + // @ts-expect-error delete dialogOptions.component super(options.id, { diff --git a/src/util/sveltePanel.ts b/src/util/sveltePanel.ts index c88a5a0f..798ea150 100644 --- a/src/util/sveltePanel.ts +++ b/src/util/sveltePanel.ts @@ -1,7 +1,7 @@ -import { ComponentConstructorOptions, SvelteComponent } from 'svelte' -import { SvelteComponentConstructor } from './misc' +import { type ComponentConstructorOptions, SvelteComponent } from 'svelte' import * as PACKAGE from '../../package.json' import { pollPromise } from '../util/promises' +import { type SvelteComponentConstructor } from './misc' type SveltePanelOptions = Omit< PanelOptions, diff --git a/src/variants.ts b/src/variants.ts index 97d3f28c..8aa5a3c5 100644 --- a/src/variants.ts +++ b/src/variants.ts @@ -1,4 +1,4 @@ -import { IBlueprintVariantJSON } from './blueprintFormat' +import type { IBlueprintVariantJSON } from './blueprintFormat' import { getKeyframeVariant, setKeyframeVariant } from './mods/customKeyframesMod' import { events } from './util/events' import { sanitizePathName } from './util/minecraftUtil' diff --git a/tools/plugins/assetOverridePlugin.ts b/tools/plugins/assetOverridePlugin.ts index 20fe6997..c254c614 100644 --- a/tools/plugins/assetOverridePlugin.ts +++ b/tools/plugins/assetOverridePlugin.ts @@ -1,4 +1,4 @@ -import { Plugin } from 'esbuild' +import type { Plugin } from 'esbuild' import * as fs from 'fs/promises' import * as pathjs from 'path' @@ -20,7 +20,6 @@ function plugin(): Plugin { if (path.endsWith('.json') || path.endsWith('.png')) { const key = pathjs .join('assets', dir, path) - // @ts-expect-error .replaceAll(pathjs.sep, '/') .replace(ASSET_OVERRIDES_PATH, '') if (path.endsWith('.json')) { diff --git a/tools/plugins/mcbCompressionPlugin.ts b/tools/plugins/mcbCompressionPlugin.ts index 6effe73d..bcd9f883 100644 --- a/tools/plugins/mcbCompressionPlugin.ts +++ b/tools/plugins/mcbCompressionPlugin.ts @@ -1,7 +1,7 @@ -import { Plugin } from 'esbuild' +import type { Plugin } from 'esbuild' +import * as fflate from 'fflate' import * as fs from 'fs/promises' import * as pathjs from 'path' -import * as fflate from 'fflate' function zip(data: fflate.AsyncZippable): Promise { return new Promise((resolve, reject) => { @@ -21,7 +21,7 @@ export default function plugin(): Plugin { build.onLoad({ filter: /\.mcb$/ }, async ({ path }) => { const localPath = pathjs.relative(process.cwd(), path).replace(/\\/g, '/') const data = await fs.readFile(path) - mcbFiles.set(localPath, data) + mcbFiles.set(localPath, new Uint8Array(data)) return { contents: ` diff --git a/tools/plugins/packagerPlugin.ts b/tools/plugins/packagerPlugin.ts index 39a4f1eb..5b43fac8 100644 --- a/tools/plugins/packagerPlugin.ts +++ b/tools/plugins/packagerPlugin.ts @@ -1,8 +1,9 @@ -import { Plugin } from 'esbuild' +import type { Plugin } from 'esbuild' import * as fs from 'fs' import { readFileSync, writeFileSync } from 'fs' import { Octokit } from 'octokit' import * as pathjs from 'path' +// @ts-expect-error import * as prettier from 'prettier' import * as c from 'svelte/compiler' import * as svelteInternal from 'svelte/internal' @@ -81,6 +82,11 @@ function plugin(): Plugin { ) let pings = '' const version = getVersionNumbers(PACKAGE.version) + if (!version) { + throw new Error( + `Version ${PACKAGE.version} in package.json is not valid semver!` + ) + } const latestRelease = getVersionNumbers( ( await octokit.request('GET /repos/{owner}/{repo}/releases', { @@ -121,26 +127,26 @@ function plugin(): Plugin { } const changeList = versionChangelog.categories.find( - c => c.title === 'Changes' + (c: any) => c.title === 'Changes' ) let changes = '' if (changeList) { changes = '### Changes\n\n' + changeList.list - .map(v => '- ' + v) + .map((v: any) => '- ' + v) .join('\n') .replace('[BREAKING]', '⚠️ **BREAKING CHANGE** — ') } const fixList = versionChangelog.categories.find( - c => c.title === 'Fixes' + (c: any) => c.title === 'Fixes' ) let fixes = '' if (fixList) { fixes = '### Fixes\n\n' + fixList.list - .map(v => '- ' + v) + .map((v: any) => '- ' + v) .join('\n') .replace('[BREAKING]', '⚠️ **BREAKING CHANGE** — ') } @@ -155,7 +161,6 @@ function plugin(): Plugin { if (content.includes('[[ESCAPE_URLS]]')) { content = content .replace('[[ESCAPE_URLS]]', '') - // @ts-expect-error .replaceAll(URL_REGEX, (match: string) => '<' + match + '>') } diff --git a/tools/plugins/sveltePlugin.ts b/tools/plugins/sveltePlugin.ts index 79068aaa..f4a3292d 100644 --- a/tools/plugins/sveltePlugin.ts +++ b/tools/plugins/sveltePlugin.ts @@ -6,10 +6,10 @@ 'use strict' Object.defineProperty(exports, '__esModule', { value: true }) +import type { Plugin } from 'esbuild' import { readFile } from 'fs/promises' -import { preprocess, compile } from 'svelte/compiler' import { relative } from 'path' -import { Plugin } from 'esbuild' +import { compile, preprocess } from 'svelte/compiler' /** * Convert a warning or error emitted from the svelte compiler for esbuild. */ From d2c8aee44a3d8f505d9e21cac13538d84e6115d8 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 25 Sep 2025 13:21:49 -0400 Subject: [PATCH 012/182] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20worker?= =?UTF-8?q?Plugin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/esbuild.ts | 9 ++-- tools/plugins/workerPlugin.ts | 82 ----------------------------------- 2 files changed, 4 insertions(+), 87 deletions(-) delete mode 100644 tools/plugins/workerPlugin.ts diff --git a/tools/esbuild.ts b/tools/esbuild.ts index 374b1437..ac4d39ff 100644 --- a/tools/esbuild.ts +++ b/tools/esbuild.ts @@ -20,7 +20,6 @@ import bufferPatchPlugin from './plugins/bufferPatchFunction.js' import mcbCompressionPlugin from './plugins/mcbCompressionPlugin' import packagerPlugin from './plugins/packagerPlugin' import sveltePlugin from './plugins/sveltePlugin' -import inlineWorkerPlugin from './plugins/workerPlugin' const PACKAGE = JSON.parse(fs.readFileSync('./package.json', 'utf-8')) const INFO_PLUGIN: esbuild.Plugin = { @@ -163,7 +162,10 @@ const yamlPlugin: (opts: { }) build.onLoad({ filter: /.*/, namespace: 'yaml' }, async args => { const yamlContent = await fs.promises.readFile(args.path) - let parsed = load(new TextDecoder().decode(yamlContent), options?.loadOptions) + let parsed = load( + new TextDecoder().decode(new Uint8Array(yamlContent)), + options?.loadOptions + ) if (options?.transform && options.transform(parsed, args.path) !== void 0) parsed = options.transform(parsed, args.path) return { @@ -217,7 +219,6 @@ const devConfig: esbuild.BuildOptions = { yamlPlugin({}), sveltePlugin(svelteConfig), packagerPlugin(), - inlineWorkerPlugin(devWorkerConfig), assetOverridePlugin(), mcbCompressionPlugin(), DEPENDENCY_QUARKS, @@ -243,11 +244,9 @@ const prodConfig: esbuild.BuildOptions = { limit: -1, }), INFO_PLUGIN, - inlineWorkerPlugin({}), yamlPlugin({}), sveltePlugin(svelteConfig), packagerPlugin(), - inlineWorkerPlugin({}), assetOverridePlugin(), mcbCompressionPlugin(), DEPENDENCY_QUARKS, diff --git a/tools/plugins/workerPlugin.ts b/tools/plugins/workerPlugin.ts deleted file mode 100644 index a5cd59b9..00000000 --- a/tools/plugins/workerPlugin.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-env node */ -import * as esbuild from 'esbuild' -import findCacheDir from 'find-cache-dir' -import * as fs from 'fs' -import * as path from 'path' - -export { inlineWorkerPlugin as default } - -function inlineWorkerPlugin(extraConfig) { - return { - name: 'esbuild-plugin-inline-worker', - - setup(build) { - build.onLoad({ filter: /\.worker\.(js|jsx|ts|tsx)$/ }, async ({ path: workerPath }) => { - // let workerCode = await fs.promises.readFile(workerPath, { - // encoding: 'utf-8', - // }); - - let workerCode = await buildWorker(workerPath, extraConfig) - return { - contents: `import inlineWorker from '__inline-worker' -export default function Worker() { - return inlineWorker(${JSON.stringify(workerCode)}); -} -`, - loader: 'js', - } - }) - - const name = extraConfig.workerName ? { name: extraConfig.workerName } : {} - - const inlineWorkerFunctionCode = ` -export default function inlineWorker(scriptText) { - let blob = new Blob([scriptText], {type: 'text/javascript'}); - let url = URL.createObjectURL(blob); - let worker = new Worker(url, ${JSON.stringify(name)}); - URL.revokeObjectURL(url); - return worker; -} -` - - build.onResolve({ filter: /^__inline-worker$/ }, ({ path }) => { - return { path, namespace: 'inline-worker' } - }) - build.onLoad({ filter: /.*/, namespace: 'inline-worker' }, () => { - return { contents: inlineWorkerFunctionCode, loader: 'js' } - }) - }, - } -} - -let cacheDir = findCacheDir({ - name: 'esbuild-plugin-inline-worker', - create: true, -}) - -async function buildWorker(workerPath, extraConfig) { - let scriptNameParts = path.basename(workerPath).split('.') - scriptNameParts.pop() - scriptNameParts.push('js') - let scriptName = scriptNameParts.join('.') - let bundlePath = path.resolve(cacheDir, scriptName) - - if (extraConfig) { - delete extraConfig.entryPoints - delete extraConfig.outfile - delete extraConfig.outdir - delete extraConfig.workerName - } - - await esbuild.build({ - entryPoints: [workerPath], - bundle: true, - minify: true, - outfile: bundlePath, - target: 'es2017', - format: 'esm', - ...extraConfig, - }) - - return fs.promises.readFile(bundlePath, { encoding: 'utf-8' }) -} From fb7dc249113f0c7de22e14dca065d7f1bfd4025a Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 25 Sep 2025 13:24:24 -0400 Subject: [PATCH 013/182] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20relative=20paths?= =?UTF-8?q?=20not=20being=20recognized=20on=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `isRelativePath` just wasn't checking for backslashes. --- src/util/fileUtil.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/util/fileUtil.ts b/src/util/fileUtil.ts index 4ca9be1c..8053a19b 100644 --- a/src/util/fileUtil.ts +++ b/src/util/fileUtil.ts @@ -26,7 +26,12 @@ export function resolveEnvVariables(path: string) { } export function isRelativePath(path: string) { - return path.startsWith('./') || path.startsWith('../') + return ( + path.startsWith('./') || + path.startsWith('../') || + path.startsWith('.\\') || + path.startsWith('..\\') + ) } export function resolveRelativePath(path: string) { From 96d0938e9cf163a1722265122003c7cd43401e77 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 25 Sep 2025 13:33:56 -0400 Subject: [PATCH 014/182] =?UTF-8?q?=F0=9F=8E=A8=20Remove=20`path=5Fname`?= =?UTF-8?q?=20in=20favor=20of=20`storage=5Fname`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improves maintainability by reducing complexity. --- src/blueprintFormat.ts | 6 +- src/outliner/util.ts | 4 +- src/systems/animationRenderer.ts | 5 +- .../datapackCompiler/1.20.4/animation.mcb | 98 +++++++++---------- .../datapackCompiler/1.20.4/static.mcb | 34 +++---- .../datapackCompiler/1.20.5/animation.mcb | 98 +++++++++---------- .../datapackCompiler/1.20.5/static.mcb | 34 +++---- .../datapackCompiler/1.21.2/animation.mcb | 98 +++++++++---------- .../datapackCompiler/1.21.2/static.mcb | 34 +++---- .../datapackCompiler/1.21.4/animation.mcb | 98 +++++++++---------- .../datapackCompiler/1.21.4/static.mcb | 34 +++---- .../datapackCompiler/1.21.5/animation.mcb | 98 +++++++++---------- .../datapackCompiler/1.21.5/static.mcb | 34 +++---- src/systems/datapackCompiler/index.ts | 57 ++++++----- src/systems/resourcepackCompiler/1.20.4.ts | 4 +- src/systems/resourcepackCompiler/1.21.2.ts | 4 +- src/systems/resourcepackCompiler/1.21.4.ts | 4 +- src/systems/rigRenderer.ts | 20 +--- src/util/minecraftUtil.ts | 10 -- src/variants.ts | 4 +- 20 files changed, 382 insertions(+), 396 deletions(-) diff --git a/src/blueprintFormat.ts b/src/blueprintFormat.ts index 21c5935e..ea534339 100644 --- a/src/blueprintFormat.ts +++ b/src/blueprintFormat.ts @@ -6,7 +6,7 @@ import { type BillboardMode, BoneConfig, LocatorConfig } from './nodeConfigs' import { process } from './systems/modelDataFixerUpper' import { events } from './util/events' import { injectSvelteCompomponent } from './util/injectSvelteComponent' -import { sanitizePathName } from './util/minecraftUtil' +import { sanitizeStorageKey } from './util/minecraftUtil' import { addProjectToRecentProjects } from './util/misc' import { Valuable } from './util/stores' import { translate } from './util/translation' @@ -165,7 +165,7 @@ export function convertToBlueprint() { } for (const animation of Blockbench.Animation.all) { animation.createUniqueName(Blockbench.Animation.all.filter(a => a !== animation)) - animation.name = sanitizePathName(animation.name) + animation.name = sanitizeStorageKey(animation.name) } for (const cube of Cube.all) { cube.setUVMode(false) @@ -341,7 +341,7 @@ export const BLUEPRINT_CODEC = new Blockbench.Codec('animated_java_blueprint', { parseGroups(model.outliner) for (const group of Group.all) { - group.name = sanitizePathName(group.name) + group.name = sanitizeStorageKey(group.name) } } diff --git a/src/outliner/util.ts b/src/outliner/util.ts index 3b1582bf..dd842558 100644 --- a/src/outliner/util.ts +++ b/src/outliner/util.ts @@ -1,10 +1,10 @@ -import { sanitizePathName } from '../util/minecraftUtil' +import { sanitizeStorageKey } from '../util/minecraftUtil' import { TextDisplay } from './textDisplay' import { VanillaBlockDisplay } from './vanillaBlockDisplay' import { VanillaItemDisplay } from './vanillaItemDisplay' export function sanitizeOutlinerElementName(name: string, elementUUID: string): string { - name = sanitizePathName(name) + name = sanitizeStorageKey(name) let otherNodes: OutlinerElement[] = [ ...VanillaBlockDisplay.all, ...Group.all, diff --git a/src/systems/animationRenderer.ts b/src/systems/animationRenderer.ts index d43b412b..becda433 100644 --- a/src/systems/animationRenderer.ts +++ b/src/systems/animationRenderer.ts @@ -10,7 +10,7 @@ import { import { TextDisplay } from '../outliner/textDisplay' import { VanillaBlockDisplay } from '../outliner/vanillaBlockDisplay' import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay' -import { sanitizePathName, sanitizeStorageKey } from '../util/minecraftUtil' +import { sanitizeStorageKey } from '../util/minecraftUtil' import { eulerFromQuaternion, roundToNth } from '../util/misc' import type { AnyRenderedNode, IRenderedRig } from './rigRenderer' import { sleepForAnimationFrame } from './util' @@ -88,8 +88,6 @@ export interface IRenderedFrame { export interface IRenderedAnimation { name: string - /** A sanitized version of {@link IRenderedAnimation.name} that is safe to use in a path in a data pack or resource pack.*/ - path_name: string /** A sanitized version of {@link IRenderedAnimation.name} that is safe to use as a key in a storage object. */ storage_name: string uuid: string @@ -331,7 +329,6 @@ export function updatePreview(animation: _Animation, time: number) { export function renderAnimation(animation: _Animation, rig: IRenderedRig) { const rendered = { name: animation.name, - path_name: sanitizePathName(animation.name), storage_name: sanitizeStorageKey(animation.name), uuid: animation.uuid, loop_delay: Number(animation.loop_delay) || 0, diff --git a/src/systems/datapackCompiler/1.20.4/animation.mcb b/src/systems/datapackCompiler/1.20.4/animation.mcb index a22298b5..a9b61f12 100644 --- a/src/systems/datapackCompiler/1.20.4/animation.mcb +++ b/src/systems/datapackCompiler/1.20.4/animation.mcb @@ -10,7 +10,7 @@ dir <%export_namespace%> { } <%% animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) }) %%> } @@ -18,7 +18,7 @@ dir <%export_namespace%> { function remove_animation_objectives { <%% animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) }) %%> tellraw @a <%TELLRAW.UNINSTALL()%> @@ -74,8 +74,8 @@ dir <%export_namespace%> { } # Tick Playing Animations REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } IF (has_locators || has_cameras) { # Update locator and camera orientations @@ -84,13 +84,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run tp @s ~ ~ ~ ~ ~ @@ -105,7 +105,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -116,7 +116,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -139,7 +139,7 @@ dir <%export_namespace%> { dir animations { REPEAT (animations) as animation { - dir <%animation.path_name%> { + dir <%animation.storage_name%> { # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. function play { IF (show_function_errors) { @@ -148,8 +148,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -162,8 +162,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -176,7 +176,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function resume { @@ -186,7 +186,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function next_frame { @@ -196,11 +196,11 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } function set_frame { @@ -213,7 +213,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/set_frame with storage aj:temp args } @@ -226,7 +226,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/apply_frame with storage aj:temp args } @@ -241,9 +241,9 @@ dir <%export_namespace%> { } function *<%export_namespace%>/animations/pause_all - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> tag @s add <%TAGS.TRANSFORMS_ONLY()%> @@ -263,40 +263,40 @@ dir <%export_namespace%> { # Animation logic IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ block commands_keyframe_loop_patch { function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } } data remove storage aj:temp args execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> function ./apply_frame with storage aj:temp args IF (animation.loop_mode === 'loop') { # Loop the animation back to the start once it reaches the last frame. # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-2 + animation.loop_delay%>.. \ run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> } ELSE IF (animation.loop_mode === 'hold') { # Pause the animation at the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%>.. \ run return run \ function ../pause } ELSE IF (animation.loop_mode === 'once') { # Stop the animation once it reaches the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%> \ run return run \ function ../stop } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } IF (use_storage_for_animation) { @@ -305,7 +305,7 @@ dir <%export_namespace%> { REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ + data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ @@ -330,7 +330,7 @@ dir <%export_namespace%> { #ARGS: {frame: int} REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ data modify entity @s {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { @@ -426,7 +426,7 @@ dir <%export_namespace%> { if (transform.commands) { if (node.config?.use_entity) { frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` @@ -506,7 +506,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } } } @@ -550,8 +550,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%Math.ceil(locator.max_distance)%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -567,8 +567,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -584,8 +584,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block as_camera/<%camera.path_name%> { - # run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -595,14 +595,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -734,7 +734,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -781,7 +781,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -890,7 +890,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.tag.CustomModelData set value 1 @@ -927,7 +927,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} } } @@ -943,7 +943,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.20.4/static.mcb b/src/systems/datapackCompiler/1.20.4/static.mcb index a68f25e6..8a27205d 100644 --- a/src/systems/datapackCompiler/1.20.4/static.mcb +++ b/src/systems/datapackCompiler/1.20.4/static.mcb @@ -56,13 +56,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run tp @s ~ ~ ~ ~ ~ @@ -77,7 +77,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -88,7 +88,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -147,8 +147,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%locator.max_distance + 1%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -164,8 +164,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -181,7 +181,7 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%camera.max_distance + 1%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -191,14 +191,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -287,7 +287,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -334,7 +334,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -443,7 +443,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.tag.CustomModelData set value 1 @@ -480,7 +480,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.20.5/animation.mcb b/src/systems/datapackCompiler/1.20.5/animation.mcb index 987d6c35..0aba1b09 100644 --- a/src/systems/datapackCompiler/1.20.5/animation.mcb +++ b/src/systems/datapackCompiler/1.20.5/animation.mcb @@ -10,7 +10,7 @@ dir <%export_namespace%> { } <%% animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) }) %%> } @@ -18,7 +18,7 @@ dir <%export_namespace%> { function remove_animation_objectives { <%% animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) }) %%> tellraw @a <%TELLRAW.UNINSTALL()%> @@ -74,8 +74,8 @@ dir <%export_namespace%> { } # Tick Playing Animations REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } IF (has_locators || has_cameras) { # Update locator and camera orientations @@ -84,13 +84,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run tp @s ~ ~ ~ ~ ~ @@ -105,7 +105,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -116,7 +116,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -139,7 +139,7 @@ dir <%export_namespace%> { dir animations { REPEAT (animations) as animation { - dir <%animation.path_name%> { + dir <%animation.storage_name%> { # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. function play { IF (show_function_errors) { @@ -148,8 +148,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -162,8 +162,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -176,7 +176,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function resume { @@ -186,7 +186,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function next_frame { @@ -196,11 +196,11 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } function set_frame { @@ -213,7 +213,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/set_frame with storage aj:temp args } @@ -226,7 +226,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/apply_frame with storage aj:temp args } @@ -241,9 +241,9 @@ dir <%export_namespace%> { } function *<%export_namespace%>/animations/pause_all - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> tag @s add <%TAGS.TRANSFORMS_ONLY()%> @@ -263,40 +263,40 @@ dir <%export_namespace%> { # Animation logic IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ block commands_keyframe_loop_patch { function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } } data remove storage aj:temp args execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> function ./apply_frame with storage aj:temp args IF (animation.loop_mode === 'loop') { # Loop the animation back to the start once it reaches the last frame. # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-2 + animation.loop_delay%>.. \ run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> } ELSE IF (animation.loop_mode === 'hold') { # Pause the animation at the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%>.. \ run return run \ function ../pause } ELSE IF (animation.loop_mode === 'once') { # Stop the animation once it reaches the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%> \ run return run \ function ../stop } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } IF (use_storage_for_animation) { @@ -305,7 +305,7 @@ dir <%export_namespace%> { REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ + data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ @@ -330,7 +330,7 @@ dir <%export_namespace%> { #ARGS: {frame: int} REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ data modify entity @s {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { @@ -426,7 +426,7 @@ dir <%export_namespace%> { if (transform.commands) { if (node.config?.use_entity) { frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` @@ -506,7 +506,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } } } @@ -550,8 +550,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%Math.ceil(locator.max_distance)%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -567,8 +567,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -584,8 +584,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block as_camera/<%camera.path_name%> { - # run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -595,14 +595,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -734,7 +734,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -781,7 +781,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -890,7 +890,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components.minecraft:custom_model_data set value 1 @@ -927,7 +927,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} } } @@ -943,7 +943,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.20.5/static.mcb b/src/systems/datapackCompiler/1.20.5/static.mcb index 6b474dd2..984054fa 100644 --- a/src/systems/datapackCompiler/1.20.5/static.mcb +++ b/src/systems/datapackCompiler/1.20.5/static.mcb @@ -56,13 +56,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run tp @s ~ ~ ~ ~ ~ @@ -77,7 +77,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -88,7 +88,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -147,8 +147,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%locator.max_distance + 1%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -164,8 +164,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -181,7 +181,7 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%camera.max_distance + 1%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -191,14 +191,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -287,7 +287,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -334,7 +334,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -443,7 +443,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components.minecraft:custom_model_data set value 1 @@ -480,7 +480,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.21.2/animation.mcb b/src/systems/datapackCompiler/1.21.2/animation.mcb index 1e89a4fc..83e3f85e 100644 --- a/src/systems/datapackCompiler/1.21.2/animation.mcb +++ b/src/systems/datapackCompiler/1.21.2/animation.mcb @@ -10,7 +10,7 @@ dir <%export_namespace%> { } <%% animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) }) %%> } @@ -18,7 +18,7 @@ dir <%export_namespace%> { function remove_animation_objectives { <%% animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) }) %%> tellraw @a <%TELLRAW.UNINSTALL()%> @@ -74,8 +74,8 @@ dir <%export_namespace%> { } # Tick Playing Animations REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } IF (has_locators || has_cameras) { # Update locator and camera orientations @@ -84,13 +84,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run rotate @s ~ ~ @@ -105,7 +105,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -116,7 +116,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -139,7 +139,7 @@ dir <%export_namespace%> { dir animations { REPEAT (animations) as animation { - dir <%animation.path_name%> { + dir <%animation.storage_name%> { # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. function play { IF (show_function_errors) { @@ -148,8 +148,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -162,8 +162,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -176,7 +176,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function resume { @@ -186,7 +186,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function next_frame { @@ -196,11 +196,11 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } function set_frame { @@ -213,7 +213,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/set_frame with storage aj:temp args } @@ -226,7 +226,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/apply_frame with storage aj:temp args } @@ -241,9 +241,9 @@ dir <%export_namespace%> { } function *<%export_namespace%>/animations/pause_all - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> tag @s add <%TAGS.TRANSFORMS_ONLY()%> @@ -263,40 +263,40 @@ dir <%export_namespace%> { # Animation logic IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ block commands_keyframe_loop_patch { function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } } data remove storage aj:temp args execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> function ./apply_frame with storage aj:temp args IF (animation.loop_mode === 'loop') { # Loop the animation back to the start once it reaches the last frame. # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-2 + animation.loop_delay%>.. \ run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> } ELSE IF (animation.loop_mode === 'hold') { # Pause the animation at the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%>.. \ run return run \ function ../pause } ELSE IF (animation.loop_mode === 'once') { # Stop the animation once it reaches the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%> \ run return run \ function ../stop } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } IF (use_storage_for_animation) { @@ -305,7 +305,7 @@ dir <%export_namespace%> { REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ + data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ @@ -330,7 +330,7 @@ dir <%export_namespace%> { #ARGS: {frame: int} REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ data modify entity @s {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { @@ -426,7 +426,7 @@ dir <%export_namespace%> { if (transform.commands) { if (node.config?.use_entity) { frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` @@ -506,7 +506,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } } } @@ -550,8 +550,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%Math.ceil(locator.max_distance)%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -567,8 +567,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -584,8 +584,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block as_camera/<%camera.path_name%> { - # run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -595,14 +595,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -734,7 +734,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -781,7 +781,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -890,7 +890,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components."minecraft:item_model" set value "animated_java:empty" @@ -927,7 +927,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} } } @@ -943,7 +943,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.21.2/static.mcb b/src/systems/datapackCompiler/1.21.2/static.mcb index 5e93d120..2c663217 100644 --- a/src/systems/datapackCompiler/1.21.2/static.mcb +++ b/src/systems/datapackCompiler/1.21.2/static.mcb @@ -56,13 +56,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run rotate @s ~ ~ @@ -77,7 +77,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -88,7 +88,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -147,8 +147,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%locator.max_distance + 1%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -164,8 +164,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -181,7 +181,7 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%camera.max_distance + 1%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -191,14 +191,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -287,7 +287,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -334,7 +334,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -443,7 +443,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components.minecraft:item_model set value "animated_java:empty" @@ -480,7 +480,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.21.4/animation.mcb b/src/systems/datapackCompiler/1.21.4/animation.mcb index 81b14b60..f6e7ea01 100644 --- a/src/systems/datapackCompiler/1.21.4/animation.mcb +++ b/src/systems/datapackCompiler/1.21.4/animation.mcb @@ -10,7 +10,7 @@ dir <%export_namespace%> { } <%% animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) }) %%> } @@ -18,7 +18,7 @@ dir <%export_namespace%> { function remove_animation_objectives { <%% animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) }) %%> tellraw @a <%TELLRAW.UNINSTALL()%> @@ -74,8 +74,8 @@ dir <%export_namespace%> { } # Tick Playing Animations REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } IF (has_locators || has_cameras) { # Update locator and camera orientations @@ -84,13 +84,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run rotate @s ~ ~ @@ -105,7 +105,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -116,7 +116,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -139,7 +139,7 @@ dir <%export_namespace%> { dir animations { REPEAT (animations) as animation { - dir <%animation.path_name%> { + dir <%animation.storage_name%> { # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. function play { IF (show_function_errors) { @@ -148,8 +148,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -162,8 +162,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -176,7 +176,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function resume { @@ -186,7 +186,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function next_frame { @@ -196,11 +196,11 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } function set_frame { @@ -213,7 +213,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/set_frame with storage aj:temp args } @@ -226,7 +226,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/apply_frame with storage aj:temp args } @@ -241,9 +241,9 @@ dir <%export_namespace%> { } function *<%export_namespace%>/animations/pause_all - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> tag @s add <%TAGS.TRANSFORMS_ONLY()%> @@ -263,40 +263,40 @@ dir <%export_namespace%> { # Animation logic IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ block commands_keyframe_loop_patch { function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } } data remove storage aj:temp args execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> function ./apply_frame with storage aj:temp args IF (animation.loop_mode === 'loop') { # Loop the animation back to the start once it reaches the last frame. # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-2 + animation.loop_delay%>.. \ run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> } ELSE IF (animation.loop_mode === 'hold') { # Pause the animation at the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%>.. \ run return run \ function ../pause } ELSE IF (animation.loop_mode === 'once') { # Stop the animation once it reaches the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%> \ run return run \ function ../stop } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } IF (use_storage_for_animation) { @@ -305,7 +305,7 @@ dir <%export_namespace%> { REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ + data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ @@ -330,7 +330,7 @@ dir <%export_namespace%> { #ARGS: {frame: int} REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ data modify entity @s {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { @@ -426,7 +426,7 @@ dir <%export_namespace%> { if (transform.commands) { if (node.config?.use_entity) { frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` @@ -506,7 +506,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } } } @@ -550,8 +550,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%Math.ceil(locator.max_distance)%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -567,8 +567,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -584,8 +584,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block as_camera/<%camera.path_name%> { - # run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -595,14 +595,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -734,7 +734,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -781,7 +781,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -890,7 +890,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" @@ -927,7 +927,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} } } @@ -943,7 +943,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.21.4/static.mcb b/src/systems/datapackCompiler/1.21.4/static.mcb index b611a990..c4d21e87 100644 --- a/src/systems/datapackCompiler/1.21.4/static.mcb +++ b/src/systems/datapackCompiler/1.21.4/static.mcb @@ -56,13 +56,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run rotate @s ~ ~ @@ -77,7 +77,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -88,7 +88,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -147,8 +147,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%locator.max_distance + 1%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -164,8 +164,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -181,7 +181,7 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%camera.max_distance + 1%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -191,14 +191,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -287,7 +287,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -334,7 +334,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -443,7 +443,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" @@ -480,7 +480,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.21.5/animation.mcb b/src/systems/datapackCompiler/1.21.5/animation.mcb index 237b9e88..d9a9ff19 100644 --- a/src/systems/datapackCompiler/1.21.5/animation.mcb +++ b/src/systems/datapackCompiler/1.21.5/animation.mcb @@ -10,7 +10,7 @@ dir <%export_namespace%> { } <%% animations.forEach(animation => { - emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.path_name)} dummy`) + emit(`scoreboard objectives add ${OBJECTIVES.FRAME(animation.storage_name)} dummy`) }) %%> } @@ -18,7 +18,7 @@ dir <%export_namespace%> { function remove_animation_objectives { <%% animations.forEach(animation => { - emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.path_name)}`) + emit(`scoreboard objectives remove ${OBJECTIVES.FRAME(animation.storage_name)}`) }) %%> tellraw @a <%TELLRAW.UNINSTALL()%> @@ -74,8 +74,8 @@ dir <%export_namespace%> { } # Tick Playing Animations REPEAT (animations) as animation { - execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%>] run \ - function *<%export_namespace%>/animations/<%animation.path_name%>/zzz/on_tick + execute if entity @s[tag=<%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%>] run \ + function *<%export_namespace%>/animations/<%animation.storage_name%>/zzz/on_tick } IF (has_locators || has_cameras) { # Update locator and camera orientations @@ -84,13 +84,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run rotate @s ~ ~ @@ -105,7 +105,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -116,7 +116,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -139,7 +139,7 @@ dir <%export_namespace%> { dir animations { REPEAT (animations) as animation { - dir <%animation.path_name%> { + dir <%animation.storage_name%> { # TODO: Maybe add an exclusive argument to the play function that will pause all other animations before playing this one. function play { IF (show_function_errors) { @@ -148,8 +148,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -162,8 +162,8 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 0 + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 0 tag @s add <%TAGS.TRANSFORMS_ONLY()%> execute at @s run function ./zzz/set_frame {frame: 0} tag @s remove <%TAGS.TRANSFORMS_ONLY()%> @@ -176,7 +176,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function resume { @@ -186,7 +186,7 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } function next_frame { @@ -196,11 +196,11 @@ dir <%export_namespace%> { function *global/errors/function_not_executed_as_root_entity \ {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches <%animation.duration%>.. run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 data remove storage aj:temp args - execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + execute store result storage aj:temp args.frame int 1 run scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> execute at @s run function ./zzz/apply_frame with storage aj:temp args - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } function set_frame { @@ -213,7 +213,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/set_frame with storage aj:temp args } @@ -226,7 +226,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } data remove storage aj:temp args - $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(frame) + $execute store result storage aj:temp args.frame int 1 run scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(frame) execute at @s run function ./zzz/apply_frame with storage aj:temp args } @@ -241,9 +241,9 @@ dir <%export_namespace%> { } function *<%export_namespace%>/animations/pause_all - tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s add <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> $scoreboard players set @s <%OBJECTIVES.TWEEN_DURATION()%> $(duration) - $scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> $(to_frame) + $scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> $(to_frame) scoreboard players operation #this <%OBJECTIVES.I()%> = @s <%OBJECTIVES.TWEEN_DURATION()%> tag @s add <%TAGS.TRANSFORMS_ONLY()%> @@ -263,40 +263,40 @@ dir <%export_namespace%> { # Animation logic IF (animation.loop_mode === 'loop' && animation.loop_delay === 0) { # Makes sure commands keyframes in the last frame of the animation are activated. - execute if score @s <%OBJECTIVES.FRAME(animation.path_name)%> matches -1 run \ + execute if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> matches -1 run \ block commands_keyframe_loop_patch { function ./apply_frame {frame: <%animation.duration-1%>} - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } } data remove storage aj:temp args execute store result storage aj:temp args.frame int 1 run \ - scoreboard players get @s <%OBJECTIVES.FRAME(animation.path_name)%> + scoreboard players get @s <%OBJECTIVES.FRAME(animation.storage_name)%> function ./apply_frame with storage aj:temp args IF (animation.loop_mode === 'loop') { # Loop the animation back to the start once it reaches the last frame. # If loop_delay is 0, the animation will loop instantly, otherwise, it will wait for the specified amount of ticks. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-2 + animation.loop_delay%>.. \ run return run \ - scoreboard players set @s <%OBJECTIVES.FRAME(animation.path_name)%> <%animation.loop_delay === 0 ? -1 : 0%> + scoreboard players set @s <%OBJECTIVES.FRAME(animation.storage_name)%> <%animation.loop_delay === 0 ? -1 : 0%> } ELSE IF (animation.loop_mode === 'hold') { # Pause the animation at the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%>.. \ run return run \ function ../pause } ELSE IF (animation.loop_mode === 'once') { # Stop the animation once it reaches the last frame. execute \ - if score @s <%OBJECTIVES.FRAME(animation.path_name)%> \ + if score @s <%OBJECTIVES.FRAME(animation.storage_name)%> \ matches <%animation.duration-1%> \ run return run \ function ../stop } - scoreboard players add @s <%OBJECTIVES.FRAME(animation.path_name)%> 1 + scoreboard players add @s <%OBJECTIVES.FRAME(animation.storage_name)%> 1 } IF (use_storage_for_animation) { @@ -305,7 +305,7 @@ dir <%export_namespace%> { REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { $execute on passengers run \ - data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] {} merge from \ + data modify entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { $execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run \ @@ -330,7 +330,7 @@ dir <%export_namespace%> { #ARGS: {frame: int} REPEAT (Object.values(animation.modified_nodes).sort(nodeSorter)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.path_name)%>] run \ + $execute on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ data modify entity @s {} merge from \ storage aj.<%export_namespace%>:animations <%animation.storage_name%>.$(frame).<%node.type.charAt(0) + '_' + node.storage_name%> } ELSE IF (['locator', 'camera'].includes(node.type)) { @@ -426,7 +426,7 @@ dir <%export_namespace%> { if (transform.commands) { if (node.config?.use_entity) { frameFunc += - `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.path_name}) ` + `\n$execute on vehicle unless entity @s[tag=${TAGS.TRANSFORMS_ONLY()}] as $(${node.type + '_' + node.storage_name}) ` + `positioned ^${roundTo(transform.pos[0], 10)} ^${roundTo(transform.pos[1], 10)} ^${roundTo(transform.pos[2], 10)} ` + `rotated ~${roundTo(transform.head_rot[1], 10)} ~${roundTo(transform.head_rot[0], 10)} ` + `${transform.commands_execute_condition ? transform.commands_execute_condition + ' ' : ''}run ` @@ -506,7 +506,7 @@ dir <%export_namespace%> { {'export_namespace': '<%export_namespace%>', 'function_path': '<%arguments[1].functions.at(-1)%>'} } REPEAT (animations) as animation { - tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.path_name)%> + tag @s remove <%TAGS.ANIMATION_PLAYING(export_namespace, animation.storage_name)%> } } } @@ -550,8 +550,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%Math.ceil(locator.max_distance)%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -567,8 +567,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -584,8 +584,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%Math.ceil(camera.max_distance)%> \ ] \ - run block as_camera/<%camera.path_name%> { - # run block ../as_camera/<%camera.path_name%> { + run block as_camera/<%camera.storage_name%> { + # run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -595,14 +595,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -734,7 +734,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -781,7 +781,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -890,7 +890,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" @@ -927,7 +927,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: 0} } } @@ -943,7 +943,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/1.21.5/static.mcb b/src/systems/datapackCompiler/1.21.5/static.mcb index b611a990..c4d21e87 100644 --- a/src/systems/datapackCompiler/1.21.5/static.mcb +++ b/src/systems/datapackCompiler/1.21.5/static.mcb @@ -56,13 +56,13 @@ dir <%export_namespace%> { if entity @s[tag=<%TAGS.PROJECT_DATA(export_namespace)%>] \ run block zzz/on_tick/locators_and_cameras { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator')) as node { - block select_locator_<%node.path_name%> { with entity @s data.locators.<%node.storage_name%> + block select_locator_<%node.storage_name%> { with entity @s data.locators.<%node.storage_name%> IF (node.config?.use_entity) { $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { tp @s ~ ~ ~ ~ ~ IF (node.config?.sync_passenger_rotation) { execute on passengers run rotate @s ~ ~ @@ -77,7 +77,7 @@ dir <%export_namespace%> { $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ rotated ~$(roty) ~$(rotx) \ - run block as_locator_<%node.path_name%> { + run block as_locator_<%node.storage_name%> { <%% if (node.config?.ticking_commands) { emit.mcb(node.config.ticking_commands) @@ -88,7 +88,7 @@ dir <%export_namespace%> { } } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as node { - block select_camera_<%node.path_name%> { with entity @s data.cameras.<%node.storage_name%> + block select_camera_<%node.storage_name%> { with entity @s data.cameras.<%node.storage_name%> $execute \ as $(uuid) \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -147,8 +147,8 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_LOCATOR_NAMED(export_namespace, locator.storage_name)%>, \ distance=..<%locator.max_distance + 1%> \ ] \ - run block as_locator/<%locator.path_name%> { - # run block ../as_locator/<%locator.path_name%> { + run block as_locator/<%locator.storage_name%> { + # run block ../as_locator/<%locator.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string @@ -164,8 +164,8 @@ dir <%export_namespace%> { } %%> } - data modify entity @s data.uuids.<%locator.type + '_' + locator.name%> set from storage aj:uuid main.out - data modify entity @s data.locators.<%locator.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.locators.<%locator.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { @@ -181,7 +181,7 @@ dir <%export_namespace%> { tag=<%TAGS.PROJECT_CAMERA_NAMED(export_namespace, camera.storage_name)%>, \ distance=..<%camera.max_distance + 1%> \ ] \ - run block ../as_camera/<%camera.path_name%> { + run block ../as_camera/<%camera.storage_name%> { tag @s remove <%TAGS.NEW()%> function *global/internal/gu/convert_uuid_array_to_string tp @s \ @@ -191,14 +191,14 @@ dir <%export_namespace%> { ~<%roundTo(camera.default_transform.head_rot[1], 10)%> \ ~<%roundTo(camera.default_transform.head_rot[0], 10)%> } - data modify entity @s data.uuids.<%camera.type + '_' + camera.name%> set from storage aj:uuid main.out - data modify entity @s data.cameras.<%camera.name%>.uuid set from storage aj:uuid main.out + data modify entity @s data.uuids.<%camera.type + '_' + camera.storage_name%> set from storage aj:uuid main.out + data modify entity @s data.cameras.<%camera.storage_name%>.uuid set from storage aj:uuid main.out } REPEAT (Object.values(rig.nodes).filter(v => ['bone', 'text_display', 'item_display', 'block_display'].includes(v.type))) as node { - execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.name)%>] run \ + execute on vehicle on passengers if entity @s[tag=<%TAGS.PROJECT_NODE_NAMED(export_namespace, node.storage_name)%>] run \ function *global/internal/gu/convert_uuid_array_to_string - data modify entity @s data.uuids.<%node.type + '_' + node.name%> set from storage aj:uuid main.out + data modify entity @s data.uuids.<%node.type + '_' + node.storage_name%> set from storage aj:uuid main.out } } @@ -287,7 +287,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/as_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.name%> + data modify storage aj:temp args.uuid set from entity @s data.uuids.<%locator.type + '_' + locator.storage_name%> block execute_as_uuid { with storage aj:temp args $execute as $(uuid) at @s run $(command) } @@ -334,7 +334,7 @@ dir <%export_namespace%> { $data modify storage aj:temp args.command set value '$(command)' execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/at_all_locators/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { - data modify storage aj:temp args merge from entity @s data.locators.<%locator.name%> + data modify storage aj:temp args merge from entity @s data.locators.<%locator.storage_name%> block execute_at_transform { with storage aj:temp args $execute \ positioned ^$(posx) ^$(posy) ^$(posz) \ @@ -443,7 +443,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (node.type === 'bone' && !variant.excluded_nodes.includes(node.uuid) && (variant.models[node.uuid] !== undefined || node.configs.variants[variant.uuid] !== undefined)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run block zzz/apply_to_bone_<%node.path_name%> { + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run block zzz/apply_to_bone_<%node.storage_name%> { IF (variant.models[node.uuid] !== undefined) { IF (variant.models[node.uuid].model === null) { data modify entity @s item.components."minecraft:custom_model_data".strings[0] set value "AJ_INTERNAL_EMPTY" @@ -480,7 +480,7 @@ dir <%export_namespace%> { } REPEAT (Object.values(rig.nodes)) as node { IF (['bone', 'text_display', 'item_display', 'block_display'].includes(node.type)) { - execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.path_name%>] run \ + execute on passengers if entity @s[tag=aj.<%export_namespace%>.bone.<%node.storage_name%>] run \ data merge entity @s {transformation: <%matrixToNbtFloatArray(node.default_transform.matrix).toString()%>, start_interpolation: -1} } } diff --git a/src/systems/datapackCompiler/index.ts b/src/systems/datapackCompiler/index.ts index 112c8430..6e65d240 100644 --- a/src/systems/datapackCompiler/index.ts +++ b/src/systems/datapackCompiler/index.ts @@ -65,7 +65,7 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { // Root is ignored } else if (n.parent) { parentNames.push({ - name: rig.nodes[n.parent].path_name, + name: rig.nodes[n.parent].storage_name, type: rig.nodes[n.parent].type, }) recurseParents(rig.nodes[n.parent]) @@ -80,11 +80,11 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { TAGS.NEW(), TAGS.GLOBAL_ENTITY(), TAGS.GLOBAL_NODE(), - TAGS.GLOBAL_NODE_NAMED(node.path_name), + TAGS.GLOBAL_NODE_NAMED(node.storage_name), // Project TAGS.PROJECT_ENTITY(Project!.animated_java.export_namespace), TAGS.PROJECT_NODE(Project!.animated_java.export_namespace), - TAGS.PROJECT_NODE_NAMED(Project!.animated_java.export_namespace, node.path_name) + TAGS.PROJECT_NODE_NAMED(Project!.animated_java.export_namespace, node.storage_name) ) if (!hasParent) { @@ -94,19 +94,22 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'bone': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_BONE(), - TAGS.GLOBAL_BONE_TREE(node.path_name), // Tree includes self - TAGS.GLOBAL_BONE_TREE_BONE(node.path_name), // Tree includes self + TAGS.GLOBAL_BONE_TREE(node.storage_name), // Tree includes self + TAGS.GLOBAL_BONE_TREE_BONE(node.storage_name), // Tree includes self // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_BONE(Project!.animated_java.export_namespace), - TAGS.PROJECT_BONE_NAMED(Project!.animated_java.export_namespace, node.path_name), - TAGS.PROJECT_BONE_TREE(Project!.animated_java.export_namespace, node.path_name), // Tree includes self - TAGS.PROJECT_BONE_TREE_BONE(Project!.animated_java.export_namespace, node.path_name) // Tree includes self + TAGS.PROJECT_BONE_NAMED(Project!.animated_java.export_namespace, node.storage_name), + TAGS.PROJECT_BONE_TREE(Project!.animated_java.export_namespace, node.storage_name), // Tree includes self + TAGS.PROJECT_BONE_TREE_BONE( + Project!.animated_java.export_namespace, + node.storage_name + ) // Tree includes self ) if (!hasParent) { // Nodes without parents are assumed to be root nodes @@ -146,17 +149,17 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'item_display': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_ITEM_DISPLAY(), // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_ITEM_DISPLAY(Project!.animated_java.export_namespace), TAGS.PROJECT_ITEM_DISPLAY_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ) ) if (!hasParent) { @@ -198,17 +201,17 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'block_display': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_BLOCK_DISPLAY(), // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_BLOCK_DISPLAY(Project!.animated_java.export_namespace), TAGS.PROJECT_BLOCK_DISPLAY_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ) ) if (!hasParent) { @@ -250,17 +253,17 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { case 'text_display': { tags.push( // Global - TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.path_name), + TAGS.GLOBAL_DISPLAY_NODE_NAMED(node.storage_name), TAGS.GLOBAL_TEXT_DISPLAY(), // Project TAGS.PROJECT_DISPLAY_NODE_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ), TAGS.PROJECT_TEXT_DISPLAY(Project!.animated_java.export_namespace), TAGS.PROJECT_TEXT_DISPLAY_NAMED( Project!.animated_java.export_namespace, - node.path_name + node.storage_name ) ) if (!hasParent) { @@ -305,7 +308,10 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { TAGS.GLOBAL_LOCATOR(), // Project TAGS.PROJECT_LOCATOR(Project!.animated_java.export_namespace), - TAGS.PROJECT_LOCATOR_NAMED(Project!.animated_java.export_namespace, node.path_name) + TAGS.PROJECT_LOCATOR_NAMED( + Project!.animated_java.export_namespace, + node.storage_name + ) ) if (!hasParent) { // Nodes without parents are assumed to be root nodes @@ -349,7 +355,10 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { TAGS.GLOBAL_CAMERA(), // Project TAGS.PROJECT_CAMERA(Project!.animated_java.export_namespace), - TAGS.PROJECT_CAMERA_NAMED(Project!.animated_java.export_namespace, node.path_name) + TAGS.PROJECT_CAMERA_NAMED( + Project!.animated_java.export_namespace, + node.storage_name + ) ) if (!hasParent) { // Nodes without parents are assumed to be root nodes @@ -555,7 +564,7 @@ namespace TELLRAW { ' ', ' ', { text: ' ● ', color: 'gray' }, - { text: anim.path_name, color: 'yellow' }, + { text: anim.storage_name, color: 'yellow' }, ]) ), TELLRAW_SUFFIX(), @@ -719,7 +728,7 @@ async function generateRootEntityPassengers( const item = new NbtCompound() const variantModel = rig.variants[Variant.getDefault().uuid].models[uuid] if (!variantModel) { - throw new Error(`Model for bone '${node.path_name}' not found!`) + throw new Error(`Model for bone '${node.storage_name}' not found!`) } passenger.set('item', item.set('id', new NbtString(aj.display_item))) switch (version) { @@ -867,7 +876,7 @@ async function generateRootEntityPassengers( const parsed = await parseBlock(node.block) if (!parsed) { throw new Error( - `Invalid Blockstate '${node.block}' in node '${node.path_name}'!` + `Invalid Blockstate '${node.block}' in node '${node.storage_name}'!` ) } diff --git a/src/systems/resourcepackCompiler/1.20.4.ts b/src/systems/resourcepackCompiler/1.20.4.ts index a045b639..148719f1 100644 --- a/src/systems/resourcepackCompiler/1.20.4.ts +++ b/src/systems/resourcepackCompiler/1.20.4.ts @@ -1,6 +1,6 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' -import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' +import { isResourcePackPath, sanitizeStorageKey } from '../../util/minecraftUtil' import type { ITextureAtlas } from '../minecraft/textureAtlas' import type { IRenderedNodes } from '../rigRenderer' import { sortObjectKeys } from '../util' @@ -186,7 +186,7 @@ const compileResourcePack: ResourcePackCompiler = async ({ throw new Error(`Texture ${texture.name} is missing it's image data.`) } - let textureName = sanitizePathName(texture.name) + let textureName = sanitizeStorageKey(texture.name) if (!texture.name.endsWith('.png')) textureName += '.png' versionedFiles.set(PathModule.join(textureExportFolder, textureName), { content: image }) if (mcmeta !== undefined) diff --git a/src/systems/resourcepackCompiler/1.21.2.ts b/src/systems/resourcepackCompiler/1.21.2.ts index c443cd37..a5059bc5 100644 --- a/src/systems/resourcepackCompiler/1.21.2.ts +++ b/src/systems/resourcepackCompiler/1.21.2.ts @@ -1,7 +1,7 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' import { safeReadSync } from '../../util/fileUtil' -import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' +import { isResourcePackPath, sanitizeStorageKey } from '../../util/minecraftUtil' import { type ITextureAtlas } from '../minecraft/textureAtlas' import type { IRenderedNodes } from '../rigRenderer' @@ -84,7 +84,7 @@ const compileResourcePack: ResourcePackCompiler = async ({ throw new Error(`Texture ${texture.name} is missing it's image data.`) } - let textureName = sanitizePathName(texture.name) + let textureName = sanitizeStorageKey(texture.name) if (!texture.name.endsWith('.png')) textureName += '.png' versionedFiles.set(PathModule.join(textureExportFolder, textureName), { content: image }) if (mcmeta !== undefined) diff --git a/src/systems/resourcepackCompiler/1.21.4.ts b/src/systems/resourcepackCompiler/1.21.4.ts index 6d72edc6..a5b3dae7 100644 --- a/src/systems/resourcepackCompiler/1.21.4.ts +++ b/src/systems/resourcepackCompiler/1.21.4.ts @@ -1,6 +1,6 @@ import type { ResourcePackCompiler } from '.' import { PROGRESS_DESCRIPTION } from '../../interface/dialog/exportProgress' -import { isResourcePackPath, sanitizePathName } from '../../util/minecraftUtil' +import { isResourcePackPath, sanitizeStorageKey } from '../../util/minecraftUtil' import { Variant } from '../../variants' import type { IItemDefinition } from '../minecraft/itemDefinitions' import { type ITextureAtlas } from '../minecraft/textureAtlas' @@ -94,7 +94,7 @@ const compileResourcePack: ResourcePackCompiler = async ({ throw new Error(`Texture ${texture.name} is missing it's image data.`) } - let textureName = sanitizePathName(texture.name) + let textureName = sanitizeStorageKey(texture.name) if (!texture.name.endsWith('.png')) textureName += '.png' versionedFiles.set(PathModule.join(textureExportFolder, textureName), { content: image }) if (mcmeta !== undefined) diff --git a/src/systems/rigRenderer.ts b/src/systems/rigRenderer.ts index 9623d89a..309e541c 100644 --- a/src/systems/rigRenderer.ts +++ b/src/systems/rigRenderer.ts @@ -13,7 +13,6 @@ import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay' import { type IMinecraftResourceLocation, parseResourcePackPath, - sanitizePathName, sanitizeStorageKey, } from '../util/minecraftUtil' import { Variant } from '../variants' @@ -66,8 +65,6 @@ export interface IRenderedNode { type: string /** The origin name of the node */ name: string - /** A sanitized version of {@link IRenderedNode.name} that is safe to use in a path in a data pack or resource pack.*/ - path_name: string /** A sanitized version of {@link IRenderedNode.name} that is safe to use as a key in a storage object. */ storage_name: string /** @@ -294,7 +291,7 @@ export function getTextureResourceLocation(texture: Texture, rig: IRenderedRig) return parsed } } - const path = PathModule.join(rig.texture_export_folder, sanitizePathName(texture.name)) + const path = PathModule.join(rig.texture_export_folder, sanitizeStorageKey(texture.name)) const parsed = parseResourcePackPath(path) if (parsed) { TEXTURE_RESOURCE_LOCATION_CACHE.set(texture.uuid, parsed) @@ -347,7 +344,6 @@ function renderGroup( const renderedBone: IRenderedNodes['Bone'] = { type: 'bone', name: group.name, - path_name: sanitizePathName(group.name), storage_name: sanitizeStorageKey(group.name), uuid: group.uuid, parent: parentId, @@ -415,7 +411,6 @@ function renderGroup( const struct: IRenderedNodes['Struct'] = { type: 'struct', name: group.name, - path_name: sanitizePathName(group.name), storage_name: sanitizeStorageKey(group.name), uuid: group.uuid, parent: parentId, @@ -458,7 +453,6 @@ function renderItemDisplay(display: VanillaItemDisplay, rig: IRenderedRig) { const renderedBone: IRenderedNodes['ItemDisplay'] = { type: 'item_display', name: display.name, - path_name: sanitizePathName(display.name), storage_name: sanitizeStorageKey(display.name), uuid: display.uuid, parent: parentId, @@ -487,7 +481,6 @@ function renderBlockDisplay(display: VanillaBlockDisplay, rig: IRenderedRig) { const renderedBone: IRenderedNodes['BlockDisplay'] = { type: 'block_display', name: display.name, - path_name: sanitizePathName(display.name), storage_name: sanitizeStorageKey(display.name), uuid: display.uuid, block: display.block, @@ -515,7 +508,6 @@ function renderTextDisplay(display: TextDisplay, rig: IRenderedRig): INodeStruct const renderedBone: IRenderedNodes['TextDisplay'] = { type: 'text_display', name: display.name, - path_name: sanitizePathName(display.name), storage_name: sanitizeStorageKey(display.name), uuid: display.uuid, parent: parentId, @@ -545,7 +537,6 @@ function renderLocator(locator: Locator, rig: IRenderedRig) { const renderedLocator: IRenderedNodes['Locator'] = { type: 'locator', name: locator.name, - path_name: sanitizePathName(locator.name), storage_name: sanitizeStorageKey(locator.name), uuid: locator.uuid, parent: parentId, @@ -564,7 +555,6 @@ function renderCamera(camera: ICamera, rig: IRenderedRig) { const renderedCamera: IRenderedNodes['Camera'] = { type: 'camera', name: camera.name, - path_name: sanitizePathName(camera.name), storage_name: sanitizeStorageKey(camera.name), uuid: camera.uuid, parent: parentId, @@ -614,15 +604,15 @@ function renderVariantModels(variant: Variant, rig: IRenderedRig) { continue } - const modelParent = PathModule.join(rig.model_export_folder, bone.path_name + '.json') + const modelParent = PathModule.join(rig.model_export_folder, bone.storage_name + '.json') const parsed = parseResourcePackPath(modelParent) if (!parsed) { - throw new Error(`Invalid Bone Name: '${bone.path_name}' -> '${modelParent}'`) + throw new Error(`Invalid Bone Name: '${bone.storage_name}' -> '${modelParent}'`) } const modelPath = variant.isDefault - ? PathModule.join(rig.model_export_folder, bone.path_name + '.json') - : PathModule.join(rig.model_export_folder, variant.name, bone.path_name + '.json') + ? PathModule.join(rig.model_export_folder, bone.storage_name + '.json') + : PathModule.join(rig.model_export_folder, variant.name, bone.storage_name + '.json') const parsedModelPath = parseResourcePackPath(modelPath) if (!parsedModelPath) { throw new Error(`Invalid Variant Name: '${variant.name}' -> '${modelPath}'`) diff --git a/src/util/minecraftUtil.ts b/src/util/minecraftUtil.ts index 2b3a6d37..f2fae0ee 100644 --- a/src/util/minecraftUtil.ts +++ b/src/util/minecraftUtil.ts @@ -17,16 +17,6 @@ export interface IMinecraftResourceLocation { type: string } -/** - * Return a sanitized version of {@param str} that is safe to use as a path name in a data pack or resource pack. - * - * Function names can only contain lowercase letters, numbers, underscores, and periods. - * All other characters are replaced with underscores. - */ -export function sanitizePathName(str: string): string { - return str.toLowerCase().replace(/[^a-z0-9_.]+/g, '_') -} - /** * Return a sanitized version of {@param str} that is safe to use as a storage object key. * diff --git a/src/variants.ts b/src/variants.ts index 8aa5a3c5..83c25d2b 100644 --- a/src/variants.ts +++ b/src/variants.ts @@ -1,7 +1,7 @@ import type { IBlueprintVariantJSON } from './blueprintFormat' import { getKeyframeVariant, setKeyframeVariant } from './mods/customKeyframesMod' import { events } from './util/events' -import { sanitizePathName } from './util/minecraftUtil' +import { sanitizeStorageKey } from './util/minecraftUtil' export class TextureMap { map: Map @@ -212,7 +212,7 @@ export class Variant { } public static makeNameUnique(variant: Variant, name: string): string { - name = sanitizePathName(name) + name = sanitizeStorageKey(name) if (!Variant.all.some(v => v !== variant && v.name === name)) { return name } From 0caba9069c0147444b2afaa7023ed3ba969b62f0 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 25 Sep 2025 13:34:38 -0400 Subject: [PATCH 015/182] =?UTF-8?q?=F0=9F=97=91=EF=B8=8F=20Remove=20unused?= =?UTF-8?q?=20mcb=20file.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/systems/datapackCompiler/1.21.5/tests.mcb | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/systems/datapackCompiler/1.21.5/tests.mcb diff --git a/src/systems/datapackCompiler/1.21.5/tests.mcb b/src/systems/datapackCompiler/1.21.5/tests.mcb deleted file mode 100644 index e69de29b..00000000 From c68dca9c700f13d54344d5c85b33b1eb44566a06 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 25 Sep 2025 13:38:51 -0400 Subject: [PATCH 016/182] =?UTF-8?q?=F0=9F=90=9B=20Fix=20#444?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related issues: - Fixed #443 - Fixed #441 - Fixed #435 - Fixed #434 - Fixed #427 - Fixed #421 - Fixed #441 --- src/systems/datapackCompiler/1.20.4/animation.mcb | 4 ++-- src/systems/datapackCompiler/1.20.4/static.mcb | 4 ++-- src/systems/datapackCompiler/1.20.5/animation.mcb | 4 ++-- src/systems/datapackCompiler/1.20.5/static.mcb | 4 ++-- src/systems/datapackCompiler/1.21.2/animation.mcb | 4 ++-- src/systems/datapackCompiler/1.21.2/static.mcb | 4 ++-- src/systems/datapackCompiler/1.21.4/animation.mcb | 4 ++-- src/systems/datapackCompiler/1.21.4/static.mcb | 4 ++-- src/systems/datapackCompiler/1.21.5/animation.mcb | 4 ++-- src/systems/datapackCompiler/1.21.5/static.mcb | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/systems/datapackCompiler/1.20.4/animation.mcb b/src/systems/datapackCompiler/1.20.4/animation.mcb index a9b61f12..7d4c958c 100644 --- a/src/systems/datapackCompiler/1.20.4/animation.mcb +++ b/src/systems/datapackCompiler/1.20.4/animation.mcb @@ -847,10 +847,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.20.4/static.mcb b/src/systems/datapackCompiler/1.20.4/static.mcb index 8a27205d..bdabd06a 100644 --- a/src/systems/datapackCompiler/1.20.4/static.mcb +++ b/src/systems/datapackCompiler/1.20.4/static.mcb @@ -400,10 +400,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.20.5/animation.mcb b/src/systems/datapackCompiler/1.20.5/animation.mcb index 0aba1b09..f4e387fe 100644 --- a/src/systems/datapackCompiler/1.20.5/animation.mcb +++ b/src/systems/datapackCompiler/1.20.5/animation.mcb @@ -847,10 +847,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.20.5/static.mcb b/src/systems/datapackCompiler/1.20.5/static.mcb index 984054fa..80402ece 100644 --- a/src/systems/datapackCompiler/1.20.5/static.mcb +++ b/src/systems/datapackCompiler/1.20.5/static.mcb @@ -400,10 +400,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.21.2/animation.mcb b/src/systems/datapackCompiler/1.21.2/animation.mcb index 83e3f85e..7743a477 100644 --- a/src/systems/datapackCompiler/1.21.2/animation.mcb +++ b/src/systems/datapackCompiler/1.21.2/animation.mcb @@ -847,10 +847,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.21.2/static.mcb b/src/systems/datapackCompiler/1.21.2/static.mcb index 2c663217..8c0e913a 100644 --- a/src/systems/datapackCompiler/1.21.2/static.mcb +++ b/src/systems/datapackCompiler/1.21.2/static.mcb @@ -400,10 +400,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.21.4/animation.mcb b/src/systems/datapackCompiler/1.21.4/animation.mcb index f6e7ea01..bb271893 100644 --- a/src/systems/datapackCompiler/1.21.4/animation.mcb +++ b/src/systems/datapackCompiler/1.21.4/animation.mcb @@ -847,10 +847,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.21.4/static.mcb b/src/systems/datapackCompiler/1.21.4/static.mcb index c4d21e87..54bcff58 100644 --- a/src/systems/datapackCompiler/1.21.4/static.mcb +++ b/src/systems/datapackCompiler/1.21.4/static.mcb @@ -400,10 +400,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.21.5/animation.mcb b/src/systems/datapackCompiler/1.21.5/animation.mcb index d9a9ff19..bc3c2e6d 100644 --- a/src/systems/datapackCompiler/1.21.5/animation.mcb +++ b/src/systems/datapackCompiler/1.21.5/animation.mcb @@ -847,10 +847,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } diff --git a/src/systems/datapackCompiler/1.21.5/static.mcb b/src/systems/datapackCompiler/1.21.5/static.mcb index c4d21e87..54bcff58 100644 --- a/src/systems/datapackCompiler/1.21.5/static.mcb +++ b/src/systems/datapackCompiler/1.21.5/static.mcb @@ -400,10 +400,10 @@ dir <%export_namespace%> { execute on passengers if entity @s[tag=<%TAGS.GLOBAL_DATA()%>] run block zzz/this/as_data { REPEAT (Object.values(rig.nodes).filter(v => v.type === 'locator' && v.config?.use_entity)) as locator { # REVIEW - This will not kill locators or cameras that have been summoned using an old export of the rig. - function ./zzz/this/kill_locator with entity @s data.locators.<%locator.type + '_' + locator.name%> + function ./zzz/this/kill_locator with entity @s data.locators.<%locator.storage_name%> } REPEAT (Object.values(rig.nodes).filter(v => v.type === 'camera')) as camera { - function ./zzz/this/kill_camera with entity @s data.locators.<%camera.type + '_' + camera.name%> + function ./zzz/this/kill_camera with entity @s data.cameras.<%camera.storage_name%> } } } From cb3b43e2445d0fd48eb3d1337093cf217ebb6883 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Fri, 26 Sep 2025 10:03:38 -0400 Subject: [PATCH 017/182] =?UTF-8?q?=F0=9F=A9=B9=20Fix=20broken=20Animated?= =?UTF-8?q?=20Java=20bar=20item=20condition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interface/animatedJavaBarItem.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/interface/animatedJavaBarItem.ts b/src/interface/animatedJavaBarItem.ts index b09d3aa2..ed21c9c9 100644 --- a/src/interface/animatedJavaBarItem.ts +++ b/src/interface/animatedJavaBarItem.ts @@ -5,9 +5,9 @@ import { cleanupExportedFiles } from '../systems/cleaner' import { exportProject } from '../systems/exporter' import { createAction, createBarMenu } from '../util/moddingTools' import { translate } from '../util/translation' +import { openChangelogDialog } from './changelogDialog' import { openAboutDialog } from './dialog/about' import { openBlueprintSettingsDialog } from './dialog/blueprintSettings' -import { openChangelogDialog } from './changelogDialog' function createIconImg() { const IMG = document.createElement('img') @@ -27,7 +27,9 @@ function createIconImg() { } const MENU_ID = `${PACKAGE.name}:menu` as `animated_java:menu` const BLOCKBENCH_MENU_BAR = document.querySelector('#menu_bar') as HTMLDivElement -export const MENU = createBarMenu(MENU_ID, [], () => Format === BLUEPRINT_FORMAT) as BarMenu & { +export const MENU = createBarMenu(MENU_ID, [], { + condition: () => Format === BLUEPRINT_FORMAT, +}) as BarMenu & { label: HTMLDivElement } MENU.label.style.display = 'inline-block' From 345e9e9dac792aa78cd2e4df0f142cb0f792d89b Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Fri, 26 Sep 2025 10:06:38 -0400 Subject: [PATCH 018/182] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20Fix=20a=20few?= =?UTF-8?q?=20more=20import=20type=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interface/dialog/blueprintSettings.ts | 4 ++-- src/interface/importAJModelLoader.ts | 6 +++--- src/interface/panel/textDisplayElement.ts | 8 +------- src/systems/rigRenderer.ts | 4 ++-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/interface/dialog/blueprintSettings.ts b/src/interface/dialog/blueprintSettings.ts index ba5fad0a..af80f57a 100644 --- a/src/interface/dialog/blueprintSettings.ts +++ b/src/interface/dialog/blueprintSettings.ts @@ -3,7 +3,7 @@ import { defaultValues, ExportMode } from '../../blueprintSettings' import BlueprintSettingsDialogSvelteComponent from '../../components/blueprintSettingsDialog.svelte' import { PACKAGE } from '../../constants' import { MinecraftVersion } from '../../systems/global' -import { sanitizePathName } from '../../util/minecraftUtil' +import { sanitizeStorageKey } from '../../util/minecraftUtil' import { Valuable } from '../../util/stores' import { SvelteDialog } from '../../util/svelteDialog' import { translate } from '../../util/translation' @@ -28,7 +28,7 @@ function getSettings() { if (!value) { return defaultValues.export_namespace } - return sanitizePathName(value) + return sanitizeStorageKey(value) }), resourcePackExportMode: new Valuable( Project!.animated_java.resource_pack_export_mode as string diff --git a/src/interface/importAJModelLoader.ts b/src/interface/importAJModelLoader.ts index a0d31fb6..dd09d5a5 100644 --- a/src/interface/importAJModelLoader.ts +++ b/src/interface/importAJModelLoader.ts @@ -1,10 +1,10 @@ import { SvelteComponentDev } from 'svelte/internal' -import { BLUEPRINT_CODEC, IBlueprintFormatJSON } from '../blueprintFormat' +import { BLUEPRINT_CODEC, type IBlueprintFormatJSON } from '../blueprintFormat' import ImportAjModelLoaderDialog from '../components/importAJModelLoaderDialog.svelte' import { PACKAGE } from '../constants' import * as ModelDatFixerUpper from '../systems/modelDataFixerUpper' import { injectSvelteCompomponent } from '../util/injectSvelteComponent' -import { sanitizePathName } from '../util/minecraftUtil' +import { sanitizeStorageKey } from '../util/minecraftUtil' import { createModelLoader } from '../util/moddingTools' import { translate } from '../util/translation' import { openUnexpectedErrorDialog } from './dialog/unexpectedError' @@ -52,7 +52,7 @@ export function convertAJModelToBlueprint(path: string) { name: 'Upgrade .ajmodel to Blueprint', path, }) - blueprint.blueprint_settings!.export_namespace ??= sanitizePathName(Project!.name) + blueprint.blueprint_settings!.export_namespace ??= sanitizeStorageKey(Project!.name) requestAnimationFrame(() => { Project!.save_path = '' diff --git a/src/interface/panel/textDisplayElement.ts b/src/interface/panel/textDisplayElement.ts index dc42a04c..a16dce24 100644 --- a/src/interface/panel/textDisplayElement.ts +++ b/src/interface/panel/textDisplayElement.ts @@ -1,7 +1,7 @@ import { isCurrentFormat } from '../../blueprintFormat' import TextDisplayElementPanel from '../../components/textDisplayElementPanel.svelte' import { PACKAGE } from '../../constants' -import { Alignment, TextDisplay } from '../../outliner/textDisplay' +import { type Alignment, TextDisplay } from '../../outliner/textDisplay' import { injectSvelteCompomponentMod } from '../../util/injectSvelteComponent' import { floatToHex } from '../../util/misc' import { translate } from '../../util/translation' @@ -92,9 +92,6 @@ export const TEXT_DISPLAY_SHADOW_TOGGLE = new Toggle(`${PACKAGE.name}:textDispla icon: 'check_box_outline_blank', description: translate('tool.text_display.text_shadow.description'), condition: () => isCurrentFormat() && !!TextDisplay.selected.length, - click() { - // - }, onChange() { if (!Project) return const scope = TEXT_DISPLAY_SHADOW_TOGGLE @@ -153,9 +150,6 @@ export const TEXT_DISPLAY_SEE_THROUGH_TOGGLE = new Toggle( icon: 'check_box_outline_blank', description: translate('tool.text_display.see_through.description'), condition: () => isCurrentFormat() && !!TextDisplay.selected.length, - click() { - // - }, onChange() { if (!Project) return const scope = TEXT_DISPLAY_SEE_THROUGH_TOGGLE diff --git a/src/systems/rigRenderer.ts b/src/systems/rigRenderer.ts index 309e541c..79aef9f0 100644 --- a/src/systems/rigRenderer.ts +++ b/src/systems/rigRenderer.ts @@ -9,7 +9,7 @@ import type { import { BoneConfig } from '../nodeConfigs' import { type Alignment, TextDisplay } from '../outliner/textDisplay' import { VanillaBlockDisplay } from '../outliner/vanillaBlockDisplay' -import { VanillaItemDisplay } from '../outliner/vanillaItemDisplay' +import { type ItemDisplayMode, VanillaItemDisplay } from '../outliner/vanillaItemDisplay' import { type IMinecraftResourceLocation, parseResourcePackPath, @@ -139,7 +139,7 @@ export interface IRenderedNodes { ItemDisplay: IRenderedNode & { type: 'item_display' item: string - item_display: string + item_display: ItemDisplayMode /** * The base scale of the bone, used to offset any rescaling done to the bone's model due to exceeding the 3x3x3 model size limit. */ From e47ce96829bcd672666914ebccf84d28054164d4 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Fri, 26 Sep 2025 10:08:46 -0400 Subject: [PATCH 019/182] =?UTF-8?q?=E2=9C=A8=20Disable=20rotation=20lock?= =?UTF-8?q?=20for=20MC=201.21.4+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/blockbenchTypeMods.d.ts | 4 +- src/blueprintFormat.ts | 33 +++++++++++++-- src/mods/cubeOutlineMod.ts | 82 +++++++++++++++++++++++++++++-------- src/systems/exporter.ts | 14 +++---- src/systems/util.ts | 50 +++++++++++----------- 5 files changed, 124 insertions(+), 59 deletions(-) diff --git a/src/blockbenchTypeMods.d.ts b/src/blockbenchTypeMods.d.ts index aa2befcc..aafcee0f 100644 --- a/src/blockbenchTypeMods.d.ts +++ b/src/blockbenchTypeMods.d.ts @@ -35,8 +35,6 @@ declare global { pluginMode: Valuable transparentTexture: Texture - showingInvalidCubeRotations: boolean - variants: Variant[] textDisplays: TextDisplay[] vanillaItemDisplays: VanillaItemDisplay[] @@ -80,7 +78,7 @@ declare global { } interface Cube { - rotationInvalid: boolean + isRotationValid: boolean } interface CubeFace { diff --git a/src/blueprintFormat.ts b/src/blueprintFormat.ts index ea534339..130876f7 100644 --- a/src/blueprintFormat.ts +++ b/src/blueprintFormat.ts @@ -681,15 +681,40 @@ export function saveBlueprint() { BLUEPRINT_CODEC.write(BLUEPRINT_CODEC.compile(), Project.save_path) } -export function updateRotationLock() { - if (!isCurrentFormat()) return - BLUEPRINT_FORMAT.rotation_limit = !( +export function checkTargetVersionsMeetRequirement(version: string): boolean { + return !!Project?.animated_java.target_minecraft_versions.every( + v => !compareVersions(version, v) + ) +} + +export function shouldEnableRotationLock(): boolean { + if (!isCurrentFormat()) return false + + if (checkTargetVersionsMeetRequirement('1.21.4')) { + return false + } + + return !( !!Group.first_selected || !!AnimatedJava.API.TextDisplay.selected.length || !!AnimatedJava.API.VanillaItemDisplay.selected.length || !!AnimatedJava.API.VanillaBlockDisplay.selected.length || - !!(OutlinerElement.types.camera?.selected && OutlinerElement.types.camera?.selected) + !!( + Array.isArray(OutlinerElement.types.locator?.selected) && + OutlinerElement.types.locator.selected.length + ) || + !!( + Array.isArray(OutlinerElement.types.camera?.selected) && + OutlinerElement.types.camera.selected.length + ) ) +} + +export function updateRotationLock() { + if (!isCurrentFormat()) return + + // If any of these node types are selected, we disable rotation lock. + BLUEPRINT_FORMAT.rotation_limit = shouldEnableRotationLock() BLUEPRINT_FORMAT.rotation_snap = BLUEPRINT_FORMAT.rotation_limit } diff --git a/src/mods/cubeOutlineMod.ts b/src/mods/cubeOutlineMod.ts index 0654adf4..0cbb40f6 100644 --- a/src/mods/cubeOutlineMod.ts +++ b/src/mods/cubeOutlineMod.ts @@ -1,4 +1,5 @@ -import { isCurrentFormat } from '../blueprintFormat' +import { translate } from 'src/util/translation' +import { checkTargetVersionsMeetRequirement, isCurrentFormat } from '../blueprintFormat' import { PACKAGE } from '../constants' import { isCubeValid } from '../systems/util' import { createBlockbenchMod, createPropertySubscribable } from '../util/moddingTools' @@ -6,6 +7,45 @@ import { createBlockbenchMod, createPropertySubscribable } from '../util/modding const ERROR_OUTLINE_MATERIAL = Canvas.outlineMaterial.clone() ERROR_OUTLINE_MATERIAL.color.set('#ff0000') +export function updateAllCubeOutlines() { + for (const cube of Cube.all) { + Cube.preview_controller.updateTransform(cube) + } +} + +function updateCubeValidity(cube: Cube, isValid: boolean) { + if (cube.isRotationValid === isValid) return + cube.mesh.outline.material = isValid ? Canvas.outlineMaterial : ERROR_OUTLINE_MATERIAL + cube.isRotationValid = isValid +} + +let toastNotification: Deletable | null = null + +function showToastNotification() { + if (!toastNotification) { + toastNotification = Blockbench.showToastNotification({ + text: translate( + checkTargetVersionsMeetRequirement('1.21.4') + ? 'toast.invalid_rotations_post_1_21_4' + : 'toast.invalid_rotations' + ), + color: 'var(--color-error)', + click: () => false, + }) + const intervalId = setInterval(() => { + if ( + Cube.all.some(cube => isCubeValid(cube) === 'invalid') && + document.querySelector('li.toast_notification') + ) + return + clearInterval(intervalId) + toastNotification?.delete() + toastNotification = null + requestAnimationFrame(updateAllCubeOutlines) + }, 1000) + } +} + createBlockbenchMod( `${PACKAGE.name}:cubeOutlineMod`, { @@ -15,19 +55,24 @@ createBlockbenchMod( context => { Cube.preview_controller.updateTransform = function (cube: Cube) { if (isCurrentFormat()) { - const isValid = isCubeValid(cube) - if (cube.rotationInvalid && isValid) { - cube.mesh.outline.material = Canvas.outlineMaterial - cube.rotationInvalid = false - } else if (!cube.rotationInvalid && !isValid) { - cube.mesh.outline.material = ERROR_OUTLINE_MATERIAL - cube.rotationInvalid = true - if (!Project!.showingInvalidCubeRotations) { - Blockbench.showToastNotification({ - text: 'Invalid Cube Rotation!\nCubes can only be rotated in 22.5 degree increments (45, 22.5, 0, -22.5, -45) and can only be rotated on a single axis.\nThe offending cubes have been highlighted in red.', - color: 'var(--color-error)', - }) - Project!.showingInvalidCubeRotations = true + const validity = isCubeValid(cube) + + switch (validity) { + case 'valid': { + updateCubeValidity(cube, true) + break + } + case '1.21.4+': { + if (checkTargetVersionsMeetRequirement('1.21.4')) { + updateCubeValidity(cube, true) + break + } + // Fallthrough to invalid if the target version is below 1.21.4 + } + case 'invalid': { + updateCubeValidity(cube, false) + showToastNotification() + break } } } @@ -37,11 +82,12 @@ createBlockbenchMod( Cube.prototype.init = function (this: Cube) { const cube = context.originalInit.call(this) - cube.rotationInvalid = false + cube.isRotationValid = true - const [get] = createPropertySubscribable(this.mesh.outline, 'visible') - get.subscribe(({ storage }) => { - if (isCurrentFormat()) storage.value = this.rotationInvalid || storage.value + const [, setVisible] = createPropertySubscribable(cube.mesh.outline, 'visible') + setVisible.subscribe(({ storage }) => { + if (!isCurrentFormat()) return + storage.value = !cube.isRotationValid || storage.value }) return cube diff --git a/src/systems/exporter.ts b/src/systems/exporter.ts index f6e905b7..13db1eb3 100644 --- a/src/systems/exporter.ts +++ b/src/systems/exporter.ts @@ -1,4 +1,4 @@ -import { saveBlueprint } from '../blueprintFormat' +import { checkTargetVersionsMeetRequirement, saveBlueprint } from '../blueprintFormat' import { blueprintSettingErrors } from '../blueprintSettings' import { openBlueprintSettingsDialog } from '../interface/dialog/blueprintSettings' import { PROGRESS_DESCRIPTION, openExportProgressDialog } from '../interface/dialog/exportProgress' @@ -170,14 +170,14 @@ async function actuallyExportProject(forceSave = true) { export async function exportProject(forceSave = true) { if (!Project) return // TODO: Handle this error better - if ( - // Check if 1.21.3 is newer than the target version - // compareVersions('1.21.3', Project.animated_java.target_minecraft_versions) && - !Cube.all.allAre(c => isCubeValid(c)) - ) { + if (Cube.all.some(cube => isCubeValid(cube) === 'invalid')) { Blockbench.showMessageBox({ title: translate('misc.failed_to_export.title'), - message: translate('misc.failed_to_export.invalid_rotation.message'), + message: translate( + checkTargetVersionsMeetRequirement('1.21.4') + ? 'misc.failed_to_export.invalid_rotation.message_post_1_21_4' + : 'misc.failed_to_export.invalid_rotation.message' + ), buttons: [translate('misc.failed_to_export.button')], }) return diff --git a/src/systems/util.ts b/src/systems/util.ts index 4c19279c..0e7c4c04 100644 --- a/src/systems/util.ts +++ b/src/systems/util.ts @@ -7,6 +7,7 @@ import { type AsyncZippable, type Unzipped, } from 'fflate/browser' +import { checkTargetVersionsMeetRequirement } from 'src/blueprintFormat' import { roundTo } from '../util/misc' import type { INodeTransform } from './animationRenderer' @@ -91,33 +92,28 @@ export const unzip = (data: Uint8Array, options: AsyncUnzipOptions) => { }) } -export function isCubeValid(cube: Cube) { - // Cube is automatically valid if it has no rotation - if (cube.rotation[0] === 0 && cube.rotation[1] === 0 && cube.rotation[2] === 0) { - return true - } - const rotation = cube.rotation[0] + cube.rotation[1] + cube.rotation[2] - // prettier-ignore - if ( - // Make sure the cube is rotated in only one axis by adding all the rotations together, and checking if the sum is equal to one of the rotations. - ( - rotation === cube.rotation[0] || - rotation === cube.rotation[1] || - rotation === cube.rotation[2] - ) - && - // Make sure the cube is rotated in one of the allowed 22.5 degree increments - ( - rotation === -45 || - rotation === -22.5 || - rotation === 0 || - rotation === 22.5 || - rotation === 45 - ) - ) { - return true - } - return false +export function isCubeValid(cube: Cube): '1.21.4+' | 'valid' | 'invalid' { + const totalRotation = cube.rotation[0] + cube.rotation[1] + cube.rotation[2] + + if (totalRotation === 0) return 'valid' + + const isSingleAxisRotation = + totalRotation === cube.rotation[0] || + totalRotation === cube.rotation[1] || + totalRotation === cube.rotation[2] + + if (isSingleAxisRotation && checkTargetVersionsMeetRequirement('1.21.4')) return '1.21.4+' + + const isRotationInAllowedSteps = + totalRotation === -45 || + totalRotation === -22.5 || + totalRotation === 0 || + totalRotation === 22.5 || + totalRotation === 45 + + if (isSingleAxisRotation && isRotationInAllowedSteps) return 'valid' + + return 'invalid' } export function getFunctionNamespace(version: string): 'function' | 'functions' { From b464bb321a1b5546be914e52860b060c9ec16306 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Fri, 26 Sep 2025 10:11:33 -0400 Subject: [PATCH 020/182] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bone=20config=20go?= =?UTF-8?q?ing=20off-screen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #422 --- src/components/boneConfigDialog.svelte | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/boneConfigDialog.svelte b/src/components/boneConfigDialog.svelte index 38cfef2e..b666ec3a 100644 --- a/src/components/boneConfigDialog.svelte +++ b/src/components/boneConfigDialog.svelte @@ -1,20 +1,19 @@ -
+
{translate('dialog.bone_config.selected_variant', variant.displayName)}
@@ -308,6 +307,11 @@
diff --git a/src/components/dialogItems/numberSlider.svelte b/src/components/dialogItems/numberSlider.svelte index 9195d5f7..a565dd67 100644 --- a/src/components/dialogItems/numberSlider.svelte +++ b/src/components/dialogItems/numberSlider.svelte @@ -77,9 +77,18 @@ bind:value={$value} inputmode="decimal" /> -
+
code
+ + diff --git a/src/components/textDisplayConfigDialog.svelte b/src/components/textDisplayConfigDialog.svelte index 4925302f..7649e90a 100644 --- a/src/components/textDisplayConfigDialog.svelte +++ b/src/components/textDisplayConfigDialog.svelte @@ -1,16 +1,17 @@ -
- -
- - + + + + + + + + + + +{#if !$autoRenderBox} - - - - +{/if} - {#if !$autoBoundingBox} - - {/if} + - + - - - + /> --> - {#if $enablePluginMode} - +{#if $enablePluginMode} + - + - - {:else} - - - - {#if $resourcePackExportMode !== 'none'} - + - - - {/if} + + + + + + + + + + + + + + + + {/if} -
- -{#if showSupportMePopup} -
-
-
- ❤️ - Animated Java? - - - close - -
- - - -
-
-
{/if} diff --git a/src/components/boneConfigDialog.svelte b/src/components/boneConfigDialog.svelte index d53ed400..ed5eb193 100644 --- a/src/components/boneConfigDialog.svelte +++ b/src/components/boneConfigDialog.svelte @@ -1,7 +1,6 @@ -
-
- {translate('dialog.bone_config.selected_variant', variant.displayName)} +
+ {translate('dialog.bone_config.selected_variant', variant.displayName)} +
+{#if variant.isDefault} +
+ {translate('dialog.bone_config.default_variant_subtitle')}
- {#if variant.isDefault} -
- {translate('dialog.bone_config.default_variant_subtitle')} -
- {:else} -
- {translate('dialog.bone_config.selected_variant_subtitle')} -
- {/if} - - {#if pluginModeEnabled} - - - - - + {translate('dialog.bone_config.selected_variant_subtitle')} +
+{/if} + +{#if pluginModeEnabled} + + + + + + + - + {#if $overrideGlowColor} + + {/if} + - + {#if $overrideBrightness} + + {/if} - - {:else} - - - - - - - - - {#if $useNBT} -

- {translate('dialog.bone_config.use_nbt.use_nbt_warning')} -

- - {:else} - + {/if} +
@@ -55,10 +117,45 @@ flex-direction: column; } textarea { - resize: vertical; - text-wrap: nowrap; - min-height: 4em; + color: var(--color-error); + background-color: var(--color-back); + padding: 3px 6px; + overflow: auto; + height: min-content; + font-size: 0.9rem; font-family: var(--font-code); - width: 100%; + margin: 8px; + margin-top: 0px; + border-radius: 0 0 0.3em 0.3em; + border: 1px solid var(--color-error); + white-space: pre; + tab-size: 4; + } + .codejar-container :global(.language-snbtTextComponent) { + & .brackets { + color: #5ba8c5; + } + & .token.punctuation { + color: #89ddff; + } + & .token.property { + color: #eeffff; + } + & .token.escape-sequence { + color: #89ddff; + } + & .token.constant { + color: #c767d7; + border-bottom: 1px solid; + } + & .token.number { + color: #f92672; + } + & .token.boolean { + color: #f78c6c; + } + & .token.string { + color: #aef941; + } } diff --git a/src/components/dialogItems/collection.svelte b/src/components/dialogItems/collection.svelte index ed822d30..02bf4721 100644 --- a/src/components/dialogItems/collection.svelte +++ b/src/components/dialogItems/collection.svelte @@ -1,10 +1,10 @@

Create advanced animated models for Vanilla Java Edition

@@ -59,23 +19,8 @@ > to learn how to use Animated Java.

-

-

Good to know:

-

-
    -
  • - The rotations of elements are limited to 22.5 degree steps, and only one axis can be rotated - at a time. However, the rotation of bones is not restricted. -
  • -
  • - Documentation for Animated Java can be found at https://animated-java.dev/docs -
  • -
-
- @@ -86,7 +31,4 @@ width: 100%; height: 40px; } - li { - margin-left: 24px; - } diff --git a/src/components/installedPopup.svelte b/src/components/installedPopup.svelte index 304049ee..9f0dfc72 100644 --- a/src/components/installedPopup.svelte +++ b/src/components/installedPopup.svelte @@ -8,10 +8,7 @@

Welcome to Animated Java!

-

- A Blockbench plugin that makes complex animation a breeze in Minecraft: Java - Edition. -

+

Effortlessly craft complex animations for Minecraft: Java Edition

@@ -25,10 +22,10 @@

You can open our docs at any time via the Animated Java menu at the top of the screen.

-

Need more help?

+

Need Help?

- Feel free to join our Discord server to ask - questions via our support channel. + Join our Discord server to ask questions + via our support channel.

diff --git a/src/components/keyframeEasings.svelte b/src/components/keyframeEasings.svelte index 97bee3d9..b64e16f3 100644 --- a/src/components/keyframeEasings.svelte +++ b/src/components/keyframeEasings.svelte @@ -1,7 +1,7 @@ + + + +{#if showSupportMePopup} +
+
+
+ ❤️ + Animated Java? + + + close + +
+ + + +
+
+{/if} + + diff --git a/src/components/locatorConfigDialog.svelte b/src/components/locatorConfigDialog.svelte index 851a7763..4e89b3e4 100644 --- a/src/components/locatorConfigDialog.svelte +++ b/src/components/locatorConfigDialog.svelte @@ -11,8 +11,8 @@ export let useEntity: Valuable export let entityType: Valuable export let syncPassengerRotation: Valuable - export let summonCommands: Valuable - export let tickingCommands: Valuable + export let onSummonFunction: Valuable + export let onTickFunction: Valuable const entityTypeValidator: DialogItemValueChecker = (value: string) => { if (value.length === 0) { @@ -65,17 +65,23 @@ /> {/if} {/if} diff --git a/src/components/textDisplayConfigDialog.svelte b/src/components/textDisplayConfigDialog.svelte index 7649e90a..0aa12997 100644 --- a/src/components/textDisplayConfigDialog.svelte +++ b/src/components/textDisplayConfigDialog.svelte @@ -1,17 +1,17 @@ -
- {#if pluginModeEnabled} +{#if pluginModeEnabled} + - + {#if $overrideGlowColor} + + {/if} + - - - - {:else} - - - {#if $useNBT} -

- {translate('dialog.text_display_config.use_nbt.use_nbt_warning')} -

- - {:else} - + + + + + + + + + + + + + + + + + + +{:else} + + + {#if $useNBT} +

+ {translate('dialog.bone_config.use_nbt.use_nbt_warning')} +

+ - + {:else} + - - - - - - {#if $overrideGlowColor} - - {/if} - + {#if $overrideBrightness} + {/if} - - - - - {#if $overrideBrightness} - - {/if} - - - {/if} {/if} -
+{/if} diff --git a/src/components/vanillaItemDisplayConfigDialog.svelte b/src/components/vanillaItemDisplayConfigDialog.svelte index 14869d62..b2127f9f 100644 --- a/src/components/vanillaItemDisplayConfigDialog.svelte +++ b/src/components/vanillaItemDisplayConfigDialog.svelte @@ -65,7 +65,7 @@ } catch (e: any) { return { type: 'error', - message: translate('dialog.bone_config.custom_name.invalid_json.error', e.message), + message: e.message, } } @@ -73,22 +73,112 @@ } -
- {#if pluginModeEnabled} - + - + {#if $overrideGlowColor} + + {/if} + - - - - {:else} - - - {#if $useNBT} -

- {translate('dialog.bone_config.use_nbt.use_nbt_warning')} -

- - {:else} - - - - - diff --git a/src/components/textDisplayElementPanel.svelte b/src/components/textDisplayElementPanel.svelte index 29cf9144..a7411d43 100644 --- a/src/components/textDisplayElementPanel.svelte +++ b/src/components/textDisplayElementPanel.svelte @@ -23,11 +23,9 @@ let lastSelected: TextDisplay | undefined let selected = TextDisplay.selected.at(0) - // @ts-expect-error - let text = selected?._text ?? new Valuable('') + let text = selected?.getTextValuable() ?? new Valuable('') let lastText: string | undefined = selected?.text - // @ts-expect-error - let error = selected?._textError ?? new Valuable('') + let error = selected?.textError ?? new Valuable('') let codeJarElement: HTMLPreElement | undefined @@ -45,9 +43,7 @@ lastSelected = selected selected = TextDisplay.selected.at(0) if (!selected || Group.first_selected) return - // This might be a bit hacky, but svelte seems to handle it fine. - // @ts-ignore - text = selected._text + text = selected.getTextValuable() lastText = selected.text error = selected.textError diff --git a/src/components/vanillaBlockDisplayConfigDialog.svelte b/src/components/vanillaBlockDisplayConfigDialog.svelte index b2127f9f..9cc44216 100644 --- a/src/components/vanillaBlockDisplayConfigDialog.svelte +++ b/src/components/vanillaBlockDisplayConfigDialog.svelte @@ -9,28 +9,8 @@ import { JsonText } from '../systems/jsonText' import { Valuable } from '../util/stores' import { translate } from '../util/translation' - - - -{#if pluginModeEnabled} + + +{#if PLUGIN_MODE} diff --git a/src/components/vanillaItemDisplayConfigDialog.svelte b/src/components/vanillaItemDisplayConfigDialog.svelte index b2127f9f..9cc44216 100644 --- a/src/components/vanillaItemDisplayConfigDialog.svelte +++ b/src/components/vanillaItemDisplayConfigDialog.svelte @@ -9,28 +9,8 @@ import { JsonText } from '../systems/jsonText' import { Valuable } from '../util/stores' import { translate } from '../util/translation' - - - -{#if pluginModeEnabled} + + +{#if PLUGIN_MODE} diff --git a/src/formats/blueprint/format.ts b/src/formats/blueprint/format.ts index 34327e73..7470d8cc 100644 --- a/src/formats/blueprint/format.ts +++ b/src/formats/blueprint/format.ts @@ -273,7 +273,6 @@ export const BLUEPRINT_FORMAT = registerModelFormat( }, }, - // eslint-disable-next-line @typescript-eslint/no-unused-vars onSetup(project, newModel) { if (!Project) return console.log('Animated Java Blueprint format setup') diff --git a/src/systems/datapackCompiler/index.ts b/src/systems/datapackCompiler/index.ts index c70b3a29..8da05211 100644 --- a/src/systems/datapackCompiler/index.ts +++ b/src/systems/datapackCompiler/index.ts @@ -959,9 +959,9 @@ async function removeFiles(ajmeta: AJMeta, dataPackFolder: string) { } const dataPackCompiler: DataPackCompiler = async ({ - ajmeta, + // ajmeta, version, - coreFiles, + // coreFiles, versionedFiles, rig, animations, @@ -972,7 +972,7 @@ const dataPackCompiler: DataPackCompiler = async ({ JsonText.defaultTargetVersion = version const aj = Project!.animated_java - const is_static = animations.length === 0 + const isStatic = animations.length === 0 const variables = { export_namespace: aj.export_namespace, interpolation_duration: aj.interpolation_duration, @@ -1007,7 +1007,7 @@ const dataPackCompiler: DataPackCompiler = async ({ Object.values(rig.nodes).filter(n => n.type === 'locator' && n.config?.use_entity) .length > 0, has_cameras: Object.values(rig.nodes).filter(n => n.type === 'camera').length > 0, - is_static, + is_static: isStatic, getNodeTags, BONE_TYPES, project_storage: `animated_java:${aj.export_namespace}`, @@ -1076,7 +1076,7 @@ async function writeFiles(exportedFiles: Map, dataPackFold }) ) if (writeQueue.size >= maxWriteThreads) { - await Promise.any(writeQueue) + await Promise.any(writeQueue.values()) } } await Promise.all(writeQueue.values()) diff --git a/src/systems/resourcepackCompiler/index.ts b/src/systems/resourcepackCompiler/index.ts index 506711be..b9d0e7b2 100644 --- a/src/systems/resourcepackCompiler/index.ts +++ b/src/systems/resourcepackCompiler/index.ts @@ -197,31 +197,5 @@ export default async function compileResourcePack( } } else if (aj.resource_pack_export_mode === 'zip') { throw new IntentionalExportError('ZIP export is not yet implemented.') - // exportedFiles.set( - // PathModule.join(options.resourcePackFolder, 'pack.mcmeta'), - // autoStringify({ - // pack: { - // // FIXME - This should be a setting - // pack_format: 32, - // description: `${Project!.name}. Generated with Animated Java`, - // }, - // }) - // ) - - // PROGRESS_DESCRIPTION.set('Writing Resource Pack Zip...') - // const data: Record = {} - // for (const [path, file] of exportedFiles) { - // const relativePath = PathModule.relative(options.resourcePackFolder, path) - // if (typeof file === 'string') { - // data[relativePath] = Buffer.from(file) - // } else { - // data[relativePath] = file - // } - // } - // const zipped = await zip(data, {}) - // await fs.promises.writeFile( - // options.resourcePackFolder + (options.resourcePackFolder.endsWith('.zip') ? '' : '.zip'), - // zipped - // ) } } diff --git a/src/systems/rigRenderer.ts b/src/systems/rigRenderer.ts index 1e3f3d28..c0f645cb 100644 --- a/src/systems/rigRenderer.ts +++ b/src/systems/rigRenderer.ts @@ -417,7 +417,7 @@ function renderGroup( } // Export a struct instead of a bone if no elements are present - if (!groupModel.model || !groupModel.model.elements || groupModel.model.elements.length === 0) { + if (!groupModel.model?.elements || groupModel.model.elements.length === 0) { delete defaultVariant.models[group.uuid] const struct: IRenderedNodes['Struct'] = { type: 'struct', From b929d9ebef21e18eaeef7c913669ea0f1f7ccf1a Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:09:48 -0400 Subject: [PATCH 107/182] =?UTF-8?q?=F0=9F=8E=A8=20Switch=20to=20using=20a?= =?UTF-8?q?=20TS=20enum=20for=20MC=20versions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/formats/blueprint/dfu.ts | 5 +-- src/formats/blueprint/settings.ts | 6 +-- src/interface/dialog/blueprintSettings.ts | 4 +- src/systems/datapackCompiler/index.ts | 8 ++-- src/systems/datapackCompiler/mcbCompiler.ts | 4 +- src/systems/datapackCompiler/mcbFiles.ts | 42 ++++++++++++--------- src/systems/global.ts | 10 ++++- src/systems/jsonCompiler.ts | 7 ++-- src/systems/resourcepackCompiler/index.ts | 22 ++++++----- src/util/minecraftUtil.ts | 40 ++++++++++++-------- 10 files changed, 87 insertions(+), 61 deletions(-) diff --git a/src/formats/blueprint/dfu.ts b/src/formats/blueprint/dfu.ts index 7b10f80a..af9856ed 100644 --- a/src/formats/blueprint/dfu.ts +++ b/src/formats/blueprint/dfu.ts @@ -3,7 +3,6 @@ import TransparentTexture from '../../assets/transparent.png' import { PACKAGE } from '../../constants' import { openUnexpectedErrorDialog } from '../../interface/dialog/unexpectedError' import { BoneConfig } from '../../nodeConfigs' -import { MinecraftVersion } from '../../systems/global' import { type IBlueprintFormatJSON, getDefaultProjectSettings } from './format' export function process(model: any): IBlueprintFormatJSON { @@ -607,8 +606,8 @@ function updateModelTo1_8_0(model: any) { // Update target version settings if (Array.isArray(model.blueprint_settings?.target_minecraft_versions)) { fixed.blueprint_settings.target_minecraft_version = - (model.blueprint_settings.target_minecraft_versions.at(0) as MinecraftVersion) ?? - '1.21.5' + model.blueprint_settings.target_minecraft_versions.at(0) ?? + getDefaultProjectSettings().target_minecraft_version // @ts-expect-error delete fixed.blueprint_settings.target_minecraft_versions } diff --git a/src/formats/blueprint/settings.ts b/src/formats/blueprint/settings.ts index 6681167b..e3442945 100644 --- a/src/formats/blueprint/settings.ts +++ b/src/formats/blueprint/settings.ts @@ -1,4 +1,4 @@ -import type { MinecraftVersion } from '../../systems/global' +import { SUPPORTED_MINECRAFT_VERSIONS } from '../../systems/global' import { Valuable } from '../../util/stores' export type ExportMode = 'folder' | 'zip' | 'none' @@ -13,7 +13,7 @@ export interface BlueprintSettings { enable_plugin_mode: boolean resource_pack_export_mode: ExportMode data_pack_export_mode: ExportMode - target_minecraft_version: MinecraftVersion + target_minecraft_version: SUPPORTED_MINECRAFT_VERSIONS // Resource Pack Settings display_item: string custom_model_data_offset: number @@ -46,7 +46,7 @@ export const defaultValues: BlueprintSettings = { enable_plugin_mode: false, resource_pack_export_mode: 'folder' as ExportMode, data_pack_export_mode: 'folder' as ExportMode, - target_minecraft_version: '1.21.5' as MinecraftVersion, + target_minecraft_version: SUPPORTED_MINECRAFT_VERSIONS['1.21.9'], // Resource Pack Settings display_item: 'minecraft:white_dye', diff --git a/src/interface/dialog/blueprintSettings.ts b/src/interface/dialog/blueprintSettings.ts index 55e7bb09..6b4f6e90 100644 --- a/src/interface/dialog/blueprintSettings.ts +++ b/src/interface/dialog/blueprintSettings.ts @@ -1,6 +1,6 @@ import KofiPopup from 'src/components/kofiPopup.svelte' import { updateAllCubeOutlines } from 'src/mods/cube' -import type { MinecraftVersion } from 'src/systems/global' +import { SUPPORTED_MINECRAFT_VERSIONS } from 'src/systems/global' import BlueprintSettingsDialogSvelteComponent from '../../components/blueprintSettingsDialog.svelte' import { PACKAGE } from '../../constants' import { updateRenderBoxPreview, updateRotationLock } from '../../formats/blueprint/format' @@ -88,7 +88,7 @@ function setSettings(settings: ReturnType) { settings.resourcePackExportMode.get() as ExportMode Project.animated_java.data_pack_export_mode = settings.dataPackExportMode.get() as ExportMode Project.animated_java.target_minecraft_version = - settings.targetMinecraftVersion.get() as MinecraftVersion + settings.targetMinecraftVersion.get() as SUPPORTED_MINECRAFT_VERSIONS // Resource Pack Settings Project.animated_java.display_item = settings.displayItem.get() Project.animated_java.custom_model_data_offset = settings.customModelDataOffset.get() diff --git a/src/systems/datapackCompiler/index.ts b/src/systems/datapackCompiler/index.ts index 8da05211..c68f3135 100644 --- a/src/systems/datapackCompiler/index.ts +++ b/src/systems/datapackCompiler/index.ts @@ -17,7 +17,7 @@ import { Variant } from '../../variants' import type { IRenderedAnimation } from '../animationRenderer' import mcbFiles from '../datapackCompiler/mcbFiles' import { IntentionalExportError } from '../exporter' -import { AJMeta, type MinecraftVersion, PackMeta, type PackMetaFormats } from '../global' +import { AJMeta, PackMeta, type PackMetaFormats, SUPPORTED_MINECRAFT_VERSIONS } from '../global' import { JsonText } from '../jsonText' import type { AnyRenderedNode, IRenderedRig } from '../rigRenderer' import { @@ -393,7 +393,7 @@ function getNodeTags(node: AnyRenderedNode, rig: IRenderedRig): NbtList { } async function generateRootEntityPassengers( - version: MinecraftVersion, + version: SUPPORTED_MINECRAFT_VERSIONS, rig: IRenderedRig, rigHash: string ) { @@ -752,7 +752,7 @@ function nodeSorter(a: AnyRenderedNode, b: AnyRenderedNode): number { interface DataPackCompilerOptions { ajmeta: AJMeta - version: MinecraftVersion + version: SUPPORTED_MINECRAFT_VERSIONS coreFiles: Map versionedFiles: Map rig: IRenderedRig @@ -774,7 +774,7 @@ interface CompileDataPackOptions { } export default async function compileDataPack( - targetVersions: MinecraftVersion[], + targetVersions: SUPPORTED_MINECRAFT_VERSIONS[], options: CompileDataPackOptions ) { console.time('Data Pack Compilation took') diff --git a/src/systems/datapackCompiler/mcbCompiler.ts b/src/systems/datapackCompiler/mcbCompiler.ts index a989197d..5ca30fbc 100644 --- a/src/systems/datapackCompiler/mcbCompiler.ts +++ b/src/systems/datapackCompiler/mcbCompiler.ts @@ -1,14 +1,14 @@ import { Compiler, Parser, SyncIo, Tokenizer } from 'mc-build' import { VariableMap } from 'mc-build/dist/mcl/Compiler' import { getDataPackFormat } from '../../util/minecraftUtil' -import type { MinecraftVersion } from '../global' +import { SUPPORTED_MINECRAFT_VERSIONS } from '../global' import type { ExportedFile } from '../util' interface CompilerOptions { sourceFiles: Record destPath: string variables: Record - version: MinecraftVersion + version: SUPPORTED_MINECRAFT_VERSIONS exportedFiles: Map } diff --git a/src/systems/datapackCompiler/mcbFiles.ts b/src/systems/datapackCompiler/mcbFiles.ts index 25349d87..7dd6ee87 100644 --- a/src/systems/datapackCompiler/mcbFiles.ts +++ b/src/systems/datapackCompiler/mcbFiles.ts @@ -1,7 +1,7 @@ -import { type MinecraftVersion } from '../global' +import { SUPPORTED_MINECRAFT_VERSIONS } from '../global' import animation_1_20_4 from './1.20.4/animation.mcb' -import core_1_20_4 from './1.20.4/global.mcb' -import coreTemplates_1_20_4 from './1.20.4/global.mcbt' +import global_1_20_4 from './1.20.4/global.mcb' +import globalTemplates_1_20_4 from './1.20.4/global.mcbt' import static_1_20_4 from './1.20.4/static.mcb' import animation_1_20_5 from './1.20.5/animation.mcb' @@ -22,46 +22,52 @@ import static_1_21_5 from './1.21.5/static.mcb' interface MCBFiles { animation: string static: string - core: string - coreTemplates: string + global: string + globalTemplates: string } -const MCB_FILES: Record = { +const MCB_FILES: Record = { + '1.21.9': { + animation: animation_1_21_5, + static: static_1_21_5, + global: global_1_20_4, + globalTemplates: globalTemplates_1_20_4, + }, '1.21.5': { animation: animation_1_21_5, static: static_1_21_5, - core: core_1_20_4, - coreTemplates: coreTemplates_1_20_4, + global: global_1_20_4, + globalTemplates: globalTemplates_1_20_4, }, '1.21.4': { animation: animation_1_21_4, static: static_1_21_4, - core: core_1_20_4, - coreTemplates: coreTemplates_1_20_4, + global: global_1_20_4, + globalTemplates: globalTemplates_1_20_4, }, '1.21.2': { animation: animation_1_21_2, static: static_1_21_2, - core: core_1_20_4, - coreTemplates: coreTemplates_1_20_4, + global: global_1_20_4, + globalTemplates: globalTemplates_1_20_4, }, '1.21.0': { animation: animation_1_20_5, static: static_1_20_5, - core: core_1_20_4, - coreTemplates: coreTemplates_1_20_4, + global: global_1_20_4, + globalTemplates: globalTemplates_1_20_4, }, '1.20.5': { animation: animation_1_20_5, static: static_1_20_5, - core: core_1_20_4, - coreTemplates: coreTemplates_1_20_4, + global: global_1_20_4, + globalTemplates: globalTemplates_1_20_4, }, '1.20.4': { animation: animation_1_20_4, static: static_1_20_4, - core: core_1_20_4, - coreTemplates: coreTemplates_1_20_4, + global: global_1_20_4, + globalTemplates: globalTemplates_1_20_4, }, } diff --git a/src/systems/global.ts b/src/systems/global.ts index a2ac1c8d..2abfd2ee 100644 --- a/src/systems/global.ts +++ b/src/systems/global.ts @@ -2,7 +2,15 @@ import { normalizePath } from '../util/fileUtil' import { IntentionalExportError, IntentionalExportErrorFromInvalidFile } from './exporter' import { sortObjectKeys } from './util' -export type MinecraftVersion = '1.20.4' | '1.20.5' | '1.21.0' | '1.21.2' | '1.21.4' | '1.21.5' +export enum SUPPORTED_MINECRAFT_VERSIONS { + '1.21.9' = '1.21.9', + '1.21.5' = '1.21.5', + '1.21.4' = '1.21.4', + '1.21.2' = '1.21.2', + '1.21.0' = '1.21.0', + '1.20.5' = '1.20.5', + '1.20.4' = '1.20.4', +} interface OldSerializedAJMeta { [key: string]: { diff --git a/src/systems/jsonCompiler.ts b/src/systems/jsonCompiler.ts index 8f4df4ab..8e988aa7 100644 --- a/src/systems/jsonCompiler.ts +++ b/src/systems/jsonCompiler.ts @@ -16,6 +16,7 @@ import { resolvePath } from '../util/fileUtil' import { detectCircularReferences, mapObjEntries, scrubUndefined } from '../util/misc' import { Variant } from '../variants' import type { INodeTransform, IRenderedAnimation, IRenderedFrame } from './animationRenderer' +import { JsonText } from './jsonText' import type { AnyRenderedNode, IRenderedModel, @@ -64,7 +65,7 @@ type ExportedBakedAnimation = Omit< frames: ExportedAnimationFrame[] modified_nodes: string[] } -type ExportedKeyframe = { +interface ExportedKeyframe { time: number channel: string value?: [string, string, string] @@ -96,7 +97,7 @@ type ExportedKeyframe = { repeat_frequency?: number } type ExportedAnimator = ExportedKeyframe[] -type ExportedDynamicAnimation = { +interface ExportedDynamicAnimation { name: string loop_mode: 'once' | 'hold' | 'loop' duration: number @@ -347,7 +348,7 @@ function serailizeRenderedNode(node: AnyRenderedNode): ExportedRenderedNode { break } case 'text_display': { - json.text = node.text?.toJSON() + json.text = new JsonText(node.text).toJSON() break } } diff --git a/src/systems/resourcepackCompiler/index.ts b/src/systems/resourcepackCompiler/index.ts index b9d0e7b2..96465b30 100644 --- a/src/systems/resourcepackCompiler/index.ts +++ b/src/systems/resourcepackCompiler/index.ts @@ -4,18 +4,22 @@ import { IntentionalExportError } from '../exporter' import { type IRenderedRig } from '../rigRenderer' import type { ExportedFile } from '../util' -import { AJMeta, type MinecraftVersion, PackMeta, type PackMetaFormats } from '../global' +import { AJMeta, PackMeta, SUPPORTED_MINECRAFT_VERSIONS, type PackMetaFormats } from '../global' import _1_20_4 from './1.20.4' import _1_21_2 from './1.21.2' import _1_21_4 from './1.21.4' -const VERSIONS = { - '1.20.4': _1_20_4, - '1.20.5': _1_20_4, - '1.21.0': _1_20_4, - '1.21.2': _1_21_2, - '1.21.4': _1_21_4, +const VERSIONED_RESOURCE_PACK_COMPILERS: Record< + SUPPORTED_MINECRAFT_VERSIONS, + ResourcePackCompiler +> = { + '1.21.9': _1_21_4, '1.21.5': _1_21_4, + '1.21.4': _1_21_4, + '1.21.2': _1_21_2, + '1.21.0': _1_20_4, + '1.20.5': _1_20_4, + '1.20.4': _1_20_4, } interface ResourcePackCompilerOptions { @@ -41,7 +45,7 @@ export interface CompileResourcePackOptions { } export default async function compileResourcePack( - targetVersions: MinecraftVersion[], + targetVersions: SUPPORTED_MINECRAFT_VERSIONS[], options: CompileResourcePackOptions ) { const aj = Project!.animated_java @@ -75,7 +79,7 @@ export default async function compileResourcePack( : options.resourcePackFolder // Move paths into versioned overlay folders. - await VERSIONS[version]({ + await VERSIONED_RESOURCE_PACK_COMPILERS[version]({ ...options, ajmeta, coreFiles, diff --git a/src/util/minecraftUtil.ts b/src/util/minecraftUtil.ts index b847c4e0..7d37553c 100644 --- a/src/util/minecraftUtil.ts +++ b/src/util/minecraftUtil.ts @@ -1,5 +1,5 @@ import * as pathjs from 'path' -import type { MinecraftVersion } from '../systems/global' +import { SUPPORTED_MINECRAFT_VERSIONS } from '../systems/global' import { BlockStateRegistryEntry, type BlockStateValue, @@ -283,46 +283,54 @@ export async function parseBlock(block: string): Promise { return compareVersions(a, b) ? -1 : 1 }) } -export function getDataPackFormat(version: MinecraftVersion): number { +export function getDataPackFormat(version: SUPPORTED_MINECRAFT_VERSIONS): number { switch (version) { - case '1.20.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.4']: return 26 - case '1.20.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.5']: return 41 - case '1.21.0': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.0']: return 48 - case '1.21.2': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.2']: return 57 - case '1.21.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.4']: return 61 - case '1.21.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.5']: return 71 + case SUPPORTED_MINECRAFT_VERSIONS['1.21.9']: + return 88.0 default: + console.warn(`Unknown Minecraft version: ${version}`) return Infinity } } -export function getResourcePackFormat(version: MinecraftVersion): number { +export function getResourcePackFormat(version: SUPPORTED_MINECRAFT_VERSIONS): number { switch (version) { - case '1.20.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.4']: return 22 - case '1.20.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.20.5']: return 32 - case '1.21.0': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.0']: return 34 - case '1.21.2': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.2']: return 42 - case '1.21.4': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.4']: return 46 - case '1.21.5': + case SUPPORTED_MINECRAFT_VERSIONS['1.21.5']: return 55 + case SUPPORTED_MINECRAFT_VERSIONS['1.21.9']: + return 69.0 default: + console.warn(`Unknown Minecraft version: ${version}`) return Infinity } } From 6a286533c5bd2ef7beceb311047ca6dc1210e449 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:11:02 -0400 Subject: [PATCH 108/182] =?UTF-8?q?=E2=9C=A8=20Improve=20property=20overri?= =?UTF-8?q?de=20mod=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/moddingTools.ts | 108 +++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/src/util/moddingTools.ts b/src/util/moddingTools.ts index 195c87bb..769ab94f 100644 --- a/src/util/moddingTools.ts +++ b/src/util/moddingTools.ts @@ -391,39 +391,50 @@ export function registerPropertyOverrideMod< K extends keyof T, O extends T[K] >(options: PropertyOverrideModOptions) { - let originalValue = options.object[options.key] - - const originalDescriptor = Object.getOwnPropertyDescriptor(options.object, options.key) ?? { - value: originalValue, - writable: true, - configurable: true, - } - - if (originalDescriptor.configurable === false) { - throw new Error( - `Cannot override property '${String( - options.key - )}' on object because it is not configurable.` - ) - } - registerMod({ ...options, apply: () => { + if (options.object == undefined) { + throw new Error(`Cannot override property on undefined object.`) + } + + const original = { + value: undefined as O, + descriptor: undefined as unknown as PropertyDescriptor, + } + + original.value = options.object[options.key] as O + + original.descriptor = Object.getOwnPropertyDescriptor(options.object, options.key) ?? { + value: original.value, + writable: true, + configurable: true, + } + + if (original.descriptor.configurable === false) { + throw new Error( + `Cannot override property '${String( + options.key + )}' on object because it is not configurable.` + ) + } + Object.defineProperty(options.object, options.key, { configurable: true, get() { - return options.override.call(this, originalValue) + return options.override.call(this, original.value) }, set(value) { - originalValue = value + original.value = value }, }) + + return { original } }, - revert: () => { - Object.defineProperty(options.object, options.key, originalDescriptor) + revert: ({ original }) => { + Object.defineProperty(options.object, options.key, original.descriptor) }, }) } @@ -452,46 +463,57 @@ export function registerConditionalPropertyOverrideMod< K extends keyof T, O extends T[K] >(options: ConditionalPropertyOverrideModOptions) { - let originalValue = options.object[options.key] - - const originalDescriptor = Object.getOwnPropertyDescriptor(options.object, options.key) ?? { - value: originalValue, - writable: true, - configurable: true, - } - - if (originalDescriptor.configurable === false) { - throw new Error( - `Cannot override property '${String( - options.key - )}' on object because it is not configurable.` - ) - } - registerMod({ ...options, apply: () => { + if (options.object == undefined) { + throw new Error(`Cannot override property on undefined object.`) + } + + const original = { + value: undefined as O, + descriptor: undefined as unknown as PropertyDescriptor, + } + + original.value = options.object[options.key] as O + + original.descriptor = Object.getOwnPropertyDescriptor(options.object, options.key) ?? { + value: original.value, + writable: true, + configurable: true, + } + + if (original.descriptor.configurable === false) { + throw new Error( + `Cannot override property '${String( + options.key + )}' on object because it is not configurable.` + ) + } + Object.defineProperty(options.object, options.key, { configurable: true, get: options.get ? function (this: T) { if (Condition(options.get!.condition, this)) { - return options.get!.override.call(this, originalValue) + return options.get!.override.call(this, original.value) } - return originalValue + return original.value } - : () => originalValue, + : () => original.value, set: options.set ? function (this: T, value) { - originalValue = value + original.value = value } - : value => (originalValue = value), + : value => (original.value = value), }) + + return { original } }, - revert: () => { - Object.defineProperty(options.object, options.key, originalDescriptor) + revert: ({ original }) => { + Object.defineProperty(options.object, options.key, original.descriptor) }, }) } From 87e97debb78a53a085a2cc5c038cae0290654997 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:11:44 -0400 Subject: [PATCH 109/182] =?UTF-8?q?=E2=9C=A8=20Improve=20hex=20number=20hi?= =?UTF-8?q?ghlighting=20in=20text=20display=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/interface/panel/textDisplayElement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interface/panel/textDisplayElement.ts b/src/interface/panel/textDisplayElement.ts index bfac28bc..29ad591a 100644 --- a/src/interface/panel/textDisplayElement.ts +++ b/src/interface/panel/textDisplayElement.ts @@ -73,7 +73,7 @@ function addPrismSyntaxForSnbtTextComponents() { boolean: { pattern: /\b(?:true|false|0b|1b)\b/i, }, - number: /[-]?(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:[eE][+-]?\d+\b)?/, + number: /(?:(?:#|0x)[\dA-Fa-f_]{1,8}\b|[-]?(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.[\d_]+)(?:[eE][+-]?[\d_]+\b)?)/, }) } From 585c70336a55837f07eb23f0d12207b5da1afd50 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:12:27 -0400 Subject: [PATCH 110/182] =?UTF-8?q?=F0=9F=90=9B=20Fix=20action-click-overr?= =?UTF-8?q?ide/export-project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mods/actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mods/actions.ts b/src/mods/actions.ts index fbe881e6..66cd5bde 100644 --- a/src/mods/actions.ts +++ b/src/mods/actions.ts @@ -46,7 +46,7 @@ registerConditionalPropertyOverrideMod({ registerConditionalPropertyOverrideMod({ id: `animated-java:action-click-override/export-project`, - object: BarItems.export_project as Action, + object: BarItems.export_over as Action, key: 'click', get: { condition: () => activeProjectIsBlueprintFormat(), From ed366902925ab7b4236c9882b56b1c4bb3738da7 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:14:07 -0400 Subject: [PATCH 111/182] =?UTF-8?q?=F0=9F=8E=A8=20Greatly=20improve=20text?= =?UTF-8?q?=20display=20update=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Also fix linting errors / warnings in `textDisplay.ts` --- src/outliner/textDisplay.ts | 127 +++++++++++++-------------- src/systems/minecraft/fontManager.ts | 2 +- 2 files changed, 64 insertions(+), 65 deletions(-) diff --git a/src/outliner/textDisplay.ts b/src/outliner/textDisplay.ts index 97ebef4d..014be79c 100644 --- a/src/outliner/textDisplay.ts +++ b/src/outliner/textDisplay.ts @@ -49,13 +49,13 @@ export class TextDisplay extends ResizableOutlinerElement { public ready = false public textError = new Valuable('') - private _isUpdating = false - private _text = new Valuable('Hello World!') - private _lineWidth = new Valuable(200) - private _backgroundColor = new Valuable('#000000') - private _backgroundAlpha = new Valuable(0.25) - private _shadow = new Valuable(false) - private _align = new Valuable('center') + private __isUpdating = false + private __text = new Valuable('Hello World!') + private __lineWidth = new Valuable(200) + private __backgroundColor = new Valuable('#000000') + private __backgroundAlpha = new Valuable(0.25) + private __shadow = new Valuable(false) + private __align = new Valuable('center') public seeThrough = false constructor(data: TextDisplayOptions, uuid = guid()) { @@ -79,12 +79,12 @@ export class TextDisplay extends ResizableOutlinerElement { this.sanitizeName() - this._text.subscribe(() => this.updateText()) - this._lineWidth.subscribe(() => this.updateText()) - this._backgroundColor.subscribe(() => this.updateText()) - this._backgroundAlpha.subscribe(() => this.updateText()) - this._shadow.subscribe(() => this.updateText()) - this._align.subscribe(() => this.updateText()) + this.__text.subscribe(() => this.updateText()) + this.__lineWidth.subscribe(() => this.updateText()) + this.__backgroundColor.subscribe(() => this.updateText()) + this.__backgroundAlpha.subscribe(() => this.updateText()) + this.__shadow.subscribe(() => this.updateText()) + this.__align.subscribe(() => this.updateText()) } public sanitizeName(): string { @@ -93,67 +93,71 @@ export class TextDisplay extends ResizableOutlinerElement { } get text() { - if (this._text === undefined) return TextDisplay.properties['text'].default as string - return this._text.get() + if (this.__text === undefined) return TextDisplay.properties.text.default as string + return this.__text.get() } set text(value) { - if (this._text === undefined) return + if (this.__text === undefined) return if (value === this.text) return - this._text.set(value) + this.__text.set(value) } get lineWidth() { - if (this._lineWidth === undefined) - return TextDisplay.properties['lineWidth'].default as number - return this._lineWidth.get() + if (this.__lineWidth === undefined) + return TextDisplay.properties.lineWidth.default as number + return this.__lineWidth.get() } set lineWidth(value) { - if (this._lineWidth === undefined) return - this._lineWidth.set(value) + if (this.__lineWidth === undefined) return + this.__lineWidth.set(value) } get backgroundColor() { - if (this._backgroundColor === undefined) - return TextDisplay.properties['backgroundColor'].default as string - return this._backgroundColor.get() + if (this.__backgroundColor === undefined) + return TextDisplay.properties.backgroundColor.default as string + return this.__backgroundColor.get() } set backgroundColor(value) { - if (this._backgroundColor === undefined) return - this._backgroundColor.set(value) + if (this.__backgroundColor === undefined) return + this.__backgroundColor.set(value) } get backgroundAlpha() { - if (this._backgroundAlpha === undefined) - return TextDisplay.properties['backgroundAlpha'].default as number - return this._backgroundAlpha.get() + if (this.__backgroundAlpha === undefined) + return TextDisplay.properties.backgroundAlpha.default as number + return this.__backgroundAlpha.get() } set backgroundAlpha(value) { - if (this._backgroundAlpha === undefined) return - this._backgroundAlpha.set(value) + if (this.__backgroundAlpha === undefined) return + this.__backgroundAlpha.set(value) } get shadow() { - if (this._shadow === undefined) return TextDisplay.properties['shadow'].default as boolean - return this._shadow.get() + if (this.__shadow === undefined) return TextDisplay.properties.shadow.default as boolean + return this.__shadow.get() } set shadow(value) { - if (this._shadow === undefined) return - this._shadow.set(value) + if (this.__shadow === undefined) return + this.__shadow.set(value) } get align() { - if (this._align === undefined) return TextDisplay.properties['align'].default as Alignment - return this._align.get() + if (this.__align === undefined) return TextDisplay.properties.align.default as Alignment + return this.__align.get() } set align(value) { - if (this._align === undefined) return - this._align.set(value) + if (this.__align === undefined) return + this.__align.set(value) + } + + getTextValuable() { + return this.__text } getUndoCopy() { @@ -218,8 +222,8 @@ export class TextDisplay extends ResizableOutlinerElement { } async updateText() { - if (this._isUpdating) return - this._isUpdating = true + if (this.__isUpdating) return + this.__isUpdating = true if (Project?.loadingPromises) { // Wait to render text until after the project is done loading @@ -227,14 +231,13 @@ export class TextDisplay extends ResizableOutlinerElement { } let latestMesh: THREE.Mesh | undefined - while (this._isUpdating) { + while (this.__isUpdating) { let text: JsonText | undefined this.textError.set('') try { - text = JsonText.fromString( - this.text, - Project!.animated_java.target_minecraft_version - ) + text = JsonText.fromString(this.text, { + minecraftVersion: Project!.animated_java.target_minecraft_version, + }) console.log('Text updated:', text) } catch (e: any) { console.error(e) @@ -243,12 +246,11 @@ export class TextDisplay extends ResizableOutlinerElement { } else { this.textError.set(e.message as string) } - text = new JsonText({ text: 'Invalid JSON Text!', color: 'red' }) } - this._isUpdating = false + this.__isUpdating = false if (text === undefined) { console.warn('Text is undefined, using fallback text') - continue + text = new JsonText({ text: 'Invalid JSON Text!', color: 'red' }) } latestMesh = await this.setText(text) } @@ -266,7 +268,7 @@ export class TextDisplay extends ResizableOutlinerElement { const font = await getVanillaFont() // Hide the geo while rendering - const { mesh: newMesh, outline } = await font.generateTextMesh({ + const { mesh: newMesh, outline } = await font.generateTextDisplayMesh({ jsonText, maxLineWidth: this.lineWidth, backgroundColor: this.backgroundColor, @@ -345,15 +347,12 @@ export const PREVIEW_CONTROLLER = new NodePreviewController(TextDisplay, { }) class TextDisplayAnimator extends BoneAnimator { - private _name: string - public uuid: string public element: TextDisplay | undefined constructor(uuid: string, animation: _Animation, name: string) { super(uuid, animation, name) this.uuid = uuid - this._name = name } getElement() { @@ -393,7 +392,7 @@ class TextDisplayAnimator extends BoneAnimator { } } - if (this.element && this.element.parent && this.element.parent !== 'root') { + if (this.element.parent && this.element.parent !== 'root') { this.element.parent.openUp() } @@ -402,7 +401,7 @@ class TextDisplayAnimator extends BoneAnimator { doRender() { this.getElement() - return !!(this.element && this.element.mesh) + return !!this.element?.mesh } displayRotation(arr: ArrayVector3 | ArrayVector4, multiplier = 1) { @@ -414,13 +413,13 @@ class TextDisplayAnimator extends BoneAnimator { if (arr) { if (arr.length === 4) { - const added_rotation = new THREE.Euler().setFromQuaternion( + const addedRotation = new THREE.Euler().setFromQuaternion( new THREE.Quaternion().fromArray(arr), 'ZYX' ) - bone.rotation.x -= added_rotation.x * multiplier - bone.rotation.y -= added_rotation.y * multiplier - bone.rotation.z += added_rotation.z * multiplier + bone.rotation.x -= addedRotation.x * multiplier + bone.rotation.y -= addedRotation.y * multiplier + bone.rotation.z += addedRotation.z * multiplier } else { bone.rotation.x -= Math.degToRad(arr[0]) * multiplier bone.rotation.y -= Math.degToRad(arr[1]) * multiplier @@ -485,7 +484,7 @@ export const CREATE_ACTION = registerAction( } selected.forEachReverse(el => el.unselect()) - Group.first_selected && Group.first_selected.unselect() + Group.first_selected?.unselect() textDisplay.select() Undo.finishEdit('Create Text Display', { @@ -499,14 +498,14 @@ export const CREATE_ACTION = registerAction( } ) -const unsubscribers: Array<() => void> = [] +const CLEANUP_CALLBACKS: Array<() => void> = [] CREATE_ACTION.onCreated(action => { Interface.Panels.outliner.menu.addAction(action, 3) Toolbars.outliner.add(action, 0) MenuBar.menus.edit.addAction(action, 8) - unsubscribers.push( + CLEANUP_CALLBACKS.push( EVENTS.SELECT_PROJECT.subscribe(project => { if (!activeProjectIsBlueprintFormat()) return project.textDisplays ??= [] @@ -527,5 +526,5 @@ CREATE_ACTION.onDeleted(action => { Toolbars.outliner.remove(action) MenuBar.menus.edit.removeAction(action) - unsubscribers.forEach(unsub => unsub()) + CLEANUP_CALLBACKS.forEach(unsub => unsub()) }) diff --git a/src/systems/minecraft/fontManager.ts b/src/systems/minecraft/fontManager.ts index c3f95817..9e2ed209 100644 --- a/src/systems/minecraft/fontManager.ts +++ b/src/systems/minecraft/fontManager.ts @@ -372,7 +372,7 @@ export class MinecraftFont { return Math.max(width, 0) } - async generateTextMesh({ + async generateTextDisplayMesh({ jsonText, maxLineWidth, backgroundColor, From a048d6a52e1883acf284ab46905e874086589369 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:16:13 -0400 Subject: [PATCH 112/182] =?UTF-8?q?=E2=9C=A8=20Improve=20text=20display=20?= =?UTF-8?q?rendering=20performance,=20and=20fix=20a=20few=20minor=20text?= =?UTF-8?q?=20wrapping=20edge-cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/systems/minecraft/fontManager.ts | 219 +++++++++++++------------- src/systems/minecraft/textWrapping.ts | 24 +-- 2 files changed, 124 insertions(+), 119 deletions(-) diff --git a/src/systems/minecraft/fontManager.ts b/src/systems/minecraft/fontManager.ts index 9e2ed209..0b5bf50a 100644 --- a/src/systems/minecraft/fontManager.ts +++ b/src/systems/minecraft/fontManager.ts @@ -63,7 +63,7 @@ interface CachedSpaceChar { type CachedChar = CachedBitmapChar | CachedSpaceChar -type CachedCharMesh = { +interface CachedCharGeo { geo?: THREE.BufferGeometry width: number } @@ -261,9 +261,10 @@ export class MinecraftFont { public providers: FontProvider[] = [] public fallback: MinecraftFont | undefined - private charCache = new Map() private loaded = false - private characterMeshCache = new Map() + private charCache = new Map() + private geoCache = new Map() + private materialCache = new Map() constructor(id: string, assetPath: string, fallback?: MinecraftFont) { this.id = id @@ -304,12 +305,7 @@ export class MinecraftFont { async load() { if (this.loaded) return this - await Promise.all(this.providers.map(provider => provider.load())).then(() => { - // // Cache commonly used characters - // for (const char of 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=[]{}\\|;:\'",.<>/?`~ ') { - // this.getChar(char) - // } - }) + await Promise.all(this.providers.map(provider => provider.load())) this.loaded = true return this } @@ -332,7 +328,6 @@ export class MinecraftFont { getTextWidth(text: UnicodeString, span?: IStyleSpan) { let width = 0 const boldExtra = span?.style.bold ? 1 : 0 - // eslint-disable-next-line @typescript-eslint/no-this-alias let font: MinecraftFont = this if (span?.style.font && span.style.font !== this.id) { @@ -356,7 +351,6 @@ export class MinecraftFont { getWordWidth(word: IComponentWord) { let width = 0 - // eslint-disable-next-line @typescript-eslint/no-this-alias let font: MinecraftFont = this for (const span of word.styles) { @@ -372,6 +366,26 @@ export class MinecraftFont { return Math.max(width, 0) } + getColorMaterial(color: tinycolor.Instance): THREE.Material { + const colorString = color.toHex8String() + let material = this.materialCache.get(colorString) + if (!material) { + const alpha = color.getAlpha() + console.log(colorString, alpha, color) + if (alpha < 1) { + material = new THREE.MeshBasicMaterial({ + color: color.toHexString(), + transparent: true, + opacity: alpha, + }) + } else { + material = new THREE.MeshBasicMaterial({ color: color.toHexString() }) + } + this.materialCache.set(colorString, material) + } + return material + } + async generateTextDisplayMesh({ jsonText, maxLineWidth, @@ -384,6 +398,7 @@ export class MinecraftFont { maxLineWidth: number backgroundColor: string backgroundAlpha: number + /** Whether or not to render any text shadow */ shadow?: boolean alignment?: Alignment }): Promise<{ mesh: THREE.Mesh; outline: THREE.LineSegments }> { @@ -391,9 +406,12 @@ export class MinecraftFont { const mesh = new THREE.Mesh() const words = getComponentWords(jsonText.toJSON()) + console.log('Component words:', words) const { lines, backgroundWidth } = await computeTextWrapping(words, maxLineWidth) + console.log('Computed lines:', lines) const width = backgroundWidth + 1 - const height = lines.length * 10 + 1 + const height = (lines.length || 1) * 10 + 1 + console.log('Text dimensions:', width, height) // // Debug output // const wordWidths = words.map(word => this.getWordWidth(word)) @@ -444,7 +462,8 @@ export class MinecraftFont { .translateZ(-0.05) mesh.add(backgroundMesh) - const geos: THREE.BufferGeometry[] = [] + const spanGeos: THREE.BufferGeometry[] = [] + const spanMaterials: THREE.Material[] = [] const cursor = { x: 0, y: height - 9 } for (const line of lines) { switch (alignment) { @@ -460,22 +479,61 @@ export class MinecraftFont { for (const word of line.words) { for (const span of word.styles) { + const charGeos: THREE.BufferGeometry[] = [] + const shadowGeos: THREE.BufferGeometry[] = [] + const text = word.text.slice(span.start, span.end) for (const char of text) { - const charMesh = this.getCharMesh(char, span.style, shadow) + const charGeo = this.getCharGeo(char, span.style) - if (!charMesh) { - console.error('Failed to get character mesh while drawing text:', char) + if (!charGeo) { + console.error('Failed to get character geometry:', char) continue } - if (charMesh.geo) { - const clone = charMesh.geo.clone() + if (charGeo.geo) { + const clone = charGeo.geo.clone() clone.translate(cursor.x, cursor.y, 0) - geos.push(clone) + charGeos.push(clone) + if (shadow) { + const shadowGeo = charGeo.geo.clone() + shadowGeo.translate(cursor.x + 1, cursor.y - 1, -0.01) + shadowGeos.push(shadowGeo) + } } - cursor.x += charMesh.width + cursor.x += charGeo.width + } + + if (charGeos.length > 0) { + spanGeos.push(mergeGeometries(charGeos)!) + + const color = JsonText.getColor(span.style.color ?? COLOR_VALUES.white) + spanMaterials.push(this.getColorMaterial(color)) + + if (shadow && shadowGeos.length > 0) { + spanGeos.push(mergeGeometries(shadowGeos)!) + + if (span.style.shadow_color) { + spanMaterials.push( + this.getColorMaterial( + JsonText.getColor(span.style.shadow_color) + ) + ) + } else { + // Default shadow color is 25% the brightness of the main color + spanMaterials.push( + this.getColorMaterial( + // This version of tinycolor doesn't have a multiply method... + tinycolor( + new THREE.Color(color.toHexString()) + .multiplyScalar(0.25) + .getHexString() + ) + ) + ) + } + } } } } @@ -483,13 +541,10 @@ export class MinecraftFont { cursor.y -= 10 } - let charGeo: THREE.BufferGeometry | undefined - if (geos.length > 0) { - charGeo = mergeGeometries(geos)! - const charMesh = new THREE.Mesh( - charGeo, - new THREE.MeshBasicMaterial({ vertexColors: true }) - ) + if (spanGeos.length > 0) { + const charGeo = mergeGeometries(spanGeos, true)! + const charMesh = new THREE.Mesh(charGeo, spanMaterials) + console.log(charGeo, spanMaterials, charMesh) mesh.add(charMesh) } @@ -516,47 +571,29 @@ export class MinecraftFont { return { mesh, outline } } - getCharMesh(char: string, style: ComponentStyle, shadow?: boolean): CachedCharMesh { - // eslint-disable-next-line @typescript-eslint/no-this-alias + getCharGeo(char: string, style: ComponentStyle): CachedCharGeo { let font: MinecraftFont = this if (style.font) { const newFont = MinecraftFont.getById(style.font) if (newFont) font = newFont } - let charData = font.getChar(char) - - let color = new THREE.Color('#ffffff') - if (typeof style.color === 'string') { - color = - style.color.startsWith('#') && style.color.length === 7 - ? new THREE.Color(style.color) - : new THREE.Color(COLOR_MAP[style.color]) || color - } - - let shadowColor: THREE.Color - if (Array.isArray(style.shadow_color)) { - shadowColor = new THREE.Color().fromArray(style.shadow_color) - } else { - shadowColor = color.clone().multiplyScalar(0.25) - } - - const boldExtra = style.bold ? 1 : 0 const hash = createHash('sha256') hash.update(char) - hash.update(';' + color.getHexString()) - if (shadow) hash.update('shadow') + hash.update(';' + font.id) if (style.bold) hash.update('bold') if (style.italic) hash.update('italic') if (style.underlined) hash.update('underlined') if (style.strikethrough) hash.update('strikethrough') - if (style.shadow_color) hash.update(';' + shadowColor.getHexString()) if (style.font) hash.update(';' + font.id) const digest = hash.digest('hex') - let charMesh = this.characterMeshCache.get(digest) + const charData = font.getChar(char) + const boldExtra = style.bold ? 1 : 0 + + let charGeo = this.geoCache.get(digest) - if (charMesh === undefined) { + if (charGeo === undefined) { const canvas = document.createElement('canvas') if (charData.type === 'space') { canvas.width = charData.width @@ -587,20 +624,22 @@ export class MinecraftFont { const data = ctx.getImageData(0, 0, canvas.width, canvas.height) const geo = new THREE.BufferGeometry() - let colors: number[] = [] - let vertices: number[] = [] - let indices: number[] = [] + + const mainGeoData = { + vertices: [] as number[], + indices: [] as number[], + } const createQuad = (x: number, y: number, w: number, h: number) => { - const vertIndex = vertices.length / 3 + const vertIndex = mainGeoData.vertices.length / 3 // prettier-ignore - vertices.push( + mainGeoData.vertices.push( x, y, 0, x + w, y, 0, x + w, y + h, 0, x, y + h, 0 ) - indices.push( + mainGeoData.indices.push( vertIndex, vertIndex + 1, vertIndex + 2, @@ -608,41 +647,6 @@ export class MinecraftFont { vertIndex + 2, vertIndex + 3 ) - // prettier-ignore - colors.push( - color.r, color.g, color.b, - color.r, color.g, color.b, - color.r, color.g, color.b, - color.r, color.g, color.b - ) - if (shadow) { - const shadowVertIndex = vertices.length / 3 - x += 1 - y -= 1 - const z = -0.01 - // prettier-ignore - vertices.push( - x, y, z, - x + w, y, z, - x + w, y + h, z, - x, y + h, z - ) - indices.push( - shadowVertIndex, - shadowVertIndex + 1, - shadowVertIndex + 2, - shadowVertIndex, - shadowVertIndex + 2, - shadowVertIndex + 3 - ) - // prettier-ignore - colors.push( - shadowColor.r, shadowColor.g, shadowColor.b, - shadowColor.r, shadowColor.g, shadowColor.b, - shadowColor.r, shadowColor.g, shadowColor.b, - shadowColor.r, shadowColor.g, shadowColor.b - ) - } } if (charData.type !== 'space') { @@ -670,18 +674,18 @@ export class MinecraftFont { } } - geo.setIndex(indices) - geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3)) - geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3)) - + geo.setIndex(mainGeoData.indices) + geo.setAttribute( + 'position', + new THREE.BufferAttribute(new Float32Array(mainGeoData.vertices), 3) + ) if (style.italic) { geo.applyMatrix4(new THREE.Matrix4().makeShear(0, 0, 0.2, 0, 0, 0)) geo.translate(-1, 0, 0) } - vertices = Array.from(geo.getAttribute('position').array) - colors = Array.from(geo.getAttribute('color').array) - indices = Array.from(geo.getIndex()!.array) + mainGeoData.vertices = Array.from(geo.getAttribute('position').array) + mainGeoData.indices = Array.from(geo.getIndex()!.array) if (style.underlined) { createQuad(-1, -1, canvas.width + 2, 1) @@ -699,20 +703,21 @@ export class MinecraftFont { } } - geo.setIndex(indices) - geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3)) - geo.setAttribute('color', new THREE.BufferAttribute(new Float32Array(colors), 3)) + geo.setIndex(mainGeoData.indices) + geo.setAttribute( + 'position', + new THREE.BufferAttribute(new Float32Array(mainGeoData.vertices), 3) + ) geo.attributes.position.needsUpdate = true - geo.attributes.color.needsUpdate = true - charMesh = { + charGeo = { geo, width: charData.width + boldExtra, } - this.characterMeshCache.set(digest, charMesh) + this.geoCache.set(digest, charGeo) } - return charMesh + return charGeo } } diff --git a/src/systems/minecraft/textWrapping.ts b/src/systems/minecraft/textWrapping.ts index f6b1a038..877122e6 100644 --- a/src/systems/minecraft/textWrapping.ts +++ b/src/systems/minecraft/textWrapping.ts @@ -1,11 +1,11 @@ -import { Component, ComponentStyle, CompositeComponent, JsonText } from '../jsonText' +import { JsonText, type ComponentStyle, type TextElement, type TextObject } from '../jsonText' import { UnicodeString } from '../jsonText/unicodeString' import { getVanillaFont } from './fontManager' // Jumpstarted by @IanSSenne (FetchBot) and refactored by @SnaveSutit to do line wrapping on JSON Text Components. // THANK U IAN <3 - SnaveSutit -function getText(component: string | CompositeComponent): UnicodeString { +function getText(component: string | TextObject): UnicodeString { if (typeof component === 'string') { return new UnicodeString(component) } @@ -67,14 +67,14 @@ interface IComponentLine { width: number } -const defaultStyle: ComponentStyle = { color: 'white' } /** * Gets the words from a JSON Text Component, while keeping track of the styles applied to each word. * * WARNING: Word width is not calculated by this function. */ -export function getComponentWords(input: Component) { - const flattenedComponents = new JsonText(input).flatten() +export function getComponentWords(input: TextElement) { + const flattenedComponents = new JsonText(input).flatten(true) + console.log('Flattened components:', JSON.stringify(flattenedComponents, null, 2)) if (!flattenedComponents.length) return [] const words: IComponentWord[] = [] @@ -84,7 +84,7 @@ export function getComponentWords(input: Component) { let componentText = getText(component) let span: IStyleSpan = { - style: JsonText.getComponentStyle(component, defaultStyle), + style: JsonText.getComponentStyle(component), start: 0, end: 0, } @@ -108,7 +108,9 @@ export function getComponentWords(input: Component) { } else if (char === '\n') { if (word) { if (Object.keys(span.style).length) { - word.styles.push({ ...span }) + if (span.start < span.end) { + word.styles.push({ ...span }) + } span.start = 0 span.end = 0 } @@ -133,9 +135,7 @@ export function getComponentWords(input: Component) { word = undefined } - if (!word) { - word = { styles: [], text: new UnicodeString(''), width: 0 } - } + word ??= { styles: [], text: new UnicodeString(''), width: 0 } word.text.append(char) span.end++ } @@ -147,13 +147,13 @@ export function getComponentWords(input: Component) { if (word) { word.styles.push(span) span = { - style: JsonText.getComponentStyle(component, defaultStyle), + style: JsonText.getComponentStyle(component), start: span.end, end: span.end, } } else { span = { - style: JsonText.getComponentStyle(component, defaultStyle), + style: JsonText.getComponentStyle(component), start: 0, end: 0, } From c91acf7f3f3ceee83cd259934cf6a1c7e36314e9 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:18:04 -0400 Subject: [PATCH 113/182] =?UTF-8?q?=F0=9F=9A=A7=20Change=20rendered=20rig?= =?UTF-8?q?=20text=20display=20text=20to=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/systems/datapackCompiler/index.ts | 36 +++++++++++++-------------- src/systems/rigRenderer.ts | 9 +++---- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/systems/datapackCompiler/index.ts b/src/systems/datapackCompiler/index.ts index c68f3135..7d6882b7 100644 --- a/src/systems/datapackCompiler/index.ts +++ b/src/systems/datapackCompiler/index.ts @@ -557,7 +557,7 @@ async function generateRootEntityPassengers( 'text', // String JSON text format new NbtString( - node.text?.toString(version) ?? + new JsonText(node.text).toString(true, version) ?? `{ "text": "Invalid Text Component", "color": "red" }` ) ) @@ -657,7 +657,7 @@ async function generateRootEntityPassengers( for (const display of Object.values(rig.nodes).filter(n => n.type === 'text_display')) { result = result.replace( '"$$$' + display.type + '_' + display.storage_name + '_text_placeholder$$$"', - display.text?.toString(version) ?? + new JsonText(display.text).toString(true, version) ?? `{ "text": "Invalid Text Component", "color": "red" }` ) } @@ -817,18 +817,18 @@ export default async function compileDataPack( versionedFiles, }) - for (let [path, file] of coreFiles) { - path = PathModule.join(coreDataPackFolder, path) - globalCoreFiles.set(path, file) + for (const [path, file] of coreFiles) { + const relative = PathModule.join(coreDataPackFolder, path) + globalCoreFiles.set(relative, file) if (file.includeInAJMeta === false) continue - ajmeta.coreFiles.add(path) + ajmeta.coreFiles.add(relative) } - for (let [path, file] of versionedFiles) { - path = PathModule.join(versionedDataPackFolder, path) - globalVersionSpecificFiles.set(path, file) + for (const [path, file] of versionedFiles) { + const relative = PathModule.join(versionedDataPackFolder, path) + globalVersionSpecificFiles.set(relative, file) if (file.includeInAJMeta === false) continue - ajmeta.versionedFiles.add(path) + ajmeta.versionedFiles.add(relative) } console.groupEnd() @@ -837,7 +837,7 @@ export default async function compileDataPack( console.log('Exported Files:', globalCoreFiles.size + globalVersionSpecificFiles.size) const packMetaPath = PathModule.join(options.dataPackFolder, 'pack.mcmeta') - let packMeta = PackMeta.fromFile(packMetaPath) + const packMeta = PackMeta.fromFile(packMetaPath) packMeta.content.pack ??= {} packMeta.content.pack.pack_format = getDataPackFormat(targetVersions[0]) packMeta.content.pack.description ??= `Animated Java Data Pack for ${targetVersions.join(', ')}` @@ -848,7 +848,7 @@ export default async function compileDataPack( packMeta.content.overlays.entries ??= [] for (const version of targetVersions) { - let format: PackMetaFormats = getDataPackFormat(version) + const format: PackMetaFormats = getDataPackFormat(version) packMeta.content.pack.supported_formats.push(format) const existingOverlay = packMeta.content.overlays.entries.find( @@ -871,7 +871,7 @@ export default async function compileDataPack( }) if (aj.data_pack_export_mode === 'folder') { - await removeFiles(ajmeta, options.dataPackFolder) + await removeFiles(ajmeta) // Write new files ajmeta.coreFiles = new Set(globalCoreFiles.keys()) @@ -891,7 +891,7 @@ export default async function compileDataPack( console.timeEnd('Data Pack Compilation took') } -async function removeFiles(ajmeta: AJMeta, dataPackFolder: string) { +async function removeFiles(ajmeta: AJMeta) { console.time('Removing Files took') const aj = Project!.animated_java if (aj.data_pack_export_mode === 'folder') { @@ -969,7 +969,7 @@ const dataPackCompiler: DataPackCompiler = async ({ animationHash, debugMode, }) => { - JsonText.defaultTargetVersion = version + JsonText.defaultMinecraftVersion = version const aj = Project!.animated_java const isStatic = animations.length === 0 @@ -1019,9 +1019,9 @@ const dataPackCompiler: DataPackCompiler = async ({ compileMcbProject({ sourceFiles: { - 'src/global.mcbt': mcbFiles[version].coreTemplates, - 'src/animated_java.mcb': mcbFiles[version].core, - [`src/animated_java/${aj.export_namespace}.mcb`]: is_static + 'src/global.mcbt': mcbFiles[version].globalTemplates, + 'src/animated_java.mcb': mcbFiles[version].global, + [`src/animated_java/${aj.export_namespace}.mcb`]: isStatic ? mcbFiles[version].static : mcbFiles[version].animation, }, diff --git a/src/systems/rigRenderer.ts b/src/systems/rigRenderer.ts index c0f645cb..2d74bead 100644 --- a/src/systems/rigRenderer.ts +++ b/src/systems/rigRenderer.ts @@ -23,7 +23,6 @@ import { updatePreview, } from './animationRenderer' import { IntentionalExportError } from './exporter' -import { JsonText } from './jsonText' export interface IRenderedFace { uv: number[] @@ -121,7 +120,7 @@ export interface IRenderedNodes { } TextDisplay: IRenderedNode & { type: 'text_display' - text?: JsonText + text: string line_width: number background_color: string background_alpha: number @@ -522,7 +521,7 @@ function renderTextDisplay(display: TextDisplay, rig: IRenderedRig): INodeStruct storage_name: sanitizeStorageKey(display.name), uuid: display.uuid, parent: parentId, - text: JsonText.fromString(display.text, Project!.animated_java.target_minecraft_version), + text: display.text, line_width: display.lineWidth, background_color: display.backgroundColor, background_alpha: display.backgroundAlpha, @@ -680,9 +679,7 @@ export function hashRig(rig: IRenderedRig) { case 'camera': break case 'text_display': { - hash.update( - `;${node.text?.toString(Project!.animated_java.target_minecraft_version)}` - ) + hash.update(`;${node.text}`) if (node.config) { hash.update(';' + JSON.stringify(node.config)) } From 67f3732f92c0e6812e67996c78ba38bc99f93f9d Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:24:19 -0400 Subject: [PATCH 114/182] =?UTF-8?q?=E2=9C=A8=20Major=20overhaul=20of=20Jso?= =?UTF-8?q?nText=20system.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Started using TS Enums over objects and string unions where possible. - Added support for 1.21.9 and above JSON text components. - Made JsonText accept any JSON text version as input, and transform the output depending on the target MC version. - Greatly improved JsonText parsing, error messages, and stringification. - Added tests for JsonTextParser. - Added quite a few syntax sugar features to JsonText parser. - Added validation for 95% of Json TextObject formats. The `jsonText/parser.ts` file is now 2281 lines long 💀. --- src/outliner/textDisplay.ts | 4 +- src/systems/datapackCompiler/tellraw.ts | 4 +- src/systems/jsonText/index.ts | 438 ++-- src/systems/jsonText/parser.ts | 2571 ++++++++++++++++------- src/systems/jsonText/stringifier.ts | 294 +-- src/systems/minecraft/fontManager.ts | 2 +- src/tests/jsonText.test.ts | 1013 +++++++++ 7 files changed, 3269 insertions(+), 1057 deletions(-) create mode 100644 src/tests/jsonText.test.ts diff --git a/src/outliner/textDisplay.ts b/src/outliner/textDisplay.ts index 014be79c..4d1d59cd 100644 --- a/src/outliner/textDisplay.ts +++ b/src/outliner/textDisplay.ts @@ -5,7 +5,7 @@ import { } from '../formats/blueprint/format' import { registerAction } from '../util/moddingTools' // import * as MinecraftFull from '../assets/MinecraftFull.json' -import { JsonParserError } from 'src/systems/jsonText/parser' +import { JsonTextSyntaxError } from 'src/systems/jsonText/parser' import { TextDisplayConfig } from '../nodeConfigs' import { JsonText } from '../systems/jsonText' import { getVanillaFont } from '../systems/minecraft/fontManager' @@ -241,7 +241,7 @@ export class TextDisplay extends ResizableOutlinerElement { console.log('Text updated:', text) } catch (e: any) { console.error(e) - if (e instanceof JsonParserError) { + if (e instanceof JsonTextSyntaxError) { this.textError.set(e.getOriginErrorMessage()) } else { this.textError.set(e.message as string) diff --git a/src/systems/datapackCompiler/tellraw.ts b/src/systems/datapackCompiler/tellraw.ts index 31178e31..63a4b019 100644 --- a/src/systems/datapackCompiler/tellraw.ts +++ b/src/systems/datapackCompiler/tellraw.ts @@ -1,6 +1,6 @@ import { toSmallCaps } from 'src/util/minecraftUtil' import { type IRenderedAnimation } from '../animationRenderer' -import { Component, JsonText } from '../jsonText' +import { JsonText, TextElement } from '../jsonText' import { type IRenderedVariant } from '../rigRenderer' import { TAGS } from './tags' @@ -19,7 +19,7 @@ namespace TELLRAW { const TELLRAW_SUFFIX = () => '\n' - const TELLRAW_ERROR = (errorName: string, details: Component) => + const TELLRAW_ERROR = (errorName: string, details: TextElement) => new JsonText({ text: '', color: 'red', diff --git a/src/systems/jsonText/index.ts b/src/systems/jsonText/index.ts index ffe82a93..f13be85f 100644 --- a/src/systems/jsonText/index.ts +++ b/src/systems/jsonText/index.ts @@ -1,7 +1,5 @@ -import { MinecraftVersion } from '../global' import { JsonTextParser } from './parser' import { JsonTextStringifier } from './stringifier' -export { COLOR_MAP } from './parser' export const FONT = '16px MinecraftFull' @@ -16,28 +14,64 @@ export enum STYLE_KEYS { SHADOW_COLOR = 'shadow_color', } -export type ComponentStyle = Pick - -export type Color = - | 'dark_red' - | 'red' - | 'gold' - | 'yellow' - | 'dark_green' - | 'green' - | 'aqua' - | 'dark_aqua' - | 'dark_blue' - | 'blue' - | 'light_purple' - | 'dark_purple' - | 'white' - | 'gray' - | 'dark_gray' - | 'black' - | `#${string}` - -export interface ClickEvent { +export enum CONTENT_TYPES { + TEXT = 'text', + TRANSLATABLE = 'translatable', + SCORE = 'score', + SELECTOR = 'selector', + KEYBIND = 'keybind', + NBT = 'nbt', + OBJECT = 'object', +} + +export enum CONTENT_KEYS { + TEXT = 'text', + TRANSLATE = 'translate', + SCORE = 'score', + SELECTOR = 'selector', + KEYBIND = 'keybind', + NBT = 'nbt', + SPRITE = 'sprite', + PLAYER = 'player', +} + +export enum EVENT_KEYS { + CLICK_EVENT = 'click_event', + HOVER_EVENT = 'hover_event', + LEGACY_CLICK_EVENT = 'clickEvent', + LEGACY_HOVER_EVENT = 'hoverEvent', +} + +export enum NBT_SOURCE_KEYS { + BLOCK = 'block', + ENTITY = 'entity', + STORAGE = 'storage', +} + +export const COLOR_VALUES = { + dark_red: '#AA0000', + red: '#FF5555', + gold: '#FFAA00', + yellow: '#FFFF55', + dark_green: '#00AA00', + green: '#55FF55', + aqua: '#55FFFF', + dark_aqua: '#00AAAA', + dark_blue: '#0000AA', + blue: '#5555FF', + light_purple: '#FF55FF', + dark_purple: '#AA00AA', + white: '#FFFFFF', + gray: '#AAAAAA', + dark_gray: '#555555', + black: '#000000', +} as const + +export type ComponentStyle = Pick +export type TextObjectColor = keyof typeof COLOR_VALUES | `#${string}` +export type TextObjectShadowColor = number | [number, number, number, number] + +export interface LegacyClickEvent { action: | 'open_url' | 'open_file' @@ -48,28 +82,41 @@ export interface ClickEvent { value: string } -export type HoverEvent = - | { action: 'show_text'; contents: Component } +export type LegacyHoverEvent = + | { action: 'show_text'; contents: TextElement } | { action: 'show_item' - contents: { - id: string - count?: number - // Text displays dont support hover events anyway, - // so I'll just ignore this for now. - tag?: any - } + contents: + | string + | { + id: string + count?: number + // Text displays dont support hover events anyway, + // so I'll just ignore this for now. + tag?: any + } } | { action: 'show_entity' contents: { type: string - id: string + id: string | [number, number, number, number] name?: string } } -export type ClickEvent_1_21_5 = +export enum MODERN_CLICK_EVENT_SUBKEYS { + ID = 'id', + URL = 'url', + PATH = 'path', + COMMAND = 'command', + PAGE = 'page', + VALUE = 'value', + DIALOG = 'dialog', + PAYLOAD = 'payload', +} + +export type ModernClickEvent = | { action: 'open_url' url: string @@ -105,191 +152,156 @@ export type ClickEvent_1_21_5 = payload?: any } -export type HoverEvent_1_21_5 = +export type ModernHoverEvent = | { action: 'show_text' - value: Component + value: TextElement } | { action: 'show_item' id: string count?: number - // Text displays dont support hover events anyway, - // so I'll just ignore this for now. - components?: string + components?: any } | { action: 'show_entity' - name?: string id: string uuid: string | [number, number, number, number] + name?: string } -export type BaseComponent = { - type?: string - - font?: string - color?: Color - bold?: boolean - italic?: boolean - underlined?: boolean - strikethrough?: boolean - obfuscated?: boolean - - extra?: Component[] - insertion?: string +export interface TextObject { + type?: CONTENT_TYPES - // before 1.21.5 - clickEvent?: ClickEvent - hoverEvent?: HoverEvent + text?: string - // since 1.21.5 - shadow_color?: number | [number, number, number, number] - click_event?: ClickEvent_1_21_5 - hover_event?: HoverEvent_1_21_5 -} - -export interface TextComponent { - type?: 'text' - text: string -} - -export type TranslatableTextComponent = { - type?: 'translatable' - translate: string + translate?: string fallback?: string - with?: Component[] -} + with?: TextElement[] -export interface ScoreComponent { - type?: 'score' - score: { + score?: { name: string objective: string } -} -export interface SelectorComponent { - type?: 'selector' - selector: string - separator?: string -} - -export interface KeybindComponent { - type?: 'keybind' - keybind: string -} + selector?: string + separator?: TextElement + + keybind?: string + + nbt?: string + source?: 'block' | 'entity' | 'storage' + block?: string + entity?: string + storage?: string + interpret?: boolean + + object?: 'atlas' | 'player' + sprite?: string + atlas?: string + player?: { + name?: string + id?: string | [number, number, number, number] + texture?: string + cape?: string + model?: 'wide' | 'slim' + hat?: boolean + properties?: Array<{ + name: string + value: string + signature?: string + }> + } -export interface BlockNbtComponent { - type?: 'nbt' - nbt: string - block: string -} + font?: string + color?: TextObjectColor + bold?: boolean + italic?: boolean + underlined?: boolean + strikethrough?: boolean + obfuscated?: boolean + shadow_color?: TextObjectShadowColor -export interface EntityNbtComponent { - type?: 'nbt' - nbt: string - entity: string -} + extra?: TextElement[] + insertion?: string -export interface StorageNbtComponent { - type?: 'nbt' - nbt: string - storage: string + clickEvent?: LegacyClickEvent + hoverEvent?: LegacyHoverEvent + click_event?: ModernClickEvent + hover_event?: ModernHoverEvent } -export type CompositeComponent = BaseComponent & - Partial< - TextComponent & - TranslatableTextComponent & - ScoreComponent & - SelectorComponent & - KeybindComponent & - BlockNbtComponent & - EntityNbtComponent & - StorageNbtComponent - > - -export type Component = string | Component[] | CompositeComponent +export type TextElement = string | TextElement[] | TextObject export class JsonText { - static defaultTargetVersion: MinecraftVersion = '1.20.4' + static defaultStyle: ComponentStyle = { color: 'white' } + static defaultMinecraftVersion = '1.21.9' isJsonTextClass = true - content: Component + content: TextElement - constructor(jsonText: Component) { + constructor(jsonText: TextElement) { this.content = jsonText } - toString(targetMinecraftVersion: MinecraftVersion = JsonText.defaultTargetVersion) { - return new JsonTextStringifier(this.content, targetMinecraftVersion).stringify() + toString(minify = true, minecraftVersion = JsonText.defaultMinecraftVersion) { + const content = minify ? this.flatten() : this.content + return new JsonTextStringifier(content, minecraftVersion).stringify() } - toJSON(): Component { + toJSON(): TextElement { return structuredClone(this.content) } - static getComponentStyle( - component: Component, - parentStyle: ComponentStyle = {} - ): ComponentStyle { - switch (true) { - case Array.isArray(component): - if (component.length === 0) return { ...parentStyle } - return JsonText.getComponentStyle(component[0], parentStyle) - - case typeof component === 'string': - return { ...parentStyle } - - case typeof component === 'object': { - const style = { ...parentStyle } - for (const key of Object.values(STYLE_KEYS)) { - if (component[key] === undefined) continue - style[key] = component[key] as any - } - return style - } - - default: - console.warn('Unknown component type in getComponentStyle:', component) - return { ...parentStyle } - } - } - /** * Returns a flattened version of this JsonText. + * + * If `explicitStyles` is true, all styles will be explicitly set on each component, + * even if they are the same as the parent style. */ - flatten(): Array { - const output: Array = [] + flatten(explicitStyles = false): Array { + const output: Array = [] - const processComponent = (component: Component, parentStyle: ComponentStyle = {}) => { + const processComponent = (element: TextElement, parentStyle: ComponentStyle = {}) => { + const style = JsonText.getComponentStyle(element, parentStyle) + const previous = output[output.length - 1] switch (true) { - case Array.isArray(component): { - // "Parent" style is inherited from the first item of the array - const style = JsonText.getComponentStyle(component, parentStyle) - for (const child of component) { + case Array.isArray(element): { + for (const child of element) { processComponent(child, style) } break } - case typeof component === 'string': - if (Object.keys(parentStyle).length === 0) { - output.push(component) + case typeof element === 'string': + // Merge with previous element if possible + if (typeof previous === 'string' && JsonText.hasSameStyle(style, parentStyle)) { + output[output.length - 1] = previous + element + break + } else if ( + typeof previous === 'object' && + previous.text !== undefined && + JsonText.hasSameStyle(style, previous) + ) { + previous.text += element + break + } + + if (!explicitStyles && JsonText.hasSameStyle(style, parentStyle)) { + output.push(element) break } - output.push({ ...parentStyle, text: component }) + output.push({ ...parentStyle, text: element }) break - case typeof component === 'object': { - const style = JsonText.getComponentStyle(component, parentStyle) - const processed = { ...component } + case typeof element === 'object': { + const style = JsonText.getComponentStyle(element, parentStyle) + const processed = { ...element } delete processed.with delete processed.extra output.push({ ...style, ...processed }) - const { with: withArray = [], extra: extraArray = [] } = component + const { with: withArray = [], extra: extraArray = [] } = element if (withArray.length > 0) { processComponent(withArray, style) @@ -301,7 +313,7 @@ export class JsonText { } default: - console.warn('Unknown component type in flatten:', component) + console.warn('Unknown component type in flatten:', element) break } } @@ -310,6 +322,100 @@ export class JsonText { return output } + static getComponentStyle( + component: TextElement, + parentStyle: ComponentStyle = JsonText.defaultStyle + ): ComponentStyle { + switch (true) { + case Array.isArray(component): + if (component.length === 0) return { ...parentStyle } + return JsonText.getComponentStyle(component[0], parentStyle) + + case typeof component === 'string': + return { ...parentStyle } + + case typeof component === 'object': { + const style = { ...parentStyle } + for (const key of Object.values(STYLE_KEYS)) { + if (component[key] === undefined) continue + style[key] = component[key] as any + } + return style + } + + default: + console.warn('Unknown component type in getComponentStyle:', component) + return { ...parentStyle } + } + } + + static hasSameStyle(a: ComponentStyle, b: ComponentStyle): boolean { + for (const key of Object.values(STYLE_KEYS)) { + if (a[key] !== b[key]) return false + } + return true + } + + static intToRgba(color: number): [number, number, number, number] { + const a = (color >> 24) & 0xff + const r = (color >> 16) & 0xff + const g = (color >> 8) & 0xff + const b = color & 0xff + return [r / 255, g / 255, b / 255, a / 255] + } + + static rgbaToInt([r, g, b, a]: [number, number, number, number]): number { + r = Math.floor(r * 255) + g = Math.floor(g * 255) + b = Math.floor(b * 255) + a = Math.floor(a * 255) + return (a << 24) | (r << 16) | (g << 8) | b + } + + static intToHex8(color: number): string { + return `#${(color >>> 0).toString(16).padStart(8, '0')}` + } + + static hexToRgba(hex: string): [number, number, number, number] { + return JsonText.intToRgba(JsonText.hexToInt(hex)) + } + + static hexToInt(hex: string): number { + if (!hex.startsWith('#') || (hex.length !== 7 && hex.length !== 9)) { + throw new Error('Invalid hex color format. Expected #RRGGBB or #AARRGGBB.') + } + if (hex.length === 7) { + hex = '#ff' + hex.slice(1) // Add alpha + } + return parseInt(hex.slice(1), 16) + } + + static getColor(color: TextObjectColor | TextObjectShadowColor): tinycolor.Instance { + if (Array.isArray(color)) { + return tinycolor({ + r: color[0] * 255, + g: color[1] * 255, + b: color[2] * 255, + a: color[3] ?? 1 * 255, + }) + } else if (typeof color === 'number') { + const rgba = JsonText.intToRgba(color) + return tinycolor({ + r: rgba[0] * 255, + g: rgba[1] * 255, + b: rgba[2] * 255, + a: rgba[3] * 255, + }) + } else if (color.startsWith('#')) { + return tinycolor(color) + } else if (color in COLOR_VALUES) { + return tinycolor(COLOR_VALUES[color as keyof typeof COLOR_VALUES]) + } else { + console.warn('Unknown color:', color) + return tinycolor('white') + } + } + /** * Attempts to parse a stringified Json Text Component. * @@ -317,9 +423,9 @@ export class JsonText { */ static fromString( str: string, - targetMinecraftVersion: MinecraftVersion = JsonText.defaultTargetVersion + options?: ConstructorParameters[0] ): JsonText | undefined { - const parser = new JsonTextParser(str, targetMinecraftVersion) - return parser.parse() + const parser = new JsonTextParser(options) + return parser.parse(str) } } diff --git a/src/systems/jsonText/parser.ts b/src/systems/jsonText/parser.ts index ab3a00a5..69c4395c 100644 --- a/src/systems/jsonText/parser.ts +++ b/src/systems/jsonText/parser.ts @@ -1,74 +1,222 @@ import { StringStream } from 'generic-stream' import { - ClickEvent, - ClickEvent_1_21_5, - Component, - HoverEvent, - HoverEvent_1_21_5, + COLOR_VALUES, + CONTENT_KEYS, + CONTENT_TYPES, JsonText, - ScoreComponent, - type Color, - type CompositeComponent, + MODERN_CLICK_EVENT_SUBKEYS, + TextObjectShadowColor, + type LegacyClickEvent, + type LegacyHoverEvent, + type ModernClickEvent, + type ModernHoverEvent, + type TextElement, + type TextObject, + type TextObjectColor, } from '.' -import { MinecraftVersion } from '../global' - -type OneLessThan = N extends 1 - ? 0 - : N extends 2 - ? 1 - : N extends 3 - ? 2 - : N extends 4 - ? 3 - : N extends 5 - ? 4 - : N extends 6 - ? 5 - : N extends 7 - ? 6 - : N extends 8 - ? 7 - : N extends 9 - ? 8 - : never - -type FixedLengthArray = L extends 0 - ? [] - : [T, ...FixedLengthArray>] - -export const COLOR_MAP: Record = { - dark_red: '#AA0000', - red: '#FF5555', - gold: '#FFAA00', - yellow: '#FFFF55', - dark_green: '#00AA00', - green: '#55FF55', - aqua: '#55FFFF', - dark_aqua: '#00AAAA', - dark_blue: '#0000AA', - blue: '#5555FF', - light_purple: '#FF55FF', - dark_purple: '#AA00AA', - white: '#FFFFFF', - gray: '#AAAAAA', - dark_gray: '#555555', - black: '#000000', +import type { MergeUnion } from '../../util/utilityTypes' + +enum NUMBER_TYPES { + ANY = 'number', + BYTE = 'byte', + SHORT = 'short', + INTEGER = 'int', + LONG = 'long', + FLOAT = 'float', + DOUBLE = 'double', + HEXADECIMAL = 'hexadecimal', + BINARY = 'binary number', } -export class JsonParserError extends Error { - private _originalMessage: string +enum FEATURES { + /** + * Allow unquoted object keys. + */ + LITERAL_KEYS = 1 << 0, + + /** + * Allow unquoted strings. + */ + LITERAL_STRINGS = 1 << 1, + + /** + * Allow using single quotes for object keys and strings. + */ + SINGLE_QUOTES = 1 << 2, + + /** + * Allow trailing commas in objects and arrays. + */ + TRAILING_COMMAS = 1 << 3, + + /** + * Allow omitting commas in objects and arrays. + * + * @NOTE This is not supported by Minecraft. Any JSON text components using this feature must be processed by this parser before using them in-game. + */ + OPTIONAL_COMMAS = 1 << 4, + + /** + * Use modern event format for text component mouse events. + * - `clickEvent` -> `click_event` + * - `hoverEvent` -> `hover_event` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5) or above. + + * See [this](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=Text%20component%20format) article for more information. + */ + MODERN_EVENT_FORMAT = 1 << 5, + + /** + * Enables the `show_dialog` action for `click_event`. + * + * Requires {@link MODERN_EVENT_FORMAT} to be enabled + * + * Requires [Minecraft 1.21.6](https://minecraft.wiki/w/Java_Edition_1.21.6) or above. + */ + CLICK_EVENT_ACTION_SHOW_DIALOG = 1 << 6, + + /** + * Enables use of `type: "object"` in text objects. + * + * Requires [Minecraft 1.21.9](https://minecraft.wiki/w/Java_Edition_1.21.9) or above. + * + * See [this](https://minecraft.wiki/w/Java_Edition_1.21.9#Command_format:~:text=Text%20component%20format) article for more information. + */ + TEXT_OBJECT_TYPE_OBJECT = 1 << 7, + + /** + * Enables the `shadow_color` field in text objects. + * + * Requires [Minecraft 1.21.4](https://minecraft.wiki/w/Java_Edition_1.21.4) or above. + * + * See [this](https://minecraft.wiki/w/Java_Edition_1.21.4#Command_format:~:text=Raw%20JSON%20text%20format) article for more information. + */ + SHADOW_COLOR = 1 << 8, + + /** + * Enables the `shadow_color` field to accept {@link TextObjectColor} values, in addition to the normal number and array formats. + * + * Requires {@link SHADOW_COLOR} to be enabled + * + * @NOTE This is not supported by Minecraft. Any JSON text components using this feature must be processed by this parser before using them in-game. + */ + SHADOW_COLOR_ACCEPTS_STRING = 1 << 9, + + /** + * Enables `\s` as an escape sequence for space characters in strings. + * + * E.g. `"Hello,\sWorld!"` -> `"Hello, World!"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + SPACE_ESCAPE_SEQUENCE = 1 << 10, + + /** + * Enables `\x` as an escape sequence for hexadecimal characters in strings. + * + * E.g. `"\x41"` -> `"A"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + HEX_ESCAPE_SEQUENCE = 1 << 11, + + /** + * Enables `\U` as an escape sequence for 8-digit unicode characters in strings. + * + * E.g. `"\U0001F600"` -> `"😀"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + EIGHT_DIGIT_UNICODE_ESCAPE_SEQUENCE = 1 << 12, + + /** + * Enables `\N{...}` as an escape sequence for named unicode characters in strings. + * + * E.g. `"\N{Snowman}"` -> `"☃"` + * + * Requires [Minecraft 1.21.5](https://minecraft.wiki/w/Java_Edition_1.21.5#Command_format:~:text=network%20format%20directly.-,SNBT%20format) or above. + */ + NAMED_UNICODE_ESCAPE_SEQUENCE = 1 << 13, + + /** + * Allows text objects to infer `text` as an empty string if no other {@link CONTENT_KEYS} are present. + * + * E.g. `{ color: green }` -> `{ text: '', color: green }` + */ + IMPLICIT_TEXT_KEY = 1 << 14, + + /** + * Allows text objects to infer keys from keyless values. + * + * Inferrable keys: + * - `text` + * - `{ 'Hello, World!', color: red }` -> `{ text: 'Hello, World!', color: red }` + * - `{ 'Hello, World!' }` -> `{ text: 'Hello, World!' }` + * - `translate` (if `with` or `fallback` is present) + * - `{ my.translation.key, with: [...] }` -> `{ translate: my.translation.key, with: [...] }` + * - `{ my.translation.key, fallback: 'Hello, World!' }` -> `{ translate: my.translation.key, fallback: 'Hello, World!' }` + * - `color` (if `text` or `translate` is present) + * - `{ text: 'Hello, World!', red }` -> `{ text: 'Hello, World!', color: red }` + * - `{ text: '', '#00aced' }` -> `{ text: '', color: '#00aced' }` + */ + TEXT_OBJECT_INFERRED_KEYS = 1 << 15, +} + +function compareVersions(a: string, b: string): number { + const aParts = a.split('.').map(Number) + const bParts = b.split('.').map(Number) + for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) { + const aPart = aParts[i] ?? 0 + const bPart = bParts[i] ?? 0 + if (aPart > bPart) return 1 + if (aPart < bPart) return -1 + } + return 0 +} + +export class JsonTextParserError extends Error { + constructor(message: string) { + super(message) + this.name = 'JsonTextParserError' + } +} + +interface JsonTextSyntaxErrorOptions { + child?: Error + line?: number + column?: number + pointerLength?: number +} + +export class JsonTextSyntaxError extends Error { + private originalMessage: string + + stream: StringStream + child?: Error + line: number + column: number + pointerLength: number constructor( message: string, - public stream: StringStream, - public child?: Error, - public line = stream.line, - public column = stream.column, - public pointerLength = 1 + stream: StringStream, + { + child, + line = stream.line, + column = stream.column, + pointerLength = 1, + }: JsonTextSyntaxErrorOptions = {} ) { super(message) + this.name = 'JsonTextSyntaxError' + this.stream = stream + this.child = child + this.line = line + this.column = column + this.pointerLength = pointerLength - this._originalMessage = message + this.originalMessage = message if (this.child) { this.message = `${this.message} at ${this.line}:${this.column}\n${this.child.message}` @@ -80,7 +228,7 @@ export class JsonParserError extends Error { getOriginErrorMessage(): string { if (this.child) { - if (this.child instanceof JsonParserError) { + if (this.child instanceof JsonTextSyntaxError) { return this.child.getOriginErrorMessage() } return this.child.message @@ -103,161 +251,513 @@ export class JsonParserError extends Error { const actualColumn = lineString.slice(0, this.column - 1).replace(/\t/g, ' ').length + 1 const pointer = ' '.repeat(actualColumn - 1) + '↑'.repeat(this.pointerLength) - this.message = `${this._originalMessage} at ${this.line}:${this.column}\n${lineString}\n${pointer}` + this.message = `${this.originalMessage} at ${this.line}:${this.column}\n${lineString}\n${pointer}` } } -namespace CHARS { +export namespace CHARS { export const ALPHA_LOWER = Array.from('abcdefghijklmnopqrstuvwxyz') export const ALPHA_UPPER = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ') export const ALPHA = ALPHA_LOWER.concat(ALPHA_UPPER) export const NUMBER = Array.from('0123456789') + export const INT_START = Array.from('-123456789') + export const NUMBER_START = NUMBER.concat(Array.from('-.')) export const ALPHANUMERIC = ALPHA.concat(NUMBER) export const VERTICAL_WHITESPACE = Array.from('\n\r') export const WHITESPACE = Array.from(' \t').concat(VERTICAL_WHITESPACE) - // This parser only parses SNBT-based JSON text components, so we don't need to support all SNBT unquoted string characters. - export const UNQUOTED_STRING = ALPHANUMERIC.concat('_') - export const UNQUOTED_STRING_START = ALPHA.concat('_') - export const STRING_QUOTE = Array.from(`"'`) + export const LITERAL = ALPHANUMERIC.concat(Array.from('._-+')) + export const LITERAL_START = ALPHA.concat('_') + export const QUOTES = Array.from(`"'`) + export const BINARY = Array.from('01') export const HEXADECIMAL = NUMBER.concat(Array.from('abcdef'), Array.from('ABCDEF')) - export const SYNTAX_BOUNDARY = WHITESPACE.concat(Array.from(',:]}')) + export const SYNTAX_BOUNDARY = WHITESPACE.concat(QUOTES, Array.from(',:[]{}')) +} + +interface SourcePosition { + index: number + line: number + column: number + equals(other: { line: number; column: number }): boolean } +/** + * A multi-version JSON text parser. + */ export class JsonTextParser { - private s: StringStream + static maxNestingDepth = 512 + static maxArrayLength = 2 ** 31 - 9 + + static defaultFeatures = + // Minecraft syntax sugar + FEATURES.LITERAL_KEYS | + FEATURES.LITERAL_STRINGS | + FEATURES.SINGLE_QUOTES | + FEATURES.TRAILING_COMMAS | + // Custom syntax sugar + FEATURES.OPTIONAL_COMMAS | + FEATURES.SHADOW_COLOR_ACCEPTS_STRING | + FEATURES.TEXT_OBJECT_INFERRED_KEYS | + FEATURES.IMPLICIT_TEXT_KEY + + private s!: StringStream + private currentNestingDepth = 0 + + minecraftVersion: string + enabledFeatures = 0 + + /** + * If {@link featureFlags} is not provided, features will be automatically enabled based on {@link minecraftVersion}. + * + * Individual features can be enabled/disabled post-construction by modifying {@link enabledFeatures}. + * + * By default, the following features are always enabled: + * - {@link FEATURES.LITERAL_KEYS} + * - {@link FEATURES.LITERAL_STRINGS} + * - {@link FEATURES.SINGLE_QUOTES} + * - {@link FEATURES.TRAILING_COMMAS} + * - {@link FEATURES.OPTIONAL_COMMAS} + * - {@link FEATURES.SHADOW_COLOR_ACCEPTS_STRING} + * - {@link FEATURES.TEXT_OBJECT_INFERRED_KEYS} + * - {@link FEATURES.IMPLICIT_TEXT_KEY} + * See {@link FEATURES} for information on each feature. + * + * @example Disable *only* literal strings and enable modern event format: + * ```ts + * const parser = new JsonTextParser('{ text: Hello }') + * parser.enabledFeatures &= ~FEATURES.LITERAL_STRINGS + * parser.enabledFeatures |= FEATURES.MODERN_EVENT_FORMAT + * ``` + * + */ + constructor(options?: { minecraftVersion?: string; featureFlags?: number }) { + const { minecraftVersion = JsonText.defaultMinecraftVersion, featureFlags } = options ?? {} + + this.minecraftVersion = minecraftVersion + this.reset() + + if (typeof featureFlags === 'number') { + this.enabledFeatures = featureFlags + } else { + this.enabledFeatures |= JsonTextParser.defaultFeatures - constructor(public text: string, public targetMinecraftVersion: MinecraftVersion) { - this.s = new StringStream(text) + if (compareVersions(this.minecraftVersion, '1.21.4') >= 0) { + this.enabledFeatures |= FEATURES.SHADOW_COLOR + } + if (compareVersions(this.minecraftVersion, '1.21.5') >= 0) { + this.enabledFeatures |= + FEATURES.MODERN_EVENT_FORMAT | + FEATURES.SPACE_ESCAPE_SEQUENCE | + FEATURES.HEX_ESCAPE_SEQUENCE | + FEATURES.EIGHT_DIGIT_UNICODE_ESCAPE_SEQUENCE | + FEATURES.NAMED_UNICODE_ESCAPE_SEQUENCE + } + if (compareVersions(this.minecraftVersion, '1.21.6') >= 0) { + this.enabledFeatures |= FEATURES.CLICK_EVENT_ACTION_SHOW_DIALOG + } + if (compareVersions(this.minecraftVersion, '1.21.9') >= 0) { + this.enabledFeatures |= FEATURES.TEXT_OBJECT_TYPE_OBJECT + } + } } - parse(): JsonText { - const result = this.parseTextElement() + parse(text: string): JsonText { + this.s = new StringStream(text) + this.reset() + + try { + this.consumeWhitespace() + const result = this.parseTextElement() + this.consumeWhitespace() + if (this.s.item) { + this.throwSyntax(`Unexpected trailing '${this.s.item}' after JsonTextElement`) + } + return new JsonText(result) + } catch (e: any) { + if (e instanceof JsonTextParserError) { + throw new JsonTextParserError( + `Internal Parser Error:\n\t${e.message}\nThis is a bug, please report it.` + ) + } + throw e + } + } - this.consumeWhitespace() + throwSyntax(message: string, options?: JsonTextSyntaxErrorOptions): never { + throw new JsonTextSyntaxError(message, this.s, options) + } - if (this.s.item) { - throw new JsonParserError( - `Unexpected trailing '${this.s.item}' after JsonTextElement`, - this.s - ) + private recordPosition(): SourcePosition { + return { + index: this.s.index, + line: this.s.line, + column: this.s.column, + equals(other: { line: number; column: number }): boolean { + return this.line === other.line && this.column === other.column + }, } + } - return new JsonText(result) + private reset() { + this.currentNestingDepth = 0 } private consumeWhitespace() { this.s.consumeWhile(s => !!s.item && CHARS.WHITESPACE.includes(s.item)) } - private parseTextElement(): Component { - let result: Component - - this.consumeWhitespace() - + private parseTextElement(): TextElement { + let result: TextElement if (this.s.item === '{') { result = this.parseTextObject() } else if (this.s.item === '[') { - result = this.parseArray(this.parseTextElement.bind(this)) + result = this.parseTextElementArray() } else if ( !this.s.item || - CHARS.STRING_QUOTE.includes(this.s.item) || - CHARS.UNQUOTED_STRING_START.includes(this.s.item) + CHARS.QUOTES.includes(this.s.item) || + CHARS.LITERAL_START.includes(this.s.item) ) { result = this.parseString() } else { - throw new JsonParserError(`Unexpected '${this.s.item}' in JsonTextElement`, this.s) + this.throwSyntax(`Unexpected '${this.s.item}' in JsonTextElement`) } return result } - private parseObject( - valueParser: (key: keyof T, obj: T) => void, - validator?: (obj: T) => void - ): T { - const { line, column } = this.s - const keys = new Set() + private parseObject({ + objectName, + // Defaults to an empty set to avoid having to check for undefined later + keys = new Set(), + required, + parseKey, + parseValue, + validateResult, + }: { + /** Name of the object being parsed, for error messages. */ + objectName: string + /** Keys that are allowed in the object. */ + keys?: Set + /** Keys that are required in the object. */ + required?: Set + /** + * Called to parse a field's key. If not provided, keys will be parsed as strings. + * + * @returns The parsed key, or `undefined` to indicate the default key parser should be used. + * + * Can be used to parse keyless values. + */ + parseKey?: (keys: Set, obj: Partial) => { key: K; value?: T[K] & string } | undefined + /** + * Called to parse a field's value, and set it on the object. + * + * Will never be called for fields not in {@link expectedFields}. + */ + parseValue: (key: K, obj: Partial) => void + /** + * Called with the completed object for extra validation. + * + * Returns a string to indicate an error, or undefined if the object is valid. + */ + validateResult?: (obj: Partial) => string | void + }): T { + this.currentNestingDepth++ + if (this.currentNestingDepth > JsonTextParser.maxNestingDepth) { + this.throwSyntax('Nesting depth limit exceeded') + } + + const startPosition = this.recordPosition() try { - this.expect(this.s.item, '{', 'to begin JsonTextObject', true) + this.expect(this.s.item, '{', 'to begin ' + objectName, true) this.consumeWhitespace() - const obj: any = {} + const obj = {} as Partial while (this.s.item !== '}') { - const { line: keyLine, column: keyColumn } = this.s - const key = this.parseString() as keyof T - if (keys.has(key)) { - throw new JsonParserError( - `Duplicate key '${String(key)}'`, - this.s, - undefined, - keyLine, - keyColumn, - String(key).length - ) + const keyPosition = this.recordPosition() + const quoted = CHARS.QUOTES.includes(this.s.item!) + if (quoted) keyPosition.column++ + + let key: K | undefined + const result = parseKey?.(keys, obj) + if (result) { + key = result.key + } else { + // Default key handling + if (this.enabledFeatures & FEATURES.LITERAL_KEYS) { + key = this.parseString() as K + } else { + key = this.parseQuotedString() as K + } } - keys.add(key) - this.consumeWhitespace() + if (result?.value !== undefined) { + obj[key] = result.value + this.consumeWhitespace() + // If a colon is found after a keyless value, it was actually an unknown key + if (this.s.item === ':') { + this.throwSyntax(`Unknown key '${result.value}' in ${objectName}`, { + ...keyPosition, + pointerLength: result.value.length, + }) + } + } else { + if (obj[key] !== undefined) { + this.throwSyntax(`Duplicate key '${key}'`, { + ...keyPosition, + pointerLength: key.length, + }) + } - this.expect(this.s.item, ':', `to follow key '${String(key)}'`, true) - this.consumeWhitespace() + if (keys.size > 0 && !keys.has(key)) { + this.throwSyntax(`Unknown key '${key}' in ${objectName}`, { + ...keyPosition, + pointerLength: key.length, + }) + } + + this.expectSyntaxBoundaryAfter(`key '${key}'`) + this.consumeWhitespace() - valueParser(key, obj) - this.expectSyntaxBoundaryAfter(`value for '${String(key)}'`) + this.expect(this.s.item, ':', `to follow key '${key}'`, true) + this.consumeWhitespace() + + if (!this.s.item) { + this.throwSyntax(`Unexpected EOF in ` + objectName, this.s) + } + const valuePosition = this.recordPosition() + parseValue(key, obj) + // If the value parser didn't consume anything, assume the value is missing. + if (valuePosition.equals(this.s)) { + this.throwSyntax(`Missing value for '${key}'`, this.s) + } + } this.consumeWhitespace() if (this.s.item === ',') { this.s.consume() this.consumeWhitespace() if (this.s.item === ',') { - throw new JsonParserError(`Extra comma`, this.s) + this.throwSyntax(`Extra comma`, this.s) + } + if (this.s.item === '}' && !(this.enabledFeatures & FEATURES.TRAILING_COMMAS)) { + this.throwSyntax(`Trailing comma in ` + objectName, this.s) } } else if (this.s.item === '}') { break } else if (this.s.item === undefined) { - throw new JsonParserError('Unexpected EOF in JsonTextObject', this.s) - } else { - throw new JsonParserError( - `Unexpected '${this.s.item}' in JsonTextObject`, - this.s - ) + this.throwSyntax('Unexpected EOF in ' + objectName, this.s) + } else if (!(this.enabledFeatures & FEATURES.OPTIONAL_COMMAS)) { + this.throwSyntax(`Expected ',' or '}' after ${objectName} entry`) } } this.s.consume() // } - if (validator) { - try { - validator(obj) - } catch (e: any) { - if (e instanceof JsonParserError) { - if (e.line === this.s.line && e.column === this.s.column) { - // Point the error to the start of the object instead of the end - e.line = line - e.column = column - e.updatePointerMessage() - } - } - throw e + if (required) { + for (const key of required) { + if (obj[key] !== undefined) continue + this.throwSyntax( + `Missing required field '${String(key)}' in ` + objectName, + startPosition + ) + } + } + + if (validateResult) { + const error = validateResult(obj) + if (error) { + this.throwSyntax(error, startPosition) } } - return obj + this.currentNestingDepth-- + return obj as T } catch (e: any) { - throw new JsonParserError('Invalid JsonTextObject', this.s, e as Error, line, column) + this.throwSyntax('Invalid ' + objectName, { child: e, ...startPosition }) + } + } + + private normalizeHexColor(color: string): string { + if (color.startsWith('0x')) color = '#' + color.substring(2) + if (!/^#[0-9a-fA-F]{6}$/i.test(color)) { + this.throwSyntax(`Invalid hex color '${color}'`, this.s) + } + return color.toLowerCase() + } + + private normalizeHex8Color(color: string): string { + if (color.startsWith('0x')) color = '#' + color.substring(2) + if (!/^#[0-9a-fA-F]{8}$/i.test(color)) { + this.throwSyntax(`Invalid hex color '${color}'`, this.s) + } + return color.toLowerCase() + } + + private assertTextObjectColorIsValid(color: string): asserts color is TextObjectColor { + if (color.startsWith('#')) { + this.normalizeHexColor(color) + } else if (!(color in COLOR_VALUES)) { + this.throwSyntax(`Unknown color '${color}'`, this.s) + } + } + + private parseTextObjectColor(): TextObjectColor { + const color = this.parseString() + if (color.startsWith('#')) { + this.normalizeHexColor(color) + return color as `#${string}` + } else if (color in COLOR_VALUES) { + return color as keyof typeof COLOR_VALUES + } else { + this.throwSyntax(`Unknown color '${color}'`, this.s) + } + } + + private parseTextObjectShadowColor(): TextObjectShadowColor { + if (!(this.enabledFeatures & FEATURES.SHADOW_COLOR)) { + this.throwSyntax(`'shadow_color' field is only available in Minecraft 1.21.4 and above`) + } + + if (this.s.item === '[') { + const rgba = this.parseFloatArray(4) as [number, number, number, number] + for (const n of rgba) { + if (typeof n !== 'number' || n < 0 || n > 1) { + this.throwSyntax(`'shadow_color' array values must be numbers between 0 and 1`) + } + } + return rgba + } else if (CHARS.NUMBER.includes(this.s.item!)) { + const colorString = this.parseNumber(NUMBER_TYPES.INTEGER) + let color: number + if (colorString.startsWith('0x')) { + color = parseInt(colorString.substring(2), 16) + } else { + color = parseInt(colorString, 10) + } + if (typeof color !== 'number' || color < 0 || color > 0xffffffff) { + this.throwSyntax(`'shadow_color' value must be a number between 0 and 0xffffffff`) + } + return JsonText.intToRgba(color) } + + if (!(this.enabledFeatures & FEATURES.SHADOW_COLOR_ACCEPTS_STRING)) { + this.throwSyntax(`Expected value of 'shadow_color' to be a float-array or integer.`) + } + + let hexColor = this.parseString() + if (hexColor.startsWith('#')) { + hexColor = this.normalizeHex8Color(hexColor) + } else if (hexColor in COLOR_VALUES) { + hexColor = COLOR_VALUES[hexColor as keyof typeof COLOR_VALUES] + } else { + this.throwSyntax(`Unknown color '${hexColor}'`, this.s) + } + return JsonText.hexToRgba(hexColor) } private parseTextObject() { - return this.parseObject( - (key, obj) => { + return this.parseObject({ + objectName: 'TextObject', + keys: new Set([ + 'type', + 'font', + 'color', + 'bold', + 'italic', + 'underlined', + 'strikethrough', + 'obfuscated', + 'extra', + 'insertion', + 'clickEvent', + 'hoverEvent', + 'shadow_color', + 'click_event', + 'hover_event', + 'text', + 'translate', + 'fallback', + 'with', + 'score', + 'selector', + 'separator', + 'keybind', + 'nbt', + 'block', + 'entity', + 'storage', + 'interpret', + 'object', + 'sprite', + 'atlas', + 'player', + ]), + parseKey: (keys, obj) => { + if (this.enabledFeatures & FEATURES.TEXT_OBJECT_INFERRED_KEYS) { + if (this.s.item === '#') { + if (obj.color !== undefined) { + this.throwSyntax( + `Cannot infer keyless value as 'color' when 'color' is already defined`, + this.s + ) + } + const colorString = this.parseHashedHexColor() + return { key: 'color', value: colorString } + } else if (this.s.look(0, 2) === '0x') { + if (obj.color !== undefined) { + this.throwSyntax( + `Cannot infer keyless value as 'color' when 'color' is already defined`, + this.s + ) + } + // Parse as hex color + const colorString = this.parseNumber(NUMBER_TYPES.HEXADECIMAL) + return { key: 'color', value: this.normalizeHexColor(colorString) } + } + } + + let key: keyof TextObject | undefined + if (this.enabledFeatures & FEATURES.LITERAL_KEYS) { + key = this.parseString() as keyof TextObject + } else { + key = this.parseQuotedString() as keyof TextObject + } + // Let the main parser handle known and duplicate keys + if (keys.has(key) || obj[key] != undefined) return { key } + + if (this.enabledFeatures & FEATURES.TEXT_OBJECT_INFERRED_KEYS) { + // Unknown key, try to infer + if ( + obj.color === undefined && + (obj.text !== undefined || obj.translate !== undefined) + ) { + this.assertTextObjectColorIsValid(key) + return { key: 'color', value: key } + } else if ( + obj.translate === undefined && + (obj.fallback !== undefined || obj.with !== undefined) + ) { + return { key: 'translate', value: key } + } else if (obj.text === undefined) { + return { key: 'text', value: key } + } + } + + return { key } // Let the main parser handle unknown keys + }, + parseValue: (key, obj) => { switch (key) { - case 'block': - case 'entity': - case 'font': + case 'sprite': + case 'atlas': + if (!(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT)) { + this.throwSyntax( + `'${key}' field is only available in Minecraft 1.21.9 and above` + ) + } case 'insertion': + case 'font': case 'keybind': - case 'nbt': case 'selector': - case 'separator': + case 'nbt': + case 'block': + case 'entity': case 'storage': case 'text': case 'translate': @@ -265,594 +765,710 @@ export class JsonTextParser { obj[key] = this.parseString() break - case 'color': { - const color = this.parseString() as Color - if (!(COLOR_MAP[color] || color.startsWith('#'))) { - throw new JsonParserError(`Unknown color '${color}'`, this.s) + case 'bold': + case 'italic': + case 'obfuscated': + case 'strikethrough': + case 'underlined': + case 'interpret': + obj[key] = this.parseBoolean() + break + + case 'source': + obj[key] = this.parseString(['storage', 'block', 'entity']) + break + + case 'separator': + obj[key] = this.parseTextElement() + break + + case 'object': + if (!(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT)) { + this.throwSyntax( + `'object' field is only available in Minecraft 1.21.9 and above` + ) } - obj.color = color + obj[key] = this.parseString(['atlas', 'player']) break - } - case 'shadow_color': { - if (compareVersions(this.targetMinecraftVersion, '1.21.4')) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support shadow_color.`, - this.s + case 'player': + if (!(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT)) { + this.throwSyntax( + `'player' field is only available in Minecraft 1.21.9 and above` ) } + obj[key] = this.parsePlayerObject() + break - if ((this.s.item as string) === '[') { - const rgba = this.parseArray(this.parseFloat.bind(this), 4) - for (const n of rgba) { - if (typeof n !== 'number' || n < 0 || n > 1) { - throw new JsonParserError( - `shadow_color array values must be numbers between 0 and 1`, - this.s - ) - } + case 'type': { + const type = this.parseString(Object.values(CONTENT_TYPES)) + if ( + type === CONTENT_TYPES.OBJECT && + !(this.enabledFeatures & FEATURES.TEXT_OBJECT_TYPE_OBJECT) + ) { + this.throwSyntax( + `Object type '${CONTENT_TYPES.OBJECT}' is only available in Minecraft 1.21.9 and above` + ) + } + obj[key] = type + break + } + + case 'color': + obj[key] = this.parseTextObjectColor() + break + + case 'shadow_color': + obj[key] = this.parseTextObjectShadowColor() + break + + case 'with': + case 'extra': + obj[key] = this.parseTextElementArray() + break + + case 'score': + obj[key] = this.parseScoreObject() + break + + case 'clickEvent': { + const event = this.parseLegacyClickEventObject() + if (this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT) { + if (obj.click_event !== undefined) { + this.throwSyntax( + `Cannot use both 'clickEvent' and 'click_event' fields. For Minecraft versions below 1.21.5, use only 'clickEvent'` + ) } - obj.shadow_color = rgba + obj.click_event = this.transformLegacyClickEventToModern(event) + delete obj.clickEvent break - } else if (CHARS.NUMBER.includes(this.s.item as string)) { - const rgba = this.parseFloat() - if (typeof rgba !== 'number' || rgba < 0 || rgba > 0xffffffff) { - throw new JsonParserError( - `shadow_color value must be a number between 0 and 0xffffffff`, - this.s + } + obj[key] = event + break + } + + case 'click_event': { + const event = this.parseModernClickEventObject() + if (!(this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT)) { + if (obj.clickEvent !== undefined) { + this.throwSyntax( + `Cannot use both 'clickEvent' and 'click_event' fields. For Minecraft versions 1.21.5 and above, use only 'click_event'` ) } - // Convert from int color to rgba array - const r = (rgba >> 24) & 0xff - const g = (rgba >> 16) & 0xff - const b = (rgba >> 8) & 0xff - const a = rgba & 0xff - obj.shadow_color = [r / 255, g / 255, b / 255, a / 255] + obj.clickEvent = this.transformModernClickEventObjectToLegacy(event) + delete obj.click_event break } - const color = this.parseString() as Color - if (!(COLOR_MAP[color] || color.startsWith('#'))) { - throw new JsonParserError(`Unknown color '${color}'`, this.s) + obj[key] = event + break + } + + case 'hoverEvent': { + const event = this.parseLegacyHoverEventObject() + if (this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT) { + if (obj.hover_event !== undefined) { + this.throwSyntax( + `Cannot use both 'hoverEvent' and 'hover_event' fields. For Minecraft versions below 1.21.5, use only 'hoverEvent'` + ) + } + obj.hover_event = this.transformLegacyHoverEventObjectToModern(event) + delete obj.hoverEvent + break } - const hex = color.startsWith('#') ? color : COLOR_MAP[color] - const rgba = new tinycolor(hex).toRgb() - // Apparently shadow color is actually a rgba value now... Dumb. - obj.shadow_color = [rgba.r / 255, rgba.g / 255, rgba.b / 255, rgba.a / 255] + obj[key] = event break } - case 'bold': - case 'italic': - case 'obfuscated': - case 'strikethrough': - case 'underlined': - obj[key] = this.parseBoolean() + case 'hover_event': + const event = this.parseModernHoverEventObject() + if (!(this.enabledFeatures & FEATURES.MODERN_EVENT_FORMAT)) { + if (obj.hoverEvent !== undefined) { + this.throwSyntax( + `Cannot use both 'hoverEvent' and 'hover_event' fields. For Minecraft versions 1.21.5 and above, use only 'hover_event'` + ) + } + obj.hoverEvent = this.transformModernHoverEventObjectToLegacy(event) + delete obj.hover_event + break + } + obj[key] = event break + } + }, + validateResult: obj => { + if (this.enabledFeatures & FEATURES.TEXT_OBJECT_INFERRED_KEYS) { + if ( + obj.text !== undefined && + obj.translate === undefined && + (obj.with !== undefined || obj.fallback !== undefined) + ) { + obj.translate = obj.text + delete obj.text + } + } - case 'with': - case 'extra': - obj[key] = this.parseArray(this.parseTextElement.bind(this)) + const contentKeys = Object.values(CONTENT_KEYS).filter(k => obj[k] !== undefined) + if (contentKeys.length > 1) { + return `Only one content field may be present, but found ${ + contentKeys.length + }: '${contentKeys.join("', '")}'` + } + + if (Object.values(CONTENT_KEYS).every(k => obj[k] == undefined)) { + if (this.enabledFeatures & FEATURES.IMPLICIT_TEXT_KEY) { + obj.text ??= '' + } else { + return `At least one content field must be present: ${Object.values( + CONTENT_KEYS + ).join(', ')}` + } + } + + switch (obj.type) { + case CONTENT_TYPES.TEXT: + if (obj.text == undefined) { + return `'text' is required when 'type' is '${CONTENT_TYPES.TEXT}'` + } break - case 'score': - obj[key] = this.parseScoreObject() + case CONTENT_TYPES.TRANSLATABLE: + if (obj.translate == undefined) { + return `'translate' is required when 'type' is '${CONTENT_TYPES.TRANSLATABLE}'` + } break - case 'clickEvent': - if (!compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support 'clickEvents'. Use 'click_event' instead`, - this.s - ) + case CONTENT_TYPES.SCORE: + if (obj.score == undefined) { + return `'score' is required when 'type' is '${CONTENT_TYPES.SCORE}'` } - obj[key] = this.parseClickEventObject() break - case 'click_event': - if (compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support 'click_events'. Use 'clickEvent' instead`, - this.s - ) + case CONTENT_TYPES.SELECTOR: + if (obj.selector == undefined) { + return `'selector' is required when 'type' is '${CONTENT_TYPES.SELECTOR}'` } - obj[key] = this.parse1_21_5ClickEventObject() break - case 'hoverEvent': - if (!compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support 'hoverEvents'. Use 'hover_event' instead`, - this.s - ) + case CONTENT_TYPES.KEYBIND: + if (obj.keybind == undefined) { + return `'keybind' is required when 'type' is '${CONTENT_TYPES.KEYBIND}'` } - obj[key] = this.parseHoverEventObject() break - case 'hover_event': - if (compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support 'hover_events'. Use 'hoverEvent' instead`, - this.s - ) + case CONTENT_TYPES.NBT: + if (obj.nbt == undefined) { + return `'nbt' is required when 'type' is '${CONTENT_TYPES.NBT}'` } - obj[key] = this.parse1_21_5HoverEventObject() break - default: - throw new JsonParserError(`Unknown key '${key}' in JsonTextObject`, this.s) + case CONTENT_TYPES.OBJECT: + if (obj.sprite == undefined) { + return `'sprite' is required when 'type' is '${CONTENT_TYPES.OBJECT}'` + } + break } - }, - obj => { - // Make sure the object has at least one of the required keys - if ( - obj.text === undefined && - obj.translate === undefined && - obj.score === undefined && - obj.selector === undefined && - obj.keybind === undefined && - obj.nbt === undefined - ) { - throw new JsonParserError( - `JsonTextObject does not include one of 'text', 'translate', 'score', 'selector', 'keybind', or 'nbt'.`, - this.s - ) + + if (obj.translate == undefined && obj.with != undefined) + return `'with' requires 'translate'` + else if (obj.translate == undefined && obj.fallback != undefined) + return `'fallback' requires 'translate'` + + if (obj.separator != undefined && obj.nbt == undefined && obj.selector == undefined) + return `'separator' requires 'nbt' or 'selector'` + + if (obj.object != undefined) { + if (obj.object === 'player' && obj.player == undefined) + return `player object requires 'player'` + else if (obj.object === 'atlas' && obj.sprite == undefined) + return `atlas object requires 'sprite'` + else if (obj.player == undefined && obj.sprite == undefined) + return `'object' requires 'player' or 'sprite'` + } + + if (obj.atlas != undefined && obj.sprite == undefined) { + return `'atlas' requires 'sprite'` + } + + if (obj.nbt == undefined) { + if (obj.source != undefined) return `'source' requires 'nbt'` + else if (obj.block != undefined) return `'block' requires 'nbt'` + else if (obj.entity != undefined) return `'entity' requires 'nbt'` + else if (obj.storage != undefined) return `'storage' requires 'nbt'` } - // Validate the NBT key - if ( - obj.nbt !== undefined && - obj.block === undefined && - obj.entity === undefined && - obj.storage === undefined - ) { - throw new JsonParserError( - `JsonTextObject includes 'nbt' but does not include one of 'block', 'entity', or 'storage'.`, - this.s + if (obj.nbt != undefined) { + if ( + obj.block == undefined && + obj.entity == undefined && + obj.storage == undefined ) + return `'nbt' requires 'block', 'entity', or 'storage'` + + switch (obj.source) { + case 'block': + if (obj.block == undefined) + return `'nbt' with source of 'block' requires 'block'` + break + + case 'entity': + if (obj.entity == undefined) + return `'nbt' with source of 'entity' requires 'entity'` + break + + case 'storage': + if (obj.storage == undefined) + return `'nbt' with source of 'storage' requires 'storage'` + break + } } - } - ) + }, + }) + } + + private parsePlayerPropertyObject() { + return this.parseObject< + NonNullable['properties']>[number] + >({ + objectName: 'player property object', + keys: new Set(['name', 'value', 'signature']), + required: new Set(['name', 'value']), + parseValue: (key, obj) => { + switch (key) { + case 'name': + case 'value': + case 'signature': + obj[key] = this.parseString() + break + } + }, + }) + } + + private parsePlayerPropertiesArray() { + return this.parseArray['properties']>>({ + arrayName: 'player properties array', + parseItem: this.parsePlayerPropertyObject.bind(this), + }) + } + + private parsePlayerObject() { + return this.parseObject>({ + objectName: 'player object', + keys: new Set(['name', 'id', 'texture', 'cape', 'model', 'properties', 'hat']), + parseValue: (key, obj) => { + switch (key) { + case 'id': + if (this.s.item === '[') { + obj[key] = this.parseIntArray<[number, number, number, number]>( + false, + 4 + ) + break + } + // fallthrough + case 'name': + case 'texture': + case 'cape': + obj[key] = this.parseString() + break + + case 'hat': + obj[key] = this.parseBoolean() + break + + case 'model': + obj[key] = this.parseString(['wide', 'slim']) + break + + case 'properties': + obj[key] = this.parsePlayerPropertiesArray() + break + } + }, + }) + } + + private parseUnknownArray(arrayName: string) { + return this.parseArray({ + arrayName, + parseItem: this.parseUnknownValue.bind(this), + }) + } + + private parseUnknownValue() { + if (this.s.item === '{') { + return this.parseUnknownObject('object') + } else if (this.s.item === '[') { + return this.parseUnknownArray('array') + } else if (!this.s.item) { + this.throwSyntax('Unexpected EOF', this.s) + } else if (CHARS.QUOTES.includes(this.s.item)) { + return this.parseQuotedString() + } else if (this.s.item === 't' || this.s.item === 'f') { + return this.parseBoolean() + } else if (CHARS.NUMBER_START.includes(this.s.item)) { + return this.parseNumber() + } else if (CHARS.LITERAL_START.includes(this.s.item)) { + return this.parseLiteral() + } else { + this.throwSyntax(`Unexpected '${this.s.item}'`, this.s) + } + } + + private parseUnknownObject(objectName: string) { + return this.parseObject>({ + objectName, + parseValue: (key, obj) => { + obj[key] = this.parseUnknownValue() + }, + }) } private parseScoreObject() { - return this.parseObject( - (key, obj) => { + return this.parseObject>({ + objectName: 'score object', + keys: new Set(['name', 'objective']), + required: new Set(['name', 'objective']), + parseValue: (key, obj) => { switch (key) { case 'name': case 'objective': obj[key] = this.parseString() break - default: - throw new JsonParserError( - `Unknown key '${key}' in JsonTextObject.score`, - this.s - ) } }, - obj => { - if (obj.name === undefined || obj.objective === undefined) { - throw new JsonParserError( - `JsonTextObject.score must include 'name' and 'objective'`, - this.s - ) - } - } - ) + }) } - private parseClickEventObject() { - return this.parseObject( - (key, obj: ClickEvent) => { + private parseLegacyClickEventObject() { + return this.parseObject({ + objectName: 'clickEvent', + keys: new Set(['action', 'value']), + required: new Set(['action', 'value']), + parseValue: (key, obj) => { switch (key) { case 'action': - obj[key] = this.parseString([ + const value = this.parseString([ 'open_url', 'open_file', 'run_command', 'suggest_command', 'change_page', 'copy_to_clipboard', - ]) + ] as const) + if (value === 'open_file') { + this.throwSyntax(`clickEvent 'open_file' cannot be used by commands`) + } + obj[key] = value break + case 'value': obj[key] = this.parseString() break - default: - throw new JsonParserError( - `Unknown key '${key}' in JsonTextObject.clickEvent`, - this.s - ) } }, - obj => { - if (obj.action === undefined) { - throw new JsonParserError( - `JsonTextObject.clickEvent must include 'action'`, - this.s - ) - } else if (obj.value === undefined) { - throw new JsonParserError( - `JsonTextObject.clickEvent must include 'value'`, - this.s - ) - } - } - ) + }) } - private parseHoverEventObjectShowItemContents() { - return this.parseObject<(HoverEvent & { action: 'show_item' })['contents']>( - (key, item) => { + private parseModernClickEventObject() { + return this.parseObject>({ + objectName: 'click_event', + keys: new Set([ + 'action', + 'url', + 'path', + 'command', + 'page', + 'value', + 'dialog', + 'id', + 'payload', + ]), + required: new Set(['action']), + parseValue: (key, obj) => { switch (key) { - case 'id': - item[key] = this.parseString() - break - case 'count': - item[key] = this.parseFloat() - break - case 'tag': - item[key] = this.parseString() + case 'action': + const value = this.parseString([ + 'open_url', + 'open_file', + 'run_command', + 'suggest_command', + 'change_page', + 'copy_to_clipboard', + 'show_dialog', + 'custom', + ] as const) + if (value === 'open_file') { + this.throwSyntax(`Click event 'open_file' cannot be used by commands`) + } + if (value === 'show_dialog') { + if (!(this.enabledFeatures & FEATURES.CLICK_EVENT_ACTION_SHOW_DIALOG)) { + this.throwSyntax( + `Click event 'show_dialog' is only available in Minecraft 1.21.6 and above` + ) + } + } + obj[key] = value break - default: - throw new JsonParserError( - `Unknown key '${key}' in JsonTextObject.itemHoverEvent.contents`, - this.s - ) - } - }, - obj => { - if (obj.id === undefined) { - throw new JsonParserError( - `JsonTextObject.itemHoverEvent.contents must include 'id'`, - this.s - ) - } - } - ) - } - private parse1_21_5ClickEventObject() { - const valueParser = (key: string, obj: ClickEvent_1_21_5) => { - switch (key) { - case 'action': - obj[key] = this.parseString([ - 'open_url', - 'open_file', - 'run_command', - 'suggest_command', - 'change_page', - 'copy_to_clipboard', - 'show_dialog', - 'custom', - ]) - if (obj[key] === 'show_dialog') { - throw new JsonParserError( - `'show_dialog' click_event is not supported`, - this.s - ) - } - break - - case 'url': - if (obj.action !== 'open_url') { - throw new JsonParserError( - `'url' is only valid when click_event action is 'open_url'`, - this.s - ) - } - obj[key] = this.parseString() - break - - case 'path': - if (obj.action !== 'open_file') { - throw new JsonParserError( - `'path' is only valid when click_event action is 'open_file'`, - this.s - ) - } - obj[key] = this.parseString() - break + case 'url': + const url: string = this.parseString() + let parsedUrl: URL + try { + parsedUrl = new URL(url) + } catch (e: any) { + this.throwSyntax( + `Invalid URL format for 'url' in 'open_url' clickEvent: ${e.message}` + ) + } - case 'command': - if (obj.action !== 'run_command' && obj.action !== 'suggest_command') { - throw new JsonParserError( - `'command' is only valid when click_event action is 'run_command' or 'suggest_command'`, - this.s - ) - } - obj[key] = this.parseString() - break + if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { + this.throwSyntax(`Invalid URL: Protocol must be 'http:' or 'https:'`) + } + obj[key] = url + break - case 'page': - if (obj.action !== 'change_page') { - throw new JsonParserError( - `'page' is only valid when click_event action is 'change_page'`, - this.s - ) - } - obj[key] = this.parseFloat() - break + case 'path': + obj[key] = this.parseString() + break - case 'value': - if (obj.action !== 'copy_to_clipboard') { - throw new JsonParserError( - `'value' is only valid when click_event action is 'copy_to_clipboard'`, - this.s - ) - } - obj[key] = this.parseString() - break + case 'command': + obj[key] = this.parseString() + break - case 'dialog': - if (obj.action !== 'show_dialog') { - throw new JsonParserError( - `'dialog' is only valid when click_event action is 'show_dialog'`, - this.s - ) - } - throw new JsonParserError( - `click_events of type 'show_dialog' are not supported`, - this.s - ) - break + case 'page': + obj[key] = parseInt(this.parseNumber(NUMBER_TYPES.INTEGER)) + break - case 'id': - if (obj.action !== 'custom') { - throw new JsonParserError( - `'id' is only valid when click_event action is 'custom'`, - this.s - ) - } - obj[key] = this.parseString() - break + case 'value': + obj[key] = this.parseString() + break - case 'payload': - if (obj.action !== 'custom') { - throw new JsonParserError( - `'payload' is only valid when click_event action is 'custom'`, - this.s - ) - } - obj[key] = this.parseString() - break - } - } + case 'dialog': + obj[key] = this.parseUnknownObject('click_event.dialog') + break - const validator = (obj: ClickEvent_1_21_5) => { - if (obj.action === undefined) { - throw new JsonParserError(`'click_event' must include 'action'`, this.s) - } + case 'id': + obj[key] = this.parseString() + break - switch (obj.action) { - case 'open_url': - if (obj.url === undefined) { - throw new JsonParserError( - `click_event of type 'open_url' missing required key 'url'`, - this.s - ) - } - break + case 'payload': + obj[key] = this.parseUnknownValue() + break + } + }, + validateResult: obj => { + let subkeys = Object.values(MODERN_CLICK_EVENT_SUBKEYS) + switch (obj.action) { + case 'open_url': + if (obj.url === undefined) { + return `Click event of type 'open_url' missing required key 'url'` + } + subkeys = subkeys.filter(k => k !== 'url') + break - case 'open_file': - if (obj.path === undefined) { - throw new JsonParserError( - `click_event of type 'open_file' missing required key 'path'`, - this.s - ) - } - break + case 'open_file': + if (obj.path === undefined) { + return `Click event of type 'open_file' missing required key 'path'` + } + subkeys = subkeys.filter(k => k !== 'path') + break - case 'run_command': - if (obj.command === undefined) { - throw new JsonParserError( - `click_event of type 'run_command' missing required key 'command'`, - this.s - ) - } - break + case 'run_command': + if (obj.command === undefined) { + return `Click event of type 'run_command' missing required key 'command'` + } + subkeys = subkeys.filter(k => k !== 'command') + break - case 'suggest_command': - if (obj.command === undefined) { - throw new JsonParserError( - `click_event of type 'suggest_command' missing required key 'command'`, - this.s - ) - } - break + case 'suggest_command': + if (obj.command === undefined) { + return `Click event of type 'suggest_command' missing required key 'command'` + } + subkeys = subkeys.filter(k => k !== 'command') + break - case 'change_page': - if (obj.page === undefined) { - throw new JsonParserError( - `click_event of type 'change_page' missing required key 'page'`, - this.s - ) - } - break + case 'change_page': + if (obj.page === undefined) { + return `Click event of type 'change_page' missing required key 'page'` + } + subkeys = subkeys.filter(k => k !== 'page') + break - case 'copy_to_clipboard': - if (obj.value === undefined) { - throw new JsonParserError( - `click_event of type 'copy_to_clipboard' missing required key 'value'`, - this.s - ) - } - break + case 'copy_to_clipboard': + if (obj.value === undefined) { + return `Click event of type 'copy_to_clipboard' missing required key 'value'` + } + subkeys = subkeys.filter(k => k !== 'value') + break - case 'custom': - if (obj.id === undefined) { - throw new JsonParserError( - `click_event of type 'custom' missing required key 'id'`, - this.s - ) - } - break + case 'custom': + if (obj.id === undefined) { + return `Click event of type 'custom' missing required key 'id'` + } + subkeys = subkeys.filter(k => k !== 'id') + subkeys = subkeys.filter(k => k !== 'payload') + break - case 'show_dialog': - throw new JsonParserError( - `click_events of type 'show_dialog' are not supported`, - this.s - ) - } - } + case 'show_dialog': + if (obj.dialog === undefined) { + return `Click event of type 'show_dialog' missing required key 'dialog'` + } + subkeys = subkeys.filter(k => k !== 'dialog') + break + } - return this.parseObject(valueParser, validator) + if (subkeys.some(k => obj[k] !== undefined)) { + return `Click event of type '${obj.action}' cannot have keys: ${subkeys + .map(k => `'${k}'`) + .join(', ')}` + } + }, + }) as ModernClickEvent } - private parse1_21_5HoverEventObject() { - const valueParser = (key: string, obj: HoverEvent_1_21_5) => { - switch (key) { - case 'action': - obj[key] = this.parseString(['show_text', 'show_item', 'show_entity']) - break + private transformModernClickEventObjectToLegacy(event: ModernClickEvent): LegacyClickEvent { + switch (event.action) { + case 'open_url': + return { + action: 'open_url', + value: event.url, + } - case 'value': - if (obj.action !== 'show_text') { - throw new JsonParserError( - `'value' is only valid when hover_event action is 'show_text'`, - this.s - ) - } - obj[key] = this.parseString() - break + case 'open_file': + this.throwSyntax(`Click event 'open_file' cannot be used by commands`) - case 'id': - if (obj.action !== 'show_item' && obj.action !== 'show_entity') { - throw new JsonParserError( - `'id' is only valid when hover_event action is 'show_item' or 'show_entity'`, - this.s - ) - } - obj[key] = this.parseString() - break + case 'run_command': + case 'suggest_command': + return { + action: event.action, + value: event.command, + } - case 'count': - if (obj.action !== 'show_item') { - throw new JsonParserError( - `'count' is only valid when hover_event action is 'show_item'`, - this.s - ) - } - obj[key] = this.parseFloat() - break + case 'change_page': + return { + action: 'change_page', + value: String(event.page), + } - case 'components': - if (obj.action !== 'show_item') { - throw new JsonParserError( - `'components' is only valid when hover_event action is 'show_item'`, - this.s - ) - } - throw new JsonParserError( - `'components' key in hover_event is not supported`, - this.s - ) - break + case 'copy_to_clipboard': + return { + action: 'copy_to_clipboard', + value: event.value, + } - case 'name': - if (obj.action !== 'show_entity') { - throw new JsonParserError( - `'name' is only valid when hover_event action is 'show_entity'`, - this.s - ) - } - obj[key] = this.parseString() - break + case 'show_dialog': + this.throwSyntax( + `Click events of type 'show_dialog' are not supported in versions below 1.21.6` + ) - case 'uuid': - if (obj.action !== 'show_entity') { - throw new JsonParserError( - `'uuid' is only valid when hover_event action is 'show_entity'`, - this.s - ) - } - if (this.s.item === '[') { - const array = this.parseIntArray(false, 4) - obj[key] = array - } else { - obj[key] = this.parseString() - } - break - } + case 'custom': + this.throwSyntax( + `Click events of type 'custom' are not supported in versions below 1.21.5` + ) } + } - const validator = (obj: HoverEvent_1_21_5) => { - if (obj.action === undefined) { - throw new JsonParserError(`'hover_event' must include 'action'`, this.s) + private transformLegacyClickEventToModern(event: LegacyClickEvent): ModernClickEvent { + switch (event.action) { + case 'open_url': { + let url: string + try { + url = new URL(event.value).toString() + } catch { + this.throwSyntax(`Invalid URL format for 'open_url' clickEvent`) + } + if (!/^https?:\/\//.exec(event.value)) { + this.throwSyntax( + `Invalid URL format for 'open_url' clickEvent. URL must start with 'http://' or 'https://'` + ) + } + return { + action: 'open_url', + url: url, + } } - switch (obj.action) { - case 'show_text': - if (obj.value === undefined) { - throw new JsonParserError( - `hover_event of type 'show_text' missing required key 'value'`, - this.s - ) - } - break + case 'open_file': + this.throwSyntax(`Click event 'open_file' cannot be used by commands`) - case 'show_item': - if (obj.id === undefined) { - throw new JsonParserError( - `hover_event of type 'show_item' missing required key 'id'`, - this.s - ) - } - break + case 'run_command': + case 'suggest_command': + return { + action: event.action, + command: event.value, + } - case 'show_entity': - if (obj.id === undefined) { - throw new JsonParserError( - `hover_event of type 'show_entity' missing required key 'id'`, - this.s - ) - } - if (obj.uuid === undefined) { - throw new JsonParserError( - `hover_event of type 'show_entity' missing required key 'uuid'`, - this.s - ) - } - break - } - } + case 'change_page': + return { + action: 'change_page', + page: Number(event.value), + } - return this.parseObject(valueParser, validator) + case 'copy_to_clipboard': + return { + action: 'copy_to_clipboard', + value: event.value, + } + } } - private parseHoverEventObjectShowEntityContents() { - return this.parseObject<(HoverEvent & { action: 'show_entity' })['contents']>( - (key, entity) => { + private parseLegacyHoverEventObjectShowItemContents() { + return this.parseObject< + Exclude<(LegacyHoverEvent & { action: 'show_item' })['contents'], string> + >({ + objectName: 'hoverEvent.contents', + keys: new Set(['id', 'count', 'tag']), + required: new Set(['id']), + parseValue: (key, obj) => { switch (key) { - case 'type': - entity[key] = this.parseString() - break case 'id': - entity[key] = this.parseString() + obj[key] = this.parseString() break - case 'name': - entity[key] = this.parseString() + + case 'count': + obj[key] = parseInt(this.parseNumber(NUMBER_TYPES.BYTE)) + break + + case 'tag': + obj[key] = this.parseUnknownValue() break - default: - throw new JsonParserError( - `Unknown key '${key}' in JsonTextObject.entityHoverEvent.contents`, - this.s - ) } }, - obj => { - if (obj.type === undefined) { - throw new JsonParserError( - `JsonTextObject.entityHoverEvent.contents must include 'type'`, - this.s - ) + }) + } + + private parseLegacyHoverEventObjectShowEntityContents() { + return this.parseObject<(LegacyHoverEvent & { action: 'show_entity' })['contents']>({ + objectName: 'hoverEvent.contents', + keys: new Set(['type', 'id', 'name']), + required: new Set(['type', 'id']), + parseValue: (key, obj) => { + switch (key) { + case 'id': + if (this.s.item === '[') { + obj[key] = this.parseIntArray<[number, number, number, number]>( + false, + 4 + ) + break + } + // ID should fallthrough to string parsing if not an int array + case 'type': + case 'name': + obj[key] = this.parseString() + break } - } - ) + }, + }) } - private parseHoverEventObject() { - return this.parseObject( - (key, obj) => { + private parseLegacyHoverEventObject() { + return this.parseObject({ + objectName: 'hoverEvent', + keys: new Set(['action', 'contents']), + required: new Set(['action', 'contents']), + parseValue: (key, obj) => { switch (key) { case 'action': obj[key] = this.parseString(['show_text', 'show_item', 'show_entity']) @@ -860,76 +1476,253 @@ export class JsonTextParser { case 'contents': switch (obj.action) { case undefined: { - throw new JsonParserError( - `'action' must be defined before 'contents' in hoverEvent`, - this.s + this.throwSyntax( + `'action' must be defined before 'contents' in hoverEvent` ) } + case 'show_text': { obj[key] = this.parseTextElement() break } + case 'show_item': { - obj[key] = this.parseHoverEventObjectShowItemContents() + if (this.s.item === '{') { + obj[key] = this.parseLegacyHoverEventObjectShowItemContents() + break + } + obj[key] = this.parseString() break } + case 'show_entity': { - obj[key] = this.parseHoverEventObjectShowEntityContents() + obj[key] = this.parseLegacyHoverEventObjectShowEntityContents() break } } break - default: - throw new JsonParserError( - `Unknown key '${key}' in JsonTextObject.hoverEvent`, - this.s - ) } }, - obj => { + }) + } + + private parseModernHoverEventObject() { + return this.parseObject>({ + objectName: 'hover_event', + keys: new Set(['action', 'value', 'id', 'count', 'components', 'name', 'uuid']), + required: new Set(['action']), + parseValue: (key, obj) => { + switch (key) { + case 'action': + obj[key] = this.parseString(['show_text', 'show_item', 'show_entity']) + break + + case 'uuid': + if (this.s.item === '[') { + obj[key] = this.parseIntArray<[number, number, number, number]>( + false, + 4 + ) + break + } + // UUID should fallthrough to string parsing if not an int array + case 'id': + case 'name': + obj[key] = this.parseString() + break + + case 'value': + obj[key] = this.parseTextElement() + break + + case 'count': + obj[key] = parseInt(this.parseNumber(NUMBER_TYPES.BYTE)) + break + + case 'components': + obj[key] = this.parseUnknownValue() + break + } + }, + validateResult: obj => { if (obj.action === undefined) { - throw new JsonParserError( - `JsonTextObject.hoverEvent must include 'action'`, - this.s + return `Hover event must include 'action' field` + } + + switch (obj.action) { + case 'show_text': + if (obj.value === undefined) { + return `Hover event 'show_text' missing required field 'value'` + } + break + + case 'show_item': + if (obj.id === undefined) { + return `Hover event 'show_item' missing required field 'id'` + } + break + + case 'show_entity': + if (obj.id === undefined) { + return `Hover event 'show_entity' missing required field 'id'` + } + if (obj.uuid === undefined) { + return `Hover event 'show_entity' missing required field 'uuid'` + } + break + } + + switch (true) { + case obj.value !== undefined && obj.action !== 'show_text': + return `'value' is only valid when hover_event action is 'show_text'` + + case obj.count !== undefined && obj.action !== 'show_item': + return `'count' is only valid when hover_event action is 'show_item'` + + case obj.id !== undefined && + obj.action !== 'show_item' && + obj.action !== 'show_entity': + return `'id' is only valid when hover_event action is 'show_item' or 'show_entity'` + + case obj.components !== undefined && obj.action !== 'show_item': + return `'components' is only valid when hover_event action is 'show_item'` + + case obj.name !== undefined && obj.action !== 'show_entity': + return `'name' is only valid when hover_event action is 'show_entity'` + + case obj.uuid !== undefined && obj.action !== 'show_entity': + return `'uuid' is only valid when hover_event action is 'show_entity'` + } + }, + }) as ModernHoverEvent + } + + private transformLegacyHoverEventObjectToModern(event: LegacyHoverEvent): ModernHoverEvent { + switch (event.action) { + case 'show_text': + return { + action: 'show_text', + value: event.contents, + } + + case 'show_item': + if (typeof event.contents === 'string') { + return { + action: 'show_item', + id: event.contents, + } + } + if (event.contents.tag !== undefined) { + this.throwSyntax( + `Cannot transform 'hoverEvent' with 'tag' into modern 'hover_event'.` + + ` Please use 'hover_event' for Minecraft versions 1.21.5 and above` ) - } else if (obj.contents === undefined) { - throw new JsonParserError( - `JsonTextObject.hoverEvent must include 'contents'`, - this.s + } + return { + action: 'show_item', + id: event.contents.id, + count: event.contents.count, + } + + case 'show_entity': + return { + action: 'show_entity', + id: event.contents.type, + uuid: event.contents.id, + name: event.contents.name, + } + } + } + + private transformModernHoverEventObjectToLegacy(event: ModernHoverEvent): LegacyHoverEvent { + switch (event.action) { + case 'show_text': + return { + action: 'show_text', + contents: event.value, + } + + case 'show_item': + if (event.components !== undefined) { + this.throwSyntax( + `Cannot transform 'hover_event' with 'components' into legacy 'hoverEvent'.` + + ` Please use 'hoverEvent' for Minecraft versions below 1.21.5` ) } - } - ) + return { + action: 'show_item', + contents: { + id: event.id, + count: event.count, + }, + } + + case 'show_entity': + if (Array.isArray(event.uuid)) { + this.throwSyntax( + `Cannot transform 'hover_event' with 'uuid' as int-array into legacy 'hoverEvent'.` + + ` Please either use a string UUID, or use 'hoverEvent' for Minecraft versions below 1.21.5` + ) + } + return { + action: 'show_entity', + contents: { + type: event.id, + id: event.uuid, + name: event.name, + }, + } + } } - private parseArray(valueParser: () => T): T[] - private parseArray( - valueParser: () => T, - expectedLength?: L - ): L extends number ? FixedLengthArray : T[] - private parseArray( - valueParser: () => T, - expectedLength?: L - ): T[] | FixedLengthArray { - this.expect(this.s.item, '[', 'to begin JsonTextArray', true) + private parseArray({ + arrayName, + parsePrefix, + parseItem, + expectedLength, + }: { + arrayName: string + /** + * Optional function to parse the array prefix (e.g., type identifier). + */ + parsePrefix?: () => void + parseItem: () => T[number] + expectedLength?: number + }): T { + this.currentNestingDepth++ + if (this.currentNestingDepth > JsonTextParser.maxNestingDepth) { + this.throwSyntax('Nesting depth limit exceeded', this.s) + } + this.expect(this.s.item, '[', 'to begin ' + arrayName, true) this.consumeWhitespace() - const array: T[] = [] + + if (parsePrefix) { + parsePrefix() + this.consumeWhitespace() + } + + const array = [] as unknown as T while (this.s.item !== ']') { this.consumeWhitespace() - const value = valueParser() + const itemPosition = this.recordPosition() + const value = parseItem() + if (itemPosition.equals(this.s)) { + throw new JsonTextParserError( + `parseItem function for ${arrayName} did not consume any input` + ) + } array.push(value) - this.expectSyntaxBoundaryAfter('array element') - const { line, column } = this.s + if (array.length > JsonTextParser.maxArrayLength) { + this.throwSyntax('Array length limit exceeded', this.s) + } + this.expectSyntaxBoundaryAfter('array item') this.consumeWhitespace() if (expectedLength !== undefined && array.length > expectedLength) { - throw new JsonParserError( - `Too many elements in array (expected ${expectedLength})`, - this.s, - undefined, - line, - column + this.throwSyntax( + `Too many items in ` + arrayName + ` (expected ${expectedLength})`, + itemPosition ) } @@ -937,36 +1730,62 @@ export class JsonTextParser { this.s.consume() this.consumeWhitespace() if (this.s.item === ',') { - throw new JsonParserError(`Extra comma`, this.s) + this.throwSyntax(`Extra comma`, this.s) + } + if (this.s.item === ']' && !(this.enabledFeatures & FEATURES.TRAILING_COMMAS)) { + this.throwSyntax(`Trailing comma in ` + arrayName, this.s) } } else if (this.s.item === ']') { break } else if (this.s.item === undefined) { - throw new JsonParserError('Unexpected EOF in JsonTextArray', this.s) - } else { - throw new JsonParserError( - `Expected comma or closing bracket after array element`, - this.s, - undefined, - line, - column - ) + this.throwSyntax('Unexpected EOF in ' + arrayName, this.s) + } else if (!(this.enabledFeatures & FEATURES.OPTIONAL_COMMAS)) { + this.throwSyntax(`Expected ',' or ']' after ${arrayName} item`, this.s) } } + + if (expectedLength !== undefined && array.length < expectedLength) { + this.throwSyntax( + `Not enough items in ` + + arrayName + + ` (expected ${expectedLength}, got ${array.length})` + ) + } + this.s.consume() // ] return array } - private parseIntArray(requireTypeIdentifier?: boolean, length?: L) { - this.expect(this.s.item, '[', 'to begin int-array', true) - if (this.s.item === 'I') { - this.s.consume() - this.expect(this.s.item, ';', 'to follow array type identifier', true) - } else if (requireTypeIdentifier) { - throw new JsonParserError(`Expected explicit int-array`, this.s) - } + private parseTextElementArray(): TextElement[] { + return this.parseArray({ + arrayName: 'TextElementArray', + parseItem: this.parseTextElement.bind(this), + }) + } + + private parseIntArray(requireTypeIdentifier?: boolean, length?: number): T { + return this.parseArray({ + arrayName: 'int-array', + parsePrefix: () => { + if (this.s.item === 'I') { + this.s.consume() + this.consumeWhitespace() + this.expect(this.s.item, ';', 'to follow array type identifier', true) + } else if (requireTypeIdentifier) { + this.throwSyntax(`Expected explicit int-array`, this.s) + } + }, + parseItem: () => parseInt(this.parseNumber(NUMBER_TYPES.INTEGER)), + expectedLength: length, + }) + } - return this.parseArray(this.parseInt.bind(this), length) + private parseFloatArray(length?: number): T { + return this.parseArray({ + arrayName: 'float-array', + parseItem: () => parseFloat(this.parseNumber(NUMBER_TYPES.FLOAT)), + expectedLength: length!, + }) } private collectHexDigits(count: number): string { @@ -976,15 +1795,22 @@ export class JsonTextParser { hex += this.s.item this.s.consume() } else { - throw new JsonParserError( - `Unexpected '${this.s.item!}' in ${count}-digit hex escape sequence`, - this.s + this.throwSyntax( + `Unexpected '${this.s.item!}' in ${count}-digit hex escape sequence` ) } } return hex } + private parseHashedHexColor(): string { + this.expect(this.s.item, '#', `to begin hex color`, true) + + const digits = this.parseDigits('hex', CHARS.HEXADECIMAL) + + return this.normalizeHexColor('#' + digits) + } + // Validates and returns a string representation of the escape sequence. // The renderer, such as Minecraft, will handle the actual escape sequence resolution. private parseNamedUnicodeEscapeSequence(): string { @@ -1002,7 +1828,7 @@ export class JsonTextParser { name = name.trim().toUpperCase() if (name.length === 0) { - throw new JsonParserError(`Expected name in named unicode escape sequence`, this.s) + this.throwSyntax(`Expected name in named unicode escape sequence`, this.s) } this.expect(this.s.item, '}', `to end named unicode escape sequence`, true) @@ -1038,10 +1864,9 @@ export class JsonTextParser { this.s.consume() return '\\' + item } else if (item === 's') { - if (compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support space escape sequences ('\\s').`, - this.s + if (!(this.enabledFeatures & FEATURES.SPACE_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support space escape sequences ('\\s')` ) } @@ -1050,41 +1875,44 @@ export class JsonTextParser { } else if (item === 'u') { return this.parseUnicodeEscapeSequence() } else if (item === 'x') { - if (compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support hex unicode escape sequences ('\\x00').`, - this.s + if (!(this.enabledFeatures & FEATURES.HEX_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support hex unicode escape sequences ('\\x00')` ) } return this.parseUnicodeEscapeSequence() } else if (item === 'U') { - if (compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support 8-digit unicode escape sequences ('\\U00000000').`, - this.s + if (!(this.enabledFeatures & FEATURES.EIGHT_DIGIT_UNICODE_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support 8-digit unicode escape sequences ('\\U00000000')` ) } return this.parseUnicodeEscapeSequence() } else if (item === 'N') { - if (compareVersions('1.21.5', this.targetMinecraftVersion)) { - throw new JsonParserError( - `Minecraft ${this.targetMinecraftVersion} does not support named unicode escape sequences ('\\${item}{Name}').`, - this.s + if (!(this.enabledFeatures & FEATURES.NAMED_UNICODE_ESCAPE_SEQUENCE)) { + this.throwSyntax( + `Minecraft ${this.minecraftVersion} does not support named unicode escape sequences ('\\${item}{Name}')` ) } return this.parseNamedUnicodeEscapeSequence() } else { - throw new JsonParserError(`Unknown escape sequence '\\${item!}'`, this.s) + this.throwSyntax(`Unknown escape sequence '\\${item!}'`, this.s) } } private parseQuotedString(): string { - this.expect(this.s.item, CHARS.STRING_QUOTE, 'to begin string') - const quote = this.s.item - this.s.consume() // Opening quote + const quote = this.s.item! + this.expect(this.s.item, CHARS.QUOTES, 'to begin string', true) + + if (this.s.item === "'" && !(this.enabledFeatures & FEATURES.SINGLE_QUOTES)) { + this.throwSyntax( + `Single quotes are not supported in Minecraft ${this.minecraftVersion}` + ) + } + let str = '' while (this.s.item) { if (this.s.item === '\\') { @@ -1093,65 +1921,55 @@ export class JsonTextParser { } else if (this.s.item === quote) { break } else if (CHARS.VERTICAL_WHITESPACE.includes(this.s.item)) { - throw new JsonParserError(`Expected ${quote} to close string`, this.s) + this.throwSyntax(`Expected ${quote} to close string`, this.s) } str += this.s.item this.s.consume() } if (!this.s.item) { - throw new JsonParserError('Unexpected EOF in string', this.s) + this.throwSyntax('Unexpected EOF in string', this.s) } - this.s.consume() // Closing quote + this.expect(this.s.item, quote, `to close string`, true) return str } - private parseUnquotedString(): string { - let str = '' - if (!this.s.item || !CHARS.UNQUOTED_STRING_START.includes(this.s.item)) { - throw new JsonParserError( - `Expected [a-zA-Z0-9_] to start unquoted string. Found '${this.s.item!}' instead`, - this.s + private parseLiteral(): string { + if (!(this.enabledFeatures & FEATURES.LITERAL_STRINGS)) { + this.throwSyntax( + `Literal strings are not supported in Minecraft ${this.minecraftVersion}` ) } - while (this.s.item && CHARS.UNQUOTED_STRING.includes(this.s.item)) { - str += this.s.item - this.s.consume() + if (!this.s.item || !CHARS.LITERAL_START.includes(this.s.item)) { + this.throwSyntax( + `Expected [a-zA-Z0-9_] to start literal string. Found '${this.s.item!}' instead` + ) + } + const str = this.s.collectWhile(s => !!s.item && CHARS.LITERAL.includes(s.item)) + if (str.length === 0) { + throw new JsonTextParserError('Literal string parsing failed unexpectedly') } return str } private parseString(): string private parseString(validStringOptions: T[]): T - private parseString(validStringOptions?: string[]): string { - let str: string - const { line, column } = this.s - if (this.s.item && CHARS.STRING_QUOTE.includes(this.s.item)) { - str = this.parseQuotedString() + private parseString(validStringOptions?: T[]): T { + let str: T + const startPosition = this.recordPosition() + + if (this.s.item && CHARS.QUOTES.includes(this.s.item)) { + str = this.parseQuotedString() as T } else { - str = this.parseUnquotedString() + str = this.parseLiteral() as T } + if (validStringOptions && !validStringOptions.includes(str)) { - throw new JsonParserError( - `Expected one of ${validStringOptions.join(', ')}`, - this.s, - undefined, - line, - column - ) + this.throwSyntax(`Expected one of ${validStringOptions.join(', ')}`, startPosition) } return str } private parseBoolean(): boolean { - if (this.s.item && CHARS.STRING_QUOTE.includes(this.s.item)) { - const value = this.parseQuotedString() - if (value.toLowerCase() === 'true') { - return true - } else if (value.toLowerCase() === 'false') { - return false - } - throw new JsonParserError(`Expected boolean, found '${value}'`, this.s) - } if (this.s.look(0, 4).toLowerCase() === 'true') { this.s.consumeN(4) return true @@ -1159,43 +1977,158 @@ export class JsonTextParser { this.s.consumeN(5) return false } - throw new JsonParserError(`Expected boolean`, this.s) + this.throwSyntax(`Expected boolean`, this.s) + } + + private parseDigits( + name: string, + digitChars = CHARS.NUMBER, + minLength = 0, + maxLength = Infinity + ): string { + let digits = '' + while (this.s.item && digitChars.includes(this.s.item)) { + digits += this.s.item + if (digits.length > maxLength) { + this.throwSyntax(`Too many ${name} digits (max ${maxLength})`, this.s) + } + this.s.consume() + + const beforeWhitespace = this.recordPosition() + const whitespace = CHARS.WHITESPACE.includes(this.s.item) + this.consumeWhitespace() + + if (this.s.item === '_') { + if (whitespace) { + this.throwSyntax(`Underscore must be between ${name} digits`, this.s) + } + this.s.consumeWhile(s => s.item === '_') + if (!(this.s.item && digitChars.includes(this.s.item))) { + this.throwSyntax(`Underscore must be between ${name} digits`, this.s) + } + } else if (whitespace) { + // REVIEW - We should avoid backing up the stream like this. + this.s.index = beforeWhitespace.index - 1 + this.s.consume() + break + } + } + if (digits.length < minLength) { + this.throwSyntax(`Not enough ${name} digits (min ${minLength})`, this.s) + } + return digits } - private parseFloat(): number { - let num = '' + private parseNumber(type = NUMBER_TYPES.ANY): string { + let numberString = '' let hasDecimal = false + let numberChars = CHARS.NUMBER + + const prefix = this.s.look(0, 2).toLowerCase() + if (prefix === '0x') { + this.s.consumeN(2) + numberString += '0x' + numberChars = CHARS.HEXADECIMAL + type = NUMBER_TYPES.HEXADECIMAL + } else if (prefix === '0b') { + this.s.consumeN(2) + numberString += '0b' + numberChars = CHARS.BINARY + type = NUMBER_TYPES.BINARY + } else if (this.s.item === '-') { + numberString += '-' + this.s.consume() + this.consumeWhitespace() + } - while (this.s.item) { - if (this.s.item === '.') { + if (type === NUMBER_TYPES.HEXADECIMAL && prefix !== '0x') { + this.throwSyntax(`Hexadecimal numbers must begin with '0x'`, this.s) + } else if (type === NUMBER_TYPES.BINARY && prefix !== '0b') { + this.throwSyntax(`Binary numbers must begin with '0b'`, this.s) + } + + const expectFloat = type === NUMBER_TYPES.FLOAT || type === NUMBER_TYPES.DOUBLE + + if (this.s.item === '.') { + if (!expectFloat) { + this.throwSyntax(`Decimal points are not permitted in ${type}s`) + } + numberString += '.' + hasDecimal = true + this.s.consume() + this.consumeWhitespace() + } + + this.expect(this.s.item, numberChars, 'to begin ' + type) + + while ( + this.s.item && + (numberChars.includes(this.s.item) || this.s.item === '.' || this.s.item === '_') + ) { + numberString += this.parseDigits(type, numberChars) + this.consumeWhitespace() + + if (this.s.item === 'e' || this.s.item === 'E') { + if (!expectFloat) { + this.throwSyntax(`E notation is not allowed in ${type}s`, this.s) + } + + this.s.consume() // e or E + this.consumeWhitespace() + + const floatValue = parseFloat(numberString) + if (isNaN(floatValue)) { + this.throwSyntax(`Invalid ${type} before exponent`, this.s) + } + + const exponent = parseInt(this.parseNumber(NUMBER_TYPES.INTEGER)) + if (isNaN(exponent)) { + this.throwSyntax(`Invalid exponent`, this.s) + } + + return floatValue.toString() + 'e' + exponent.toString() + } else if ((this.s.item as string) === '.') { if (hasDecimal) { - throw new JsonParserError('Unexpected second decimal point in number', this.s) + this.throwSyntax('Second decimal point in ' + type, this.s) } hasDecimal = true + numberString += '.' + this.s.consume() + this.consumeWhitespace() } - num += this.s.item - this.s.consume() } - this.expectSyntaxBoundaryAfter('number') - return parseFloat(num) - } + this.expectSyntaxBoundaryAfter(type) - private parseInt(): number { - let num = '' - if (this.s.item === '-') { - num += '-' - this.s.consume() + let value: number + switch (type) { + case NUMBER_TYPES.BYTE: + case NUMBER_TYPES.SHORT: + case NUMBER_TYPES.INTEGER: + case NUMBER_TYPES.LONG: + value = parseInt(numberString) + break + + case NUMBER_TYPES.HEXADECIMAL: + value = parseInt(numberString.substring(2), 16) + break + + case NUMBER_TYPES.BINARY: + value = parseInt(numberString.substring(2), 2) + break + + case NUMBER_TYPES.FLOAT: + case NUMBER_TYPES.DOUBLE: + case NUMBER_TYPES.ANY: + value = parseFloat(numberString) + break } - this.expect(this.s.item, CHARS.NUMBER, 'to begin number') - while (this.s.item && CHARS.NUMBER.includes(this.s.item)) { - num += this.s.item - this.s.consume() + if (isNaN(value)) { + this.throwSyntax(`Invalid ` + type, this.s) } - this.expectSyntaxBoundaryAfter('number') - return parseInt(num) + return numberString } private expect( @@ -1211,14 +2144,12 @@ export class JsonTextParser { : thing !== toBe ) { if (Array.isArray(toBe)) { - throw new JsonParserError( - `Expected one of '${toBe.join("', '")}' ${message}. Found '${thing}' instead.`, - this.s + this.throwSyntax( + `Expected one of '${toBe.join("', '")}' ${message}. Found '${thing}' instead`, ) } - throw new JsonParserError( - `Expected '${toBe}' ${message}. Found '${thing}' instead.`, - this.s + this.throwSyntax( + `Expected '${toBe}' ${message}. Found '${thing}' instead`, ) } if (consume) { @@ -1232,7 +2163,119 @@ export class JsonTextParser { private expectSyntaxBoundaryAfter(message: string) { if (this.s.item && !CHARS.SYNTAX_BOUNDARY.includes(this.s.item)) { - throw new JsonParserError(`Unexpected '${this.s.item}' after ${message}`, this.s) + const previous = this.s.string.at(this.s.index - 1) + if (previous && CHARS.SYNTAX_BOUNDARY.includes(previous)) return + this.throwSyntax(`Unexpected '${this.s.item}' after ${message}`, this.s) } } } + +// type KeyConstraint = +// | keyof T +// | { +// /** The key that is expected to be present in the object. */ +// key: keyof T +// /** +// * If true, this field is always required. +// */ +// required?: +// | boolean +// | { +// /** Require this field if the {@link dependsOn} field is present in the object. */ +// dependsOn: (keyof T)[] +// } + +// /** Keys that are expected to be present if this key is present. */ +// expects?: (keyof T)[] +// /** At least one of these fields must be present if this key is present. */ +// expectsOneOf?: (keyof T)[] +// /** Keys that are mutually exclusive with this key. */ +// mutuallyExclusiveKeys?: (keyof T)[] +// } + +// interface TestParseObjectOptions { +// name: string +// /** Fields that are allowed in the object. */ +// keys?: KeyConstraint[] +// /** At least one of these fields must be present in the object. */ +// requireOneOf?: (keyof T)[] +// /** +// * Called to parse a field's value in the object. +// * +// * Will never be called for fields not in {@link expectedFields}. +// */ +// parseValue: (ctx: { key: keyof T; obj: Partial; existingKeys: Set }) => void +// /** +// * Called with the completed object for extra validation. +// * +// * Returns a string to indicate an error, or undefined if the object is valid. +// */ +// validateObject?: (obj: Partial) => string | void +// } + +// function test(options: TestParseObjectOptions) {} +// test>({ +// name: 'test', +// keys: [ +// 'type', +// 'text', +// 'font', +// 'color', +// 'bold', +// 'italic', +// 'underlined', +// 'obfuscated', +// 'strikethrough', +// 'shadow_color', +// 'insertion', +// { +// key: 'extra', +// expectsOneOf: Object.values(CONTENT_KEYS), +// }, +// { +// key: 'clickEvent', +// mutuallyExclusiveKeys: ['click_event'], +// }, +// { +// key: 'hoverEvent', +// mutuallyExclusiveKeys: ['hover_event'], +// }, +// { +// key: 'click_event', +// mutuallyExclusiveKeys: ['clickEvent'], +// }, +// { +// key: 'hover_event', +// mutuallyExclusiveKeys: ['hoverEvent'], +// }, +// { +// key: 'translate', +// required: { dependsOn: ['with'] }, +// expects: ['with'], +// }, +// { key: 'with', expects: ['translate'] }, +// { key: 'fallback', expects: ['translate'] }, +// 'score', +// { +// key: 'selector', +// required: { dependsOn: ['separator'] }, +// }, +// { key: 'separator', expects: ['selector'] }, +// 'keybind', +// { +// key: 'nbt', +// required: { dependsOn: ['block', 'entity', 'storage'] }, +// expectsOneOf: ['block', 'entity', 'storage'], +// }, +// { key: 'block', expects: ['nbt'] }, +// { key: 'entity', expects: ['nbt'] }, +// { key: 'storage', expects: ['nbt'] }, +// 'sprite', +// { +// key: 'atlas', +// required: { dependsOn: ['sprite'] }, +// expects: ['sprite'], +// }, +// ], +// parseValue: ({ key, obj }) => {}, +// }) diff --git a/src/systems/jsonText/stringifier.ts b/src/systems/jsonText/stringifier.ts index f8fce11c..7e1d8fd3 100644 --- a/src/systems/jsonText/stringifier.ts +++ b/src/systems/jsonText/stringifier.ts @@ -1,32 +1,30 @@ import { - type ClickEvent, - type ClickEvent_1_21_5, - type Component, - type CompositeComponent, - type HoverEvent, - type HoverEvent_1_21_5, - type ScoreComponent, + type LegacyClickEvent, + type LegacyHoverEvent, + type ModernClickEvent, + type ModernHoverEvent, + type TextElement, + type TextObject, } from '.' -import { type MinecraftVersion } from '../global' + +enum FEATURES { + REQUIRE_DOUBLE_QUOTES = 1 << 0, + RESOLVE_SPACE_ESCAPE_SEQUENCES = 1 << 1, +} export class JsonTextStringifier { - private enforceDoubleQuotes = false - private useModernClickEventFormat = false - private useModernHoverEventFormat = false - /** Transform '\s' to spaces in strings */ - private resolveSpaceEscapeSequences = false - - constructor(private element: Component, private targetMinecraftVersion: MinecraftVersion) { - if (compareVersions('1.21.5', this.targetMinecraftVersion)) { - this.enforceDoubleQuotes = true - this.useModernClickEventFormat = false - this.useModernHoverEventFormat = false - this.resolveSpaceEscapeSequences = true + enabledFeatures = FEATURES.REQUIRE_DOUBLE_QUOTES + + constructor(private element: TextElement, private minecraftVersion: string) { + // targetMinecraftVersion >= 1.21.5 + if (!compareVersions('1.21.5', this.minecraftVersion)) { + this.enabledFeatures &= ~FEATURES.REQUIRE_DOUBLE_QUOTES + this.enabledFeatures |= FEATURES.RESOLVE_SPACE_ESCAPE_SEQUENCES } } stringify(): string { - return this.stringifyComponent(this.element) + return this.stringifyTextElement(this.element) } /** @@ -38,7 +36,7 @@ export class JsonTextStringifier { */ private stringifyString(str: string): string { str = str.replaceAll('\n', '\\n') - if (this.resolveSpaceEscapeSequences) { + if (this.enabledFeatures & FEATURES.RESOLVE_SPACE_ESCAPE_SEQUENCES) { str = str.replaceAll('\\s', ' ') } @@ -47,7 +45,7 @@ export class JsonTextStringifier { const hasSingle = unescaped.includes("'") const hasDouble = unescaped.includes('"') - if (this.enforceDoubleQuotes) { + if (this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES) { return `"${unescaped.replace(/"/g, '\\"')}"` } else if (hasSingle && hasDouble) { // Both quotes present, fallback to single quotes and escape single quotes @@ -61,12 +59,12 @@ export class JsonTextStringifier { } } - private stringifyComponentArray(arr: Component[]): string { - return `[${arr.map(e => this.stringifyComponent(e)).join(',')}]` + private stringifyTextElementArray(arr: TextElement[]): string { + return `[${arr.map(e => this.stringifyTextElement(e)).join(',')}]` } - private stringifyScoreObject(score: ScoreComponent['score']): string { - if (this.enforceDoubleQuotes) { + private stringifyScoreObject(score: NonNullable): string { + if (this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES) { return ( `{"name":${this.stringifyString(score.name)}` + `,"objective":${this.stringifyString(score.objective)}}` @@ -78,30 +76,65 @@ export class JsonTextStringifier { ) } - private stringifyHoverEvent(event: HoverEvent): string { - if (this.useModernHoverEventFormat) { - throw new Error( - `Minecraft ${this.targetMinecraftVersion} does not support hoverEvents. Use hover_event instead.` - ) + private stringifyPlayerObject(player: NonNullable): string { + const q = this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES ? '"' : '' + const result: string[] = [] + if (player.name !== undefined) { + result.push(`${q}name${q}:${this.stringifyString(player.name)}`) + } + if (player.id !== undefined) { + result.push(`${q}id${q}:${JSON.stringify(player.id)}`) + } + if (player.texture !== undefined) { + result.push(`${q}texture${q}:${player.texture}`) } + if (player.cape !== undefined) { + result.push(`${q}cape${q}:${player.cape}`) + } + if (player.model !== undefined) { + result.push(`${q}model${q}:${this.stringifyString(player.model)}`) + } + if (player.hat !== undefined) { + result.push(`${q}hat${q}:${player.hat}`) + } + if (player.properties !== undefined) { + for (const prop of player.properties) { + result.push( + `{name:${this.stringifyString(prop.name)}` + + `,value:${this.stringifyString(prop.value)}` + + (prop.signature === undefined + ? '' + : `,signature:${this.stringifyString(prop.signature)}`) + + '}' + ) + } + } + return '{' + result.join(',') + '}' + } + private stringifyLegacyHoverEvent(event: LegacyHoverEvent): string { switch (event.action) { case 'show_text': { - return `{"action":"show_text","contents":${this.stringifyComponent( + return `{"action":"show_text","contents":${this.stringifyTextElement( event.contents )}}` } case 'show_item': { - let result = `{"action":"show_item","contents":{"id":${this.stringifyString( - event.contents.id - )}` + let result = `{"action":"show_item","contents":` - if (event.contents.count !== undefined) { - result += `,"count":${event.contents.count}` - } - if (event.contents.tag !== undefined) { - result += `,"tag":${this.stringifyComponent(event.contents.tag)}` + if (typeof event.contents === 'string') { + result += this.stringifyString(event.contents) + } else { + result += '{"id":' + this.stringifyString(event.contents.id) + + if (event.contents.count !== undefined) { + result += `,"count":${event.contents.count}` + } + + if (event.contents.tag !== undefined) { + result += `,"tag":${this.stringifyTextElement(event.contents.tag)}` + } } return result + `}}` @@ -110,10 +143,16 @@ export class JsonTextStringifier { case 'show_entity': { let result = `{"action":"show_entity","contents":{"type":${this.stringifyString( event.contents.type - )},"id":${this.stringifyString(event.contents.id)}` + )}` + + if (Array.isArray(event.contents.id)) { + result += `,"id":[${event.contents.id.join(',')}]` + } else if (typeof event.contents.id === 'string') { + result += `,"id":${this.stringifyString(event.contents.id)}` + } if (event.contents.name !== undefined) { - result += `,"name":${this.stringifyComponent(event.contents.name)}` + result += `,"name":${this.stringifyTextElement(event.contents.name)}` } return result + `}}` @@ -121,17 +160,12 @@ export class JsonTextStringifier { } } - private stringify1_21_5HoverEvent(event: HoverEvent_1_21_5): string { - if (!this.useModernHoverEventFormat) { - throw new Error( - `Minecraft ${this.targetMinecraftVersion} does not support hover_events. Use hoverEvent instead.` - ) - } - + private stringifyModernHoverEvent(event: ModernHoverEvent): string { switch (event.action) { case 'show_text': { - return `{action:show_text,value:${this.stringifyComponent(event.action)}}` + return `{action:show_text,value:${this.stringifyTextElement(event.value)}}` } + case 'show_item': { let result = `{action:show_item,id:${this.stringifyString(event.id)}` @@ -139,17 +173,16 @@ export class JsonTextStringifier { result += `,count:${event.count}` } - if (event.components !== undefined) { - result += `,components:${this.stringifyString(event.components)}` - } + // `components` is not supported by the parser return result + `}` } + case 'show_entity': { let result = `{action:show_entity,id:${this.stringifyString(event.id)}` if (event.name !== undefined) { - result += `,name:${this.stringifyComponent(event.name)}` + result += `,name:${this.stringifyTextElement(event.name)}` } if (Array.isArray(event.uuid)) { @@ -163,22 +196,11 @@ export class JsonTextStringifier { } } - private stringifyClickEvent(event: ClickEvent): string { - if (this.useModernClickEventFormat) { - throw new Error( - `Minecraft ${this.targetMinecraftVersion} does not support clickEvents. Use click_event instead.` - ) - } + private stringifyLegacyClickEvent(event: LegacyClickEvent): string { return `{"action":"${event.action}","value":${this.stringifyString(event.value)}}` } - private stringify1_21_5ClickEvent(event: ClickEvent_1_21_5): string { - if (this.useModernClickEventFormat) { - throw new Error( - `Minecraft ${this.targetMinecraftVersion} does not support click_events. Use clickEvent instead.` - ) - } - + private stringifyModernClickEvent(event: ModernClickEvent): string { switch (event.action) { case 'open_url': return `{action:open_url,url:${this.stringifyString(event.url)}}` @@ -205,83 +227,111 @@ export class JsonTextStringifier { let result = `{action:custom,id:${this.stringifyString(event.id)}` if (event.payload !== undefined) { - result += `,payload:${this.stringifyComponent(event.payload)}` + result += `,payload:${this.stringifyTextElement(event.payload)}` } return result + `}` } } - private stringifyCompositeComponent(obj: CompositeComponent): string { + private stringifyTextObject(obj: TextObject): string { const entries: string[] = [] - for (let [key, value] of Object.entries(obj) as [ - keyof CompositeComponent, - CompositeComponent[keyof CompositeComponent] - ][]) { - // Quote character to use for keys - const q = this.enforceDoubleQuotes ? '"' : '' + for (const key of Object.keys(obj) as Array) { + if (obj[key] === undefined) continue - if (Array.isArray(value)) { - if (typeof value[0] === 'number') { - entries.push(`${q + key + q}:${JSON.stringify(value)}`) - } else { - // @ts-expect-error - Cannot remove [number, number, number, number] type - entries.push(`${q + key + q}:${this.stringifyComponent(value)}`) - } - } else if (typeof value === 'object') { - switch (key) { - case 'hoverEvent': { - entries.push(`${q + key + q}:${this.stringifyHoverEvent(value as any)}`) - break - } - case 'hover_event': { - entries.push( - `${q + key + q}:${this.stringify1_21_5HoverEvent(value as any)}` - ) - break - } - case 'clickEvent': { - entries.push(`${q + key + q}:${this.stringifyClickEvent(value as any)}`) - break - } - case 'click_event': { - entries.push( - `${q + key + q}:${this.stringify1_21_5ClickEvent(value as any)}` - ) - break - } - case 'score': { - entries.push(`${q + key + q}:${this.stringifyScoreObject(value as any)}`) - break - } - default: { - entries.push(`${q + key + q}:${this.stringifyComponent(value as any)}`) + // Quote character to use for keys + const q = this.enabledFeatures & FEATURES.REQUIRE_DOUBLE_QUOTES ? '"' : '' + const quotedKey = q + key + q + + switch (key) { + case 'type': + case 'text': + case 'translate': + case 'fallback': + case 'keybind': + case 'nbt': + case 'source': + case 'block': + case 'entity': + case 'storage': + case 'selector': + case 'font': + case 'insertion': + case 'object': + case 'sprite': + case 'atlas': + case 'color': + // Value is a string + entries.push(`${quotedKey}:${this.stringifyString(obj[key])}`) + break + + case 'shadow_color': + if (Array.isArray(obj[key])) { + entries.push(`${quotedKey}:${JSON.stringify(obj[key])}`) break } - } - } else if (typeof value === 'string') { - entries.push(`${q + key + q}:${this.stringifyString(value)}`) - } else if (value != undefined) { - entries.push(`${q + key + q}:${value}`) - } else { - console.warn('Undefined value in JsonTextStringifier:', key, value) + // color and shadow_color fall through to number | bool case + case 'bold': + case 'italic': + case 'obfuscated': + case 'strikethrough': + case 'underlined': + case 'interpret': + // Value is a number or boolean + entries.push(`${quotedKey}:${obj[key]}`) + break + + case 'with': + case 'extra': + case 'separator': + // Value is an array of components + entries.push(`${quotedKey}:${this.stringifyTextElement(obj[key]!)}`) + break + + case 'score': + entries.push(`${quotedKey}:${this.stringifyScoreObject(obj[key])}`) + break + + case 'player': + entries.push(`${quotedKey}:${this.stringifyPlayerObject(obj[key])}`) + break + + case 'clickEvent': + entries.push(`${quotedKey}:${this.stringifyLegacyClickEvent(obj[key])}`) + break + + case 'click_event': + entries.push(`${quotedKey}:${this.stringifyModernClickEvent(obj[key])}`) + break + + case 'hoverEvent': + entries.push(`${quotedKey}:${this.stringifyLegacyHoverEvent(obj[key])}`) + break + + case 'hover_event': + entries.push(`${quotedKey}:${this.stringifyModernHoverEvent(obj[key])}`) + break + + default: + console.warn(`Unknown key in TextObject: '${key}'`) + break } } return `{${entries.join(',')}}` } - private stringifyComponent(element: Component): string { + private stringifyTextElement(element: TextElement): string { if (typeof element === 'string') { return this.stringifyString(element) } else if (Array.isArray(element)) { - return this.stringifyComponentArray(element) + return this.stringifyTextElementArray(element) } else if (typeof element === 'object' && element !== null) { - return this.stringifyCompositeComponent(element) + return this.stringifyTextObject(element) } else { console.error(element) - throw new Error('Invalid JsonTextElement') + throw new Error('Invalid TextElement') } } } diff --git a/src/systems/minecraft/fontManager.ts b/src/systems/minecraft/fontManager.ts index 0b5bf50a..6241e8e2 100644 --- a/src/systems/minecraft/fontManager.ts +++ b/src/systems/minecraft/fontManager.ts @@ -4,7 +4,7 @@ import { type Alignment } from '../../outliner/textDisplay' import { mergeGeometries } from '../../util/bufferGeometryUtils' import EVENTS from '../../util/events' import { getPathFromResourceLocation } from '../../util/minecraftUtil' -import { COLOR_MAP, ComponentStyle, JsonText } from '../jsonText' +import { COLOR_VALUES, ComponentStyle, JsonText } from '../jsonText' import { UnicodeString } from '../jsonText/unicodeString' import * as assets from './assetManager' import { diff --git a/src/tests/jsonText.test.ts b/src/tests/jsonText.test.ts new file mode 100644 index 00000000..c737e1f6 --- /dev/null +++ b/src/tests/jsonText.test.ts @@ -0,0 +1,1013 @@ +import { COLOR_VALUES, JsonText, type TextElement } from 'src/systems/jsonText' +import { describe, expect, test } from 'vitest' +import { JsonTextParser, JsonTextSyntaxError } from '../systems/jsonText/parser' + +type ErrorPattern = string | RegExp | (new (...args: any[]) => Error) +type InputOutputMap = Array<{ input: TInput | TInput[]; expect: TOutput }> + +function text(text: TextElement) { + return new JsonText(text) +} + +function expectParsingResults( + parser: JsonTextParser['parse'], + map: InputOutputMap +) { + for (const { input, expect: expectedResult } of map) { + if (Array.isArray(input)) { + for (const inputItem of input) { + expect(parser(inputItem)).toEqual(text(expectedResult)) + } + } else { + expect(parser(input)).toEqual(text(expectedResult)) + } + } +} + +function expectErrorResults( + parser: JsonTextParser['parse'], + map: InputOutputMap +) { + for (const { input, expect: error } of map) { + if (Array.isArray(input)) { + for (const inputItem of input) { + expect(() => parser(inputItem)).toThrowError(error) + } + } else { + expect(() => parser(input)).toThrowError(error) + } + } +} + +describe('JsonTextParser', () => { + const modernParser = new JsonTextParser() + const modernParse = modernParser.parse.bind(modernParser) + const legacyParser = new JsonTextParser({ minecraftVersion: '1.20.4' }) + const legacyParse = legacyParser.parse.bind(legacyParser) + + describe('parses TextObjects', () => { + describe('text field', () => { + test('basic', () => { + expectParsingResults(modernParse, [ + { + input: [ + `{"text":"Hello, World!"}`, + `{'text':'Hello, World!'}`, + `{text:"Hello, World!"}`, + ` { text: "Hello, World!" } `, + `{\n\ttext: "Hello, World!"\n}`, + ], + expect: { text: 'Hello, World!' }, + }, + ]) + }) + + test('optional commas', () => { + expectParsingResults(modernParse, [ + { + input: [`{"text":"Hello, World!" color:red bold:true}`], + expect: { text: 'Hello, World!', color: 'red', bold: true }, + }, + ]) + }) + + test('inferred', () => { + expectParsingResults(modernParse, [ + { + input: [ + `{"Hello, World!"}`, + ` { "Hello, World!" } `, + `{\n\t'Hello, World!'\n}`, + ], + expect: { text: 'Hello, World!' }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{Hello, World}`, + expect: /Unknown color 'World'/, + }, + { + input: `{color: red, Hello, World}`, + expect: /Unknown key 'World'/, + }, + ]) + }) + + test('implicit', () => { + expect(modernParse(`{}`)).toEqual(text({ text: '' })) + }) + }) + + describe('color field', () => { + test('named colors', () => { + for (const color of Object.keys(COLOR_VALUES)) { + const result = modernParse(`{color:${color}}`) + const expected = text({ color: color as keyof typeof COLOR_VALUES, text: '' }) + expect(result).toEqual(expected) + } + }) + + test('hex colors', () => { + const validCases: Array<[string, TextElement]> = [ + ['{color:"#00aced"}', { color: '#00aced', text: '' }], + ['{color:"#FF5555"}', { color: '#FF5555', text: '' }], + ] + for (const [input, expected] of validCases) { + expect(modernParse(input)).toEqual(text(expected)) + } + + expectErrorResults(modernParse, [ + { + input: ['{color:"#FF55550"}', '{color:"#FF555"}'], + expect: /Invalid hex color/, + }, + { + input: '{color:#FF5555}', + expect: /Expected \[a\-zA\-Z0\-9_\] to start literal string/, + }, + ]) + }) + + test('inferred', () => { + expectParsingResults(modernParse, [ + { input: '{#00aced}', expect: { color: '#00aced', text: '' } }, + { input: '{0xFF5555}', expect: { color: '#ff5555', text: '' } }, + { + input: '{"Hello, World!" #00aced}', + expect: { color: '#00aced', text: 'Hello, World!' }, + }, + { + input: '{"Hello, World!" 0xFF5555}', + expect: { color: '#ff5555', text: 'Hello, World!' }, + }, + { + input: '{#00aced "Hello, World!"}', + expect: { color: '#00aced', text: 'Hello, World!' }, + }, + { + input: '{0xFF5555 "Hello, World!"}', + expect: { color: '#ff5555', text: 'Hello, World!' }, + }, + { + input: '{"Hello, World!" "#00aced"}', + expect: { color: '#00aced', text: 'Hello, World!' }, + }, + { + input: '{"Hello, World!" red}', + expect: { color: 'red', text: 'Hello, World!' }, + }, + ]) + + expectErrorResults(modernParse, [ + { + input: [ + '{#FF55550}', + '{#FF555}', + '{0xFF555}', + '{color:red, #FF5555}', + '{color:red, 0xFF5555}', + '{"#FF5555", "Hello, World!"}', + '{red, "Hello, World!"}', + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + }) + + test('shadow_color field', () => { + expectParsingResults(modernParse, [ + { + input: `{shadow_color:"#ff00aced"}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + { + input: `{shadow_color:0xff00aced}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + { + input: `{shadow_color:[${JsonText.hexToRgba('#ff00aced')}]}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + { + input: `{shadow_color:${JsonText.hexToInt('#ff00aced')}}`, + expect: { shadow_color: JsonText.hexToRgba('#ff00aced'), text: '' }, + }, + ]) + }) + + test('boolean style fields', () => { + const fields = ['bold', 'italic', 'underlined', 'strikethrough', 'obfuscated'] as const + + const cases = [ + { value: true, variants: ['true', 'TRUE'] }, + { value: false, variants: ['false', 'FALSE'] }, + ] + + for (const field of fields) { + for (const { value, variants } of cases) { + for (const variant of variants) { + const input = `{${field}:${variant}}` + const expected = text({ [field]: value, text: '' }) + expect(modernParse(input)).toEqual(expected) + } + } + } + + // Test invalid values + expectErrorResults(modernParse, [ + { + input: [ + '{bold:yes}', + '{italic:no}', + '{underlined:1}', + '{strikethrough:0}', + '{obfuscated:null}', + '{bold:"true"}', + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + + test('font field', () => { + expectParsingResults(modernParse, [ + { + input: `{font:"minecraft:illager"}`, + expect: { font: 'minecraft:illager', text: '' }, + }, + ]) + }) + + test('keybind field', () => { + expectParsingResults(modernParse, [ + { + input: `{keybind:"key.jump"}`, + expect: { keybind: 'key.jump' }, + }, + ]) + }) + + test('extra field', () => { + expectParsingResults(modernParse, [ + { + input: `{extra:["some_extra_text", {"and a text object"}]}`, + expect: { extra: ['some_extra_text', { text: 'and a text object' }], text: '' }, + }, + { + input: `{extra:[{extra:["some_extra_text", {"and a text object"}]}]}`, + expect: { + extra: [ + { extra: ['some_extra_text', { text: 'and a text object' }], text: '' }, + ], + text: '', + }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{extra:"not-an-array"}`, + expect: /Expected '\[' to begin TextElementArray/, + }, + ]) + }) + + test('insertion field', () => { + expectParsingResults(modernParse, [ + { + input: `{insertion:'some_text'}`, + expect: { insertion: 'some_text', text: '' }, + }, + ]) + }) + + test('translate field', () => { + expectParsingResults(modernParse, [ + { + input: `{translate:'my.translation.key'}`, + expect: { translate: 'my.translation.key' }, + }, + { + input: [ + `{translate:'my.translation.key', with:['arg1','arg2']}`, + `{'my.translation.key', with:['arg1','arg2']}`, + ], + expect: { translate: 'my.translation.key', with: ['arg1', 'arg2'] }, + }, + { + input: [ + `{translate:'my.translation.key', fallback:'fallback text'}`, + `{'my.translation.key', fallback:'fallback text'}`, + ], + expect: { translate: 'my.translation.key', fallback: 'fallback text' }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{with:['arg1','arg2']}`, + expect: /'with' requires 'translate'/, + }, + { + input: `{fallback:'fallback text'}`, + expect: /'fallback' requires 'translate'/, + }, + ]) + }) + + test('score field', () => { + expectParsingResults(modernParse, [ + { + input: `{score:{name:'Player',objective:'obj1'}}`, + expect: { score: { name: 'Player', objective: 'obj1' } }, + }, + ]) + }) + + test('selector field', () => { + expectParsingResults(modernParse, [ + { + input: `{selector:'@e'}`, + expect: { selector: '@e' }, + }, + { + input: `{selector:'@e',separator:{', '}}`, + expect: { selector: '@e', separator: { text: ', ' } }, + }, + { + input: `{selector:'@e',separator:', '}`, + expect: { selector: '@e', separator: ', ' }, + }, + ]) + }) + + test('nbt field', () => { + expectParsingResults(modernParse, [ + { + input: `{nbt:'Items[]',block:'0 0 0', interpret:true,separator:', '}`, + expect: { nbt: 'Items[]', block: '0 0 0', interpret: true, separator: ', ' }, + }, + { + input: `{nbt:'SelectedItem',entity:'@s', interpret:false,separator:', '}`, + expect: { + nbt: 'SelectedItem', + entity: '@s', + interpret: false, + separator: ', ', + }, + }, + { + input: `{nbt:'temp[{id:2}]',storage:'animated_java:test'}`, + expect: { nbt: 'temp[{id:2}]', storage: 'animated_java:test' }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: `{nbt:'Items[]'}`, + expect: /'nbt' requires 'block', 'entity', or 'storage'/, + }, + { + input: `{block:'~ ~ ~'}`, + expect: /'block' requires 'nbt'/, + }, + { + input: `{entity:'@s'}`, + expect: /'entity' requires 'nbt'/, + }, + { + input: `{storage:'animated_java:test'}`, + expect: /'storage' requires 'nbt'/, + }, + ]) + }) + + test('separator field', () => { + expectErrorResults(modernParse, [ + { + input: `{separator:', '}`, + expect: /'separator' requires 'nbt' or 'selector'/, + }, + ]) + }) + + test('object field', () => { + expectErrorResults(modernParse, [ + { + input: `{object:'atlas'}`, + expect: /atlas object requires 'sprite'/, + }, + { + input: `{object:'player'}`, + expect: /player object requires 'player'/, + }, + ]) + }) + + test('sprite field', () => { + expectParsingResults(modernParse, [ + { + input: `{sprite:'minecraft:stone'}`, + expect: { sprite: 'minecraft:stone' }, + }, + { + input: `{sprite:'minecraft:diamond'}`, + expect: { sprite: 'minecraft:diamond' }, + }, + ]) + }) + + test('atlas object field', () => { + expectErrorResults(modernParse, [ + { + input: `{atlas:'minecraft:items'}`, + expect: /'atlas' requires 'sprite'/, + }, + ]) + }) + + test('player object field', () => { + expectParsingResults(modernParse, [ + { + input: `{player:{}}`, + expect: { player: {} as any }, + }, + { + input: `{player:{name:'SnaveSutit',id:'0-0-0-0-0',texture:'some:texture/path',cape:'some:cape/path',model:'wide',hat:true}}`, + expect: { + player: { + name: 'SnaveSutit', + id: '0-0-0-0-0', + texture: 'some:texture/path', + cape: 'some:cape/path', + model: 'wide', + hat: true, + properties: undefined, + }, + }, + }, + { + input: `{player:{name:'SnaveSutit',properties:[{name:'textures',value:'some_base64_value',signature:'some_base64_signature'}]}}`, + expect: { + player: { + name: 'SnaveSutit', + properties: [ + { + name: 'textures', + value: 'some_base64_value', + signature: 'some_base64_signature', + }, + ], + }, + }, + }, + ]) + }) + + test('legacy clickEvent field', () => { + expectParsingResults(legacyParse, [ + { + input: `{clickEvent:{action:'open_url',value:'https://example.com'}}`, + expect: { + clickEvent: { action: 'open_url', value: 'https://example.com' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'run_command',value:'say hi'}}`, + expect: { + clickEvent: { action: 'run_command', value: 'say hi' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'suggest_command',value:'say hi'}}`, + expect: { + clickEvent: { action: 'suggest_command', value: 'say hi' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'change_page',value:'5'}}`, + expect: { + clickEvent: { action: 'change_page', value: '5' }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + clickEvent: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + { + input: `{clickEvent:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + clickEvent: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + ]) + expectErrorResults(legacyParse, [ + { + input: [ + // Cannot use `open_file` in legacy clickEvent + `{clickEvent:{action:'open_file',value:'file:///example/path'}}`, + // Unknown action + `{clickEvent:{action:'unknown_action',value:'test'}}`, + // Missing value + `{clickEvent:{action:'open_url'}}`, + // Missing action + `{clickEvent:{value:'test'}}`, + // Non-object clickEvent + `{clickEvent:'not-an-object'}`, + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + + test('modern click_event field', () => { + expectParsingResults(modernParse, [ + { + input: `{click_event:{action:'open_url',url:'https://example.com'}}`, + expect: { + click_event: { action: 'open_url', url: 'https://example.com' }, + text: '', + }, + }, + { + input: `{click_event:{action:'run_command',command:'say hi'}}`, + expect: { + click_event: { action: 'run_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'suggest_command',command:'say hi'}}`, + expect: { + click_event: { action: 'suggest_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'run_command',command:'say hi'}}`, + expect: { + click_event: { action: 'run_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'suggest_command',command:'say hi'}}`, + expect: { + click_event: { action: 'suggest_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'suggest_command',command:'say hi'}}`, + expect: { + click_event: { action: 'suggest_command', command: 'say hi' }, + text: '', + }, + }, + { + input: `{click_event:{action:'change_page',page:5}}`, + expect: { + click_event: { action: 'change_page', page: 5 }, + text: '', + }, + }, + { + input: `{click_event:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + click_event: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + { + input: `{click_event:{action:'copy_to_clipboard',value:'Hello, Clipboardy World!'}}`, + expect: { + click_event: { + action: 'copy_to_clipboard', + value: 'Hello, Clipboardy World!', + }, + text: '', + }, + }, + { + input: `{click_event:{action:'show_dialog',dialog:{type:'minecraft:notice',title:'test',body:[{type:'plain_message',contents:{text:'test'}}]}}}`, + expect: { + click_event: { + action: 'show_dialog', + dialog: { + type: 'minecraft:notice', + title: 'test', + body: [{ type: 'plain_message', contents: { text: 'test' } }], + }, + }, + text: '', + }, + }, + { + input: `{click_event:{action:'custom',id:'my_custom_action'}}`, + expect: { + click_event: { action: 'custom', id: 'my_custom_action' }, + text: '', + }, + }, + { + input: `{click_event:{action:'custom',id:'my_custom_action',payload:{some:'data'}}}`, + expect: { + click_event: { + action: 'custom', + id: 'my_custom_action', + payload: { some: 'data' }, + }, + text: '', + }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: [ + // Cannot use `open_file` in commands + `{click_event:{action:'open_file',path:'file:///example/path'}}`, + // Unknown action + `{click_event:{action:'unknown_action',foo:'bar'}}`, + // Missing required field for open_url + `{click_event:{action:'open_url'}}`, + // Missing required field for run_command + `{click_event:{action:'run_command'}}`, + // Missing required field for suggest_command + `{click_event:{action:'suggest_command'}}`, + // Missing required field for change_page + `{click_event:{action:'change_page'}}`, + // Missing required field for copy_to_clipboard + `{click_event:{action:'copy_to_clipboard'}}`, + // Non-object click_event + `{click_event:'not-an-object'}`, + // Invalid dialog structure + `{click_event:{action:'show_dialog',dialog:'not-an-object'}}`, + // Custom action missing id + `{click_event:{action:'custom'}}`, + ], + expect: JsonTextSyntaxError, + }, + ]) + }) + + test('legacy hoverEvent field', () => { + expectParsingResults(legacyParse, [ + { + input: `{hoverEvent:{action:'show_text',contents:'Hello!'}}`, + expect: { + hoverEvent: { action: 'show_text', contents: 'Hello!' }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_text',contents:{text:'Hello!'}}}`, + expect: { + hoverEvent: { action: 'show_text', contents: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1}}}`, + expect: { + hoverEvent: { + action: 'show_item', + contents: { id: 'minecraft:stone', count: 1 }, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1,tag:{some:'data'}}}}`, + expect: { + hoverEvent: { + action: 'show_item', + contents: { id: 'minecraft:stone', count: 1, tag: { some: 'data' } }, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:'0-0-0-0-0',name:'Player'}}}`, + expect: { + hoverEvent: { + action: 'show_entity', + contents: { + type: 'minecraft:player', + id: '0-0-0-0-0', + name: 'Player', + }, + }, + text: '', + }, + }, + { + input: [ + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[0,0,0,0],name:'Player'}}}`, + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[I;0,0,0,0],name:'Player'}}}`, + ], + expect: { + hoverEvent: { + action: 'show_entity', + contents: { + type: 'minecraft:player', + id: [0, 0, 0, 0], + name: 'Player', + }, + }, + text: '', + }, + }, + // Transforms modern hover_event into legacy hoverEvent + { + input: `{hover_event:{action:'show_text',value:'Hello!'}}`, + expect: { + hoverEvent: { action: 'show_text', contents: 'Hello!' }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_text',value:{text:'Hello!'}}}`, + expect: { + hoverEvent: { action: 'show_text', contents: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1}}`, + expect: { + hoverEvent: { + action: 'show_item', + contents: { + id: 'minecraft:stone', + count: 1, + }, + }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:'0-0-0-0-0',name:'Player'}}`, + expect: { + hoverEvent: { + action: 'show_entity', + contents: { + type: 'minecraft:player', + id: '0-0-0-0-0', + name: 'Player', + }, + }, + text: '', + }, + }, + ]) + expectErrorResults(legacyParse, [ + { + input: [ + // Unknown action + `{hoverEvent:{action:'unknown_action',value:'test'}}`, + // Missing value for show_text + `{hoverEvent:{action:'show_text'}}`, + // Missing id for show_item + `{hoverEvent:{action:'show_item',count:1}}`, + // Missing type for show_entity + `{hoverEvent:{action:'show_entity',id:'1234',name:'Player'}}`, + // Non-object hoverEvent + `{hoverEvent:'not-an-object'}`, + ], + expect: JsonTextSyntaxError, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1,components:{'minecraft:custom_data':{some:'data'}}}}`, + expect: /Cannot transform 'hover_event' with 'components' into legacy 'hoverEvent'/, + }, + { + input: [ + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[0,0,0,0],name:'Player'}}`, + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[I;0,0,0,0],name:'Player'}}`, + ], + expect: /Cannot transform 'hover_event' with 'uuid' as int-array into legacy 'hoverEvent'/, + }, + ]) + }) + + test('modern hover_event field', () => { + expectParsingResults(modernParse, [ + { + input: `{hover_event:{action:'show_text',value:'Hello!'}}`, + expect: { + hover_event: { action: 'show_text', value: 'Hello!' }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_text',value:{text:'Hello!'}}}`, + expect: { + hover_event: { action: 'show_text', value: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_item',id:'minecraft:stone',count:1,components:{'minecraft:custom_data':{some:'data'}}}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + components: { 'minecraft:custom_data': { some: 'data' } }, + }, + text: '', + }, + }, + { + input: `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:'0-0-0-0-0',name:'Player'}}`, + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: '0-0-0-0-0', + name: 'Player', + }, + text: '', + }, + }, + { + input: [ + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[0,0,0,0],name:'Player'}}`, + `{hover_event:{action:'show_entity',id:'minecraft:player',uuid:[I;0,0,0,0],name:'Player'}}`, + ], + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: [0, 0, 0, 0], + name: 'Player', + }, + text: '', + }, + }, + // Transforms legacy hoverEvent into modern hover_event + { + input: `{hoverEvent:{action:'show_text',contents:'Hello!'}}`, + expect: { + hover_event: { action: 'show_text', value: 'Hello!' }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_text',contents:{text:'Hello!'}}}`, + expect: { + hover_event: { action: 'show_text', value: { text: 'Hello!' } }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1}}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1}}}`, + expect: { + hover_event: { + action: 'show_item', + id: 'minecraft:stone', + count: 1, + }, + text: '', + }, + }, + { + input: `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:'0-0-0-0-0',name:'Player'}}}`, + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: '0-0-0-0-0', + name: 'Player', + }, + text: '', + }, + }, + { + input: [ + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[0,0,0,0],name:'Player'}}}`, + `{hoverEvent:{action:'show_entity',contents:{type:'minecraft:player',id:[I;0,0,0,0],name:'Player'}}}`, + ], + expect: { + hover_event: { + action: 'show_entity', + id: 'minecraft:player', + uuid: [0, 0, 0, 0], + name: 'Player', + }, + text: '', + }, + }, + ]) + expectErrorResults(modernParse, [ + { + input: [ + // Unknown action + `{hover_event:{action:'unknown_action',value:'test'}}`, + // Missing value for show_text + `{hover_event:{action:'show_text'}}`, + // Missing id for show_item + `{hover_event:{action:'show_item',count:1}}`, + // Missing id for show_entity + `{hover_event:{action:'show_entity',uuid:'0-0-0-0-0',name:'Player'}}`, + // Non-object hover_event + `{hover_event:'not-an-object'}`, + ], + expect: JsonTextSyntaxError, + }, + { + input: `{hoverEvent:{action:'show_item',contents:{id:'minecraft:stone',count:1,tag:{some:'data'}}}}`, + expect: /Cannot transform 'hoverEvent' with 'tag' into modern 'hover_event'/, + }, + ]) + }) + }) + + test('parses TextElement arrays', () => { + expectParsingResults(modernParse, [ + // Simple object arrays + { + input: [ + '[{"text":"Hello, "},{"text":"World!"}]', + '[{text:"Hello, "},{text:"World!"}]', + ' [ { text: "Hello, " }, { text: "World!" } ] ', + `[\n\t{\n\t\ttext: "Hello, "\n\t},\n\t{\n\t\ttext: "World!"\n\t}\n]`, + '[{"Hello, "},{"World!"}]', + ], + expect: [{ text: 'Hello, ' }, { text: 'World!' }], + }, + // Simple string arrays + { + input: [ + '["Hello, ", "World!"]', + '["Hello, " "World!"]', + ' [ "Hello, " "World!" ] ', + `[\n\t"Hello, "\n\t,\n\t"World!"\n]`, + ], + expect: ['Hello, ', 'World!'], + }, + // Nested string arrays + { + input: [ + '["Hello, ", ["Recursive"], "World!"]', + '["Hello, " ["Recursive"] "World!"]', + ' [ "Hello, " ["Recursive"] "World!" ] ', + `[\n\t"Hello, "\n\t,\n\t["Recursive"]\n\t,\n\t"World!"\n]`, + ], + expect: ['Hello, ', ['Recursive'], 'World!'], + }, + // Nested object arrays + { + input: [ + '[{"text":"Hello, "}, [{"text":"Recursive"}], {"text":"World!"}]', + '[{text:"Hello, "}, [{text:"Recursive"}], {text:"World!"}]', + ' [ { text: "Hello, " }, [ { text: "Recursive" } ], { text: "World!" } ] ', + `[\n\t{\n\t\ttext: "Hello, "\n\t},\n\t[\n\t\t{\n\t\t\ttext: "Recursive"\n\t\t}\n\t],\n\t{\n\t\ttext: "World!"\n\t}\n]`, + ], + expect: [{ text: 'Hello, ' }, [{ text: 'Recursive' }], { text: 'World!' }], + }, + // Additional edge cases + { input: [`[]`], expect: [] }, + { input: [`[{}]`], expect: [{ text: '' }] }, + { input: [`[{"text":""}]`], expect: [{ text: '' }] }, + ]) + }) +}) From ef2080288c1c632ab3827a75603c4968b5231228 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:25:46 -0400 Subject: [PATCH 115/182] =?UTF-8?q?=E2=9C=A8=20Misc=20unused=20utility=20t?= =?UTF-8?q?ypes=20that=20I=20don't=20want=20to=20forget.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/utilityTypes.d.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/util/utilityTypes.d.ts diff --git a/src/util/utilityTypes.d.ts b/src/util/utilityTypes.d.ts new file mode 100644 index 00000000..6162bf0f --- /dev/null +++ b/src/util/utilityTypes.d.ts @@ -0,0 +1,25 @@ +export interface Tuple extends Array { + 0: T + length: L +} + +/** Gets all keys of an object union */ +export type AllKeys = T extends any ? keyof T : never +/** Gets keys that are common between all members of a object union */ +export type CommonKeys = Extract, keyof T> +/** Gets keys that aren't common between all members of a object union */ +export type UncommonKeys = Exclude, CommonKeys> +/** Gets the type of a key in an object, or undefined if the key doesn't exist in the object */ +export type PickType> = T extends { [k in K]?: any } ? T[K] : undefined +/** Gets the type of a key in an object union, or never if the key doesn't exist in any member of the union */ +export type PickTypeOf = K extends AllKeys + ? PickType + : never +/** + * Merges an object union into a single object, where keys that are common between all members are required, and keys that aren't common are optional. + */ +export type MergeUnion = { + [k in CommonKeys]: PickTypeOf +} & { + [k in UncommonKeys]?: PickTypeOf +} From 127eb502681d34fcc761c18895831bd30361097b Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Wed, 8 Oct 2025 23:26:00 -0400 Subject: [PATCH 116/182] =?UTF-8?q?=F0=9F=A7=AA=20Update=20test=20packs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blueprints/armor_stand_1.20.4.ajblueprint | 24 +- .../armor_stand_minimal_1.20.4.ajblueprint | 2 +- .../rotated_blockstates_1.20.4.ajblueprint | 515 ++++++++++++++++++ 3 files changed, 530 insertions(+), 11 deletions(-) create mode 100644 test-packs/1.20.4/blueprints/rotated_blockstates_1.20.4.ajblueprint diff --git a/test-packs/1.20.4/blueprints/armor_stand_1.20.4.ajblueprint b/test-packs/1.20.4/blueprints/armor_stand_1.20.4.ajblueprint index 8456ce81..d7abf198 100644 --- a/test-packs/1.20.4/blueprints/armor_stand_1.20.4.ajblueprint +++ b/test-packs/1.20.4/blueprints/armor_stand_1.20.4.ajblueprint @@ -857,24 +857,26 @@ "type": "locator" }, { + "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", + "type": "animated_java:text_display", "name": "text_display", "position": [0, 38, 0], "rotation": [22.5, 0, 0], "scale": [1, 1, 1], "visibility": true, + "locked": false, + "export": true, "block": "minecraft:stone", "config": {}, "item": "minecraft:diamond", "itemDisplay": "none", - "text": "[\n\t{ text: \"someone's string\\n\" },\n\t{ \"text\": \"\\u2603\" },\n\t{text: 'test', hoverEvent: {\n\t\taction:show_item,\n\t\tcontents: {id:'minecraft:stone'}\n\t}},\n\t'awd\"',\n\t\"awd'\", \n\t\"awd\\\"\",\n\t'awd\\'',\n\t'awd\\'\"',\n\t'\\n\\u2604\\n',\n\tno_quotes_on_me,\n\t'\\n',\n\t{\n\t\ttext: orange,\n\t\tcolor: '#ffaa00',\n\t\titalic: true,\n\t\tunderlined: \"true\",\n\t\tstrikethrough: TRUE\n\t}\n]", + "text": "[\n\t{0x00aced}yeah', 'I' '[{love light_purple}' 'text]{' displays''#FF5555'}\n\t'\\n'[{#55_FF_55}definitly' 'quite' 'cursed]\n\t'\\n'{what red}{amI #6395EE}{evenlookingat #c02c38}\n]", "lineWidth": 400, "backgroundColor": "#000000", "backgroundAlpha": 0.12941, "align": "center", "shadow": true, - "seeThrough": true, - "uuid": "c2e217f1-b50a-5c9a-b342-71a35e984046", - "type": "animated_java:text_display" + "seeThrough": true }, { "name": "camera", @@ -889,11 +891,15 @@ "uuid": "3ffeee76-901e-f8a2-c29a-82f90e16fd1e" }, { + "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", + "type": "animated_java:vanilla_item_display", "name": "item_display", "position": [6, 12, -4], "rotation": [-90, 0, 0], "scale": [0.5, 0.5, 0.5], "visibility": true, + "locked": false, + "export": true, "block": "minecraft:stone", "config": {}, "item": "minecraft:diamond", @@ -904,9 +910,7 @@ "backgroundAlpha": 0.25, "align": "center", "shadow": false, - "seeThrough": false, - "uuid": "bc141119-7f51-9690-7e21-e119b34fb3a2", - "type": "animated_java:vanilla_item_display" + "seeThrough": false } ], "outliner": [ @@ -1587,7 +1591,7 @@ "length": 1, "snapping": 20, "selected": false, - "saved": false, + "saved": true, "path": "", "anim_time_update": "", "blend_weight": "", @@ -1850,7 +1854,7 @@ "length": 2, "snapping": 20, "selected": false, - "saved": false, + "saved": true, "path": "", "anim_time_update": "", "blend_weight": "", @@ -1912,7 +1916,7 @@ "length": 1, "snapping": 20, "selected": false, - "saved": false, + "saved": true, "path": "", "anim_time_update": "", "blend_weight": "", diff --git a/test-packs/1.20.4/blueprints/armor_stand_minimal_1.20.4.ajblueprint b/test-packs/1.20.4/blueprints/armor_stand_minimal_1.20.4.ajblueprint index 0e4e6f80..60741617 100644 --- a/test-packs/1.20.4/blueprints/armor_stand_minimal_1.20.4.ajblueprint +++ b/test-packs/1.20.4/blueprints/armor_stand_minimal_1.20.4.ajblueprint @@ -1016,7 +1016,7 @@ "override": false, "length": 1, "snapping": 20, - "selected": false, + "selected": true, "saved": false, "path": "", "anim_time_update": "", diff --git a/test-packs/1.20.4/blueprints/rotated_blockstates_1.20.4.ajblueprint b/test-packs/1.20.4/blueprints/rotated_blockstates_1.20.4.ajblueprint new file mode 100644 index 00000000..f269c93b --- /dev/null +++ b/test-packs/1.20.4/blueprints/rotated_blockstates_1.20.4.ajblueprint @@ -0,0 +1,515 @@ +{ + "meta": { + "format": "animated-java:format/blueprint", + "format_version": "1.8.0", + "uuid": "b826aacd-866a-0b17-7e71-9de81b709a63", + "save_location": "D:\\github-repos\\animated-java\\old-animated-java\\test-packs\\1.20.4\\blueprints\\rotated_blockstates_1.20.4.ajblueprint", + "last_used_export_namespace": "rotated_blockstates" + }, + "resolution": { + "width": 16, + "height": 16 + }, + "blueprint_settings": { + "export_namespace": "rotated_blockstates", + "render_box": [48, 48], + "resource_pack_export_mode": "none", + "target_minecraft_version": "1.20.4", + "data_pack": "../datapacks/animated_java", + "auto_update_rig_orientation": false + }, + "elements": [ + { + "uuid": "fe01ee52-5d18-05eb-4a04-f4addc2e3d8a", + "type": "animated_java:vanilla_block_display", + "name": "lever_north", + "position": [-8, 16, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "22ad5245-d92c-7f66-6578-bc4f2ca565ec", + "type": "animated_java:vanilla_block_display", + "name": "lever_south", + "position": [-8, 16, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "2ecf5cb0-eae7-5434-d5c2-85f686edcaf0", + "type": "animated_java:vanilla_block_display", + "name": "lever_east", + "position": [8, 16, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "d509630d-9540-1113-a879-03ce6793117d", + "type": "animated_java:vanilla_block_display", + "name": "lever_west", + "position": [-24, 16, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:lever[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "f7e90b05-553d-be8a-0a74-a10f3c40f905", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_north", + "position": [-8, 32, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "db073962-3077-37c3-a53d-5acc41e31500", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_south", + "position": [-8, 32, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "ae5551bd-088a-8861-524d-9914984bae41", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_east", + "position": [8, 32, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "19d912ba-80b5-1c18-41d0-857ee8b4d590", + "type": "animated_java:vanilla_block_display", + "name": "stone_button_west", + "position": [-24, 32, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:stone_button[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "260e05ee-918f-b242-1570-7cc241284acb", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_north", + "position": [-8, 48, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "5290f4fe-83b8-0c33-81b4-f0407db57738", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_south", + "position": [-8, 48, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "b8918e92-660b-9728-6415-6de83f8b14f1", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_east", + "position": [8, 48, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "fd962fbf-1279-4871-adee-50664bb02c85", + "type": "animated_java:vanilla_block_display", + "name": "wall_torch_west", + "position": [-24, 48, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:wall_torch[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "27a5728f-1868-35b0-ddf2-94f58e7305bd", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_north", + "position": [-8, 0, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=north]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "9e758d97-f302-a2f5-1147-ef03c323ba27", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_south", + "position": [-8, 0, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=south]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "39d33f52-142e-60c8-5912-38b3466c4a3f", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_east", + "position": [8, 0, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=east]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "487e7ddd-0d55-a188-cc02-15e5025ac19f", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_west", + "position": [-24, 0, -8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=west]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "e42645c7-89b3-198d-dce6-8cd5e2dbccb6", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_north_east", + "position": [-24, 0, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=north,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "1dc76a78-955a-f71b-ae83-b332d3aa3196", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_south_east", + "position": [-24, 0, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=west,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "47608e95-885c-7f7c-5331-49d9fb70c979", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_south_west", + "position": [8, 0, 8], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=south,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + }, + { + "uuid": "2e72601c-6d12-cd2f-e14d-35ac2e7da732", + "type": "animated_java:vanilla_block_display", + "name": "cobblestone_stairs_north_west", + "position": [8, 0, -24], + "rotation": [0, 0, 0], + "scale": [1, 1, 1], + "visibility": true, + "locked": false, + "export": true, + "block": "minecraft:cobblestone_stairs[facing=east,shape=inner_left]", + "config": {}, + "item": "minecraft:diamond", + "itemDisplay": "none", + "text": "\"Hello World!\"", + "lineWidth": 200, + "backgroundColor": "#000000", + "backgroundAlpha": 0.25, + "align": "center", + "shadow": false, + "seeThrough": false + } + ], + "outliner": [ + "fe01ee52-5d18-05eb-4a04-f4addc2e3d8a", + "22ad5245-d92c-7f66-6578-bc4f2ca565ec", + "2ecf5cb0-eae7-5434-d5c2-85f686edcaf0", + "d509630d-9540-1113-a879-03ce6793117d", + "f7e90b05-553d-be8a-0a74-a10f3c40f905", + "db073962-3077-37c3-a53d-5acc41e31500", + "ae5551bd-088a-8861-524d-9914984bae41", + "19d912ba-80b5-1c18-41d0-857ee8b4d590", + "260e05ee-918f-b242-1570-7cc241284acb", + "5290f4fe-83b8-0c33-81b4-f0407db57738", + "b8918e92-660b-9728-6415-6de83f8b14f1", + "fd962fbf-1279-4871-adee-50664bb02c85", + "27a5728f-1868-35b0-ddf2-94f58e7305bd", + "e42645c7-89b3-198d-dce6-8cd5e2dbccb6", + "9e758d97-f302-a2f5-1147-ef03c323ba27", + "47608e95-885c-7f7c-5331-49d9fb70c979", + "39d33f52-142e-60c8-5912-38b3466c4a3f", + "2e72601c-6d12-cd2f-e14d-35ac2e7da732", + "487e7ddd-0d55-a188-cc02-15e5025ac19f", + "1dc76a78-955a-f71b-ae83-b332d3aa3196" + ], + "textures": [], + "variants": { + "default": { + "name": "default", + "display_name": "Default", + "uuid": "789ba6c9-7bed-41be-3fc4-fd271a4dc00d", + "texture_map": {}, + "excluded_nodes": [], + "is_default": true + }, + "list": [] + }, + "collections": [ + { + "uuid": "75aa7f41-1c57-be69-db96-478d11df81e9", + "name": "cobblestone_stairs", + "export_codec": "", + "children": [ + "27a5728f-1868-35b0-ddf2-94f58e7305bd", + "e42645c7-89b3-198d-dce6-8cd5e2dbccb6", + "9e758d97-f302-a2f5-1147-ef03c323ba27", + "47608e95-885c-7f7c-5331-49d9fb70c979", + "39d33f52-142e-60c8-5912-38b3466c4a3f", + "2e72601c-6d12-cd2f-e14d-35ac2e7da732", + "487e7ddd-0d55-a188-cc02-15e5025ac19f", + "1dc76a78-955a-f71b-ae83-b332d3aa3196" + ], + "visibility": false + } + ] +} \ No newline at end of file From 59c47b532aad12ae50f72e5a6364c55fc5b1b4a2 Mon Sep 17 00:00:00 2001 From: SnaveSutit Date: Thu, 9 Oct 2025 00:35:21 -0400 Subject: [PATCH 117/182] =?UTF-8?q?=F0=9F=8E=A8=20Fix=20all=20linting=20is?= =?UTF-8?q?sues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eslint.config.ts | 8 +- .../animatedJavaLoadingPopup.svelte | 1 - src/components/animationProperties.svelte | 6 +- src/components/blueprintSettingsDialog.svelte | 182 ++---- src/components/boneConfigDialog.svelte | 24 +- src/components/customKeyframePanel.svelte | 8 +- .../dialogItems/baseDialogItem.svelte | 18 +- src/components/dialogItems/checkbox.svelte | 2 +- src/components/dialogItems/codeInput.svelte | 18 +- src/components/dialogItems/collection.svelte | 14 +- src/components/dialogItems/colorPicker.svelte | 2 +- .../dialogItems/dialogItemUtil.d.ts | 6 +- src/components/dialogItems/fileSelect.svelte | 17 +- .../dialogItems/folderSelect.svelte | 17 +- src/components/dialogItems/lineInput.svelte | 14 +- src/components/dialogItems/multiSelect.svelte | 20 +- .../dialogItems/numberSlider.svelte | 2 +- src/components/dialogItems/select.svelte | 13 +- src/components/dialogItems/vector2d.svelte | 36 +- src/components/dialogItems/vector3d.svelte | 36 +- src/components/keyframeEasings.svelte | 30 +- .../commandsKeyframePanel.svelte | 20 +- .../variantKeyframePanel.svelte | 23 +- src/components/locatorConfigDialog.svelte | 22 +- src/components/textDisplayConfigDialog.svelte | 40 +- src/components/unexpectedErrorDialog.svelte | 17 +- .../vanillaBlockDisplayConfigDialog.svelte | 20 +- .../vanillaBlockDisplayElementPanel.svelte | 4 +- .../vanillaItemDisplayConfigDialog.svelte | 20 +- .../vanillaItemDisplayElementPanel.svelte | 4 +- src/components/variantConfigDialog.svelte | 32 +- src/components/variantsPanel.svelte | 14 +- src/formats/blueprint/codec.ts | 14 +- src/formats/blueprint/dfu.ts | 34 +- src/formats/blueprint/format.ts | 4 +- src/index.ts | 4 +- src/interface/animatedJavaBarItem.ts | 8 +- src/interface/dialog/boneConfig.ts | 2 +- src/interface/importAJModelLoader.ts | 6 +- src/interface/index.ts | 27 - .../panel/vanillaItemDisplayElement.ts | 1 - src/mods/actions.ts | 2 +- src/mods/animation.ts | 2 +- src/mods/eventHooks.ts | 3 +- src/mods/keyframeEasing.ts | 4 +- src/mods/locatorAnimatorMod.ts | 8 +- src/mods/molangMod.ts | 10 +- src/mods/pluginsDialogMod.ts | 1 - src/mods/showDefaultPoseMod.ts | 4 +- src/nodeConfigs.ts | 596 +++++++++--------- src/outliner/resizableOutlinerElement.ts | 26 +- src/outliner/textDisplay.ts | 24 +- src/outliner/util.ts | 2 +- src/outliner/vanillaBlockDisplay.ts | 57 +- src/outliner/vanillaItemDisplay.ts | 69 +- src/systems/animationRenderer.ts | 12 +- src/systems/datapackCompiler/mcbFiles.ts | 81 +-- src/systems/datapackCompiler/tellraw.ts | 90 +-- src/systems/global.ts | 24 +- src/systems/jsonText/unicodeString.ts | 20 +- src/systems/minecraft/assetManager.ts | 2 +- src/systems/minecraft/blockModelManager.ts | 6 +- src/systems/minecraft/blockstateManager.ts | 6 +- src/systems/minecraft/fontManager.ts | 106 +--- ...temDefinitions.ts => itemDefinitions.d.ts} | 0 src/systems/minecraft/itemModelManager.ts | 35 +- src/systems/minecraft/textWrapping.ts | 1 - src/systems/minecraft/textureShaders.ts | 190 +++--- src/systems/minecraft/versionManager.ts | 2 +- src/systems/resourcepackCompiler/1.20.4.ts | 9 +- src/systems/resourcepackCompiler/index.ts | 38 +- src/util/bbmodding.ts | 105 --- src/util/bufferGeometryUtils.ts | 18 +- src/util/events.ts | 1 - src/util/fileUtil.ts | 2 +- src/util/injectSvelteComponent.ts | 2 +- src/util/minecraftUtil.ts | 6 +- src/util/misc.ts | 2 +- src/util/moddingTools.ts | 4 +- src/util/resourceLocation.d.ts | 4 - src/util/stores.ts | 2 +- src/util/stringUtils.d.ts | 16 +- src/util/svelteDialog.ts | 2 +- src/util/sveltePanel.ts | 2 +- src/util/translation.ts | 2 +- src/util/utilityTypes.d.ts | 1 + src/variants.ts | 68 +- tsconfig.json | 8 +- 88 files changed, 1070 insertions(+), 1395 deletions(-) delete mode 100644 src/interface/index.ts rename src/systems/minecraft/{itemDefinitions.ts => itemDefinitions.d.ts} (100%) delete mode 100644 src/util/bbmodding.ts diff --git a/eslint.config.ts b/eslint.config.ts index 67d24c22..f8ca75ae 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -58,7 +58,7 @@ const CUSTOM_RULES: ConfigWithExtends['rules'] = { '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/unbound-method': 'off', '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/triple-slash-reference': 'off', + '@typescript-eslint/explicit-member-accessibility': ['error', { accessibility: 'no-public' }], // Naming conventions '@typescript-eslint/naming-convention': [ 'warn', @@ -76,6 +76,12 @@ const CUSTOM_RULES: ConfigWithExtends['rules'] = { }, format: null, }, + { + // Version Enums + selector: ['enumMember'], + filter: { regex: '^([\\d\\.]+)$', match: true }, + format: null, + }, { selector: ['import'], modifiers: ['default'], diff --git a/src/components/animatedJavaLoadingPopup.svelte b/src/components/animatedJavaLoadingPopup.svelte index 531a0474..0a9e9f45 100644 --- a/src/components/animatedJavaLoadingPopup.svelte +++ b/src/components/animatedJavaLoadingPopup.svelte @@ -1,5 +1,4 @@ diff --git a/src/components/animationProperties.svelte b/src/components/animationProperties.svelte index a0648565..547a0aa1 100644 --- a/src/components/animationProperties.svelte +++ b/src/components/animationProperties.svelte @@ -16,7 +16,7 @@ export let loopDelay: Valuable export let excludedNodes: Valuable> - const availableBones = getAvailableNodes(excludedNodes.get()) + const AVAILABLE_BONES = getAvailableNodes(excludedNodes.get()) function animationNameValueChecker(value: string): { type: string; message: string } { if (value.trim().length === 0) { @@ -24,7 +24,7 @@ type: 'error', message: translate('dialog.animation_properties.animation_name.error.empty'), } - } else if (value.match(/[^a-zA-Z0-9_\.]/)) { + } else if (/[^a-zA-Z0-9_\.]/.exec(value)) { return { type: 'error', message: translate( @@ -80,7 +80,7 @@ swapColumnsButtonTooltip={translate( 'dialog.animation_properties.swap_columns_button.tooltip' )} - availableItems={availableBones} + availableItems={AVAILABLE_BONES} bind:includedItems={excludedNodes} />
diff --git a/src/components/blueprintSettingsDialog.svelte b/src/components/blueprintSettingsDialog.svelte index 9b154e6d..63146e3a 100644 --- a/src/components/blueprintSettingsDialog.svelte +++ b/src/components/blueprintSettingsDialog.svelte @@ -25,51 +25,6 @@ document.fonts.add(font) }) } - - - + + {#if $resourcePackExportMode !== 'none'} - {#if $requiresDisplayItem} + {#if $DISPLAY_ITEM_REQUIRED} export let customNameVisible: Valuable export let billboard: Valuable - export let overrideBrightness: Valuable> - export let brightnessOverride: Valuable> - export let enchanted: Valuable> - export let glowing: Valuable> - export let overrideGlowColor: Valuable> - export let glowColor: Valuable> - export let inheritSettings: Valuable> - export let invisible: Valuable> - export let nbt: Valuable> - export let shadowRadius: Valuable> - export let shadowStrength: Valuable> - export let useNBT: Valuable> + export let overrideBrightness: Valuable> + export let brightnessOverride: Valuable> + export let enchanted: Valuable> + export let glowing: Valuable> + export let overrideGlowColor: Valuable> + export let glowColor: Valuable> + export let inheritSettings: Valuable> + export let invisible: Valuable> + export let nbt: Valuable> + export let shadowRadius: Valuable> + export let shadowStrength: Valuable> + export let useNBT: Valuable>
diff --git a/src/components/customKeyframePanel.svelte b/src/components/customKeyframePanel.svelte index 10dbc85e..8a682171 100644 --- a/src/components/customKeyframePanel.svelte +++ b/src/components/customKeyframePanel.svelte @@ -12,11 +12,11 @@ @@ -42,7 +42,7 @@ id="execute_condition" type="text" class="dark_bordered code keyframe_input tab_target" - bind:value={$executeCondition} + bind:value={$EXECUTE_CONDITION} />
diff --git a/src/components/dialogItems/baseDialogItem.svelte b/src/components/dialogItems/baseDialogItem.svelte index b73ae42c..aa24148b 100644 --- a/src/components/dialogItems/baseDialogItem.svelte +++ b/src/components/dialogItems/baseDialogItem.svelte @@ -3,15 +3,15 @@ import { translate } from '../../util/translation' export let label: string - export let tooltip: string = '' - export let warning_text: string = '' - export let error_text: string = '' + export let tooltip = '' + export let warningText = '' + export let errorText = '' export let onReset: () => void let id = guid() - $: if (error_text) { - blueprintSettingErrors.get()[label] = error_text + $: if (errorText) { + blueprintSettingErrors.get()[label] = errorText } @@ -21,19 +21,19 @@
- {#if error_text} + {#if errorText}
- {@html pureMarked(error_text)} + {@html pureMarked(errorText)}
- {:else if warning_text} + {:else if warningText}
- {@html pureMarked(warning_text)} + {@html pureMarked(warningText)}
{/if} {#if tooltip} diff --git a/src/components/dialogItems/checkbox.svelte b/src/components/dialogItems/checkbox.svelte index a5f718a3..be459128 100644 --- a/src/components/dialogItems/checkbox.svelte +++ b/src/components/dialogItems/checkbox.svelte @@ -3,7 +3,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let checked: Valuable export let defaultValue: boolean diff --git a/src/components/dialogItems/codeInput.svelte b/src/components/dialogItems/codeInput.svelte index edf99af1..c636dd0c 100644 --- a/src/components/dialogItems/codeInput.svelte +++ b/src/components/dialogItems/codeInput.svelte @@ -5,7 +5,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let value: Valuable export let defaultValue: string export let valueChecker: DialogItemValueChecker = undefined @@ -15,8 +15,8 @@ let codeJarElement: HTMLPreElement | undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function highlight(code: string, syntax?: string) { if (!syntax) return code @@ -32,8 +32,8 @@ function onValueChange() { if (valueChecker) { const result = valueChecker($value) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } forceNoWrap() @@ -96,14 +96,16 @@ white-space: pre; margin: 8px; margin-bottom: 0px; - {error_text + {errorText ? 'border: 1px solid var(--color-error); border-bottom: none; border-radius: 0.3em 0.3em 0 0;' : 'border: 1px solid var(--color-border);'} text-shadow: 0px 1px rgba(0, 0, 0, 0.3); " /> - {#if error_text} - + {#if errorText || warningText} + {/if}
diff --git a/src/components/dialogItems/collection.svelte b/src/components/dialogItems/collection.svelte index 02bf4721..fcbdf2f4 100644 --- a/src/components/dialogItems/collection.svelte +++ b/src/components/dialogItems/collection.svelte @@ -17,10 +17,10 @@ export let includedItemsColumnTooltip: string export let swapColumnsButtonTooltip: string export let availableItems: CollectionItem[] - export let includedItems: Valuable> + export let includedItems: Valuable - let includedItemsList: { id: number; title: string; [key: string]: any }[] = [] - let availableItemsList: { id: number; title: string; [key: string]: any }[] = [] + let includedItemsList: Array<{ id: number; title: string; [key: string]: any }> = [] + let availableItemsList: Array<{ id: number; title: string; [key: string]: any }> = [] for (let i = 0; i < availableItems.length; i++) { const item = availableItems[i] @@ -40,7 +40,7 @@ } function finalizeSort() { - includedItems.update(items => + includedItems.update(() => includedItemsList.map(i => availableItems.find(a => a.name === i.title)!) ) } @@ -57,7 +57,7 @@ } - +

{availableItemsColumnLable}

@@ -84,7 +84,7 @@ {/if} {item.icon || 'folder'}{item.icon ?? 'folder'}
{item.title}
@@ -123,7 +123,7 @@ {/if} {item.icon || 'folder'}{item.icon ?? 'folder'}
{item.title}
diff --git a/src/components/dialogItems/colorPicker.svelte b/src/components/dialogItems/colorPicker.svelte index ba5dc360..d8bb55df 100644 --- a/src/components/dialogItems/colorPicker.svelte +++ b/src/components/dialogItems/colorPicker.svelte @@ -5,7 +5,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let value: Valuable let colorPicker = new ColorPicker(`${PACKAGE.name}:${label}-color_picker`, { diff --git a/src/components/dialogItems/dialogItemUtil.d.ts b/src/components/dialogItems/dialogItemUtil.d.ts index 4ff027dc..d04230b1 100644 --- a/src/components/dialogItems/dialogItemUtil.d.ts +++ b/src/components/dialogItems/dialogItemUtil.d.ts @@ -2,4 +2,8 @@ type DialogItemValueChecker = | ((value: Value) => { type: string; message: string }) | undefined -type CollectionItem = { icon?: string; name: string; value: string } +interface CollectionItem { + icon?: string + name: string + value: string +} diff --git a/src/components/dialogItems/fileSelect.svelte b/src/components/dialogItems/fileSelect.svelte index 63981472..1c5dcf37 100644 --- a/src/components/dialogItems/fileSelect.svelte +++ b/src/components/dialogItems/fileSelect.svelte @@ -4,24 +4,24 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let value: Valuable export let defaultValue: string export let filters: FileFilter[] = [] - export let fileSelectMessage: string = 'Select File' + export let fileSelectMessage = 'Select File' let _value: string = value.get() export let valueChecker: DialogItemValueChecker = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker(value.get()) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } value.subscribe(() => checkValue()) @@ -31,8 +31,7 @@ } function selectFile() { - Promise.any([ - // @ts-ignore + void Promise.any([ electron.dialog.showOpenDialog({ properties: ['openFile', 'promptToCreate'], filters, @@ -54,7 +53,7 @@ onValueChange() - +
export let defaultValue: string export let filters: FileFilter[] = [] - export let fileSelectMessage: string = 'Select Folder' + export let fileSelectMessage = 'Select Folder' let _value: string = value.get() export let valueChecker: DialogItemValueChecker = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker(value.get()) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } value.subscribe(() => checkValue()) @@ -31,8 +31,7 @@ } function selectFile() { - Promise.any([ - // @ts-ignore + void Promise.any([ electron.dialog.showOpenDialog({ properties: ['openDirectory'], filters, @@ -54,7 +53,7 @@ onValueChange() - +
export let defaultValue: string - export let disabled: boolean = false + export let disabled = false export let valueChecker: DialogItemValueChecker = undefined let _value: string = value.get() - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function onValueChange() { if (valueChecker) { const result = valueChecker(_value) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } value.set(_value) @@ -33,7 +33,7 @@ onValueChange() - +
import MultiSelect, { type ObjectOption } from 'svelte-multiselect' import { Valuable } from '../../util/stores' - import { translate } from '../../util/translation' import BaseDialogItem from './baseDialogItem.svelte' type StringOption = ObjectOption & { value: string } export let label: string - export let tooltip: string = '' + export let tooltip = '' export let options: StringOption[] export let defaultValue: StringOption[] export let value: Valuable @@ -16,8 +15,6 @@ .filter(v => options.find(o => o.value == v)) .map(v => options.find(o => o.value == v)!) - let warning_text = '' - if (selected.length === 0) { selected = defaultValue } @@ -26,19 +23,6 @@ selected = options.filter(o => v.includes(o.value)) }) - $: { - value.set(selected.map(v => v.value)) - // FIXME - This warning is unique to the targeted minecraft version setting. - // Since the only use of this component is in the targeted minecraft version setting, this is fine... for now. - if (selected.length > 1) { - warning_text = translate( - 'dialog.blueprint_settings.target_minecraft_versions.warning.multiple_versions' - ) - } else { - warning_text = '' - } - } - function sortByIndex(a: StringOption, b: StringOption) { return options.indexOf(a) - options.indexOf(b) } @@ -48,7 +32,7 @@ } - +
export let defaultValue: number export let min = -Infinity diff --git a/src/components/dialogItems/select.svelte b/src/components/dialogItems/select.svelte index 3b0a82ac..c94efb61 100644 --- a/src/components/dialogItems/select.svelte +++ b/src/components/dialogItems/select.svelte @@ -3,7 +3,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let options: Record export let defaultOption: string export let value: Valuable @@ -12,12 +12,11 @@ if (!(value.get() || options[value.get()])) value.set(defaultOption) - // @ts-ignore - const selectInput = new Interface.CustomElements.SelectInput('dialog-select', { + const SELECT_ELEMENT = new Interface.CustomElements.SelectInput('dialog-select', { options, value: value.get(), onChange() { - const v = selectInput.node.getAttribute('value') + const v = SELECT_ELEMENT.node.getAttribute('value') if (v == undefined) { console.warn('Select value is undefined') return @@ -28,13 +27,13 @@ function onReset() { value.set(defaultOption) - if (selectInput.node) { - selectInput.set(defaultOption) + if (SELECT_ELEMENT.node) { + SELECT_ELEMENT.set(defaultOption) } } requestAnimationFrame(() => { - container.appendChild(selectInput.node) + container.appendChild(SELECT_ELEMENT.node) }) diff --git a/src/components/dialogItems/vector2d.svelte b/src/components/dialogItems/vector2d.svelte index 63dd2f5e..2947cd85 100644 --- a/src/components/dialogItems/vector2d.svelte +++ b/src/components/dialogItems/vector2d.svelte @@ -3,7 +3,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let step: number | undefined = undefined @@ -19,19 +19,19 @@ export let valueChecker: DialogItemValueChecker<{ x: number; y: number }> = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker({ x: valueX.get(), y: valueY.get() }) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } valueX.subscribe(() => checkValue()) valueY.subscribe(() => checkValue()) - const molangParser = new Molang() + const MOLANG_PARSER = new Molang() let inputX: HTMLInputElement let sliderX: HTMLElement @@ -47,22 +47,22 @@ ) { addEventListeners(target, 'mousedown touchstart', (e1: any) => { convertTouchEvent(e1) - let last_difference = 0 + let lastDifference = 0 function move(e2: any) { convertTouchEvent(e2) - let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step || 1) - if (difference != last_difference) { + let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step ?? 1) + if (difference != lastDifference) { value.set( Math.clamp( - value.get() + (difference - last_difference), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity + value.get() + (difference - lastDifference), + min ?? -Infinity, + max ?? Infinity ) ) - last_difference = difference + lastDifference = difference } } - function stop(e2: any) { + function stop() { removeEventListeners(document, 'mousemove touchmove', move) removeEventListeners(document, 'mouseup touchend', stop) } @@ -72,11 +72,7 @@ addEventListeners(inputX, 'focusout dblclick', () => { value.set( - Math.clamp( - molangParser.parse(value.get()), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity - ) + Math.clamp(MOLANG_PARSER.parse(value.get()), min ?? -Infinity, max ?? Infinity) ) }) } @@ -92,7 +88,7 @@ }) - +
diff --git a/src/components/dialogItems/vector3d.svelte b/src/components/dialogItems/vector3d.svelte index fedd06ba..ba5e985d 100644 --- a/src/components/dialogItems/vector3d.svelte +++ b/src/components/dialogItems/vector3d.svelte @@ -3,7 +3,7 @@ import BaseDialogItem from './baseDialogItem.svelte' export let label: string - export let tooltip: string = '' + export let tooltip = '' export let step: number | undefined = undefined @@ -24,20 +24,20 @@ export let valueChecker: DialogItemValueChecker<{ x: number; y: number; z: number }> = undefined - let warning_text = '' - let error_text = '' + let warningText = '' + let errorText = '' function checkValue() { if (!valueChecker) return const result = valueChecker({ x: valueX.get(), y: valueY.get(), z: valueZ.get() }) - result.type === 'error' ? (error_text = result.message) : (error_text = '') - result.type === 'warning' ? (warning_text = result.message) : (warning_text = '') + result.type === 'error' ? (errorText = result.message) : (errorText = '') + result.type === 'warning' ? (warningText = result.message) : (warningText = '') } valueX.subscribe(() => checkValue()) valueY.subscribe(() => checkValue()) valueZ.subscribe(() => checkValue()) - const molangParser = new Molang() + const MOLANG_PARSER = new Molang() let inputX: HTMLInputElement let sliderX: HTMLElement @@ -56,22 +56,22 @@ ) { addEventListeners(target, 'mousedown touchstart', (e1: any) => { convertTouchEvent(e1) - let last_difference = 0 + let lastDifference = 0 function move(e2: any) { convertTouchEvent(e2) - let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step || 1) - if (difference != last_difference) { + let difference = Math.trunc((e2.clientX - e1.clientX) / 10) * (step ?? 1) + if (difference != lastDifference) { value.set( Math.clamp( - value.get() + (difference - last_difference), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity + value.get() + (difference - lastDifference), + min ?? -Infinity, + max ?? Infinity ) ) - last_difference = difference + lastDifference = difference } } - function stop(e2: any) { + function stop() { removeEventListeners(document, 'mousemove touchmove', move) removeEventListeners(document, 'mouseup touchend', stop) } @@ -81,11 +81,7 @@ addEventListeners(inputX, 'focusout dblclick', () => { value.set( - Math.clamp( - molangParser.parse(value.get()), - min !== undefined ? min : -Infinity, - max !== undefined ? max : Infinity - ) + Math.clamp(MOLANG_PARSER.parse(value.get()), min ?? -Infinity, max ?? Infinity) ) }) } @@ -103,7 +99,7 @@ }) - +
diff --git a/src/components/keyframeEasings.svelte b/src/components/keyframeEasings.svelte index 4e1978bf..a0939141 100644 --- a/src/components/keyframeEasings.svelte +++ b/src/components/keyframeEasings.svelte @@ -15,14 +15,12 @@ ) const EASING_MODE_ICONS: Record = { - in: ICONS['expo'], - out: ICONS['out'], - inout: ICONS['inout'], + in: ICONS.expo, + out: ICONS.out, + inout: ICONS.inout, } - - + + @@ -43,7 +43,7 @@