use_item_on: a placed block takes the state its item's block_state names

Vanilla's BlockItem.place runs updateBlockStateFromTag once the click chose the
placement state: it reads the item's minecraft:block_state component, applies
every named property the placed block has and sets the block again when the
state changed. That is how a Ctrl + Pick Block of a turned stair, a top slab, a
disabled hopper or a 4-tick repeater places the block it was copied from.

The placement code already does that (Game::apply_item_block_state); the parity
cases never carried a block_state component, so a regression there would have
gone unnoticed. Add block_state_case: twelve block and property pairs whose
state differs from what the click alone chooses, plus a property the block has
no such thing as and an empty map, which BlockItemStateProperties.apply skips.
This commit is contained in:
MSedgeMC committed 2026-11-02 17:09:36 +00:00
1 parent af48a5eb47
commit 8f97c29e8b
1 file changed
+37
+37
View File
@@ -331,6 +331,37 @@ def jukebox_case(rng, states, case):
'creative': True, 'sneaking': False, 'food': 20}
# A block placed from an item carrying `minecraft:block_state`: the block and the properties the
# component sets, none of them the ones the click would have chosen. The last two name a property the
# block has no such thing as, and no property at all, which `BlockItemStateProperties.apply` skips.
STATED = [('oak_stairs', '{half:"top",facing:"east",shape:"outer_right"}'),
('oak_slab', '{type:"top"}'),
('oak_log', '{axis:"x"}'),
('oak_trapdoor', '{half:"top",open:"true"}'),
('hopper', '{enabled:"false"}'),
('repeater', '{delay:"4",facing:"west",locked:"true"}'),
('comparator', '{mode:"subtract",powered:"true"}'),
('beehive', '{honey_level:"5"}'),
('chest', '{facing:"east"}'),
('composter', '{level:"6"}'),
('oak_fence_gate', '{in_wall:"true",open:"true"}'),
('oak_stairs', '{half:"sideways"}'),
('stone', '{facing:"east"}'),
('oak_stairs', '{}')]
def block_state_case(rng, states, case):
"""A block placed from an item that carries a `minecraft:block_state` component (Ctrl + Pick Block
of a turned stair, a half slab, a disabled hopper): `BlockItem.updateBlockStateFromTag` sets those
properties on the state `getStateForPlacement` chose, so the block ends up in the state the item
names. Ignoring the component leaves the state the click alone would have placed."""
case['cells'] = [[0, 0, 0, states.get(rng.choice(['stone', 'dirt', 'grass_block']))]]
block, props = rng.choice(STATED)
case['use_item'] = {'item': '{id:"minecraft:%s",count:1,components:{"minecraft:block_state":%s}}'
% (block, props),
'creative': rng.random() < 0.7, 'sneaking': rng.random() < 0.2, 'food': 20}
def cases(facts, count, seed):
rng = random.Random(seed)
states = d.States(facts)
@@ -392,6 +423,12 @@ def cases(facts, count, seed):
case['mode'] = 'dispense'
yield case
continue
if rng.random() < 0.06:
block_state_case(rng, states, case)
case['items'] = []
case['mode'] = 'dispense'
yield case
continue
cells = []
item = rng.choice(ITEMS)
if rng.random() < 0.15: