Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/changelog/v3.2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**.

- <abbr title="New Features">✨</abbr> **[#403](https://github.com/mcbookshelf/bookshelf/issues/403)** - Added the `#bs.load:status` function to view loaded modules.
- <abbr title="Enhancements">⚡</abbr> **[#501](https://github.com/mcbookshelf/bookshelf/pull/501)** - Optimized tp commands used in macros.
Expand All @@ -31,6 +31,7 @@ Bookshelf is now based on **Minecraft 1.21.9/10**.
- <abbr title="Deprecations">🗑️</abbr> **[#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.
- <abbr title="Deprecations">🗑️</abbr> **[#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.
- <abbr title="Deprecations">🗑️</abbr> **[#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.
- <abbr title="New Features">✨</abbr> **[#502](https://github.com/mcbookshelf/bookshelf/pull/502)** - Added new 1.21.11 entities.


### `🏃 bs.move`
Expand Down
2 changes: 1 addition & 1 deletion modules/bs.block/data/bs.block/test/get/block.mcfunction
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,]" }
Original file line number Diff line number Diff line change
Expand Up @@ -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" } }
Original file line number Diff line number Diff line change
Expand Up @@ -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" } }
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Original file line number Diff line number Diff line change
Expand Up @@ -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" } }
75 changes: 42 additions & 33 deletions modules/bs.block/gen_block.py
Original file line number Diff line number Diff line change
@@ -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),
Expand Down Expand Up @@ -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()
Expand All @@ -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:
Expand Down Expand Up @@ -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,
)),
}]}

Expand Down Expand Up @@ -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", [])))
Expand Down Expand Up @@ -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":[{
Expand All @@ -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],
}))
})
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -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)
44 changes: 28 additions & 16 deletions modules/bs.environment/gen_environment.py
Original file line number Diff line number Diff line change
@@ -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(
Expand All @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Loading
Loading