mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a91f4a7999 |
No files matched your search
@@ -426,6 +426,29 @@ impl World {
|
||||
}
|
||||
false
|
||||
}
|
||||
/// What `matches` says of the block at (x, y, z) when its section's
|
||||
/// palette alone tells: every entry matches, or none does. `None` when
|
||||
/// only the cell can tell (a mixed or long palette, or no section there):
|
||||
/// the caller reads [`Self::block`] then, and gets the same answer.
|
||||
pub fn palette_answer(
|
||||
&self,
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
mut matches: impl FnMut(BlockStateId) -> bool,
|
||||
) -> Option<bool> {
|
||||
let chunk = self.chunks.get(&(x >> 4, z >> 4))?;
|
||||
let blocks = &chunk.sections[chunk.section_index(y)?].blocks;
|
||||
// Each entry is asked twice at most: past 16 of them, reading the
|
||||
// cell costs less.
|
||||
if blocks.is_global() || blocks.palette_len() > 16 {
|
||||
return None;
|
||||
}
|
||||
if !blocks.maybe_has(|v| matches(BlockStateId(v))) {
|
||||
return Some(false);
|
||||
}
|
||||
(!blocks.maybe_has(|v| !matches(BlockStateId(v)))).then_some(true)
|
||||
}
|
||||
/// The block entities to tick this tick, in order (`BlockEntities::tickers`).
|
||||
pub fn block_entity_tickers(
|
||||
&mut self,
|
||||
@@ -613,6 +636,32 @@ mod tests {
|
||||
/// A box misses a state only where no section it reaches lists it, and
|
||||
/// unloaded columns or heights past the world count as their air.
|
||||
#[test]
|
||||
fn a_palette_answer_is_the_block_s_own() {
|
||||
let mut world = World::new(BlockStateId(0), 0);
|
||||
let mut chunk = Chunk::empty(0, 0);
|
||||
chunk.set(3, 70, 3, 9);
|
||||
world.chunks.insert((0, 0), Arc::new(chunk));
|
||||
let nine = |s: BlockStateId| s.0 == 9;
|
||||
let any = |s: BlockStateId| s.0 == 9 || s.0 == 0;
|
||||
for y in [-100, -1, 0, 50, 64, 70, 79, 80, 400] {
|
||||
for (x, z) in [(3, 3), (0, 0), (15, 15), (20, 3), (-1, -1)] {
|
||||
for matches in [&nine as &dyn Fn(BlockStateId) -> bool, &any] {
|
||||
if let Some(answer) = world.palette_answer(x, y, z, matches) {
|
||||
assert_eq!(answer, matches(world.block(x, y, z)), "({x}, {y}, {z})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// An air section answers for all its cells; the section holding 9
|
||||
// answers only where every entry agrees.
|
||||
assert_eq!(world.palette_answer(1, 50, 1, nine), Some(false));
|
||||
assert_eq!(world.palette_answer(1, 70, 1, nine), None);
|
||||
assert_eq!(world.palette_answer(1, 70, 1, any), Some(true));
|
||||
// No chunk, or no section: the caller reads the block.
|
||||
assert_eq!(world.palette_answer(20, 70, 3, nine), None);
|
||||
assert_eq!(world.palette_answer(1, 400, 1, nine), None);
|
||||
}
|
||||
#[test]
|
||||
fn may_hold_reads_the_palettes_of_the_reached_sections() {
|
||||
let mut world = World::new(BlockStateId(0), 0);
|
||||
let mut chunk = Chunk::empty(0, 0);
|
||||
|
||||
@@ -447,6 +447,19 @@ impl SpawnLevel for GameLevel<'_, '_> {
|
||||
}
|
||||
}
|
||||
impl NaturalLevel for GameLevel<'_, '_> {
|
||||
/// From the section's palette when it answers for every block (a section
|
||||
/// of stone and ores, or of air): reading the cell itself is a cache miss
|
||||
/// in a random section of every chunk, each tick.
|
||||
fn redstone_conductor(&self, x: i32, y: i32, z: i32) -> bool {
|
||||
let data = self.w.data;
|
||||
let conducts = |s: BlockStateId| {
|
||||
data.state(s).flags & horizon_client::mobs::data::REDSTONE_CONDUCTOR != 0
|
||||
};
|
||||
self.w
|
||||
.world
|
||||
.palette_answer(x, y, z, conducts)
|
||||
.unwrap_or_else(|| conducts(self.state(x, y, z)))
|
||||
}
|
||||
fn spawn_hooks(&self) -> bool {
|
||||
!horizon_client::mod_api::mods().is_empty()
|
||||
}
|
||||
|
||||
@@ -301,6 +301,11 @@ fn spawns_far_from_player(s: Species, category: Category) -> bool {
|
||||
pub type MobType = (Species, f32, f32, Category);
|
||||
|
||||
pub trait NaturalLevel: SpawnLevel {
|
||||
/// `BlockState.isRedstoneConductor` of the block at (x, y, z): the start
|
||||
/// of a spawn attempt, read for every chunk and category each tick.
|
||||
fn redstone_conductor(&self, x: i32, y: i32, z: i32) -> bool {
|
||||
self.mob_data().state(self.state(x, y, z)).flags & super::data::REDSTONE_CONDUCTOR != 0
|
||||
}
|
||||
/// Whether server mods hear `potential_spawns`.
|
||||
fn spawn_hooks(&self) -> bool {
|
||||
false
|
||||
@@ -1092,7 +1097,7 @@ pub fn spawn_category_for_position<L: NaturalLevel, R: RandomSource>(
|
||||
trace: &mut dyn FnMut(Event),
|
||||
) {
|
||||
let [x0, y, z0] = start;
|
||||
if l.mob_data().state(l.state(x0, y, z0)).flags & super::data::REDSTONE_CONDUCTOR != 0 {
|
||||
if l.redstone_conductor(x0, y, z0) {
|
||||
return;
|
||||
}
|
||||
let mut spawned: Vec<Aabb> = Vec::new();
|
||||
|
||||
Reference in new issue
Block a user