mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
EntityStorage
This commit is contained in:
parent
f993e79a7e
commit
614b211298
7 changed files with 260 additions and 175 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -193,8 +193,10 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"azalea-block",
|
"azalea-block",
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
|
"azalea-entity",
|
||||||
"azalea-nbt",
|
"azalea-nbt",
|
||||||
"azalea-protocol",
|
"azalea-protocol",
|
||||||
|
"nohash-hasher",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -771,6 +773,12 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
@ -19,7 +19,7 @@ use azalea_protocol::{
|
||||||
},
|
},
|
||||||
resolver, ServerAddress,
|
resolver, ServerAddress,
|
||||||
};
|
};
|
||||||
use azalea_world::{ChunkStorage, World};
|
use azalea_world::{ChunkStorage, EntityStorage, World};
|
||||||
use std::{fmt::Debug, sync::Arc};
|
use std::{fmt::Debug, sync::Arc};
|
||||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
@ -266,6 +266,7 @@ impl Client {
|
||||||
height,
|
height,
|
||||||
min_y,
|
min_y,
|
||||||
storage: ChunkStorage::new(16),
|
storage: ChunkStorage::new(16),
|
||||||
|
entities: EntityStorage::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
conn.lock()
|
conn.lock()
|
||||||
|
@ -356,6 +357,7 @@ impl Client {
|
||||||
y: p.y,
|
y: p.y,
|
||||||
z: p.z,
|
z: p.z,
|
||||||
};
|
};
|
||||||
|
p.id;
|
||||||
}
|
}
|
||||||
GamePacket::ClientboundSetEntityDataPacket(p) => {
|
GamePacket::ClientboundSetEntityDataPacket(p) => {
|
||||||
// println!("Got set entity data packet {:?}", p);
|
// println!("Got set entity data packet {:?}", p);
|
||||||
|
|
|
@ -4,7 +4,18 @@ use azalea_core::EntityPos;
|
||||||
pub struct Entity {
|
pub struct Entity {
|
||||||
/// The incrementing numerical id of the entity.
|
/// The incrementing numerical id of the entity.
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub pos: EntityPos,
|
pos: EntityPos,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity {
|
||||||
|
pub fn pos(&self) -> &EntityPos {
|
||||||
|
&self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_pos(&mut self, pos: EntityPos) {
|
||||||
|
// TODO: check if it moved to another chunk
|
||||||
|
self.pos = pos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
|
|
|
@ -6,7 +6,12 @@ version = "0.1.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
azalea-block = {path = "../azalea-block"}
|
||||||
azalea-core = {path = "../azalea-core"}
|
azalea-core = {path = "../azalea-core"}
|
||||||
|
azalea-entity = {path = "../azalea-entity"}
|
||||||
azalea-nbt = {path = "../azalea-nbt"}
|
azalea-nbt = {path = "../azalea-nbt"}
|
||||||
azalea-protocol = {path = "../azalea-protocol"}
|
azalea-protocol = {path = "../azalea-protocol"}
|
||||||
azalea-block = {path = "../azalea-block"}
|
nohash-hasher = "0.2.0"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
|
182
azalea-world/src/chunk.rs
Normal file
182
azalea-world/src/chunk.rs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
use crate::bit_storage::BitStorage;
|
||||||
|
use crate::palette::PalettedContainer;
|
||||||
|
use crate::palette::PalettedContainerType;
|
||||||
|
use crate::World;
|
||||||
|
use azalea_block::BlockState;
|
||||||
|
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
|
||||||
|
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
|
||||||
|
use std::{
|
||||||
|
io::{Read, Write},
|
||||||
|
ops::{Index, IndexMut},
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SECTION_HEIGHT: u32 = 16;
|
||||||
|
|
||||||
|
pub struct ChunkStorage {
|
||||||
|
pub view_center: ChunkPos,
|
||||||
|
chunk_radius: u32,
|
||||||
|
view_range: u32,
|
||||||
|
// chunks is a list of size chunk_radius * chunk_radius
|
||||||
|
chunks: Vec<Option<Arc<Mutex<Chunk>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// java moment
|
||||||
|
// it might be possible to replace this with just a modulo, but i copied java's floorMod just in case
|
||||||
|
fn floor_mod(x: i32, y: u32) -> u32 {
|
||||||
|
if x < 0 {
|
||||||
|
y - ((-x) as u32 % y)
|
||||||
|
} else {
|
||||||
|
x as u32 % y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChunkStorage {
|
||||||
|
pub fn new(chunk_radius: u32) -> Self {
|
||||||
|
let view_range = chunk_radius * 2 + 1;
|
||||||
|
ChunkStorage {
|
||||||
|
view_center: ChunkPos::new(0, 0),
|
||||||
|
chunk_radius,
|
||||||
|
view_range,
|
||||||
|
chunks: vec![None; (view_range * view_range) as usize],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_index(&self, chunk_pos: &ChunkPos) -> usize {
|
||||||
|
(floor_mod(chunk_pos.x, self.view_range) * self.view_range
|
||||||
|
+ floor_mod(chunk_pos.z, self.view_range)) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool {
|
||||||
|
(chunk_pos.x - self.view_center.x).unsigned_abs() <= self.chunk_radius
|
||||||
|
&& (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_block_state(&self, pos: &BlockPos, min_y: i32) -> Option<BlockState> {
|
||||||
|
let chunk_pos = ChunkPos::from(pos);
|
||||||
|
println!("chunk_pos {:?} block_pos {:?}", chunk_pos, pos);
|
||||||
|
let chunk = &self[&chunk_pos];
|
||||||
|
match chunk {
|
||||||
|
Some(chunk) => Some(chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<&ChunkPos> for ChunkStorage {
|
||||||
|
type Output = Option<Arc<Mutex<Chunk>>>;
|
||||||
|
|
||||||
|
fn index(&self, pos: &ChunkPos) -> &Self::Output {
|
||||||
|
&self.chunks[self.get_index(pos)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IndexMut<&ChunkPos> for ChunkStorage {
|
||||||
|
fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
|
||||||
|
let index = self.get_index(pos);
|
||||||
|
&mut self.chunks[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Chunk {
|
||||||
|
pub sections: Vec<Section>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunk {
|
||||||
|
pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> {
|
||||||
|
Self::read_with_world_height(buf, data.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> {
|
||||||
|
let section_count = world_height / SECTION_HEIGHT;
|
||||||
|
let mut sections = Vec::with_capacity(section_count as usize);
|
||||||
|
for _ in 0..section_count {
|
||||||
|
let section = Section::read_into(buf)?;
|
||||||
|
sections.push(section);
|
||||||
|
}
|
||||||
|
Ok(Chunk { sections })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn section_index(&self, y: i32, min_y: i32) -> u32 {
|
||||||
|
// TODO: check the build height and stuff, this code will be broken if the min build height is 0
|
||||||
|
// (LevelHeightAccessor.getMinSection in vanilla code)
|
||||||
|
assert!(y >= 0);
|
||||||
|
let min_section_index = min_y.div_floor(16);
|
||||||
|
(y.div_floor(16) - min_section_index) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> BlockState {
|
||||||
|
let section_index = self.section_index(pos.y, min_y);
|
||||||
|
// TODO: make sure the section exists
|
||||||
|
let section = &self.sections[section_index as usize];
|
||||||
|
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
||||||
|
let block_state = section.get(chunk_section_pos);
|
||||||
|
block_state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McBufWritable for Chunk {
|
||||||
|
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||||
|
for section in &self.sections {
|
||||||
|
section.write_into(buf)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Section {
|
||||||
|
pub block_count: u16,
|
||||||
|
pub states: PalettedContainer,
|
||||||
|
pub biomes: PalettedContainer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for Section {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
let block_count = u16::read_into(buf)?;
|
||||||
|
|
||||||
|
// this is commented out because the vanilla server is wrong
|
||||||
|
// assert!(
|
||||||
|
// block_count <= 16 * 16 * 16,
|
||||||
|
// "A section has more blocks than what should be possible. This is a bug!"
|
||||||
|
// );
|
||||||
|
|
||||||
|
let states = PalettedContainer::read_with_type(buf, &PalettedContainerType::BlockStates)?;
|
||||||
|
|
||||||
|
for i in 0..states.storage.size() {
|
||||||
|
if !BlockState::is_valid_state(states.storage.get(i) as u32) {
|
||||||
|
return Err(format!(
|
||||||
|
"Invalid block state {} (index {}) found in section.",
|
||||||
|
states.storage.get(i),
|
||||||
|
i
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let biomes = PalettedContainer::read_with_type(buf, &PalettedContainerType::Biomes)?;
|
||||||
|
Ok(Section {
|
||||||
|
block_count,
|
||||||
|
states,
|
||||||
|
biomes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McBufWritable for Section {
|
||||||
|
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||||
|
self.block_count.write_into(buf)?;
|
||||||
|
self.states.write_into(buf)?;
|
||||||
|
self.biomes.write_into(buf)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Section {
|
||||||
|
fn get(&self, pos: ChunkSectionBlockPos) -> BlockState {
|
||||||
|
// TODO: use the unsafe method and do the check earlier
|
||||||
|
self.states
|
||||||
|
.get(pos.x as usize, pos.y as usize, pos.z as usize)
|
||||||
|
.try_into()
|
||||||
|
.expect("Invalid block state.")
|
||||||
|
}
|
||||||
|
}
|
44
azalea-world/src/entity.rs
Normal file
44
azalea-world/src/entity.rs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use azalea_core::ChunkPos;
|
||||||
|
use azalea_entity::Entity;
|
||||||
|
use nohash_hasher::IntMap;
|
||||||
|
|
||||||
|
pub struct EntityStorage {
|
||||||
|
by_id: IntMap<u32, Entity>,
|
||||||
|
// TODO: this doesn't work yet (should be updated in the set_pos method in azalea-entity)
|
||||||
|
by_chunk: HashMap<ChunkPos, u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntityStorage {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
by_id: IntMap::default(),
|
||||||
|
by_chunk: HashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an entity to the storage.
|
||||||
|
#[inline]
|
||||||
|
pub fn insert(&mut self, entity: Entity) {
|
||||||
|
self.by_id.insert(entity.id, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an entity from the storage by its id.
|
||||||
|
#[inline]
|
||||||
|
pub fn remove_by_id(&mut self, id: u32) {
|
||||||
|
self.by_id.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to an entity by its id.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_by_id(&self, id: u32) -> Option<&Entity> {
|
||||||
|
self.by_id.get(&id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a mutable reference to an entity by its id.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_mut_by_id(&mut self, id: u32) -> Option<&mut Entity> {
|
||||||
|
self.by_id.get_mut(&id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,16 @@
|
||||||
#![feature(int_roundings)]
|
#![feature(int_roundings)]
|
||||||
|
|
||||||
mod bit_storage;
|
mod bit_storage;
|
||||||
|
mod chunk;
|
||||||
|
mod entity;
|
||||||
mod palette;
|
mod palette;
|
||||||
|
|
||||||
use crate::palette::PalettedContainerType;
|
|
||||||
use azalea_block::BlockState;
|
use azalea_block::BlockState;
|
||||||
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
|
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
|
||||||
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
|
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
|
||||||
pub use bit_storage::BitStorage;
|
pub use bit_storage::BitStorage;
|
||||||
use palette::PalettedContainer;
|
pub use chunk::{Chunk, ChunkStorage};
|
||||||
|
pub use entity::EntityStorage;
|
||||||
use std::{
|
use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut},
|
||||||
|
@ -24,10 +26,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SECTION_HEIGHT: u32 = 16;
|
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub storage: ChunkStorage,
|
pub storage: ChunkStorage,
|
||||||
|
pub entities: EntityStorage,
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
pub min_y: i32,
|
pub min_y: i32,
|
||||||
}
|
}
|
||||||
|
@ -83,171 +84,3 @@ impl IndexMut<&ChunkPos> for World {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub struct ChunkStorage {
|
|
||||||
view_center: ChunkPos,
|
|
||||||
chunk_radius: u32,
|
|
||||||
view_range: u32,
|
|
||||||
// chunks is a list of size chunk_radius * chunk_radius
|
|
||||||
chunks: Vec<Option<Arc<Mutex<Chunk>>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// java moment
|
|
||||||
// it might be possible to replace this with just a modulo, but i copied java's floorMod just in case
|
|
||||||
fn floor_mod(x: i32, y: u32) -> u32 {
|
|
||||||
if x < 0 {
|
|
||||||
y - ((-x) as u32 % y)
|
|
||||||
} else {
|
|
||||||
x as u32 % y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChunkStorage {
|
|
||||||
pub fn new(chunk_radius: u32) -> Self {
|
|
||||||
let view_range = chunk_radius * 2 + 1;
|
|
||||||
ChunkStorage {
|
|
||||||
view_center: ChunkPos::new(0, 0),
|
|
||||||
chunk_radius,
|
|
||||||
view_range,
|
|
||||||
chunks: vec![None; (view_range * view_range) as usize],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_index(&self, chunk_pos: &ChunkPos) -> usize {
|
|
||||||
(floor_mod(chunk_pos.x, self.view_range) * self.view_range
|
|
||||||
+ floor_mod(chunk_pos.z, self.view_range)) as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool {
|
|
||||||
(chunk_pos.x - self.view_center.x).unsigned_abs() <= self.chunk_radius
|
|
||||||
&& (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_block_state(&self, pos: &BlockPos, min_y: i32) -> Option<BlockState> {
|
|
||||||
let chunk_pos = ChunkPos::from(pos);
|
|
||||||
println!("chunk_pos {:?} block_pos {:?}", chunk_pos, pos);
|
|
||||||
let chunk = &self[&chunk_pos];
|
|
||||||
match chunk {
|
|
||||||
Some(chunk) => Some(chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<&ChunkPos> for ChunkStorage {
|
|
||||||
type Output = Option<Arc<Mutex<Chunk>>>;
|
|
||||||
|
|
||||||
fn index(&self, pos: &ChunkPos) -> &Self::Output {
|
|
||||||
&self.chunks[self.get_index(pos)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl IndexMut<&ChunkPos> for ChunkStorage {
|
|
||||||
fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
|
|
||||||
let index = self.get_index(pos);
|
|
||||||
&mut self.chunks[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Chunk {
|
|
||||||
pub sections: Vec<Section>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Chunk {
|
|
||||||
pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> {
|
|
||||||
Self::read_with_world_height(buf, data.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> {
|
|
||||||
let section_count = world_height / SECTION_HEIGHT;
|
|
||||||
let mut sections = Vec::with_capacity(section_count as usize);
|
|
||||||
for _ in 0..section_count {
|
|
||||||
let section = Section::read_into(buf)?;
|
|
||||||
sections.push(section);
|
|
||||||
}
|
|
||||||
Ok(Chunk { sections })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn section_index(&self, y: i32, min_y: i32) -> u32 {
|
|
||||||
// TODO: check the build height and stuff, this code will be broken if the min build height is 0
|
|
||||||
// (LevelHeightAccessor.getMinSection in vanilla code)
|
|
||||||
assert!(y >= 0);
|
|
||||||
let min_section_index = min_y.div_floor(16);
|
|
||||||
(y.div_floor(16) - min_section_index) as u32
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> BlockState {
|
|
||||||
let section_index = self.section_index(pos.y, min_y);
|
|
||||||
// TODO: make sure the section exists
|
|
||||||
let section = &self.sections[section_index as usize];
|
|
||||||
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
|
|
||||||
let block_state = section.get(chunk_section_pos);
|
|
||||||
block_state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufWritable for Chunk {
|
|
||||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
|
||||||
for section in &self.sections {
|
|
||||||
section.write_into(buf)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Section {
|
|
||||||
pub block_count: u16,
|
|
||||||
pub states: PalettedContainer,
|
|
||||||
pub biomes: PalettedContainer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for Section {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
let block_count = u16::read_into(buf)?;
|
|
||||||
|
|
||||||
// this is commented out because the vanilla server is wrong
|
|
||||||
// assert!(
|
|
||||||
// block_count <= 16 * 16 * 16,
|
|
||||||
// "A section has more blocks than what should be possible. This is a bug!"
|
|
||||||
// );
|
|
||||||
|
|
||||||
let states = PalettedContainer::read_with_type(buf, &PalettedContainerType::BlockStates)?;
|
|
||||||
|
|
||||||
for i in 0..states.storage.size() {
|
|
||||||
if !BlockState::is_valid_state(states.storage.get(i) as u32) {
|
|
||||||
return Err(format!(
|
|
||||||
"Invalid block state {} (index {}) found in section.",
|
|
||||||
states.storage.get(i),
|
|
||||||
i
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let biomes = PalettedContainer::read_with_type(buf, &PalettedContainerType::Biomes)?;
|
|
||||||
Ok(Section {
|
|
||||||
block_count,
|
|
||||||
states,
|
|
||||||
biomes,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufWritable for Section {
|
|
||||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
|
||||||
self.block_count.write_into(buf)?;
|
|
||||||
self.states.write_into(buf)?;
|
|
||||||
self.biomes.write_into(buf)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Section {
|
|
||||||
fn get(&self, pos: ChunkSectionBlockPos) -> BlockState {
|
|
||||||
// TODO: use the unsafe method and do the check earlier
|
|
||||||
self.states
|
|
||||||
.get(pos.x as usize, pos.y as usize, pos.z as usize)
|
|
||||||
.try_into()
|
|
||||||
.expect("Invalid block state.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue