mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-08 08:07:53 -10:00
Merge pull request #1107 from ludovic111/fix/beehive-break-releases-bees
🤖 Agent: merged. Verify passes. Local integration of #1101–#1107 on main caeffffb: parity --changed 302/304, and the two failures (block_particles, entity_model) fail identically on plain main. CI parity's secure_chat failure on #1101 logged 'transcripts identical' (flaky).
This commit is contained in:
7 files changed
+81
-19
No files matched your search
@@ -812,6 +812,16 @@ impl LevelAccess for GameLevel<'_> {
|
||||
fn remove_block_entity(&mut self, pos: BlockPos, old: BlockStateId, side_effects: bool) -> u8 {
|
||||
self.remove_entity(pos, old, side_effects)
|
||||
}
|
||||
fn load_block_entity(&mut self, pos: BlockPos, mut nbt: horizon_world::nbt::Compound) {
|
||||
use horizon_world::nbt::Tag;
|
||||
for (k, v) in [("x", pos.x), ("y", pos.y), ("z", pos.z)] {
|
||||
nbt.insert(k.into(), Tag::Int(v));
|
||||
}
|
||||
if let Some((key, entity)) = horizon_client::save::decode_block_entity(&Tag::Compound(nbt))
|
||||
{
|
||||
self.world.block_entities.insert(key, entity);
|
||||
}
|
||||
}
|
||||
fn set_command_block_powered(&mut self, pos: BlockPos, powered: bool) -> Option<(bool, bool)> {
|
||||
command_blocks::set_powered(self.world, self.registry, (pos.x, pos.y, pos.z), powered)
|
||||
}
|
||||
|
||||
@@ -122,6 +122,17 @@ impl<L: LevelAccess + ?Sized> PlacementContext for LevelFeatureWorld<'_, '_, L>
|
||||
) -> Result<bool, PlacementQueryError> {
|
||||
Ok(self.sim.b.has_flag(state, state_flags::COLLISION_EMPTY))
|
||||
}
|
||||
/// BeehiveDecorator's bees go into the nest's block entity (the tree
|
||||
/// checked the nest is there).
|
||||
fn store_beehive_occupants(
|
||||
&mut self,
|
||||
position: [i32; 3],
|
||||
ticks_in_hive: &[i32],
|
||||
) -> Result<(), PlacementQueryError> {
|
||||
let nbt = crate::worldgen::tree::beehive_occupants(ticks_in_hive);
|
||||
self.sim.level.load_block_entity(pos(position), nbt);
|
||||
Ok(())
|
||||
}
|
||||
/// Live levels hold no loot-table containers placed by these features.
|
||||
fn set_block_entity_loot_table(
|
||||
&mut self,
|
||||
|
||||
@@ -55,6 +55,7 @@ struct Level {
|
||||
events: Vec<LevelEvent>,
|
||||
biome: &'static str,
|
||||
day: i64,
|
||||
block_entities: Vec<(BlockPos, crate::nbt::Compound)>,
|
||||
}
|
||||
impl LevelAccess for Level {
|
||||
fn day_time(&self) -> i64 {
|
||||
@@ -81,6 +82,9 @@ impl LevelAccess for Level {
|
||||
fn biome_name(&self, _pos: BlockPos) -> Option<&str> {
|
||||
Some(self.biome)
|
||||
}
|
||||
fn load_block_entity(&mut self, pos: BlockPos, nbt: crate::nbt::Compound) {
|
||||
self.block_entities.push((pos, nbt));
|
||||
}
|
||||
}
|
||||
|
||||
struct World {
|
||||
@@ -104,6 +108,7 @@ impl World {
|
||||
events: Vec::new(),
|
||||
biome: "minecraft:plains",
|
||||
day: 6000,
|
||||
block_entities: Vec::new(),
|
||||
},
|
||||
f,
|
||||
ticks,
|
||||
@@ -186,6 +191,32 @@ fn oak_sapling_grows_with_bone_meal() {
|
||||
grow("oak_sapling", "grass_block", &[(0, 0)], "oak_log");
|
||||
}
|
||||
#[test]
|
||||
fn a_tree_with_bees_fills_its_nest() {
|
||||
let Some(f) = fixture() else { return };
|
||||
let mut w = World::new(f, 3);
|
||||
w.floor("grass_block", 12);
|
||||
let origin = BlockPos::new(0, 64, 0);
|
||||
let placed = f.behaviours.place_feature(
|
||||
&mut w.level,
|
||||
&mut w.ticks,
|
||||
"minecraft:fancy_oak_bees",
|
||||
origin,
|
||||
);
|
||||
assert_eq!(placed, Some(true));
|
||||
assert_eq!(w.count("bee_nest"), 1);
|
||||
let [(at, nbt)] = &w.level.block_entities[..] else {
|
||||
panic!("one block entity: {:?}", w.level.block_entities);
|
||||
};
|
||||
assert_eq!(w.name(*at), "minecraft:bee_nest");
|
||||
let Some(crate::nbt::Tag::List(bees)) = nbt.get(&"bees".into()) else {
|
||||
panic!("bees: {nbt:?}");
|
||||
};
|
||||
assert!(
|
||||
(2..=3).contains(&bees.len()),
|
||||
"BeehiveDecorator stores 2 or 3 bees"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn single_saplings_grow() {
|
||||
grow("birch_sapling", "dirt", &[(0, 0)], "birch_log");
|
||||
grow("spruce_sapling", "dirt", &[(0, 0)], "spruce_log");
|
||||
|
||||
@@ -416,6 +416,10 @@ pub trait LevelAccess {
|
||||
) -> u8 {
|
||||
0
|
||||
}
|
||||
/// Load saved block-entity data into the block entity of the block just
|
||||
/// placed at `pos` (`BlockEntity.loadWithComponents`): a bee nest a grown
|
||||
/// tree placed gets its bees. Levels without block entities drop it.
|
||||
fn load_block_entity(&mut self, _pos: BlockPos, _nbt: crate::nbt::Compound) {}
|
||||
/// Biome identifier at `pos` (`minecraft:plains`), for features placed by
|
||||
/// block behaviour (bone meal flowers). None when unknown.
|
||||
fn biome_name(&self, _pos: BlockPos) -> Option<&str> {
|
||||
|
||||
@@ -698,13 +698,12 @@ impl PlacementContext for World<'_> {
|
||||
self.set_block_entity(position, nbt)
|
||||
}
|
||||
/// BeehiveDecorator: `getBlockEntity(pos, BEEHIVE)` then
|
||||
/// `storeBee(Occupant.create(ticks))` for each bee (min ticks in hive 600).
|
||||
/// `storeBee(Occupant.create(ticks))` for each bee.
|
||||
fn store_beehive_occupants(
|
||||
&mut self,
|
||||
position: [i32; 3],
|
||||
ticks_in_hive: &[i32],
|
||||
) -> Result<(), PlacementQueryError> {
|
||||
use crate::nbt::Tag;
|
||||
let chunk = self.region.chunk(position[0] >> 4, position[2] >> 4)?;
|
||||
let state = chunk.block(position[0], position[1], position[2]);
|
||||
let registry = self.decoration.bindings.registry();
|
||||
@@ -714,22 +713,7 @@ impl PlacementContext for World<'_> {
|
||||
) {
|
||||
return Ok(());
|
||||
}
|
||||
let bees = ticks_in_hive
|
||||
.iter()
|
||||
.map(|&ticks| {
|
||||
let mut entity = crate::nbt::Compound::new();
|
||||
entity.insert("id".into(), Tag::String("minecraft:bee".into()));
|
||||
let mut bee = crate::nbt::Compound::new();
|
||||
bee.insert("entity_data".into(), Tag::Compound(entity));
|
||||
bee.insert("ticks_in_hive".into(), Tag::Int(ticks));
|
||||
bee.insert("min_ticks_in_hive".into(), Tag::Int(600));
|
||||
Tag::Compound(bee)
|
||||
})
|
||||
.collect();
|
||||
let mut nbt = crate::nbt::Compound::new();
|
||||
nbt.insert("id".into(), Tag::String("minecraft:beehive".into()));
|
||||
nbt.insert("bees".into(), Tag::List(bees));
|
||||
self.set_block_entity(position, nbt)
|
||||
self.set_block_entity(position, super::tree::beehive_occupants(ticks_in_hive))
|
||||
}
|
||||
/// SpawnerBlockEntity.setEntityId: the next spawn data and no potentials.
|
||||
fn configure_spawner(
|
||||
|
||||
@@ -21,8 +21,8 @@ use super::{
|
||||
value_provider::IntProvider,
|
||||
};
|
||||
use crate::registry::BlockStateId;
|
||||
pub(crate) use decorator::GROUND_FEATURE_KEY;
|
||||
use decorator::TreeDecorator;
|
||||
pub(crate) use decorator::{GROUND_FEATURE_KEY, beehive_occupants};
|
||||
pub use fallen::FallenTreeFeature;
|
||||
use foliage::{FoliageAttachment, FoliagePlacer};
|
||||
use horizon_core::{geometry::Direction, random::RandomSource};
|
||||
|
||||
@@ -675,3 +675,25 @@ fn mushroom_pos(log: [i32; 3], facing: Direction) -> [i32; 3] {
|
||||
let n = facing.normal();
|
||||
tb::offset(log, n[0], 0, n[2])
|
||||
}
|
||||
|
||||
/// The nest's block entity after `BeehiveDecorator` stored its bees
|
||||
/// (`Occupant.create(ticks)`: a bee, with 600 minimum ticks in the hive).
|
||||
pub(crate) fn beehive_occupants(ticks_in_hive: &[i32]) -> crate::nbt::Compound {
|
||||
use crate::nbt::{Compound, Tag};
|
||||
let bees = ticks_in_hive
|
||||
.iter()
|
||||
.map(|&ticks| {
|
||||
let mut entity = Compound::new();
|
||||
entity.insert("id".into(), Tag::String("minecraft:bee".into()));
|
||||
let mut bee = Compound::new();
|
||||
bee.insert("entity_data".into(), Tag::Compound(entity));
|
||||
bee.insert("ticks_in_hive".into(), Tag::Int(ticks));
|
||||
bee.insert("min_ticks_in_hive".into(), Tag::Int(600));
|
||||
Tag::Compound(bee)
|
||||
})
|
||||
.collect();
|
||||
let mut nbt = Compound::new();
|
||||
nbt.insert("id".into(), Tag::String("minecraft:beehive".into()));
|
||||
nbt.insert("bees".into(), Tag::List(bees));
|
||||
nbt
|
||||
}
|
||||
Reference in new issue
Block a user