mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
heightmaps
This commit is contained in:
parent
61e63c0896
commit
856a3252f6
10 changed files with 321 additions and 33 deletions
|
@ -9,6 +9,7 @@ use azalea_entity::{
|
||||||
Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition, LoadedBy, LookDirection,
|
Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition, LoadedBy, LookDirection,
|
||||||
Physics, PlayerBundle, Position, RelativeEntityUpdate,
|
Physics, PlayerBundle, Position, RelativeEntityUpdate,
|
||||||
};
|
};
|
||||||
|
use azalea_nbt::NbtCompound;
|
||||||
use azalea_protocol::{
|
use azalea_protocol::{
|
||||||
connect::{ReadConnection, WriteConnection},
|
connect::{ReadConnection, WriteConnection},
|
||||||
packets::game::{
|
packets::game::{
|
||||||
|
@ -578,9 +579,20 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let heightmaps = p
|
||||||
|
.chunk_data
|
||||||
|
.heightmaps
|
||||||
|
.as_compound()
|
||||||
|
.and_then(|c| c.get(""))
|
||||||
|
.and_then(|c| c.as_compound());
|
||||||
|
// necessary to make the unwrap_or work
|
||||||
|
let empty_nbt_compound = NbtCompound::default();
|
||||||
|
let heightmaps = heightmaps.unwrap_or(&empty_nbt_compound);
|
||||||
|
|
||||||
if let Err(e) = partial_world.chunks.replace_with_packet_data(
|
if let Err(e) = partial_world.chunks.replace_with_packet_data(
|
||||||
&pos,
|
&pos,
|
||||||
&mut Cursor::new(&p.chunk_data.data),
|
&mut Cursor::new(&p.chunk_data.data),
|
||||||
|
heightmaps,
|
||||||
&mut world.chunks,
|
&mut world.chunks,
|
||||||
) {
|
) {
|
||||||
error!("Couldn't set chunk data: {}", e);
|
error!("Couldn't set chunk data: {}", e);
|
||||||
|
@ -632,6 +644,7 @@ pub fn process_packet_events(ecs: &mut World) {
|
||||||
ClientboundGamePacket::SetEntityData(p) => {
|
ClientboundGamePacket::SetEntityData(p) => {
|
||||||
debug!("Got set entity data packet {:?}", p);
|
debug!("Got set entity data packet {:?}", p);
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
let mut system_state: SystemState<(
|
let mut system_state: SystemState<(
|
||||||
Commands,
|
Commands,
|
||||||
Query<(&EntityIdIndex, &LocalPlayer)>,
|
Query<(&EntityIdIndex, &LocalPlayer)>,
|
||||||
|
|
|
@ -9,17 +9,17 @@ pub static SIN: LazyLock<[f32; 65536]> = LazyLock::new(|| {
|
||||||
});
|
});
|
||||||
|
|
||||||
/// A sine function that uses a lookup table.
|
/// A sine function that uses a lookup table.
|
||||||
pub fn sin(var0: f32) -> f32 {
|
pub fn sin(x: f32) -> f32 {
|
||||||
let var0 = var0 * 10430.378;
|
let x = x * 10430.378;
|
||||||
let var0 = var0 as usize;
|
let x = x as usize;
|
||||||
SIN[var0 & 65535]
|
SIN[x & 65535]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A cosine function that uses a lookup table.
|
/// A cosine function that uses a lookup table.
|
||||||
pub fn cos(var0: f32) -> f32 {
|
pub fn cos(x: f32) -> f32 {
|
||||||
let var0 = var0 * 10430.378 + 16384.0;
|
let x = x * 10430.378 + 16384.0;
|
||||||
let var0 = var0 as usize;
|
let x = x as usize;
|
||||||
SIN[var0 & 65535]
|
SIN[x & 65535]
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this generic
|
// TODO: make this generic
|
||||||
|
@ -56,6 +56,10 @@ pub fn lerp<T: num_traits::Float>(amount: T, a: T, b: T) -> T {
|
||||||
a + amount * (b - a)
|
a + amount * (b - a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ceil_log2(x: u32) -> u32 {
|
||||||
|
u32::BITS - x.leading_zeros()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -346,6 +346,17 @@ impl From<Vec3> for ChunkPos {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Vec3> for ChunkBlockPos {
|
||||||
|
fn from(pos: &Vec3) -> Self {
|
||||||
|
ChunkBlockPos::from(&BlockPos::from(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Vec3> for ChunkBlockPos {
|
||||||
|
fn from(pos: Vec3) -> Self {
|
||||||
|
ChunkBlockPos::from(&pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const PACKED_X_LENGTH: u64 = 1 + 25; // minecraft does something a bit more complicated to get this 25
|
const PACKED_X_LENGTH: u64 = 1 + 25; // minecraft does something a bit more complicated to get this 25
|
||||||
const PACKED_Z_LENGTH: u64 = PACKED_X_LENGTH;
|
const PACKED_Z_LENGTH: u64 = PACKED_X_LENGTH;
|
||||||
const PACKED_Y_LENGTH: u64 = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH;
|
const PACKED_Y_LENGTH: u64 = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH;
|
||||||
|
|
|
@ -27,3 +27,34 @@ pub struct BlockEntity {
|
||||||
pub kind: azalea_registry::BlockEntityKind,
|
pub kind: azalea_registry::BlockEntityKind,
|
||||||
pub data: Nbt,
|
pub data: Nbt,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compound(NbtCompound {
|
||||||
|
// inner: [("", Compound(NbtCompound {
|
||||||
|
// inner: [
|
||||||
|
// ("MOTION_BLOCKING", LongArray([2310355422147575936,
|
||||||
|
// 2292305770412047999, 2310355422147575423, 2292305770412310656,
|
||||||
|
// 2310355422013095551, 2292305839266005120, 2310320168921529983,
|
||||||
|
// 2310355422147575936, 2292305770412048512, 2310355422147575935,
|
||||||
|
// 2292305839266005120, 2310355422147313279, 2292305770546528384,
|
||||||
|
// 2310355353293618815, 2292305839266005120, 2292305770412047999,
|
||||||
|
// 2310355422147575936, 2292305770412047999, 2310355422147575423,
|
||||||
|
// 2292305770412048512, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 17079008895])),
|
||||||
|
// ("WORLD_SURFACE", LongArray([2310355422147575936,
|
||||||
|
// 2292340954784136831, 2310355422147575423, 2292305770412310656,
|
||||||
|
// 2310355422013095551, 2292305839266005120, 2310320168921529983,
|
||||||
|
// 2310355422147575936, 2292305770412048512, 2310355422147575935,
|
||||||
|
// 2292305839266005120, 2310355422147313279, 2292305770546528384,
|
||||||
|
// 2310355353293618815, 2292305839266005120, 2292305770412047999,
|
||||||
|
// 2310355422147575936, 2292305770412047999, 2310355422147575423,
|
||||||
|
// 2292305770412048512, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 2292305770412047999,
|
||||||
|
// 2292305770412047999, 2292305770412047999, 17079008895]))] }))]
|
||||||
|
// })
|
||||||
|
|
|
@ -106,7 +106,7 @@ impl BitStorage {
|
||||||
// 0 bit storage
|
// 0 bit storage
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
return Ok(BitStorage {
|
return Ok(BitStorage {
|
||||||
data: Vec::with_capacity(0),
|
data: Vec::new(),
|
||||||
bits,
|
bits,
|
||||||
size,
|
size,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
use crate::heightmap::Heightmap;
|
||||||
|
use crate::heightmap::HeightmapKind;
|
||||||
use crate::palette::PalettedContainer;
|
use crate::palette::PalettedContainer;
|
||||||
use crate::palette::PalettedContainerKind;
|
use crate::palette::PalettedContainerKind;
|
||||||
use azalea_block::BlockState;
|
use azalea_block::BlockState;
|
||||||
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
|
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
|
||||||
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
|
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
|
||||||
|
use azalea_nbt::NbtCompound;
|
||||||
use log::{debug, trace, warn};
|
use log::{debug, trace, warn};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
@ -43,6 +47,10 @@ pub struct ChunkStorage {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
pub sections: Vec<Section>,
|
pub sections: Vec<Section>,
|
||||||
|
/// Heightmaps are used for identifying the surface blocks in a chunk.
|
||||||
|
/// Usually for clients only `WorldSurface` and `MotionBlocking` are
|
||||||
|
/// present.
|
||||||
|
pub heightmaps: HashMap<HeightmapKind, Heightmap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A section of a chunk, i.e. a 16*16*16 block area.
|
/// A section of a chunk, i.e. a 16*16*16 block area.
|
||||||
|
@ -73,6 +81,7 @@ impl Default for Chunk {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Chunk {
|
Chunk {
|
||||||
sections: vec![Section::default(); (384 / 16) as usize],
|
sections: vec![Section::default(); (384 / 16) as usize],
|
||||||
|
heightmaps: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +128,7 @@ impl PartialChunkStorage {
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: &ChunkPos,
|
pos: &ChunkPos,
|
||||||
data: &mut Cursor<&[u8]>,
|
data: &mut Cursor<&[u8]>,
|
||||||
|
heightmaps: &NbtCompound,
|
||||||
chunk_storage: &mut ChunkStorage,
|
chunk_storage: &mut ChunkStorage,
|
||||||
) -> Result<(), BufReadError> {
|
) -> Result<(), BufReadError> {
|
||||||
debug!("Replacing chunk at {:?}", pos);
|
debug!("Replacing chunk at {:?}", pos);
|
||||||
|
@ -127,7 +137,12 @@ impl PartialChunkStorage {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let chunk = Chunk::read_with_dimension_height(data, chunk_storage.height)?;
|
let chunk = Chunk::read_with_dimension_height(
|
||||||
|
data,
|
||||||
|
chunk_storage.height,
|
||||||
|
chunk_storage.min_y,
|
||||||
|
heightmaps,
|
||||||
|
)?;
|
||||||
|
|
||||||
trace!("Loaded chunk {:?}", pos);
|
trace!("Loaded chunk {:?}", pos);
|
||||||
self.set(pos, Some(chunk), chunk_storage);
|
self.set(pos, Some(chunk), chunk_storage);
|
||||||
|
@ -229,6 +244,8 @@ impl Chunk {
|
||||||
pub fn read_with_dimension_height(
|
pub fn read_with_dimension_height(
|
||||||
buf: &mut Cursor<&[u8]>,
|
buf: &mut Cursor<&[u8]>,
|
||||||
dimension_height: u32,
|
dimension_height: u32,
|
||||||
|
min_y: i32,
|
||||||
|
heightmaps_nbt: &NbtCompound,
|
||||||
) -> Result<Self, BufReadError> {
|
) -> Result<Self, BufReadError> {
|
||||||
let section_count = dimension_height / SECTION_HEIGHT;
|
let section_count = dimension_height / SECTION_HEIGHT;
|
||||||
let mut sections = Vec::with_capacity(section_count as usize);
|
let mut sections = Vec::with_capacity(section_count as usize);
|
||||||
|
@ -236,23 +253,30 @@ impl Chunk {
|
||||||
let section = Section::read_from(buf)?;
|
let section = Section::read_from(buf)?;
|
||||||
sections.push(section);
|
sections.push(section);
|
||||||
}
|
}
|
||||||
Ok(Chunk { sections })
|
|
||||||
|
let mut heightmaps = HashMap::new();
|
||||||
|
for (name, heightmap) in heightmaps_nbt.iter() {
|
||||||
|
let Ok(kind) = HeightmapKind::from_str(name) else {
|
||||||
|
warn!("Unknown heightmap kind: {name}");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(data) = heightmap.as_long_array() else {
|
||||||
|
warn!("Heightmap {name} is not a long array");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let data: Vec<u64> = data.iter().map(|x| *x as u64).collect();
|
||||||
|
let heightmap = Heightmap::new(kind, dimension_height, min_y, data);
|
||||||
|
heightmaps.insert(kind, heightmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Chunk {
|
||||||
|
sections,
|
||||||
|
heightmaps,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> Option<BlockState> {
|
pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> Option<BlockState> {
|
||||||
if pos.y < min_y {
|
get_block_state_from_sections(&self.sections, pos, min_y)
|
||||||
// y position is out of bounds
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let section_index = section_index(pos.y, min_y) as usize;
|
|
||||||
if section_index >= self.sections.len() {
|
|
||||||
// y position is out of bounds
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
// TODO: make sure the section exists
|
|
||||||
let section = &self.sections[section_index];
|
|
||||||
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
|
||||||
Some(section.get(chunk_section_pos))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_and_set(
|
pub fn get_and_set(
|
||||||
|
@ -265,7 +289,13 @@ impl Chunk {
|
||||||
// TODO: make sure the section exists
|
// TODO: make sure the section exists
|
||||||
let section = &mut self.sections[section_index as usize];
|
let section = &mut self.sections[section_index as usize];
|
||||||
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
||||||
section.get_and_set(chunk_section_pos, state)
|
let previous_state = section.get_and_set(chunk_section_pos, state);
|
||||||
|
|
||||||
|
for heightmap in self.heightmaps.values_mut() {
|
||||||
|
heightmap.update(pos, state, &self.sections);
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_state
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, pos: &ChunkBlockPos, state: BlockState, min_y: i32) {
|
pub fn set(&mut self, pos: &ChunkBlockPos, state: BlockState, min_y: i32) {
|
||||||
|
@ -274,8 +304,34 @@ impl Chunk {
|
||||||
let section = &mut self.sections[section_index as usize];
|
let section = &mut self.sections[section_index as usize];
|
||||||
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
||||||
section.set(chunk_section_pos, state);
|
section.set(chunk_section_pos, state);
|
||||||
|
|
||||||
|
for heightmap in self.heightmaps.values_mut() {
|
||||||
|
heightmap.update(pos, state, &self.sections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the block state at the given position from a list of sections. Returns
|
||||||
|
/// `None` if the position is out of bounds.
|
||||||
|
pub fn get_block_state_from_sections(
|
||||||
|
sections: &[Section],
|
||||||
|
pos: &ChunkBlockPos,
|
||||||
|
min_y: i32,
|
||||||
|
) -> Option<BlockState> {
|
||||||
|
if pos.y < min_y {
|
||||||
|
// y position is out of bounds
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let section_index = section_index(pos.y, min_y) as usize;
|
||||||
|
if section_index >= sections.len() {
|
||||||
|
// y position is out of bounds
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
// TODO: make sure the section exists
|
||||||
|
let section = §ions[section_index];
|
||||||
|
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
||||||
|
Some(section.get(chunk_section_pos))
|
||||||
|
}
|
||||||
|
|
||||||
impl McBufWritable for Chunk {
|
impl McBufWritable for Chunk {
|
||||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||||
|
|
151
azalea-world/src/heightmap.rs
Normal file
151
azalea-world/src/heightmap.rs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
|
use azalea_block::BlockState;
|
||||||
|
use azalea_core::{math, ChunkBlockPos};
|
||||||
|
use azalea_registry::tags::blocks::LEAVES;
|
||||||
|
|
||||||
|
use crate::{chunk_storage::get_block_state_from_sections, BitStorage, Section};
|
||||||
|
|
||||||
|
// (wg stands for worldgen)
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||||
|
pub enum HeightmapKind {
|
||||||
|
WorldSurfaceWg,
|
||||||
|
WorldSurface,
|
||||||
|
OceanFloorWg,
|
||||||
|
OceanFloor,
|
||||||
|
MotionBlocking,
|
||||||
|
MotionBlockingNoLeaves,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Heightmap {
|
||||||
|
pub data: BitStorage,
|
||||||
|
pub min_y: i32,
|
||||||
|
pub kind: HeightmapKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blocks_motion(block_state: BlockState) -> bool {
|
||||||
|
// TODO
|
||||||
|
!block_state.is_air()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn motion_blocking(block_state: BlockState) -> bool {
|
||||||
|
// TODO
|
||||||
|
!block_state.is_air() || block_state.waterlogged()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeightmapKind {
|
||||||
|
pub fn is_opaque(self, block_state: BlockState) -> bool {
|
||||||
|
let block = Box::<dyn azalea_block::Block>::from(block_state);
|
||||||
|
let registry_block = block.as_registry_block();
|
||||||
|
match self {
|
||||||
|
HeightmapKind::WorldSurfaceWg => !block_state.is_air(),
|
||||||
|
HeightmapKind::WorldSurface => !block_state.is_air(),
|
||||||
|
HeightmapKind::OceanFloorWg => blocks_motion(block_state),
|
||||||
|
HeightmapKind::OceanFloor => blocks_motion(block_state),
|
||||||
|
HeightmapKind::MotionBlocking => motion_blocking(block_state),
|
||||||
|
HeightmapKind::MotionBlockingNoLeaves => {
|
||||||
|
motion_blocking(block_state) && !LEAVES.contains(®istry_block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Heightmap {
|
||||||
|
pub fn new(kind: HeightmapKind, dimension_height: u32, min_y: i32, data: Vec<u64>) -> Self {
|
||||||
|
let bits = math::ceil_log2(dimension_height + 1);
|
||||||
|
let data = BitStorage::new(bits as usize, 16 * 16, Some(data)).unwrap();
|
||||||
|
Self { kind, data, min_y }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_index(x: u8, z: u8) -> usize {
|
||||||
|
(x as usize) + (z as usize) * 16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_first_available_at_index(&self, index: usize) -> i32 {
|
||||||
|
self.data.get(index) as i32 + self.min_y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_first_available(&self, x: u8, z: u8) -> i32 {
|
||||||
|
self.get_first_available_at_index(Self::get_index(x, z))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_highest_taken(&self, x: u8, z: u8) -> i32 {
|
||||||
|
self.get_first_available(x, z) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_height(&mut self, x: u8, z: u8, height: i32) {
|
||||||
|
self.data
|
||||||
|
.set(Self::get_index(x, z), (height - self.min_y) as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the heightmap with the given block state at the given position.
|
||||||
|
pub fn update(
|
||||||
|
&mut self,
|
||||||
|
pos: &ChunkBlockPos,
|
||||||
|
block_state: BlockState,
|
||||||
|
sections: &[Section],
|
||||||
|
) -> bool {
|
||||||
|
let first_available_y = self.get_first_available(pos.x, pos.z);
|
||||||
|
if pos.y <= first_available_y - 2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if self.kind.is_opaque(block_state) {
|
||||||
|
// increase y
|
||||||
|
if pos.y >= first_available_y {
|
||||||
|
self.set_height(pos.x, pos.z, pos.y + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if first_available_y - 1 == pos.y {
|
||||||
|
// decrease y
|
||||||
|
for y in (self.min_y..pos.y).rev() {
|
||||||
|
if self.kind.is_opaque(
|
||||||
|
get_block_state_from_sections(
|
||||||
|
sections,
|
||||||
|
&ChunkBlockPos::new(pos.x, y, pos.z),
|
||||||
|
self.min_y,
|
||||||
|
)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
) {
|
||||||
|
self.set_height(pos.x, pos.z, y + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_height(pos.x, pos.z, self.min_y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for HeightmapKind {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"WORLD_SURFACE_WG" => Ok(HeightmapKind::WorldSurfaceWg),
|
||||||
|
"WORLD_SURFACE" => Ok(HeightmapKind::WorldSurface),
|
||||||
|
"OCEAN_FLOOR_WG" => Ok(HeightmapKind::OceanFloorWg),
|
||||||
|
"OCEAN_FLOOR" => Ok(HeightmapKind::OceanFloor),
|
||||||
|
"MOTION_BLOCKING" => Ok(HeightmapKind::MotionBlocking),
|
||||||
|
"MOTION_BLOCKING_NO_LEAVES" => Ok(HeightmapKind::MotionBlockingNoLeaves),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for HeightmapKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
HeightmapKind::WorldSurfaceWg => write!(f, "WORLD_SURFACE_WG"),
|
||||||
|
HeightmapKind::WorldSurface => write!(f, "WORLD_SURFACE"),
|
||||||
|
HeightmapKind::OceanFloorWg => write!(f, "OCEAN_FLOOR_WG"),
|
||||||
|
HeightmapKind::OceanFloor => write!(f, "OCEAN_FLOOR"),
|
||||||
|
HeightmapKind::MotionBlocking => write!(f, "MOTION_BLOCKING"),
|
||||||
|
HeightmapKind::MotionBlockingNoLeaves => write!(f, "MOTION_BLOCKING_NO_LEAVES"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
mod bit_storage;
|
mod bit_storage;
|
||||||
mod chunk_storage;
|
mod chunk_storage;
|
||||||
mod container;
|
mod container;
|
||||||
|
pub mod heightmap;
|
||||||
pub mod iterators;
|
pub mod iterators;
|
||||||
pub mod palette;
|
pub mod palette;
|
||||||
mod world;
|
mod world;
|
||||||
|
|
|
@ -3,17 +3,18 @@
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use azalea::ecs::query::With;
|
use azalea::ecs::query::With;
|
||||||
use azalea::entity::metadata::Player;
|
use azalea::entity::{metadata::Player, EyeHeight, Position};
|
||||||
use azalea::entity::{EyeHeight, Position};
|
|
||||||
use azalea::interact::HitResultComponent;
|
use azalea::interact::HitResultComponent;
|
||||||
use azalea::inventory::ItemSlot;
|
use azalea::inventory::ItemSlot;
|
||||||
use azalea::pathfinder::goals::BlockPosGoal;
|
use azalea::pathfinder::goals::BlockPosGoal;
|
||||||
use azalea::protocol::packets::game::ClientboundGamePacket;
|
use azalea::protocol::packets::game::ClientboundGamePacket;
|
||||||
use azalea::{prelude::*, swarm::prelude::*, BlockPos, GameProfileComponent, WalkDirection};
|
use azalea::world::{heightmap::HeightmapKind, InstanceName, MinecraftEntityId};
|
||||||
use azalea::{Account, Client, Event};
|
use azalea::SprintDirection;
|
||||||
use azalea_client::SprintDirection;
|
use azalea::{prelude::*, swarm::prelude::*};
|
||||||
use azalea_core::Vec3;
|
use azalea::{
|
||||||
use azalea_world::{InstanceName, MinecraftEntityId};
|
Account, BlockPos, ChunkPos, Client, Event, GameProfileComponent, Vec3, WalkDirection,
|
||||||
|
};
|
||||||
|
use azalea_core::ChunkBlockPos;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Default, Clone, Component)]
|
#[derive(Default, Clone, Component)]
|
||||||
|
@ -288,6 +289,26 @@ async fn handle(mut bot: Client, event: Event, _state: State) -> anyhow::Result<
|
||||||
bot.chat("no entities found");
|
bot.chat("no entities found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"heightmap" => {
|
||||||
|
let position = bot.position();
|
||||||
|
let chunk_pos = ChunkPos::from(position);
|
||||||
|
let chunk_block_pos = ChunkBlockPos::from(position);
|
||||||
|
let chunk = bot.world().read().chunks.get(&chunk_pos);
|
||||||
|
if let Some(chunk) = chunk {
|
||||||
|
let heightmaps = &chunk.read().heightmaps;
|
||||||
|
let Some(world_surface_heightmap) =
|
||||||
|
heightmaps.get(&HeightmapKind::WorldSurface)
|
||||||
|
else {
|
||||||
|
bot.chat("no world surface heightmap");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
let highest_y = world_surface_heightmap
|
||||||
|
.get_highest_taken(chunk_block_pos.x, chunk_block_pos.z);
|
||||||
|
bot.chat(&format!("highest_y: {highest_y}",));
|
||||||
|
} else {
|
||||||
|
bot.chat("no chunk found");
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub use azalea_block as blocks;
|
||||||
pub use azalea_brigadier as brigadier;
|
pub use azalea_brigadier as brigadier;
|
||||||
pub use azalea_chat::FormattedText;
|
pub use azalea_chat::FormattedText;
|
||||||
pub use azalea_client::*;
|
pub use azalea_client::*;
|
||||||
pub use azalea_core::{BlockPos, Vec3};
|
pub use azalea_core::{BlockPos, ChunkPos, Vec3};
|
||||||
pub use azalea_entity as entity;
|
pub use azalea_entity as entity;
|
||||||
pub use azalea_protocol as protocol;
|
pub use azalea_protocol as protocol;
|
||||||
pub use azalea_registry::{Block, EntityKind, Item};
|
pub use azalea_registry::{Block, EntityKind, Item};
|
||||||
|
|
Loading…
Add table
Reference in a new issue