diff --git a/docs/changelog/v3.2.0.md b/docs/changelog/v3.2.0.md index 609cbd14e..5343115f6 100644 --- a/docs/changelog/v3.2.0.md +++ b/docs/changelog/v3.2.0.md @@ -4,7 +4,7 @@ > > *Introduces new block data and deprecates several older features in preparation for the future.* -Bookshelf is now based on **Minecraft 1.21.9/10**. +Bookshelf is now based on **Minecraft 1.21.9-1.21.11**. - **[#403](https://github.com/mcbookshelf/bookshelf/issues/403)** - Added the `#bs.load:status` function to view loaded modules. - **[#501](https://github.com/mcbookshelf/bookshelf/pull/501)** - Optimized tp commands used in macros. @@ -31,6 +31,7 @@ Bookshelf is now based on **Minecraft 1.21.9/10**. - 🗑️ **[#484](https://github.com/mcbookshelf/bookshelf/pull/484)** - Block tag `has_offset` has been **deprecated** and will be removed in a v4.0.0. Please use `has_shape_offset` instead. - 🗑️ **[#484](https://github.com/mcbookshelf/bookshelf/pull/484)** - Function `#bs.hitbox:get_block` has been **deprecated** and will be removed in a v4.0.0. Please use `#bs.hitbox:get_block_shape` or `#bs.hitbox:get_block_collision` instead. - 🗑️ **[#484](https://github.com/mcbookshelf/bookshelf/pull/484)** - Functions `#bs.hitbox:is_entity_in_block_interaction`, `#bs.hitbox:is_entity_in_blocks_interaction`, and `#bs.hitbox:is_in_block_interaction` have been **deprecated** and will be removed in a v4.0.0. Please use `#bs.hitbox:is_entity_in_block_shape`, `#bs.hitbox:is_entity_in_blocks_shape`, or `#bs.hitbox:is_in_block_shape` instead. +- **[#502](https://github.com/mcbookshelf/bookshelf/pull/502)** - Added new 1.21.11 entities. ### `🏃 bs.move` diff --git a/modules/bs.block/data/bs.block/test/get/block.mcfunction b/modules/bs.block/data/bs.block/test/get/block.mcfunction index 52b7a6a11..f1a1c1438 100644 --- a/modules/bs.block/data/bs.block/test/get/block.mcfunction +++ b/modules/bs.block/data/bs.block/test/get/block.mcfunction @@ -16,7 +16,7 @@ setblock ~ ~ ~ minecraft:stone_stairs[facing=west,half=top,shape=straight,waterlogged=false] function #bs.block:get_block -assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,waterlogged=false,facing=west,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,waterlogged=false,facing=west,]", properties: { facing: "west", half: "top", shape: "straight", waterlogged: "false" } } +assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,facing=west,waterlogged=false,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,facing=west,waterlogged=false,]", properties: { facing: "west", half: "top", shape: "straight", waterlogged: "false" } } setblock ~ ~ ~ minecraft:campfire function #bs.block:get_block diff --git a/modules/bs.block/data/bs.block/test/transform/map_type.mcfunction b/modules/bs.block/data/bs.block/test/transform/map_type.mcfunction index 561708da1..3f3bdb82d 100644 --- a/modules/bs.block/data/bs.block/test/transform/map_type.mcfunction +++ b/modules/bs.block/data/bs.block/test/transform/map_type.mcfunction @@ -17,7 +17,7 @@ setblock ~ ~ ~ minecraft:spruce_stairs[facing=west,half=top,shape=straight,water function #bs.block:get_block function #bs.block:map_type {type:"minecraft:oak_planks",mapping_registry:"bs.shapes"} -assert data storage bs:out block{ block: "minecraft:oak_stairs[shape=straight,half=top,waterlogged=false,facing=west,]" } +assert data storage bs:out block{ block: "minecraft:oak_stairs[shape=straight,half=top,facing=west,waterlogged=false,]" } function #bs.block:map_type {type:"minecraft:spruce_planks",mapping_registry:"bs.shapes"} -assert data storage bs:out block{ block: "minecraft:spruce_stairs[shape=straight,half=top,waterlogged=false,facing=west,]" } +assert data storage bs:out block{ block: "minecraft:spruce_stairs[shape=straight,half=top,facing=west,waterlogged=false,]" } diff --git a/modules/bs.block/data/bs.block/test/transform/merge_properties.mcfunction b/modules/bs.block/data/bs.block/test/transform/merge_properties.mcfunction index fea79bc82..dce54aae5 100644 --- a/modules/bs.block/data/bs.block/test/transform/merge_properties.mcfunction +++ b/modules/bs.block/data/bs.block/test/transform/merge_properties.mcfunction @@ -18,4 +18,4 @@ function #bs.block:get_block setblock ~ ~ ~ minecraft:furnace[facing=north] function #bs.block:merge_properties {properties:[{name:"facing"}]} -assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,waterlogged=false,facing=north,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,waterlogged=false,facing=north,]", properties: { facing: "north", half: "top", shape: "straight", waterlogged: "false" } } +assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,facing=north,waterlogged=false,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,facing=north,waterlogged=false,]", properties: { facing: "north", half: "top", shape: "straight", waterlogged: "false" } } diff --git a/modules/bs.block/data/bs.block/test/transform/replace_properties.mcfunction b/modules/bs.block/data/bs.block/test/transform/replace_properties.mcfunction index d3617c75f..9c34465a0 100644 --- a/modules/bs.block/data/bs.block/test/transform/replace_properties.mcfunction +++ b/modules/bs.block/data/bs.block/test/transform/replace_properties.mcfunction @@ -17,4 +17,4 @@ setblock ~ ~ ~ minecraft:stone_stairs[facing=west,half=top,shape=straight,waterl function #bs.block:get_block function #bs.block:replace_properties {properties:[{name:"facing",value:"north"}]} -assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,waterlogged=false,facing=north,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,waterlogged=false,facing=north,]", properties: { facing: "north", half: "top", shape: "straight", waterlogged: "false" } } +assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,facing=north,waterlogged=false,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,facing=north,waterlogged=false,]", properties: { facing: "north", half: "top", shape: "straight", waterlogged: "false" } } diff --git a/modules/bs.block/data/bs.block/test/transform/replace_type.mcfunction b/modules/bs.block/data/bs.block/test/transform/replace_type.mcfunction index 6e2f2d663..d0c85c3fb 100644 --- a/modules/bs.block/data/bs.block/test/transform/replace_type.mcfunction +++ b/modules/bs.block/data/bs.block/test/transform/replace_type.mcfunction @@ -17,7 +17,7 @@ setblock ~ ~ ~ minecraft:stone_stairs[facing=west,half=top,shape=straight,waterl function #bs.block:get_block function #bs.block:replace_type {type:"minecraft:spruce_stairs"} -assert data storage bs:out block{ block: "minecraft:spruce_stairs[shape=straight,half=top,waterlogged=false,facing=west,]", type: "minecraft:spruce_stairs", state: "[shape=straight,half=top,waterlogged=false,facing=west,]", properties: { facing: "west", half: "top", shape: "straight", waterlogged: "false" } } +assert data storage bs:out block{ block: "minecraft:spruce_stairs[shape=straight,half=top,facing=west,waterlogged=false,]", type: "minecraft:spruce_stairs", state: "[shape=straight,half=top,facing=west,waterlogged=false,]", properties: { facing: "west", half: "top", shape: "straight", waterlogged: "false" } } function #bs.block:replace_type {type:"minecraft:stone"} assert data storage bs:out block{ block: "minecraft:stone", type: "minecraft:stone" } diff --git a/modules/bs.block/data/bs.block/test/transform/shift_properties.mcfunction b/modules/bs.block/data/bs.block/test/transform/shift_properties.mcfunction index 8c5e866cd..e6d0b271e 100644 --- a/modules/bs.block/data/bs.block/test/transform/shift_properties.mcfunction +++ b/modules/bs.block/data/bs.block/test/transform/shift_properties.mcfunction @@ -17,4 +17,4 @@ setblock ~ ~ ~ minecraft:stone_stairs[facing=west,half=top,shape=straight,waterl function #bs.block:get_block function #bs.block:shift_properties {properties:[{name:"facing",by:2}]} -assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,waterlogged=false,facing=north,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,waterlogged=false,facing=north,]", properties: { facing: "north", half: "top", shape: "straight", waterlogged: "false" } } +assert data storage bs:out block{ block: "minecraft:stone_stairs[shape=straight,half=top,facing=north,waterlogged=false,]", type: "minecraft:stone_stairs", state: "[shape=straight,half=top,facing=north,waterlogged=false,]", properties: { facing: "north", half: "top", shape: "straight", waterlogged: "false" } } diff --git a/modules/bs.block/gen_block.py b/modules/bs.block/gen_block.py index 201f2a848..4f8fd41ba 100644 --- a/modules/bs.block/gen_block.py +++ b/modules/bs.block/gen_block.py @@ -1,12 +1,16 @@ +from __future__ import annotations + from collections import defaultdict -from collections.abc import Sequence +from typing import TYPE_CHECKING -from beet import Context, Function, LootTable, Predicate +from beet import Context, Function, LootTable, PackFile, Predicate -from bookshelf.definitions import MC_VERSIONS from bookshelf.models import Block, StateNode, StatePredicate, StateProperty, StateValue from bookshelf.services import minecraft +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence + ATTR_TAGS = [ ("has_state", lambda b: b.group > 0), ("can_occlude", lambda b: b.can_occlude), @@ -44,41 +48,47 @@ "sounds", } +LOOT_TABLE_INCLUDE = { + "type", + "item", + "group", + "sounds", # @deprecated sounds (removed in in 4.0.0) +} + -def beet_default(ctx: Context) -> None: - """Generate files used by the bs.block module.""" - namespace = ctx.directory.name - blocks = minecraft.get_blocks(ctx, MC_VERSIONS[-1]) +@minecraft.generator +def beet_default(ctx: Context, version: str) -> Iterable[tuple[str, PackFile]]: + """Generate files used by the module for a given version.""" + ns = ctx.directory.name + blocks = minecraft.get_blocks(ctx.cache, version) - loot_table = make_block_loot_table(blocks) - ctx.generate(f"{namespace}:internal/get_type", render=loot_table) - loot_table = make_block_loot_table(blocks, f"{namespace}:internal") - ctx.generate(f"{namespace}:internal/get_block", render=loot_table) + yield f"{ns}:internal/get_type", make_block_loot_table(blocks) + yield f"{ns}:internal/get_block", make_block_loot_table(blocks, f"{ns}:internal") for state in {s.group: s for b in blocks for s in b.properties}.values(): - loot_table = make_block_state_loot_table(state, f"{namespace}:internal") - ctx.generate(f"{namespace}:internal/group_{state.group}", render=loot_table) + location = f"{ns}:internal/group_{state.group}" + yield location, make_block_state_loot_table(state, f"{ns}:internal") for name, formatter in [ ("types", format_types_table), ("items", format_items_table), ("groups", format_groups_table), ]: - ctx.generate( - f"{namespace}:import/{name}_table", - render=Function(source_path=f"{name}_table.jinja"), - data=formatter(blocks), - ) + location = f"{ns}:import/{name}_table" + content = ctx.template.render(f"{name}_table.jinja", data=formatter(blocks)) + yield location, Function(content) for name, predicate in ATTR_TAGS: - tag = ctx.data.block_tags.get(f"{namespace}:{name}") - tag.merge(minecraft.make_block_tag(blocks, predicate)) + base = ctx.data.block_tags[location := f"{ns}:{name}"] + yield location, minecraft.make_block_tag(base, blocks, predicate) for name, attribute in ATTR_PREDICATES: groups = defaultdict(list) for block in blocks: groups[attribute(block)].append(block) - merge_attr_predicate(ctx.data.predicates.get(f"{namespace}:{name}"), groups) + + base = ctx.data.predicates[location := f"{ns}:{name}"] + yield location, make_attr_predicate(base, groups) for name, attribute in ATTR_LOOT_TABLES: seen = set() @@ -87,11 +97,11 @@ def beet_default(ctx: Context) -> None: groups[value := attribute(block)].append(block) if isinstance(value, StatePredicate) and value not in seen: seen.add(value) - file = make_attr_state_loot_table(name, value) - ctx.generate(f"{namespace}:internal/{name}_{value.group}", render=file) + location = f"{ns}:internal/{name}_{value.group}" + yield location, make_attr_state_loot_table(name, value) - file = make_attr_loot_table(name, groups, f"{namespace}:internal") - ctx.generate(f"{namespace}:internal/get_{name}", render=file) + location = f"{ns}:internal/get_{name}" + yield location, make_attr_loot_table(name, groups, f"{ns}:internal") def format_types_table(blocks: Sequence[Block]) -> dict: @@ -120,11 +130,10 @@ def format_groups_table(blocks: Sequence[Block]) -> dict: def format_block_loot_entry(entry: dict, block: Block) -> dict: """Attach block data to a loot entry.""" - # @deprecated sounds (removed in in 4.0.0) return {**entry, "functions": [{ "function": "set_custom_data", "tag": minecraft.render_snbt(block.model_dump( - include={"type", "item", "group", "sounds"}, + include=LOOT_TABLE_INCLUDE, )), }]} @@ -219,10 +228,10 @@ def make_attr_state_loot_table(attr: str, entry: StatePredicate) -> LootTable: }) -def merge_attr_predicate( - predicate: Predicate, +def make_attr_predicate( + base: Predicate, groups: dict[StateValue[bool], list[Block]], -) -> None: +) -> Predicate: """Create a predicate for a collection of blocks grouped by attribute.""" def optimize_entry(entry: dict) -> dict | None: terms = list(filter(None, entry.get("terms", []))) @@ -251,8 +260,8 @@ def build_node[T: bool](node: StateNode[T] | T) -> dict | None: return optimize_entry({"condition":"any_of","terms":terms}) - predicate.set_content(optimize_entry({ - **predicate.deserialize(predicate.get_content()), + return Predicate({ + **base.data, "condition": "any_of", "terms": [optimize_entry({ "condition": "all_of", "terms":[{ @@ -263,4 +272,4 @@ def build_node[T: bool](node: StateNode[T] | T) -> dict | None: "condition": "location_check", "predicate": {"block": {"blocks": [b.type[10:] for b in blocks]}}, } for group, blocks in groups.items() if group], - })) + }) diff --git a/modules/bs.environment/data/bs.environment/function/biome/get_biome.mcfunction b/modules/bs.environment/data/bs.environment/function/biome/get_biome.mcfunction index 41f321ef5..1ba0ef4f9 100644 --- a/modules/bs.environment/data/bs.environment/function/biome/get_biome.mcfunction +++ b/modules/bs.environment/data/bs.environment/function/biome/get_biome.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ data remove storage bs:out environment.get_biome -loot replace entity B5-0-0-0-3 contents loot bs.environment:biome/get +loot replace entity B5-0-0-0-3 contents loot bs.environment:internal/get_biome data modify storage bs:out environment.get_biome set from entity B5-0-0-0-3 item.components."minecraft:custom_data" diff --git a/modules/bs.environment/data/bs.environment/function/temperature/get_temperature.mcfunction b/modules/bs.environment/data/bs.environment/function/temperature/get_temperature.mcfunction index afd371089..4f771a866 100644 --- a/modules/bs.environment/data/bs.environment/function/temperature/get_temperature.mcfunction +++ b/modules/bs.environment/data/bs.environment/function/temperature/get_temperature.mcfunction @@ -13,7 +13,7 @@ # For more details, refer to the MPL v2.0. # ------------------------------------------------------------------------------------------------------------ -loot replace entity B5-0-0-0-3 contents loot bs.environment:biome/get +loot replace entity B5-0-0-0-3 contents loot bs.environment:internal/get_biome execute store result score $environment.celestial_angle.daytime bs.in run data get entity B5-0-0-0-3 item.components."minecraft:custom_data".temperature execute store result storage bs:ctx y double .00000001 summon minecraft:marker run function bs.environment:temperature/variation $execute store result score $environment.get_temperature bs.out run return run data get storage bs:ctx y $(scale) diff --git a/modules/bs.environment/gen_environment.py b/modules/bs.environment/gen_environment.py index 72db557f9..f1601a03d 100644 --- a/modules/bs.environment/gen_environment.py +++ b/modules/bs.environment/gen_environment.py @@ -1,38 +1,49 @@ +from __future__ import annotations + from collections import defaultdict -from collections.abc import Sequence +from typing import TYPE_CHECKING -from beet import Context, LootTable, Predicate +from beet import Context, LootTable, PackFile, Predicate -from bookshelf.definitions import MC_VERSIONS -from bookshelf.models import Biome from bookshelf.services import minecraft +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence + + from bookshelf.models import Biome + SNOW_THRESHOLD = 0.4 -def beet_default(ctx: Context) -> None: +@minecraft.generator +def beet_default(ctx: Context, version: str) -> Iterable[tuple[str, PackFile]]: """Generate files used by the environment module.""" - namespace = ctx.directory.name - biomes = minecraft.get_biomes(ctx, MC_VERSIONS[-1]) - - loot_table = make_biome_loot_table(biomes) - ctx.generate(f"{namespace}:biome/get", render=loot_table) + ns = ctx.directory.name + biomes = minecraft.get_biomes(ctx.cache, version) - if predicate := ctx.data.predicates.get(f"{namespace}:can_snow"): - predicate.data.update(make_can_snow_predicate(biomes).data) - if predicate := ctx.data.predicates.get(f"{namespace}:has_precipitation"): - predicate.data.update(make_has_precipitation_predicate(biomes).data) + yield f"{ns}:internal/get_biome", make_biome_loot_table(biomes) + base = ctx.data.predicates[location := f"{ns}:can_snow"] + yield location, make_can_snow_predicate(base, biomes) + base = ctx.data.predicates[location := f"{ns}:has_precipitation"] + yield location, make_has_precipitation_predicate(base, biomes) -def make_has_precipitation_predicate(biomes: Sequence[Biome]) -> Predicate: +def make_has_precipitation_predicate( + base: Predicate, + biomes: Sequence[Biome], +) -> Predicate: """Create a predicate to determine biomes with precipitation.""" return Predicate({ + **base.data, "condition": "minecraft:location_check", "predicate": {"biomes": [b.type for b in biomes if b.has_precipitation]}, }) -def make_can_snow_predicate(biomes: Sequence[Biome]) -> Predicate: +def make_can_snow_predicate( + base: Predicate, + biomes: Sequence[Biome], +) -> Predicate: """Create a predicate to determine where snow can occur.""" groups = defaultdict(list[str]) for biome in filter( @@ -44,6 +55,7 @@ def make_can_snow_predicate(biomes: Sequence[Biome]) -> Predicate: groups[str(y if y > 0 else -2147483648)].append(biome.type) return Predicate({ + **base.data, "condition":"minecraft:any_of", "terms": [{ "condition": "minecraft:location_check", diff --git a/modules/bs.hitbox/data/bs.hitbox/predicate/is_gliding.json b/modules/bs.hitbox/1.21.10/data/bs.hitbox/predicate/internal/is_gliding.json similarity index 100% rename from modules/bs.hitbox/data/bs.hitbox/predicate/is_gliding.json rename to modules/bs.hitbox/1.21.10/data/bs.hitbox/predicate/internal/is_gliding.json diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/10.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/10.mcfunction index bbd70531b..dbf58b1d0 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/10.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/10.mcfunction @@ -15,6 +15,6 @@ # camel group execute at @s positioned ~ ~1 ~ if entity @s[dx=0] run return run data modify storage bs:out hitbox set value {width:1.7,height:2.375} -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.7,height:0.945} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.7,height:0.945} execute at @s positioned ~ ~1 ~ if entity @s[dx=0] run return run data modify storage bs:out hitbox set value {width:0.765,height:1.06875} data modify storage bs:out hitbox set value {width:0.765,height:0.42525} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/11.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/11.mcfunction index db0b41585..5775d4837 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/11.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/11.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # cat_like group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:0.7} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:0.7} data modify storage bs:out hitbox set value {width:0.3,height:0.35} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/13.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/13.mcfunction index ba1dedd5c..dbe1fd3d2 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/13.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/13.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # chicken group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.4,height:0.7} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.4,height:0.7} data modify storage bs:out hitbox set value {width:0.2,height:0.35} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/15.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/15.mcfunction index 2a0c7788c..edeccfe17 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/15.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/15.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # cow_like group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.4} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.4} data modify storage bs:out hitbox set value {width:0.45,height:0.7} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/17.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/17.mcfunction index 53f52bd54..5f1f4662b 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/17.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/17.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # dolphin group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.585,height:0.39} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.585,height:0.39} data modify storage bs:out hitbox set value {width:0.9,height:0.6} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/18.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/18.mcfunction index 62312a7ae..605589364 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/18.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/18.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # donkey group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.3964844,height:1.5} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.3964844,height:1.5} data modify storage bs:out hitbox set value {width:0.6982422,height:0.75} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/25.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/25.mcfunction index 6fa0b277e..709f68f04 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/25.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/25.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # ghast group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:4.0,height:4.0} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:4.0,height:4.0} data modify storage bs:out hitbox set value {width:0.95,height:0.95} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/29.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/29.mcfunction index adc7080d4..8971589da 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/29.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/29.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # hoglin group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.3964844,height:1.4} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.3964844,height:1.4} data modify storage bs:out hitbox set value {width:0.6982422,height:0.7} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/30.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/30.mcfunction index 9838da94b..2281e9360 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/30.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/30.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # horse_like group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.3964844,height:1.6} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.3964844,height:1.6} data modify storage bs:out hitbox set value {width:0.6982422,height:0.8} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/36.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/36.mcfunction index 4250adda2..26dbe1f2a 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/36.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/36.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # llama group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.87} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.87} data modify storage bs:out hitbox set value {width:0.45,height:0.935} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/39.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/39.mcfunction index cb4e7c454..e8ea31297 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/39.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/39.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # panda group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.3,height:1.25} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.3,height:1.25} data modify storage bs:out hitbox set value {width:0.65,height:0.625} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/41.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/41.mcfunction index 41c00ac80..320fe15fa 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/41.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/41.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # pig group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:0.9} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:0.9} data modify storage bs:out hitbox set value {width:0.45,height:0.45} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/42.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/42.mcfunction index efe685345..aeb43fd2b 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/42.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/42.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # polar_bear group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.4,height:1.4} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.4,height:1.4} data modify storage bs:out hitbox set value {width:0.7,height:0.7} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/44.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/44.mcfunction index 83847311c..2b2de7a0d 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/44.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/44.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # rabbit group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.4,height:0.5} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.4,height:0.5} data modify storage bs:out hitbox set value {width:0.2,height:0.25} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/46.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/46.mcfunction index 96ee2172c..cefe51ae5 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/46.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/46.mcfunction @@ -14,6 +14,6 @@ # ------------------------------------------------------------------------------------------------------------ # salmon group -execute unless predicate bs.hitbox:salmon/large run return run data modify storage bs:out hitbox set value {width:1.05,height:0.6} -execute unless predicate bs.hitbox:salmon/small run return run data modify storage bs:out hitbox set value {width:0.35,height:0.2} +execute unless predicate bs.hitbox:internal/salmon/large run return run data modify storage bs:out hitbox set value {width:1.05,height:0.6} +execute unless predicate bs.hitbox:internal/salmon/small run return run data modify storage bs:out hitbox set value {width:0.35,height:0.2} data modify storage bs:out hitbox set value {width:0.7,height:0.4} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/47.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/47.mcfunction index 000ad2708..f8e4d40f0 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/47.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/47.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # sheep group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.3} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.3} data modify storage bs:out hitbox set value {width:0.45,height:0.65} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/5.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/5.mcfunction index 666a27eb5..310473320 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/5.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/5.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # axolotl group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.75,height:0.42} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.75,height:0.42} data modify storage bs:out hitbox set value {width:0.375,height:0.21} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/53.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/53.mcfunction index 36971aa15..b1aa0a2d0 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/53.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/53.mcfunction @@ -15,6 +15,6 @@ # sniffer group execute at @s positioned ~ ~1.5 ~ if entity @s[dx=0] run return run data modify storage bs:out hitbox set value {width:1.9,height:1.75} -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.9,height:0.4} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.9,height:0.4} execute at @s positioned ~ ~.5 ~ if entity @s[dx=0] run return run data modify storage bs:out hitbox set value {width:0.95,height:0.875} data modify storage bs:out hitbox set value {width:0.95,height:0.4} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/56.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/56.mcfunction index e2ced0b56..19b0e6e1f 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/56.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/56.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # squid group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.4,height:0.4} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.4,height:0.4} data modify storage bs:out hitbox set value {width:0.8,height:0.8} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/57.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/57.mcfunction index 6f8fd1c44..e115076dc 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/57.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/57.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # strider group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.7} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.9,height:1.7} data modify storage bs:out hitbox set value {width:0.45,height:0.85} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/60.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/60.mcfunction index 7fde717e7..dcbc43487 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/60.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/60.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # turtle group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:1.2,height:0.4} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:1.2,height:0.4} data modify storage bs:out hitbox set value {width:0.36,height:0.12} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/66.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/66.mcfunction index dec364e56..96e789c99 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/66.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/66.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # wolf group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:0.85} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:0.85} data modify storage bs:out hitbox set value {width:0.3,height:0.425} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/67.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/67.mcfunction index 539c0b187..6048b4443 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/67.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/67.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # zombie_like group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:1.95} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:1.95} data modify storage bs:out hitbox set value {width:0.3,height:0.975} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/68.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/68.mcfunction index bed0e5049..bd6d33cc9 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/68.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/68.mcfunction @@ -15,7 +15,7 @@ # player group execute if entity @s[gamemode=spectator] run return run data modify storage bs:out hitbox set value {width:0.0,height:0.0} -execute if predicate bs.hitbox:is_sneaking run return run data modify storage bs:out hitbox set value {width:0.6,height:1.5} -execute if predicate bs.hitbox:is_swimming run return run data modify storage bs:out hitbox set value {width:0.6,height:0.6} -execute if predicate bs.hitbox:is_gliding run return run data modify storage bs:out hitbox set value {width:0.6,height:0.6} +execute if predicate bs.hitbox:internal/is_sneaking run return run data modify storage bs:out hitbox set value {width:0.6,height:1.5} +execute if predicate bs.hitbox:internal/is_swimming run return run data modify storage bs:out hitbox set value {width:0.6,height:0.6} +execute if predicate bs.hitbox:internal/is_gliding run return run data modify storage bs:out hitbox set value {width:0.6,height:0.6} data modify storage bs:out hitbox set value {width:0.6,height:1.8} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/69.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/69.mcfunction index 99088d884..34c85d1fe 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/69.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/69.mcfunction @@ -15,5 +15,5 @@ # villager group execute at @s positioned ~ ~.5 ~ unless entity @s[dx=0] run return run data modify storage bs:out hitbox set value {width:0.2,height:0.2} -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:1.95} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.6,height:1.95} data modify storage bs:out hitbox set value {width:0.3,height:0.975} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/7.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/7.mcfunction index 991e249c9..d9d147286 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/7.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/7.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # bee group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.7,height:0.6} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.7,height:0.6} data modify storage bs:out hitbox set value {width:0.35,height:0.3} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/70.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/70.mcfunction index e7b8ca76d..48dd1f548 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/70.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/70.mcfunction @@ -15,6 +15,6 @@ # goat group execute at @s positioned ~ ~1 ~ if entity @s[dx=0] run return run data modify storage bs:out hitbox set value {width:0.9,height:1.3} -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.63,height:0.90999997} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.63,height:0.90999997} execute at @s positioned ~ ~.5 ~ if entity @s[dx=0] run return run data modify storage bs:out hitbox set value {width:0.45,height:0.65} data modify storage bs:out hitbox set value {width:0.315,height:0.45499998} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/71.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/71.mcfunction index ca14ff701..b6dd09d15 100644 --- a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/71.mcfunction +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/71.mcfunction @@ -14,5 +14,5 @@ # ------------------------------------------------------------------------------------------------------------ # armadilo group -execute unless predicate bs.hitbox:is_baby run return run data modify storage bs:out hitbox set value {width:0.7,height:0.65} +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.7,height:0.65} data modify storage bs:out hitbox set value {width:0.42,height:0.39} diff --git a/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/75.mcfunction b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/75.mcfunction new file mode 100644 index 000000000..b59871629 --- /dev/null +++ b/modules/bs.hitbox/data/bs.hitbox/function/get_entity/registry/75.mcfunction @@ -0,0 +1,18 @@ +# ------------------------------------------------------------------------------------------------------------ +# Copyright (c) 2025 Gunivers +# +# This file is part of the Bookshelf project (https://github.com/mcbookshelf/bookshelf). +# +# This source code is subject to the terms of the Mozilla Public License, v. 2.0. +# If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# Conditions: +# - You may use this file in compliance with the MPL v2.0 +# - Any modifications must be documented and disclosed under the same license +# +# For more details, refer to the MPL v2.0. +# ------------------------------------------------------------------------------------------------------------ + +# nautilus group +execute unless predicate bs.hitbox:internal/is_baby run return run data modify storage bs:out hitbox set value {width:0.875,height:0.95} +data modify storage bs:out hitbox set value {width:0.4375,height:0.475} diff --git a/modules/bs.hitbox/data/bs.hitbox/predicate/is_baby.json b/modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_baby.json similarity index 100% rename from modules/bs.hitbox/data/bs.hitbox/predicate/is_baby.json rename to modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_baby.json diff --git a/modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_gliding.json b/modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_gliding.json new file mode 100644 index 000000000..f4b1b0c66 --- /dev/null +++ b/modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_gliding.json @@ -0,0 +1,9 @@ +{ + "condition": "minecraft:entity_properties", + "entity": "this", + "predicate": { + "flags": { + "is_fall_flying": true + } + } +} diff --git a/modules/bs.hitbox/data/bs.hitbox/predicate/is_sneaking.json b/modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_sneaking.json similarity index 100% rename from modules/bs.hitbox/data/bs.hitbox/predicate/is_sneaking.json rename to modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_sneaking.json diff --git a/modules/bs.hitbox/data/bs.hitbox/predicate/is_swimming.json b/modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_swimming.json similarity index 100% rename from modules/bs.hitbox/data/bs.hitbox/predicate/is_swimming.json rename to modules/bs.hitbox/data/bs.hitbox/predicate/internal/is_swimming.json diff --git a/modules/bs.hitbox/data/bs.hitbox/predicate/salmon/large.json b/modules/bs.hitbox/data/bs.hitbox/predicate/internal/salmon/large.json similarity index 100% rename from modules/bs.hitbox/data/bs.hitbox/predicate/salmon/large.json rename to modules/bs.hitbox/data/bs.hitbox/predicate/internal/salmon/large.json diff --git a/modules/bs.hitbox/data/bs.hitbox/predicate/salmon/small.json b/modules/bs.hitbox/data/bs.hitbox/predicate/internal/salmon/small.json similarity index 100% rename from modules/bs.hitbox/data/bs.hitbox/predicate/salmon/small.json rename to modules/bs.hitbox/data/bs.hitbox/predicate/internal/salmon/small.json diff --git a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/camel.json b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/camel.json new file mode 100644 index 000000000..d6728ac8e --- /dev/null +++ b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/camel.json @@ -0,0 +1,9 @@ +{ + "values": [ + "minecraft:camel", + { + "id": "minecraft:camel_husk", + "required": false + } + ] +} diff --git a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_2.json b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_2.json index 19ef28f8e..92a352017 100644 --- a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_2.json +++ b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_2.json @@ -4,7 +4,7 @@ "minecraft:armor_stand", "#bs.hitbox:internal/bat_like", "minecraft:bee", - "minecraft:camel", + "#bs.hitbox:internal/camel", "#bs.hitbox:internal/cat_like", "minecraft:cod", "#bs.hitbox:internal/cow_like", diff --git a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_8.json b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_8.json index 06cd55314..181135774 100644 --- a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_8.json +++ b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/group_8.json @@ -2,7 +2,7 @@ "values": [ "minecraft:blaze", "#bs.hitbox:internal/boat", - "minecraft:camel", + "#bs.hitbox:internal/camel", "#bs.hitbox:internal/cat_like", "minecraft:cave_spider", "minecraft:chicken", diff --git a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/nautilus.json b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/nautilus.json new file mode 100644 index 000000000..0513f8b5f --- /dev/null +++ b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/nautilus.json @@ -0,0 +1,12 @@ +{ + "values": [ + { + "id": "minecraft:nautilus", + "required": false + }, + { + "id": "minecraft:zombie_nautilus", + "required": false + } + ] +} diff --git a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/skeleton.json b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/skeleton.json index 19e61b9d6..3a62cab54 100644 --- a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/skeleton.json +++ b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/internal/skeleton.json @@ -2,6 +2,10 @@ "values": [ "minecraft:bogged", "minecraft:skeleton", - "minecraft:stray" + "minecraft:stray", + { + "id": "minecraft:parched", + "required": false + } ] } diff --git a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/is_sized.json b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/is_sized.json index 8d8042df0..9738b3262 100644 --- a/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/is_sized.json +++ b/modules/bs.hitbox/data/bs.hitbox/tags/entity_type/is_sized.json @@ -18,6 +18,7 @@ "#bs.hitbox:internal/arrow_like", "#bs.hitbox:internal/bat_like", "#bs.hitbox:internal/boat", + "#bs.hitbox:internal/camel", "#bs.hitbox:internal/cat_like", "#bs.hitbox:internal/cow_like", "#bs.hitbox:internal/falling_block_like", @@ -41,7 +42,6 @@ "minecraft:bee", "minecraft:blaze", "minecraft:breeze", - "minecraft:camel", "minecraft:cave_spider", "minecraft:chicken", "minecraft:cod", diff --git a/modules/bs.hitbox/gen_hitbox.py b/modules/bs.hitbox/gen_hitbox.py index 3fd6b4e65..4d68b73b1 100644 --- a/modules/bs.hitbox/gen_hitbox.py +++ b/modules/bs.hitbox/gen_hitbox.py @@ -1,11 +1,17 @@ -from collections import defaultdict +from __future__ import annotations -from beet import Context, LootTable +from collections import defaultdict +from typing import TYPE_CHECKING -from bookshelf.definitions import MC_VERSIONS from bookshelf.models import Block, StatePredicate, StateValue, VoxelShape from bookshelf.services import minecraft +if TYPE_CHECKING: + from collections.abc import Iterable + + from beet import Context, LootTable, PackFile + + CUBE = ((0.0, 0.0, 0.0, 16.0, 16.0, 16.0),) INTANGIBLE = [ "minecraft:light", @@ -13,10 +19,11 @@ ] -def beet_default(ctx: Context) -> None: +@minecraft.generator +def beet_default(ctx: Context, version: str) -> Iterable[tuple[str, PackFile]]: """Generate files used by the bs.hitbox module.""" - namespace = ctx.directory.name - blocks = minecraft.get_blocks(ctx, MC_VERSIONS[-1]) + ns = ctx.directory.name + blocks = minecraft.get_blocks(ctx.cache, version) groups = {"shape": defaultdict(list), "collision": defaultdict(list)} seen = set() @@ -28,12 +35,10 @@ def beet_default(ctx: Context) -> None: for shape in (block.shape, block.collision_shape): if isinstance(shape, StatePredicate) and shape.group not in seen: seen.add(shape.group) - loot_table = make_loot_table_state(shape) - ctx.generate(f"{namespace}:block/{shape.group}", render=loot_table) + yield f"{ns}:block/{shape.group}", make_loot_table_state(shape) for name, mapping in groups.items(): - loot_table = make_shape_loot_table(mapping, f"{namespace}:block") - ctx.generate(f"{namespace}:block/get_{name}", render=loot_table) + yield f"{ns}:block/get_{name}", make_shape_loot_table(mapping, f"{ns}:block") for name, predicate in [ ("has_shape_offset", lambda b: b.has_shape_offset), @@ -44,8 +49,8 @@ def beet_default(ctx: Context) -> None: ("is_waterloggable", lambda b: any(p.name == "waterlogged" for p in b.properties)), ]: - if tag := ctx.data.block_tags.get(f"{namespace}:{name}"): - tag.merge(minecraft.make_block_tag(blocks, predicate)) + base = ctx.data.block_tags[location := f"{ns}:{name}"] + yield location, minecraft.make_block_tag(base, blocks, predicate) def make_shape_loot_table( diff --git a/modules/bs.hitbox/module.json b/modules/bs.hitbox/module.json index 0fe68d5c5..ec2c63e3c 100644 --- a/modules/bs.hitbox/module.json +++ b/modules/bs.hitbox/module.json @@ -2,14 +2,23 @@ "extend": "../config.json", "data_pack": { "name": "bs.hitbox", - "load": "." + "load": ".", + "overlays": [ + { + "directory": "1.21.10", + "min_format": 88, + "max_format": 88 + } + ] }, "meta": { "name": "Hitbox", "slug": "bookshelf-hitbox", "description": "Bookshelf module for retrieving and checking the hitboxes of blocks and entities.", "documentation": "https://docs.mcbookshelf.dev/en/latest/modules/hitbox.html", - "tags": ["runtime"], + "tags": [ + "runtime" + ], "weak_dependencies": [ "bs.log" ] diff --git a/modules/bs.position/data/bs.position/function/get/rotation/all.mcfunction b/modules/bs.position/data/bs.position/function/get/rotation/all.mcfunction index ec630aed5..ee839d076 100644 --- a/modules/bs.position/data/bs.position/function/get/rotation/all.mcfunction +++ b/modules/bs.position/data/bs.position/function/get/rotation/all.mcfunction @@ -13,6 +13,7 @@ # For more details, refer to the MPL v2.0. # ------------------------------------------------------------------------------------------------------------ +execute in minecraft:overworld run tp B5-0-0-0-1 -30000000 0 1600 0 0 execute in minecraft:overworld run tp B5-0-0-0-1 -30000000 0 1600 ~ ~ $execute store result score @s bs.rot.h run data get entity B5-0-0-0-1 Rotation[0] $(scale) $execute store result score @s bs.rot.v run data get entity B5-0-0-0-1 Rotation[1] $(scale) diff --git a/modules/bs.position/data/bs.position/function/get/rotation/h.mcfunction b/modules/bs.position/data/bs.position/function/get/rotation/h.mcfunction index 1c3b859f7..d419e7b43 100644 --- a/modules/bs.position/data/bs.position/function/get/rotation/h.mcfunction +++ b/modules/bs.position/data/bs.position/function/get/rotation/h.mcfunction @@ -13,5 +13,6 @@ # For more details, refer to the MPL v2.0. # ------------------------------------------------------------------------------------------------------------ +execute in minecraft:overworld run tp B5-0-0-0-1 -30000000 0 1600 0 0 execute in minecraft:overworld run tp B5-0-0-0-1 -30000000 0 1600 ~ ~ $execute store result score @s bs.rot.h run data get entity B5-0-0-0-1 Rotation[0] $(scale) diff --git a/modules/bs.position/data/bs.position/function/get/rotation/v.mcfunction b/modules/bs.position/data/bs.position/function/get/rotation/v.mcfunction index 2e38c52cc..fe6d6cea2 100644 --- a/modules/bs.position/data/bs.position/function/get/rotation/v.mcfunction +++ b/modules/bs.position/data/bs.position/function/get/rotation/v.mcfunction @@ -13,5 +13,6 @@ # For more details, refer to the MPL v2.0. # ------------------------------------------------------------------------------------------------------------ +execute in minecraft:overworld run tp B5-0-0-0-1 -30000000 0 1600 0 0 execute in minecraft:overworld run tp B5-0-0-0-1 -30000000 0 1600 ~ ~ $execute store result score @s bs.rot.v run data get entity B5-0-0-0-1 Rotation[1] $(scale) diff --git a/modules/bs.string/gen_string.py b/modules/bs.string/gen_string.py index 2da9ae9b7..1cf98db09 100644 --- a/modules/bs.string/gen_string.py +++ b/modules/bs.string/gen_string.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from beet import Context, Function LOWER_TO_UPPER = {chr(c): chr(c).upper() for c in range(0x110000) if chr(c).islower()} diff --git a/modules/config.json b/modules/config.json index ab6395bef..cadfdb3c0 100644 --- a/modules/config.json +++ b/modules/config.json @@ -1,5 +1,7 @@ { "pipeline": [ + "bookshelf.plugins.setup_templates", + "bookshelf.plugins.minify_json", "bookshelf.plugins.include_deps", "bookshelf.plugins.update_tags", "bookshelf.plugins.inject_import", diff --git a/pyproject.toml b/pyproject.toml index 531306682..38f758128 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,10 +10,10 @@ authors = [{ name = "Bookshelf Contributors", email = "contact@gunivers.net" }] license = { text = "MPL-2.0" } requires-python = ">=3.12" dependencies = [ - "beet>=0.112.0", + "beet>=0.112.2", "frozendict>=2.4.7", "orjson>=3.11.4", - "pydantic>=2.12.4", + "pydantic>=2.12.5", "semver>=3.0.4", ] @@ -29,7 +29,7 @@ examples = "bookshelf.commands.examples:examples" [dependency-groups] dev = [ - "click>=8.3.0", + "click>=8.3.1", "httpx>=0.28.1", "lectern>=0.34.0", "platformdirs>=4.5.0", diff --git a/src/bookshelf/commands/examples.py b/src/bookshelf/commands/examples.py index 7c250e735..833e06bb5 100644 --- a/src/bookshelf/commands/examples.py +++ b/src/bookshelf/commands/examples.py @@ -1,19 +1,15 @@ from __future__ import annotations from pathlib import Path -from typing import TYPE_CHECKING import click from bookshelf.common.logging import summarize_logs from bookshelf.common.termui import track from bookshelf.common.utils import watch_and_run -from bookshelf.definitions import BUILD_DIR, EXAMPLES, EXAMPLES_DIR, MODULES_DIR +from bookshelf.definitions import BUILD_DIR, EXAMPLES, EXAMPLES_DIR from bookshelf.services import builder -if TYPE_CHECKING: - from collections.abc import Sequence - original_read_text = Path.read_text def utf8_safe_read_text( @@ -33,14 +29,15 @@ def examples() -> None: @examples.command() -@click.argument("examples", nargs=-1) +@click.argument("examples", default=EXAMPLES, nargs=-1) def build(examples: tuple[str, ...]) -> None: """Build the specified examples.""" with summarize_logs("🔨 BUILDING EXAMPLES…"): - build_examples( - examples or EXAMPLES, - builder.BuildOptions(output=BUILD_DIR), - ) + entries = track((f"Build example [green]{e}", e) for e in examples) + builder.ExampleBuilder( + require=["bookshelf.plugins.build_pack"], + meta={"build": {"output": BUILD_DIR}}, + ).build(entries) @examples.command() @@ -48,19 +45,10 @@ def build(examples: tuple[str, ...]) -> None: def watch(examples: tuple[str, ...]) -> None: """Watch for changes in specified examples and rebuild them.""" with summarize_logs("👀 WATCHING EXAMPLES…"): - watch_and_run( - build_examples, - [MODULES_DIR, EXAMPLES_DIR], - examples or EXAMPLES, - builder.BuildOptions(output=BUILD_DIR), - ) - - -def build_examples(examples: Sequence[str], options: builder.BuildOptions) -> None: - """Run the build for each example, reporting progress to the user.""" - builder.clean_links() - for build, example in track(( - f"Build example [green]{example}", - (builder.build_example, example), - ) for example in examples): - build(example, options) + def run() -> None: + entries = track((f"Build example [green]{e}", e) for e in examples) + builder.ExampleBuilder( + require=["bookshelf.plugins.build_pack"], + meta={"build": {"output": BUILD_DIR}}, + ).build(entries) + watch_and_run(run, EXAMPLES_DIR) diff --git a/src/bookshelf/commands/modules.py b/src/bookshelf/commands/modules.py index ddd800952..9f288595a 100644 --- a/src/bookshelf/commands/modules.py +++ b/src/bookshelf/commands/modules.py @@ -3,19 +3,22 @@ import asyncio from pathlib import Path from tempfile import TemporaryDirectory -from typing import TYPE_CHECKING import click from bookshelf.common.logging import summarize_logs from bookshelf.common.termui import track from bookshelf.common.utils import watch_and_run -from bookshelf.definitions import BUILD_DIR, BUNDLES, MC_VERSIONS, MODULES, MODULES_DIR +from bookshelf.definitions import ( + BUILD_DIR, + BUNDLES, + MC_VERSIONS, + MODULES, + MODULES_DIR, + RELEASE_DIR, +) from bookshelf.services import builder, packtest, publishers -if TYPE_CHECKING: - from collections.abc import Sequence - @click.group() def modules() -> None: @@ -23,48 +26,44 @@ def modules() -> None: @modules.command() -@click.argument("modules", nargs=-1) +@click.argument("modules", default=MODULES, nargs=-1) def build(modules: tuple[str, ...]) -> None: """Build the specified modules.""" with summarize_logs("🔨 BUILDING MODULES…"): - build_modules( - modules or MODULES, - builder.BuildOptions( - require=["bookshelf.plugins.include_tests"], - output=BUILD_DIR, - ), - ) + entries = track((f"Build module [green]{m}", m) for m in modules) + builder.ModuleBuilder( + require=["bookshelf.plugins.build_pack"], + meta={"build": {"output": BUILD_DIR}}, + ).build(entries) @modules.command() -@click.argument("modules", nargs=-1) +@click.argument("modules", default=MODULES, nargs=-1) def watch(modules: tuple[str, ...]) -> None: """Watch for changes in specified modules and rebuild them.""" with summarize_logs("👀 WATCHING MODULES…"): - watch_and_run( - build_modules, - [MODULES_DIR], - modules or MODULES, - builder.BuildOptions( - require=["bookshelf.plugins.include_tests"], - output=BUILD_DIR, - ), - ) + def run() -> None: + entries = track((f"Build module [green]{m}", m) for m in modules) + builder.ModuleBuilder( + require=["bookshelf.plugins.build_pack"], + meta={"build": {"output": BUILD_DIR}}, + ).build(entries) + watch_and_run(run, MODULES_DIR) @modules.command() def release() -> None: """Build and release zipped modules.""" - packs = [] with summarize_logs("🔨 BUILDING MODULES…", exit_on_errors=True): - build_modules([*BUNDLES, *MODULES], builder.BuildOptions( - zipped=True, + packs = [] + entries = track((f"Build module [green]{m}", m) for m in [*BUNDLES, *MODULES]) + builder.ModuleBuilder( require=["bookshelf.plugins.release_pack"], - meta={ - "autosave": {"link": False}, - "publish": lambda spec: packs.append(spec), - }, - )) + meta={"release": { + "output": RELEASE_DIR, + "enqueue": lambda spec: packs.append(spec), + }, "versions": MC_VERSIONS}, + ).build(entries) with summarize_logs("🚀 PUBLISHING MODULES…", exit_on_errors=True): for publish in track([ @@ -75,46 +74,36 @@ def release() -> None: @modules.command() -@click.argument("modules", nargs=-1) +@click.argument("modules", default=MODULES, nargs=-1) @click.option("--versions", is_flag=True) def test(modules: tuple[str, ...], *, versions: bool) -> None: """Build and test modules.""" with TemporaryDirectory() as directory: + output = Path(directory) + with summarize_logs("🔨 BUILDING MODULES…", exit_on_errors=True): - build_modules( - modules or MODULES, - builder.BuildOptions( - require=["bookshelf.plugins.include_tests"], - meta={"autosave": {"link": False}}, - output=(output := Path(directory)), - zipped=True, - ), - ) + entries = track((f"Build module [green]{m}", m) for m in modules) + builder.ModuleBuilder( + require=["bookshelf.plugins.build_pack"], + meta={"build": { + "output": output, + "link": False, + }, "versions": MC_VERSIONS}, + zipped=True, + ).build(entries) with summarize_logs("🔬 TESTING MODULES…", exit_on_errors=True): - mc_versions = reversed(MC_VERSIONS) if versions else MC_VERSIONS[-1:] - asyncio.run(test_modules(output, list(mc_versions))) - - -def build_modules(modules: Sequence[str], options: builder.BuildOptions) -> None: - """Run the build for each module, reporting progress to the user.""" - builder.clean_links() - for build, module in track(( - f"Build module [green]{module}", - (builder.build_module, module), - ) for module in modules): - build(module, options) - - -async def test_modules(datapacks: Path, mc_versions: list[str]) -> None: - """Run tests for the given datapacks on specified Minecraft versions.""" - coros = [packtest.run(datapacks, v) for v in mc_versions] - tasks = [asyncio.create_task(coro) for coro in coros] - - for task in track( - (f"Test version [green]{v}[/green]", process) - for v, process in zip(mc_versions, tasks, strict=True) - ): - logs = await task - for event in logs: - event.log() + async def test_modules() -> None: + vers = list(reversed(MC_VERSIONS)) if versions else MC_VERSIONS[-1:] + coros = [packtest.run(output, v) for v in vers] + tasks = [asyncio.create_task(coro) for coro in coros] + + for task in track( + (f"Test version [green]{v}[/green]", t) + for v, t in zip(vers, tasks, strict=True) + ): + logs = await task + for event in logs: + event.log() + + asyncio.run(test_modules()) diff --git a/src/bookshelf/common/json.py b/src/bookshelf/common/json.py index 1d95b5881..863b13e57 100644 --- a/src/bookshelf/common/json.py +++ b/src/bookshelf/common/json.py @@ -1,16 +1,13 @@ from __future__ import annotations from functools import cache -from typing import TYPE_CHECKING +from pathlib import Path import orjson from pydantic import BaseModel from bookshelf.common import errors, utils -if TYPE_CHECKING: - from pathlib import Path - def dump(file: Path, data: BaseModel | dict | list, indent: int | None = 2) -> int: """Write data to a JSON file using orjson.""" @@ -19,9 +16,14 @@ def dump(file: Path, data: BaseModel | dict | list, indent: int | None = 2) -> i return file.write_bytes(contents) -def load[T: BaseModel | dict | list](file: Path, expected_type: type[T]) -> T: - """Load a JSON file and return its content.""" - data = _parse_json(file) +def load[T: BaseModel | dict | list](obj: bytes | Path, expected_type: type[T]) -> T: + """Load JSON from bytes or a file path and return its content.""" + if isinstance(obj, Path): + data = _parse_file(obj) + file = obj + else: + data = _parse_bytes(obj) + file = None if isinstance(data, expected_type): return data if isinstance(expected_type, type) and issubclass(expected_type, BaseModel): @@ -35,11 +37,16 @@ def _orjson_default(obj: object) -> object: raise TypeError +def _parse_bytes(data: bytes) -> dict | list: + try: + return orjson.loads(data) + except orjson.JSONDecodeError as e: + raise errors.JSONDecodeError(message=str(e), line=e.lineno) from e + @cache -def _parse_json(file: Path) -> dict | list: +def _parse_file(file: Path) -> dict | list: try: - raw = file.read_bytes() - return orjson.loads(raw) + return orjson.loads(file.read_bytes()) except FileNotFoundError as e: raise errors.JSONFileNotFoundError(file=file) from e except orjson.JSONDecodeError as e: diff --git a/src/bookshelf/common/utils.py b/src/bookshelf/common/utils.py index e122b5edd..9a0e81deb 100644 --- a/src/bookshelf/common/utils.py +++ b/src/bookshelf/common/utils.py @@ -14,7 +14,7 @@ from bookshelf.definitions import GITHUB_REPO, ROOT_DIR, VERSION if TYPE_CHECKING: - from collections.abc import Callable, Sequence + from collections.abc import Callable def locate_command(command: str) -> str: @@ -32,11 +32,7 @@ def raw_github_url(file: Path, version: str = VERSION) -> str: return f"https://raw.githubusercontent.com/{GITHUB_REPO}/refs/tags/v{version}/{slug}" -def validate_model[T: BaseModel]( - file: Path, - model: type[T], - obj: object, -) -> T: +def validate_model[T: BaseModel](file: Path | None, model: type[T], obj: object) -> T: """Validate a Model with custom errors.""" try: return model.model_validate(obj) @@ -54,15 +50,10 @@ def validate_model[T: BaseModel]( raise errors.BookshelfCompositeError(message, errs) from e -def watch_and_run[**P, R]( - run: Callable[P, R], - paths: Sequence[Path], - *args: P.args, - **kwargs: P.kwargs, -) -> None: +def watch_and_run(run: Callable[[], None], *paths: Path) -> None: """Run a callable once, then re-run it on file changes in the given paths.""" console = Console() - run(*args, **kwargs) + run() for changes in watch(*paths): with error_handler(format_padding=1): count = len(changes) @@ -72,4 +63,4 @@ def watch_and_run[**P, R]( f"{count} change{'s' if count != 1 else ''} detected " f"[bright_black]({', '.join(filenames)})[/bright_black]", ) - run(*args, **kwargs) + run() diff --git a/src/bookshelf/models/collection.py b/src/bookshelf/models/collection.py index 98f49546f..bec6ee840 100644 --- a/src/bookshelf/models/collection.py +++ b/src/bookshelf/models/collection.py @@ -21,3 +21,7 @@ def __iter__(self) -> Iterator[T]: def __len__(self) -> int: """Return the number of models in the collection.""" return len(self.root) + + def __hash__(self) -> int: + """Hash based on the collection items.""" + return hash(tuple(self.root)) diff --git a/src/bookshelf/models/minecraft.py b/src/bookshelf/models/minecraft.py index 743ec2a46..520bfafc6 100644 --- a/src/bookshelf/models/minecraft.py +++ b/src/bookshelf/models/minecraft.py @@ -27,6 +27,10 @@ class Biome(BaseModel, frozen=True): temperature: float has_precipitation: bool = True + def __hash__(self) -> int: + """Hash based on the biome ID.""" + return hash(self.type) + class Block(BaseModel, frozen=True): """Represents a Minecraft block.""" @@ -52,6 +56,10 @@ class Block(BaseModel, frozen=True): is_conductive: StateValue[bool] = False is_spawnable: StateValue[bool] = False + def __hash__(self) -> int: + """Hash based on the block ID.""" + return hash(self.type) + class StatePredicate[T](BaseModel, frozen=True): """Predicate over a block's state.""" diff --git a/src/bookshelf/plugins/build_pack.py b/src/bookshelf/plugins/build_pack.py new file mode 100644 index 000000000..7d1273b22 --- /dev/null +++ b/src/bookshelf/plugins/build_pack.py @@ -0,0 +1,38 @@ + +from __future__ import annotations + +from pathlib import Path # noqa: TC003 +from typing import TYPE_CHECKING + +from beet import Context, PluginOptions, configurable +from beet.contrib.link import LinkManager + +if TYPE_CHECKING: + from collections.abc import Generator + + +class BuildOptions(PluginOptions): + """Options for building packs.""" + + output: Path + tests: bool = True + link: bool = True + + +@configurable("build", validator=BuildOptions) +def beet_default(ctx: Context, opts: BuildOptions) -> Generator: + """Plugin that outputs the data pack and the resource pack in a local directory.""" + if opts.tests: + ctx.require("bookshelf.plugins.include_tests") + yield + + link = ctx.inject(LinkManager) + for pack, link_directory in zip( + ctx.packs, + [link.resource_pack, link.data_pack], + strict=True, + ): + if pack: + pack.save(opts.output, overwrite=True) + if opts.link and link_directory: + link.dirty.append(str(pack.save(link_directory, overwrite=True))) diff --git a/src/bookshelf/plugins/gen_help.py b/src/bookshelf/plugins/gen_help.py index 8b1527697..36ae6e017 100644 --- a/src/bookshelf/plugins/gen_help.py +++ b/src/bookshelf/plugins/gen_help.py @@ -5,7 +5,6 @@ def beet_default(ctx: Context) -> None: """Generate a __help__ function with its tag for the current module.""" - ctx.require("bookshelf.plugins.setup_templates") with ctx.override(generate_namespace=ctx.directory.name): ctx.generate( "__help__", diff --git a/src/bookshelf/plugins/gen_load.py b/src/bookshelf/plugins/gen_load.py index 0f3ab5b1d..1a665a527 100644 --- a/src/bookshelf/plugins/gen_load.py +++ b/src/bookshelf/plugins/gen_load.py @@ -1,14 +1,18 @@ from __future__ import annotations -from beet import Context, Function, FunctionTag +import re + +from beet import Context, Function, FunctionTag, TestEnvironment from semver import Version from bookshelf.definitions import MODULES, VERSION +from bookshelf.plugins.include_tests import TestFunction + +PERSISTENT_ENTITY_UUID = re.compile(r"B5-0-0-0-\d+") def beet_default(ctx: Context) -> None: """Generate load-related functions and tags for the current module.""" - ctx.require("bookshelf.plugins.setup_templates") version = Version.parse(VERSION) module = ctx.directory.name[3:] @@ -51,6 +55,36 @@ def beet_default(ctx: Context) -> None: ctx.meta.get("weak_dependencies", []) or [], ) + environment = f"# @environment bs.load:{module}\n" + # Render header once and compute offset for inserting environment tag + header = ctx.template.render("bookshelf/header.jinja") + offset = len(header) + count = 0 + + # Insert environment tag after header for all test files + for _, file in ctx.data.all(extend=TestFunction): + count += 1 + file.set_content(f"{file.text[:offset]}{environment}{file.text[offset:]}") + + if count > 0: + load = ctx.data.functions.get(f"{ctx.data.name}:__load__") + load_content = load.get_content() if load is not None else [] + entities = {m for i in load_content for m in PERSISTENT_ENTITY_UUID.findall(i)} + # Create a function that loads the test module with some setup commands + ctx.data.functions[f"bs.load:v{VERSION}/test/{module}"] = Function([ + header, + "function #bs.load:unload", + f"function #bs.load:module/{module}", + "", + "forceload add 0 0", + *(f"await entity {entity}" for entity in entities), + ]) + # Register the test environment to run the setup function + ctx.data.test_environments[f"bs.load:{module}"] = TestEnvironment({ + "type": "minecraft:function", + "setup": f"bs.load:v{VERSION}/test/{module}", + }) + def gen_load_tag(modules: list[str]) -> FunctionTag: """Generate a tag to load all modules.""" diff --git a/src/bookshelf/plugins/include_tests.py b/src/bookshelf/plugins/include_tests.py index 3f19be587..0a9755c71 100644 --- a/src/bookshelf/plugins/include_tests.py +++ b/src/bookshelf/plugins/include_tests.py @@ -1,66 +1,17 @@ from __future__ import annotations -import re -from typing import TYPE_CHECKING, ClassVar +from typing import ClassVar -from beet import ( - Context, - Function, - JsonFile, - NamespaceFile, - NamespaceFileScope, - TestEnvironment, -) +from beet import Context, NamespaceFile, NamespaceFileScope, TextFileBase -from bookshelf.definitions import VERSION -if TYPE_CHECKING: - from collections.abc import Generator - - -PERSISTENT_ENTITY_UUID = re.compile(r"B5-0-0-0-\d+") - - -class TestFunction(JsonFile, NamespaceFile): +class TestFunction(TextFileBase, NamespaceFile): """Represents a PackTest Minecraft function file.""" scope: ClassVar[NamespaceFileScope] = ("test",) extension: ClassVar[str] = ".mcfunction" -def beet_default(ctx: Context) -> Generator: +def beet_default(ctx: Context) -> None: """Include test functions from the test folder.""" - ctx.require("bookshelf.plugins.setup_templates") ctx.data.extend_namespace.append(TestFunction) - yield - - module = ctx.directory.name[3:] - environment = f"# @environment bs.load:{module}\n" - # Render header once and compute offset for inserting environment tag - header = ctx.template.render("bookshelf/header.jinja") - offset = len(header) - count = 0 - - # Insert environment tag after header for all test files - for _, file in ctx.data.all(extend=TestFunction): - count += 1 - file.set_content(f"{file.text[:offset]}{environment}{file.text[offset:]}") - - if count > 0: - load = ctx.data.functions.get(f"{ctx.data.name}:__load__") - load_content = load.get_content() if load is not None else [] - entities = {m for i in load_content for m in PERSISTENT_ENTITY_UUID.findall(i)} - # Create a function that loads the test module with some setup commands - ctx.data.functions[f"bs.load:v{VERSION}/test/{module}"] = Function([ - header, - "function #bs.load:unload", - f"function #bs.load:module/{module}", - "", - "forceload add 0 0", - *(f"await entity {entity}" for entity in entities), - ]) - # Register the test environment to run the setup function - ctx.data.test_environments[f"bs.load:{module}"] = TestEnvironment({ - "type": "minecraft:function", - "setup": f"bs.load:v{VERSION}/test/{module}", - }) diff --git a/src/bookshelf/plugins/inject_logger.py b/src/bookshelf/plugins/inject_logger.py index a364fe803..8e553460c 100644 --- a/src/bookshelf/plugins/inject_logger.py +++ b/src/bookshelf/plugins/inject_logger.py @@ -10,7 +10,6 @@ def beet_default(ctx: Context) -> Generator: """Inject a command to register the module in the bookshelf log namespace.""" - ctx.require("bookshelf.plugins.setup_templates") yield # Only add if the module depends on bs.log to avoid extra noise if "bs.log" in [ diff --git a/src/bookshelf/plugins/make_bundle.py b/src/bookshelf/plugins/make_bundle.py index a12549cec..53a613a71 100644 --- a/src/bookshelf/plugins/make_bundle.py +++ b/src/bookshelf/plugins/make_bundle.py @@ -4,7 +4,7 @@ from functools import cache from typing import TYPE_CHECKING -from beet import Context, subproject +from beet import Context, PngFile, subproject from bookshelf.definitions import MODULES, MODULES_DIR @@ -17,8 +17,12 @@ def beet_default(ctx: Context) -> None: """Include all modules.""" for mod in MODULES: - config = {"directory": f"{MODULES_DIR}/{mod}", "extend": "module.json"} - ctx.require(subproject(config)) + ctx.require(subproject({ + "directory": f"{MODULES_DIR}/{mod}", + "extend": "module.json", + "meta": ctx.meta, + })) + ctx.data.icon = PngFile(source_path=ctx.directory / "pack.png") @cache @@ -28,6 +32,10 @@ def plugin(ctx: Context) -> None: for mod in MODULES: meta = json.loads((MODULES_DIR / mod / "module.json").read_text("utf-8")) if tag in meta.get("meta", {}).get("tags", []): - config = {"directory": f"{MODULES_DIR}/{mod}", "extend": "module.json"} - ctx.require(subproject(config)) + ctx.require(subproject({ + "directory": f"{MODULES_DIR}/{mod}", + "extend": "module.json", + "meta": ctx.meta, + })) + ctx.data.icon = PngFile(source_path=ctx.directory / "pack.png") return plugin diff --git a/src/bookshelf/plugins/minify_json.py b/src/bookshelf/plugins/minify_json.py new file mode 100644 index 000000000..017489190 --- /dev/null +++ b/src/bookshelf/plugins/minify_json.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import orjson +from beet import JsonFileBase + +if TYPE_CHECKING: + from collections.abc import Generator + + from beet import Context + + +def beet_default(ctx: Context) -> Generator: + """Configure beet for building Bookshelf modules.""" + yield + for pack in ctx.packs: + for _, file in pack.list_files(extend=JsonFileBase): + file.encoder = lambda obj: orjson.dumps(obj).decode() + file.decoder = lambda obj: orjson.loads(obj.encode()) + file.ensure_serialized() diff --git a/src/bookshelf/plugins/release_pack.py b/src/bookshelf/plugins/release_pack.py index 3e0c62b38..d03a85a02 100644 --- a/src/bookshelf/plugins/release_pack.py +++ b/src/bookshelf/plugins/release_pack.py @@ -1,42 +1,53 @@ from __future__ import annotations import re +from collections.abc import Callable # noqa: TC003 +from pathlib import Path # noqa: TC003 from typing import TYPE_CHECKING -from bookshelf.definitions import DOC_DIR, MC_VERSIONS, RELEASE_DIR, VERSION +from beet import Context, PluginOptions, configurable + +from bookshelf.definitions import DOC_DIR, MC_VERSIONS, VERSION from bookshelf.services.publishers import PublishSpec if TYPE_CHECKING: from collections.abc import Generator - from beet import Context +class ReleaseOptions(PluginOptions): + """Options for the release pack plugin.""" + + output: Path + enqueue: Callable[[PublishSpec], None] -def beet_default(ctx: Context) -> Generator: + +@configurable("release", validator=ReleaseOptions) +def beet_default(ctx: Context, opts: ReleaseOptions) -> Generator: """Build pack to output then attempt to publish them to platforms.""" yield - if pack := ctx.data or ctx.assets: + for pack in list(filter(None, ctx.packs)): pack.name = f"{pack.name}-{MC_VERSIONS[-1]}-v{VERSION}" - file = pack.save(RELEASE_DIR, overwrite=True) + file = pack.save(opts.output, overwrite=True, zipped=True) - publish = ctx.meta["publish"] - publish(PublishSpec( + opts.enqueue(PublishSpec( file=file, name=f"Bookshelf {ctx.meta.get('name', '')}".strip(), kind="datapack" if ctx.data else "resourcepack", slug=ctx.meta["slug"], icon=ctx.directory / "pack.png", readme=ctx.directory / "README.md", - changelog=create_specialized_changelog(ctx.directory.name), + changelog=make_changelog(ctx.directory.name), description=ctx.meta.get("description", ""), documentation=ctx.meta.get("documentation", ""), )) -def create_specialized_changelog(module: str) -> str: +def make_changelog(module: str) -> str: """Create a changelog specific to the given module.""" - changelog = (DOC_DIR / f"changelog/v{VERSION}.md").read_text("utf-8") + file = DOC_DIR / f"changelog/v{VERSION}.md" + changelog = file.read_text("utf-8") sections = re.split(r"(###.*?)$", changelog, flags=re.MULTILINE) + return sections.pop(0) + "".join( "".join(sections[i:i+2]) for i in range(0, len(sections) - 1, 2) diff --git a/src/bookshelf/plugins/update_mcmeta.py b/src/bookshelf/plugins/update_mcmeta.py index de577a242..a8faf6e72 100644 --- a/src/bookshelf/plugins/update_mcmeta.py +++ b/src/bookshelf/plugins/update_mcmeta.py @@ -14,35 +14,29 @@ VERSION_META = "https://raw.githubusercontent.com/misode/mcmeta/refs/tags/{}-summary/version.json" -class VersionRange(TypedDict): - """Range of supported pack format versions.""" - - min_inclusive: int - max_inclusive: int - - class SupportedFormats(TypedDict): """Supported pack formats for data and assets.""" - data: VersionRange - assets: VersionRange + data: int + assets: int def beet_default(ctx: Context) -> Generator: """Set the pack format for the module based on supported Minecraft versions.""" yield # Retrieve supported data and asset pack formats based on MC versions - formats = get_supported_formats(ctx, MC_VERSIONS) + min_formats = get_supported_formats(ctx, MC_VERSIONS[0]) + max_formats = get_supported_formats(ctx, MC_VERSIONS[-1]) # Set metadata for resource pack - ctx.assets.description = ctx.meta["description"] - ctx.assets.min_format = formats["assets"]["min_inclusive"] - ctx.assets.max_format = formats["assets"]["max_inclusive"] + ctx.assets.description = ctx.meta.get("description") + ctx.assets.min_format = min_formats["assets"] + ctx.assets.max_format = max_formats["assets"] # Set metadata for data pack - ctx.data.description = ctx.meta["description"] - ctx.data.min_format = formats["data"]["min_inclusive"] - ctx.data.max_format = formats["data"]["max_inclusive"] + ctx.data.description = ctx.meta.get("description") + ctx.data.min_format = min_formats["data"] + ctx.data.max_format = max_formats["data"] # Ensure the mcmeta file includes the data pack id (Smithed convention) mcmeta = ctx.data.mcmeta @@ -52,25 +46,13 @@ def beet_default(ctx: Context) -> Generator: }) -def get_supported_formats(ctx: Context, versions: list[str]) -> SupportedFormats: - """Retrieve the supported formats for the given Minecraft versions.""" - # Download min version data - cache = ctx.cache[f"version/{versions[0]}"] - file = cache.download(VERSION_META.format(versions[0])) - min_version = json.load(file, dict) - - # Download max version data - cache = ctx.cache[f"version/{versions[-1]}"] - file = cache.download(VERSION_META.format(versions[-1])) - max_version = json.load(file, dict) +def get_supported_formats(ctx: Context, version: str) -> SupportedFormats: + """Retrieve the supported formats for the given Minecraft version.""" + cache = ctx.cache[f"version/{version}"] + file = cache.download(VERSION_META.format(version)) + versions = json.load(file, dict) return SupportedFormats( - data=VersionRange( - min_inclusive=min_version["data_pack_version"], - max_inclusive=max_version["data_pack_version"], - ), - assets=VersionRange( - min_inclusive=min_version["resource_pack_version"], - max_inclusive=max_version["resource_pack_version"], - ), + data=versions["data_pack_version"], + assets=versions["resource_pack_version"], ) diff --git a/src/bookshelf/services/builder.py b/src/bookshelf/services/builder.py index a327cccda..828ccd555 100644 --- a/src/bookshelf/services/builder.py +++ b/src/bookshelf/services/builder.py @@ -1,65 +1,75 @@ -# ruff: noqa: TC003 from __future__ import annotations -from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any -from beet import PackConfig, Project, ProjectBuilder, ProjectConfig -from beet.contrib.link import LinkManager +from beet import Context, PackConfig, Project, ProjectBuilder, ProjectConfig from pydantic import BaseModel, Field from bookshelf.definitions import EXAMPLES_DIR, MODULES_DIR, ROOT_DIR +if TYPE_CHECKING: + from collections.abc import Iterable -class BuildOptions(BaseModel): - """Configuration controlling how the build process operates.""" - pipeline: list[str] = Field(default_factory=list) +class BaseBuilder(BaseModel): + """Base class for build process implementations.""" + require: list[str] = Field(default_factory=list) + pipeline: list[str] = Field(default_factory=list) meta: dict[str, Any] = Field(default_factory=dict) - output: Path | None = None zipped: bool = False + def run(self, ctx: Context, entries: Iterable[str]) -> None: + """Run the Beet build pipeline for multiple entries.""" + raise NotImplementedError + + def build(self, entries: Iterable[str]) -> None: + """Run the builder for multiple entries.""" + project = Project(ProjectConfig().resolve(ROOT_DIR)) + with ProjectBuilder(project=project, root=True).build() as ctx: + self.run(ctx, entries) + + def make_pack_config(self, *, zipped: bool) -> PackConfig: + """Generate a Beet pack configuration with optional compression.""" + return PackConfig( + compression="deflate", + compression_level=9, + zipped=True, + ) if zipped else PackConfig() + + +class ModuleBuilder(BaseBuilder): + """Builder for modules.""" + + def run(self, _: Context, modules: Iterable[str]) -> None: + """Run the Beet build pipeline the given modules.""" + for module in modules: + with ProjectBuilder(Project(ProjectConfig( + extend="module.json", + data_pack=self.make_pack_config(zipped=self.zipped), + resource_pack=self.make_pack_config(zipped=self.zipped), + require=[*self.require, "bookshelf.plugins.update_mcmeta"], + pipeline=self.pipeline, + meta={**self.meta, "autosave": {"link": False}}, + ).resolve(MODULES_DIR / module))).build(): + pass + + +class ExampleBuilder(BaseBuilder): + """Builder for examples.""" -def clean_links() -> None: - """Remove the previously linked files and folders.""" - project = Project(ProjectConfig().resolve(ROOT_DIR)) - LinkManager(project.cache).clean() - - -def make_pack_config(*, zipped: bool) -> PackConfig: - """Generate a Beet pack configuration with optional compression.""" - return PackConfig( - compression="deflate", - compression_level=9, - zipped=True, - ) if zipped else PackConfig() - - -def build_module(module: str, options: BuildOptions) -> None: - """Run the Beet build pipeline for a single module.""" - with ProjectBuilder(Project(ProjectConfig( - extend="module.json", # type: ignore[arg-type] - broadcast=[MODULES_DIR / module], # type: ignore[arg-type] - data_pack=make_pack_config(zipped=options.zipped), - resource_pack=make_pack_config(zipped=options.zipped), - pipeline=[*options.pipeline], - require=[*options.require, "bookshelf.plugins.update_mcmeta"], - meta=options.meta, - output=options.output, - ).resolve(ROOT_DIR)), root=False).build(): - pass - - -def build_example(example: str, options: BuildOptions) -> None: - """Run the Beet build pipeline for a single example.""" - with ProjectBuilder(Project(ProjectConfig( - broadcast=[EXAMPLES_DIR / f"{example}.md"], # type: ignore[arg-type] - data_pack=make_pack_config(zipped=options.zipped), - resource_pack=make_pack_config(zipped=options.zipped), - pipeline=[*options.pipeline, "lectern"], - require=[*options.require, "lectern.contrib.require"], - meta={**options.meta, "lectern": {"load": "."}}, - output=options.output, - ).resolve(ROOT_DIR)), root=False).build(): - pass + def run(self, _: Context, examples: Iterable[str]) -> None: + """Run the Beet build pipeline for a single example.""" + for example in examples: + with ProjectBuilder(Project(ProjectConfig( + data_pack=self.make_pack_config(zipped=self.zipped), + resource_pack=self.make_pack_config(zipped=self.zipped), + require=["lectern.contrib.require", *self.require], + pipeline=["lectern", *self.pipeline], + meta={ + **self.meta, + "autosave": {"link": False}, + "lectern": {"load": "."}, + }, + ).resolve(EXAMPLES_DIR / f"{example}.md"))).build(): + pass diff --git a/src/bookshelf/services/minecraft/__init__.py b/src/bookshelf/services/minecraft/__init__.py index b1ecbb11c..a693ac186 100644 --- a/src/bookshelf/services/minecraft/__init__.py +++ b/src/bookshelf/services/minecraft/__init__.py @@ -5,18 +5,18 @@ from .biome import get_biomes from .block import get_blocks from .utils import ( + generator, make_block_tag, - make_loot_table, make_loot_table_binary, make_loot_table_state, render_snbt, ) __all__ = [ + "generator", "get_biomes", "get_blocks", "make_block_tag", - "make_loot_table", "make_loot_table_binary", "make_loot_table_state", "render_snbt", diff --git a/src/bookshelf/services/minecraft/biome.py b/src/bookshelf/services/minecraft/biome.py index f252f999e..2e98850e0 100644 --- a/src/bookshelf/services/minecraft/biome.py +++ b/src/bookshelf/services/minecraft/biome.py @@ -1,23 +1,21 @@ from __future__ import annotations -from typing import TYPE_CHECKING +import httpx from bookshelf.common import json from bookshelf.models import Biome, Collection from .utils import cache_version -if TYPE_CHECKING: - from beet import Context - BIOMES_URL = "https://raw.githubusercontent.com/misode/mcmeta/{}-summary/data/worldgen/biome/data.min.json" @cache_version("biomes", Collection[Biome]) -def get_biomes(ctx: Context, version: str) -> Collection[Biome]: +def get_biomes(version: str) -> Collection[Biome]: """Retrieve biomes for the provided version.""" - cache = ctx.cache[f"version/{version}"] - raw = json.load(cache.download(BIOMES_URL.format(version)), dict) + response = httpx.get(BIOMES_URL.format(version)) + response.raise_for_status() + raw = json.load(response.content, dict) return Collection(root=[Biome( type=f"minecraft:{name}", diff --git a/src/bookshelf/services/minecraft/block.py b/src/bookshelf/services/minecraft/block.py index f82d6a045..8e502f316 100644 --- a/src/bookshelf/services/minecraft/block.py +++ b/src/bookshelf/services/minecraft/block.py @@ -6,6 +6,7 @@ from logging import getLogger from typing import TYPE_CHECKING, Any +import httpx from frozendict import frozendict from bookshelf.common import json @@ -24,18 +25,17 @@ if TYPE_CHECKING: from collections.abc import Callable - from beet import Context - logger = getLogger(__name__) BLOCKS_URL = "https://raw.githubusercontent.com/mcbookshelf/mcdata/refs/tags/v1/{}/blocks/data.min.json" @cache_version("blocks", Collection[Block]) -def get_blocks(ctx: Context, version: str) -> Collection[Block]: +def get_blocks(version: str) -> Collection[Block]: """Retrieve blocks for the provided version.""" - cache = ctx.cache[f"version/{version}"] - raw = json.load(cache.download(BLOCKS_URL.format(version)), dict) + response = httpx.get(BLOCKS_URL.format(version)) + response.raise_for_status() + raw = json.load(response.content, dict) blocks: list[dict[str, Any]] = [] groups: dict[tuple[tuple[str, tuple[str, ...]], ...], int] = {(): 0} @@ -61,6 +61,7 @@ def get_blocks(ctx: Context, version: str) -> Collection[Block]: "is_spawnable": _make_state_value("is_spawnable", states), }) + blocks.sort(key=lambda b: b["type"]) return Collection(root=_make_blocks(blocks)) diff --git a/src/bookshelf/services/minecraft/utils.py b/src/bookshelf/services/minecraft/utils.py index 4c15cdca4..d9a9a908b 100644 --- a/src/bookshelf/services/minecraft/utils.py +++ b/src/bookshelf/services/minecraft/utils.py @@ -3,18 +3,21 @@ import re from collections.abc import Callable, Mapping, Sequence from functools import singledispatch, wraps -from itertools import chain from typing import TYPE_CHECKING import orjson -from beet import BlockTag, LootTable +from beet import BlockTag, Context, JsonFileBase, LootTable, PackFile from pydantic import BaseModel from bookshelf.common import json +from bookshelf.definitions import MC_VERSIONS from bookshelf.models import Block, StateNode, StatePredicate +from bookshelf.plugins.update_mcmeta import get_supported_formats if TYPE_CHECKING: - from beet import Context + from collections.abc import Iterable + + from beet import ProjectCache def quote_snbt_key(k: str) -> str: @@ -77,35 +80,53 @@ def render_snbt_mapping(obj: Mapping) -> str: def cache_version[T: BaseModel | dict | list]( key: str, expected_type: type[T], -) -> Callable[[Callable[[Context, str], T]], Callable[[Context, str], T]]: - """Cache functions that accept a ctx and a version to a JSON file.""" - def decorator(func: Callable[[Context, str], T]) -> Callable[[Context, str], T]: +) -> Callable[[Callable[[str], T]], Callable[[ProjectCache, str], T]]: + """Cache functions that accept a version to a JSON file.""" + def decorator(func: Callable[[str], T]) -> Callable[[ProjectCache, str], T]: @wraps(func) - def wrapper(ctx: Context, version: str) -> T: - file = ctx.cache[f"version/{version}"].get_path(key).with_suffix(".json") + def wrapper(cache: ProjectCache, version: str) -> T: + file = cache[f"version/{version}"].get_path(key).with_suffix(".json") if file.is_file(): return json.load(file, expected_type) - json.dump(file, result := func(ctx, version), None) + json.dump(file, result := func(version), None) return result return wrapper return decorator +def generator( + func: Callable[[Context, str], Iterable[tuple[str, PackFile]]], +) -> Callable[[Context], None]: + """Wrap a beet generator with overlay/compatibility logic.""" + def decorator(ctx: Context) -> None: + seen = {} + for version in reversed(ctx.meta.get("versions", MC_VERSIONS[-1:])): + for location, file in func(ctx, version): + if isinstance(file, JsonFileBase): + file.encoder = lambda obj: orjson.dumps(obj).decode() + file.decoder = lambda obj: orjson.loads(obj.encode()) + if location not in seen: + seen[location] = file.ensure_serialized() + ctx.data[location] = file + elif seen[location] != file.ensure_serialized(): + seen[location] = file.ensure_serialized() + overlay = ctx.data.overlays[version] + min_formats = get_supported_formats(ctx, MC_VERSIONS[0]) + max_formats = get_supported_formats(ctx, version) + overlay.max_format = max_formats["data"] + overlay.min_format = min_formats["data"] + overlay[location] = file + return decorator + + def make_block_tag( + base: BlockTag, blocks: Sequence[Block], predicate: Callable[[Block], bool], - extras: list | None = None, ) -> BlockTag: - """Create a block tag for blocks that match the predicate.""" - values = chain(extras or [], (block.type for block in blocks if predicate(block))) - return BlockTag({"replace":True,"values":sorted(values)}) - - -def make_loot_table(content: dict) -> LootTable: - """Build an optimized loot table from a dict using orjson.""" - loot_table = LootTable(content) - loot_table.text = orjson.dumps(loot_table.data).decode("utf-8") - return loot_table + """Create or update a block tag for blocks that match the predicate.""" + values = sorted(block.type for block in blocks if predicate(block)) + return BlockTag({**base.data, "values": values}) def make_loot_table_binary[T]( @@ -133,7 +154,7 @@ def build_node(entries: Sequence[T]) -> dict: right.pop("conditions") return {"type":"alternatives","children":[left, right]} - return make_loot_table({"pools":[{"rolls":1,"entries":[build_node(entries)]}]}) + return LootTable({"pools":[{"rolls":1,"entries":[build_node(entries)]}]}) def make_loot_table_state[T]( @@ -159,4 +180,4 @@ def build_node(node: StateNode[T] | T) -> dict: return {"type":"alternatives","children":children} - return make_loot_table({"pools":[{"rolls":1,"entries":[build_node(entry.tree)]}]}) + return LootTable({"pools":[{"rolls":1,"entries":[build_node(entry.tree)]}]}) diff --git a/src/bookshelf/services/publishers/utils.py b/src/bookshelf/services/publishers/utils.py index e7ba22a87..b7534658c 100644 --- a/src/bookshelf/services/publishers/utils.py +++ b/src/bookshelf/services/publishers/utils.py @@ -1,7 +1,7 @@ from __future__ import annotations from logging import getLogger -from pathlib import Path +from pathlib import Path # noqa: TC003 from typing import TYPE_CHECKING, Literal from pydantic import BaseModel diff --git a/uv.lock b/uv.lock index 501f4a934..1575c2be2 100644 --- a/uv.lock +++ b/uv.lock @@ -70,7 +70,7 @@ wheels = [ [[package]] name = "beet" -version = "0.112.0" +version = "0.112.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -84,9 +84,9 @@ dependencies = [ { name = "toml" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1f/e9/2c890be2f96a4a05fe57b77dcfbf7de05a594ba6b9bd08493b4f31291800/beet-0.112.0.tar.gz", hash = "sha256:9732467adf7f33bc99077f58209dd41bd6e6fa18e13f3ae900f0be5994fcc86f", size = 93119, upload-time = "2025-10-15T22:16:49.766Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/bf/a7de57393f9d962dd93d5397c1025bdfe0d840e2df12dbf387da61468862/beet-0.112.2.tar.gz", hash = "sha256:fb007adb29df53a2300678cc9b79fc2922b36c685e2077f5832d740ec7fcb4c2", size = 93524, upload-time = "2025-12-01T22:39:24.142Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/cd/a08fdd765d848943cd2608aaa86cd5fad6300c4256aac654e144d3f9ea31/beet-0.112.0-py3-none-any.whl", hash = "sha256:3768c0f619ae8df1444067a0b869ea93b38d667dfb6b895e237e081971e94e36", size = 117871, upload-time = "2025-10-15T22:16:48.195Z" }, + { url = "https://files.pythonhosted.org/packages/86/83/c5d5b0574f82e2ce3af7616d3385862b98b6f94d8dcd894cef062f9a9b01/beet-0.112.2-py3-none-any.whl", hash = "sha256:270e98fb16ee7a28900d6544ae96d35b1a957ff9bd9077f7478aee58edcbfc85", size = 118341, upload-time = "2025-12-01T22:39:22.971Z" }, ] [[package]] @@ -157,14 +157,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.0" +version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4", size = 276943, upload-time = "2025-09-18T17:32:23.696Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/d3/9dcc0f5797f070ec8edf30fbadfb200e71d9db6b84d211e3b2085a7589a0/click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc", size = 107295, upload-time = "2025-09-18T17:32:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] @@ -417,16 +417,16 @@ docs = [ [package.metadata] requires-dist = [ - { name = "beet", specifier = ">=0.112.0" }, + { name = "beet", specifier = ">=0.112.2" }, { name = "frozendict", specifier = ">=2.4.7" }, { name = "orjson", specifier = ">=3.11.4" }, - { name = "pydantic", specifier = ">=2.12.4" }, + { name = "pydantic", specifier = ">=2.12.5" }, { name = "semver", specifier = ">=3.0.4" }, ] [package.metadata.requires-dev] dev = [ - { name = "click", specifier = ">=8.3.0" }, + { name = "click", specifier = ">=8.3.1" }, { name = "httpx", specifier = ">=0.28.1" }, { name = "lectern", specifier = ">=0.34.0" }, { name = "platformdirs", specifier = ">=4.5.0" }, @@ -640,7 +640,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.4" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -648,9 +648,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [[package]]