Refactor chunk section management to support 1.18
This commit is contained in:
parent
fe8d8ece9c
commit
a5b3b61989
388
src/world/mod.rs
388
src/world/mod.rs
|
@ -13,6 +13,10 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
pub use steven_blocks as block;
|
pub use steven_blocks as block;
|
||||||
|
use steven_protocol::protocol::LenPrefixed;
|
||||||
|
use steven_protocol::protocol::Serializable;
|
||||||
|
use steven_protocol::protocol::VarInt;
|
||||||
|
use steven_protocol::types::bit;
|
||||||
|
|
||||||
use crate::chunk_builder;
|
use crate::chunk_builder;
|
||||||
use crate::ecs;
|
use crate::ecs;
|
||||||
|
@ -22,7 +26,8 @@ use crate::protocol;
|
||||||
use crate::render;
|
use crate::render;
|
||||||
use crate::shared::{Direction, Position};
|
use crate::shared::{Direction, Position};
|
||||||
use crate::types::hash::FNVHash;
|
use crate::types::hash::FNVHash;
|
||||||
use crate::types::{bit, nibble};
|
use crate::types::{nibble};
|
||||||
|
use byteorder::ReadBytesExt;
|
||||||
use cgmath::prelude::*;
|
use cgmath::prelude::*;
|
||||||
use flate2::read::ZlibDecoder;
|
use flate2::read::ZlibDecoder;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
@ -31,6 +36,7 @@ use std::collections::HashMap;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
|
use std::io::Cursor;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
pub mod biome;
|
pub mod biome;
|
||||||
|
@ -415,7 +421,7 @@ impl World {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| {
|
.map(|v| {
|
||||||
let chunk = self.chunks.get(&CPos(v.0, v.2)).unwrap();
|
let chunk = self.chunks.get(&CPos(v.0, v.2)).unwrap();
|
||||||
let sec = chunk.sections[v.1 as usize].as_ref().unwrap();
|
let sec = chunk.sections.get(&v.1).unwrap();
|
||||||
(*v, &sec.render_buffer)
|
(*v, &sec.render_buffer)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -423,7 +429,7 @@ impl World {
|
||||||
|
|
||||||
pub fn get_section_mut(&mut self, x: i32, y: i32, z: i32) -> Option<&mut Section> {
|
pub fn get_section_mut(&mut self, x: i32, y: i32, z: i32) -> Option<&mut Section> {
|
||||||
if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) {
|
if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) {
|
||||||
if let Some(sec) = chunk.sections[y as usize].as_mut() {
|
if let Some(sec) = chunk.sections.get_mut(&y) {
|
||||||
return Some(sec);
|
return Some(sec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,7 +447,7 @@ impl World {
|
||||||
}
|
}
|
||||||
if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) {
|
if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) {
|
||||||
let rendered = &mut chunk.sections_rendered_on[y as usize];
|
let rendered = &mut chunk.sections_rendered_on[y as usize];
|
||||||
if let Some(sec) = chunk.sections[y as usize].as_mut() {
|
if let Some(sec) = chunk.sections.get_mut(&y) {
|
||||||
return Some((Some(sec), rendered));
|
return Some((Some(sec), rendered));
|
||||||
}
|
}
|
||||||
return Some((None, rendered));
|
return Some((None, rendered));
|
||||||
|
@ -452,11 +458,9 @@ impl World {
|
||||||
pub fn get_dirty_chunk_sections(&mut self) -> Vec<(i32, i32, i32)> {
|
pub fn get_dirty_chunk_sections(&mut self) -> Vec<(i32, i32, i32)> {
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
for chunk in self.chunks.values_mut() {
|
for chunk in self.chunks.values_mut() {
|
||||||
for sec in &mut chunk.sections {
|
for (y, sec) in &mut chunk.sections {
|
||||||
if let Some(sec) = sec.as_mut() {
|
if !sec.building && sec.dirty {
|
||||||
if !sec.building && sec.dirty {
|
out.push((chunk.position.0, *y, chunk.position.1));
|
||||||
out.push((chunk.position.0, sec.y as i32, chunk.position.1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -465,7 +469,7 @@ impl World {
|
||||||
|
|
||||||
fn set_dirty(&mut self, x: i32, y: i32, z: i32) {
|
fn set_dirty(&mut self, x: i32, y: i32, z: i32) {
|
||||||
if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) {
|
if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) {
|
||||||
if let Some(sec) = chunk.sections.get_mut(y as usize).and_then(|v| v.as_mut()) {
|
if let Some(sec) = chunk.sections.get_mut(&y) {
|
||||||
sec.dirty = true;
|
sec.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,7 +477,7 @@ impl World {
|
||||||
|
|
||||||
pub fn is_section_dirty(&self, pos: (i32, i32, i32)) -> bool {
|
pub fn is_section_dirty(&self, pos: (i32, i32, i32)) -> bool {
|
||||||
if let Some(chunk) = self.chunks.get(&CPos(pos.0, pos.2)) {
|
if let Some(chunk) = self.chunks.get(&CPos(pos.0, pos.2)) {
|
||||||
if let Some(sec) = chunk.sections[pos.1 as usize].as_ref() {
|
if let Some(sec) = chunk.sections.get(&pos.1) {
|
||||||
return sec.dirty && !sec.building;
|
return sec.dirty && !sec.building;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,7 +486,7 @@ impl World {
|
||||||
|
|
||||||
pub fn set_building_flag(&mut self, pos: (i32, i32, i32)) {
|
pub fn set_building_flag(&mut self, pos: (i32, i32, i32)) {
|
||||||
if let Some(chunk) = self.chunks.get_mut(&CPos(pos.0, pos.2)) {
|
if let Some(chunk) = self.chunks.get_mut(&CPos(pos.0, pos.2)) {
|
||||||
if let Some(sec) = chunk.sections[pos.1 as usize].as_mut() {
|
if let Some(sec) = chunk.sections.get_mut(&pos.1) {
|
||||||
sec.building = true;
|
sec.building = true;
|
||||||
sec.dirty = false;
|
sec.dirty = false;
|
||||||
}
|
}
|
||||||
|
@ -491,7 +495,7 @@ impl World {
|
||||||
|
|
||||||
pub fn reset_building_flag(&mut self, pos: (i32, i32, i32)) {
|
pub fn reset_building_flag(&mut self, pos: (i32, i32, i32)) {
|
||||||
if let Some(chunk) = self.chunks.get_mut(&CPos(pos.0, pos.2)) {
|
if let Some(chunk) = self.chunks.get_mut(&CPos(pos.0, pos.2)) {
|
||||||
if let Some(section) = chunk.sections[pos.1 as usize].as_mut() {
|
if let Some(section) = chunk.sections.get_mut(&pos.1) {
|
||||||
section.building = false;
|
section.building = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -499,10 +503,8 @@ impl World {
|
||||||
|
|
||||||
pub fn flag_dirty_all(&mut self) {
|
pub fn flag_dirty_all(&mut self) {
|
||||||
for chunk in self.chunks.values_mut() {
|
for chunk in self.chunks.values_mut() {
|
||||||
for sec in &mut chunk.sections {
|
for sec in chunk.sections.values_mut() {
|
||||||
if let Some(sec) = sec.as_mut() {
|
sec.dirty = true;
|
||||||
sec.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,7 +551,7 @@ impl World {
|
||||||
if !(0..=15).contains(&cy) {
|
if !(0..=15).contains(&cy) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let section = &chunk.sections[cy as usize];
|
let section = chunk.sections.get(&cy);
|
||||||
let y1 = min(16, max(0, y - (cy << 4)));
|
let y1 = min(16, max(0, y - (cy << 4)));
|
||||||
let y2 = min(16, max(0, y + h - (cy << 4)));
|
let y2 = min(16, max(0, y + h - (cy << 4)));
|
||||||
|
|
||||||
|
@ -661,8 +663,6 @@ impl World {
|
||||||
mask: u16,
|
mask: u16,
|
||||||
data: &mut std::io::Cursor<Vec<u8>>,
|
data: &mut std::io::Cursor<Vec<u8>>,
|
||||||
) -> Result<(), protocol::Error> {
|
) -> Result<(), protocol::Error> {
|
||||||
use byteorder::ReadBytesExt;
|
|
||||||
|
|
||||||
let cpos = CPos(x, z);
|
let cpos = CPos(x, z);
|
||||||
{
|
{
|
||||||
if new {
|
if new {
|
||||||
|
@ -673,17 +673,17 @@ impl World {
|
||||||
let chunk = self.chunks.get_mut(&cpos).unwrap();
|
let chunk = self.chunks.get_mut(&cpos).unwrap();
|
||||||
|
|
||||||
for i in 0..16 {
|
for i in 0..16 {
|
||||||
if chunk.sections[i].is_none() {
|
if !chunk.sections.contains_key(&i) {
|
||||||
let mut fill_sky = chunk.sections.iter().skip(i).all(|v| v.is_none());
|
let mut fill_sky = chunk.sections.keys().any(|s_idx| *s_idx > i);
|
||||||
fill_sky &= (mask & !((1 << i) | ((1 << i) - 1))) == 0;
|
fill_sky &= (mask & !((1 << i) | ((1 << i) - 1))) == 0;
|
||||||
if !fill_sky || mask & (1 << i) != 0 {
|
if !fill_sky || mask & (1 << i) != 0 {
|
||||||
chunk.sections[i] = Some(Section::new(i as u8, fill_sky));
|
chunk.sections.insert(i, Section::new(fill_sky));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mask & (1 << i) == 0 {
|
if mask & (1 << i) == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
let section = chunk.sections.get_mut(&i).unwrap();
|
||||||
section.dirty = true;
|
section.dirty = true;
|
||||||
|
|
||||||
for bi in 0..4096 {
|
for bi in 0..4096 {
|
||||||
|
@ -720,7 +720,7 @@ impl World {
|
||||||
if mask & (1 << i) == 0 {
|
if mask & (1 << i) == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
let section = chunk.sections.get_mut(&i).unwrap();
|
||||||
|
|
||||||
data.read_exact(&mut section.block_light.data)?;
|
data.read_exact(&mut section.block_light.data)?;
|
||||||
}
|
}
|
||||||
|
@ -729,7 +729,7 @@ impl World {
|
||||||
if mask & (1 << i) == 0 {
|
if mask & (1 << i) == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
let section = chunk.sections.get_mut(&i).unwrap();
|
||||||
|
|
||||||
data.read_exact(&mut section.sky_light.data)?;
|
data.read_exact(&mut section.sky_light.data)?;
|
||||||
}
|
}
|
||||||
|
@ -764,8 +764,6 @@ impl World {
|
||||||
// Chunk metadata
|
// Chunk metadata
|
||||||
let mut metadata = std::io::Cursor::new(metadata);
|
let mut metadata = std::io::Cursor::new(metadata);
|
||||||
for _i in 0..chunk_column_count {
|
for _i in 0..chunk_column_count {
|
||||||
use byteorder::ReadBytesExt;
|
|
||||||
|
|
||||||
let x = metadata.read_i32::<byteorder::BigEndian>()?;
|
let x = metadata.read_i32::<byteorder::BigEndian>()?;
|
||||||
let z = metadata.read_i32::<byteorder::BigEndian>()?;
|
let z = metadata.read_i32::<byteorder::BigEndian>()?;
|
||||||
let mask = metadata.read_u16::<byteorder::BigEndian>()?;
|
let mask = metadata.read_u16::<byteorder::BigEndian>()?;
|
||||||
|
@ -827,20 +825,20 @@ impl World {
|
||||||
// Block type array - whole byte per block
|
// Block type array - whole byte per block
|
||||||
let mut block_types = [[0u8; 4096]; 16];
|
let mut block_types = [[0u8; 4096]; 16];
|
||||||
for i in 0..16 {
|
for i in 0..16 {
|
||||||
if chunk.sections[i].is_none() {
|
if !chunk.sections.contains_key(&i) {
|
||||||
let mut fill_sky = chunk.sections.iter().skip(i).all(|v| v.is_none());
|
let mut fill_sky = chunk.sections.keys().any(|s_idx| *s_idx > i);
|
||||||
fill_sky &= (mask & !((1 << i) | ((1 << i) - 1))) == 0;
|
fill_sky &= (mask & !((1 << i) | ((1 << i) - 1))) == 0;
|
||||||
if !fill_sky || mask & (1 << i) != 0 {
|
if !fill_sky || mask & (1 << i) != 0 {
|
||||||
chunk.sections[i] = Some(Section::new(i as u8, fill_sky));
|
chunk.sections.insert(i, Section::new(fill_sky));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mask & (1 << i) == 0 {
|
if mask & (1 << i) == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
let section = chunk.sections.get_mut(&i).unwrap();
|
||||||
section.dirty = true;
|
section.dirty = true;
|
||||||
|
|
||||||
data.read_exact(&mut block_types[i])?;
|
data.read_exact(&mut block_types[i as usize])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block metadata array - half byte per block
|
// Block metadata array - half byte per block
|
||||||
|
@ -877,7 +875,7 @@ impl World {
|
||||||
if mask & (1 << i) == 0 {
|
if mask & (1 << i) == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
let section = chunk.sections.get_mut(&i).unwrap();
|
||||||
|
|
||||||
data.read_exact(&mut section.block_light.data)?;
|
data.read_exact(&mut section.block_light.data)?;
|
||||||
}
|
}
|
||||||
|
@ -888,7 +886,7 @@ impl World {
|
||||||
if mask & (1 << i) == 0 {
|
if mask & (1 << i) == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
let section = chunk.sections.get_mut(&i).unwrap();
|
||||||
|
|
||||||
data.read_exact(&mut section.sky_light.data)?;
|
data.read_exact(&mut section.sky_light.data)?;
|
||||||
}
|
}
|
||||||
|
@ -928,7 +926,7 @@ impl World {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
let section = chunk.sections.get_mut(&(i as i32)).unwrap();
|
||||||
|
|
||||||
for bi in 0..4096 {
|
for bi in 0..4096 {
|
||||||
let id = ((block_add[i].get(bi) as u16) << 12)
|
let id = ((block_add[i].get(bi) as u16) << 12)
|
||||||
|
@ -1029,10 +1027,6 @@ impl World {
|
||||||
num_sections: usize,
|
num_sections: usize,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
) -> Result<(), protocol::Error> {
|
) -> Result<(), protocol::Error> {
|
||||||
use crate::protocol::{LenPrefixed, Serializable, VarInt};
|
|
||||||
use byteorder::ReadBytesExt;
|
|
||||||
use std::io::Cursor;
|
|
||||||
|
|
||||||
let mut data = Cursor::new(data);
|
let mut data = Cursor::new(data);
|
||||||
|
|
||||||
let cpos = CPos(x, z);
|
let cpos = CPos(x, z);
|
||||||
|
@ -1044,57 +1038,8 @@ impl World {
|
||||||
}
|
}
|
||||||
let chunk = self.chunks.get_mut(&cpos).unwrap();
|
let chunk = self.chunks.get_mut(&cpos).unwrap();
|
||||||
|
|
||||||
for i1 in 0..num_sections {
|
for i1 in 0..num_sections as i32 {
|
||||||
let i: i32 = (i1 as i32) + (self.min_y >> 4);
|
let i: i32 = (i1 as i32) + (self.min_y >> 4);
|
||||||
if i < 0 {
|
|
||||||
// TODO: support y<0 in the world (needs shifting in all section access)
|
|
||||||
let block_count = data.read_u16::<byteorder::LittleEndian>()?;
|
|
||||||
let bit_size = data.read_u8()?;
|
|
||||||
let single_value = Some(VarInt::read_from(&mut data)?.0);
|
|
||||||
if bit_size != 0 || single_value != Some(0) || block_count != 0 {
|
|
||||||
panic!("TODO: support chunk data y<0 non-air (bit_size {}, single_value {:?}, block_count {})", bit_size, single_value, block_count);
|
|
||||||
}
|
|
||||||
let _bits = LenPrefixed::<VarInt, u64>::read_from(&mut data)?.data;
|
|
||||||
|
|
||||||
// biome
|
|
||||||
let _bit_size = data.read_u8()?;
|
|
||||||
if _bit_size == 0 {
|
|
||||||
let _single_value = VarInt::read_from(&mut data)?.0;
|
|
||||||
} else {
|
|
||||||
if bit_size >= 4 {
|
|
||||||
panic!(
|
|
||||||
"TODO: handle direct palettes, bit_size {} >= 4 for biomes",
|
|
||||||
bit_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = VarInt::read_from(&mut data)?.0;
|
|
||||||
for _i in 0..count {
|
|
||||||
let _id = VarInt::read_from(&mut data)?.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _bits = LenPrefixed::<VarInt, u64>::read_from(&mut data)?.data;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let i = i as usize;
|
|
||||||
|
|
||||||
if chunk.sections[i].is_none() {
|
|
||||||
let mut fill_sky = chunk.sections.iter().skip(i).all(|v| v.is_none());
|
|
||||||
fill_sky &= (mask & !((1 << i) | ((1 << i) - 1))) == 0;
|
|
||||||
if self.protocol_version >= 757 {
|
|
||||||
// TODO: fix conditionalizing fill sky on 1.18+
|
|
||||||
fill_sky = true;
|
|
||||||
}
|
|
||||||
if !fill_sky || mask & (1 << i) != 0 {
|
|
||||||
chunk.sections[i] = Some(Section::new(i as u8, fill_sky));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if mask & (1 << i) == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let section = chunk.sections[i as usize].as_mut().unwrap();
|
|
||||||
section.dirty = true;
|
|
||||||
|
|
||||||
if self.protocol_version >= 451 {
|
if self.protocol_version >= 451 {
|
||||||
let _block_count = data.read_u16::<byteorder::LittleEndian>()?;
|
let _block_count = data.read_u16::<byteorder::LittleEndian>()?;
|
||||||
|
@ -1103,52 +1048,10 @@ impl World {
|
||||||
// section is not rendered, even if it still has blocks." per https://wiki.vg/Chunk_Format#Data_structure
|
// section is not rendered, even if it still has blocks." per https://wiki.vg/Chunk_Format#Data_structure
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bit_size = data.read_u8()?;
|
let palette = PaletteParser::new(self.protocol_version, PaletteKind::BlockStates, &mut data).parse()?;
|
||||||
let mut mappings: HashMap<usize, block::Block, BuildHasherDefault<FNVHash>> =
|
let mut section = SectionParser::new(self.protocol_version, palette, &self.id_map, &self.modded_block_ids, &mut data).parse()?;
|
||||||
HashMap::with_hasher(BuildHasherDefault::default());
|
|
||||||
let mut single_value: Option<usize> = None;
|
|
||||||
if bit_size == 0 {
|
|
||||||
if self.protocol_version >= 757 {
|
|
||||||
// Single-valued palette
|
|
||||||
single_value = Some(VarInt::read_from(&mut data)?.0.try_into().unwrap());
|
|
||||||
} else {
|
|
||||||
bit_size = 13;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let count = VarInt::read_from(&mut data)?.0;
|
|
||||||
if bit_size >= 9 {
|
|
||||||
panic!(
|
|
||||||
"TODO: handle direct palettes, bit_size {} >= 9 for block states",
|
|
||||||
bit_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for i in 0..count {
|
|
||||||
let id = VarInt::read_from(&mut data)?.0;
|
|
||||||
let bl = self
|
|
||||||
.id_map
|
|
||||||
.by_vanilla_id(id as usize, &self.modded_block_ids);
|
|
||||||
mappings.insert(i as usize, bl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bit_size > 16 {
|
|
||||||
// https://wiki.vg/Chunk_Format#Data_structure "This increase can go up to 16 bits per block"...
|
|
||||||
panic!("load_chunk19_to_117: bit_size={:?} > 16", bit_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
let bits = LenPrefixed::<VarInt, u64>::read_from(&mut data)?.data;
|
|
||||||
let padded = self.protocol_version >= 736;
|
|
||||||
let m = bit::Map::from_raw(bits, bit_size as usize, padded);
|
|
||||||
|
|
||||||
for bi in 0..4096 {
|
for bi in 0..4096 {
|
||||||
let id = single_value.unwrap_or_else(|| m.get(bi));
|
|
||||||
section.blocks.set(
|
|
||||||
bi,
|
|
||||||
mappings
|
|
||||||
.get(&id)
|
|
||||||
.cloned()
|
|
||||||
// TODO: fix or_fun_call, but do not re-borrow self
|
|
||||||
.unwrap_or(self.id_map.by_vanilla_id(id, &self.modded_block_ids)),
|
|
||||||
);
|
|
||||||
// Spawn block entities
|
// Spawn block entities
|
||||||
let b = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
||||||
|
@ -1170,36 +1073,20 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version 1.18+
|
||||||
if self.protocol_version >= 757 {
|
if self.protocol_version >= 757 {
|
||||||
// Biomes palette TODO: refactor with block states, "palette container"
|
let _palette = PaletteParser::new(self.protocol_version, PaletteKind::Biomes, &mut data).parse()?;
|
||||||
let _bit_size = data.read_u8()?;
|
|
||||||
if _bit_size == 0 {
|
|
||||||
// Single-valued palette
|
|
||||||
let _single_value = VarInt::read_from(&mut data)?.0;
|
|
||||||
} else {
|
|
||||||
if bit_size >= 4 {
|
|
||||||
panic!(
|
|
||||||
"TODO: handle direct palettes, bit_size {} >= 4 for biomes",
|
|
||||||
bit_size
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = VarInt::read_from(&mut data)?.0;
|
|
||||||
for _i in 0..count {
|
|
||||||
let _id = VarInt::read_from(&mut data)?.0;
|
|
||||||
//let bl = self
|
|
||||||
// .id_map
|
|
||||||
// .by_vanilla_id(id as usize, &self.modded_block_ids);
|
|
||||||
//mappings.insert(i as usize, bl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _bits = LenPrefixed::<VarInt, u64>::read_from(&mut data)?.data;
|
let _bits = LenPrefixed::<VarInt, u64>::read_from(&mut data)?.data;
|
||||||
|
// TODO: Use biome data
|
||||||
|
// Version 1.14 - 1.17
|
||||||
} else if self.protocol_version >= 451 {
|
} else if self.protocol_version >= 451 {
|
||||||
// Skylight in update skylight packet for 1.14+
|
// Skylight in update skylight packet for 1.14+
|
||||||
} else {
|
} else {
|
||||||
data.read_exact(&mut section.block_light.data)?;
|
data.read_exact(&mut section.block_light.data)?;
|
||||||
data.read_exact(&mut section.sky_light.data)?;
|
data.read_exact(&mut section.sky_light.data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chunk.sections.insert(i, section);
|
||||||
}
|
}
|
||||||
|
|
||||||
if read_biomes && new {
|
if read_biomes && new {
|
||||||
|
@ -1219,7 +1106,7 @@ impl World {
|
||||||
}
|
}
|
||||||
let cpos = CPos(x, z);
|
let cpos = CPos(x, z);
|
||||||
if let Some(chunk) = self.chunks.get_mut(&cpos) {
|
if let Some(chunk) = self.chunks.get_mut(&cpos) {
|
||||||
if let Some(sec) = chunk.sections[y as usize].as_mut() {
|
if let Some(sec) = chunk.sections.get_mut(&y) {
|
||||||
sec.dirty = true;
|
sec.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1300,7 +1187,7 @@ pub struct CPos(i32, i32);
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
position: CPos,
|
position: CPos,
|
||||||
|
|
||||||
sections: [Option<Section>; 16],
|
sections: HashMap<i32, Section>,
|
||||||
sections_rendered_on: [u32; 16],
|
sections_rendered_on: [u32; 16],
|
||||||
biomes: [u8; 16 * 16],
|
biomes: [u8; 16 * 16],
|
||||||
|
|
||||||
|
@ -1314,10 +1201,7 @@ impl Chunk {
|
||||||
fn new(pos: CPos) -> Chunk {
|
fn new(pos: CPos) -> Chunk {
|
||||||
Chunk {
|
Chunk {
|
||||||
position: pos,
|
position: pos,
|
||||||
sections: [
|
sections: HashMap::new(),
|
||||||
None, None, None, None, None, None, None, None, None, None, None, None, None, None,
|
|
||||||
None, None,
|
|
||||||
],
|
|
||||||
sections_rendered_on: [0; 16],
|
sections_rendered_on: [0; 16],
|
||||||
biomes: [0; 16 * 16],
|
biomes: [0; 16 * 16],
|
||||||
heightmap: [0; 16 * 16],
|
heightmap: [0; 16 * 16],
|
||||||
|
@ -1348,16 +1232,16 @@ impl Chunk {
|
||||||
if !(0..=15).contains(&s_idx) {
|
if !(0..=15).contains(&s_idx) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let s_idx = s_idx as usize;
|
if !self.sections.contains_key(&s_idx) {
|
||||||
if self.sections[s_idx].is_none() {
|
|
||||||
if let block::Air {} = b {
|
if let block::Air {} = b {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let fill_sky = self.sections.iter().skip(s_idx).all(|v| v.is_none());
|
// Fill the sky if there aren't any chunks above this one.
|
||||||
self.sections[s_idx] = Some(Section::new(s_idx as u8, fill_sky));
|
let fill_sky = self.sections.keys().any(|s_idx2| *s_idx2 > s_idx);
|
||||||
|
self.sections.insert(s_idx, Section::new(fill_sky));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let section = self.sections[s_idx as usize].as_mut().unwrap();
|
let section = self.sections.get_mut(&s_idx).unwrap();
|
||||||
if !section.set_block(x, y & 0xF, z, b) {
|
if !section.set_block(x, y & 0xF, z, b) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1390,7 +1274,7 @@ impl Chunk {
|
||||||
if !(0..=15).contains(&s_idx) {
|
if !(0..=15).contains(&s_idx) {
|
||||||
return block::Missing {};
|
return block::Missing {};
|
||||||
}
|
}
|
||||||
match self.sections[s_idx as usize].as_ref() {
|
match self.sections.get(&s_idx) {
|
||||||
Some(sec) => sec.get_block(x, y & 0xF, z),
|
Some(sec) => sec.get_block(x, y & 0xF, z),
|
||||||
None => block::Air {},
|
None => block::Air {},
|
||||||
}
|
}
|
||||||
|
@ -1401,7 +1285,7 @@ impl Chunk {
|
||||||
if !(0..=15).contains(&s_idx) {
|
if !(0..=15).contains(&s_idx) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
match self.sections[s_idx as usize].as_ref() {
|
match self.sections.get(&s_idx) {
|
||||||
Some(sec) => sec.get_block_light(x, y & 0xF, z),
|
Some(sec) => sec.get_block_light(x, y & 0xF, z),
|
||||||
None => 0,
|
None => 0,
|
||||||
}
|
}
|
||||||
|
@ -1412,15 +1296,15 @@ impl Chunk {
|
||||||
if !(0..=15).contains(&s_idx) {
|
if !(0..=15).contains(&s_idx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let s_idx = s_idx as usize;
|
if !self.sections.contains_key(&s_idx) {
|
||||||
if self.sections[s_idx].is_none() {
|
|
||||||
if light == 0 {
|
if light == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let fill_sky = self.sections.iter().skip(s_idx).all(|v| v.is_none());
|
// Fill the sky if there aren't any chunks above this one.
|
||||||
self.sections[s_idx] = Some(Section::new(s_idx as u8, fill_sky));
|
let fill_sky = self.sections.keys().any(|s_idx2| *s_idx2 > s_idx);
|
||||||
|
self.sections.insert(s_idx, Section::new(fill_sky));
|
||||||
}
|
}
|
||||||
if let Some(sec) = self.sections[s_idx].as_mut() {
|
if let Some(sec) = self.sections.get_mut(&s_idx) {
|
||||||
sec.set_block_light(x, y & 0xF, z, light)
|
sec.set_block_light(x, y & 0xF, z, light)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1430,7 +1314,7 @@ impl Chunk {
|
||||||
if !(0..=15).contains(&s_idx) {
|
if !(0..=15).contains(&s_idx) {
|
||||||
return 15;
|
return 15;
|
||||||
}
|
}
|
||||||
match self.sections[s_idx as usize].as_ref() {
|
match self.sections.get(&s_idx) {
|
||||||
Some(sec) => sec.get_sky_light(x, y & 0xF, z),
|
Some(sec) => sec.get_sky_light(x, y & 0xF, z),
|
||||||
None => 15,
|
None => 15,
|
||||||
}
|
}
|
||||||
|
@ -1441,15 +1325,15 @@ impl Chunk {
|
||||||
if !(0..=15).contains(&s_idx) {
|
if !(0..=15).contains(&s_idx) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let s_idx = s_idx as usize;
|
if !self.sections.contains_key(&s_idx) {
|
||||||
if self.sections[s_idx].is_none() {
|
|
||||||
if light == 15 {
|
if light == 15 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let fill_sky = self.sections.iter().skip(s_idx).all(|v| v.is_none());
|
// Fill the sky if there aren't any chunks above this one.
|
||||||
self.sections[s_idx] = Some(Section::new(s_idx as u8, fill_sky));
|
let fill_sky = self.sections.keys().any(|s_idx2| *s_idx2 > s_idx);
|
||||||
|
self.sections.insert(s_idx, Section::new(fill_sky));
|
||||||
}
|
}
|
||||||
if let Some(sec) = self.sections[s_idx as usize].as_mut() {
|
if let Some(sec) = self.sections.get_mut(&s_idx) {
|
||||||
sec.set_sky_light(x, y & 0xF, z, light)
|
sec.set_sky_light(x, y & 0xF, z, light)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1463,8 +1347,6 @@ pub struct Section {
|
||||||
pub cull_info: chunk_builder::CullInfo,
|
pub cull_info: chunk_builder::CullInfo,
|
||||||
pub render_buffer: render::ChunkBuffer,
|
pub render_buffer: render::ChunkBuffer,
|
||||||
|
|
||||||
y: u8,
|
|
||||||
|
|
||||||
blocks: storage::BlockStorage,
|
blocks: storage::BlockStorage,
|
||||||
|
|
||||||
block_light: nibble::Array,
|
block_light: nibble::Array,
|
||||||
|
@ -1475,11 +1357,10 @@ pub struct Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Section {
|
impl Section {
|
||||||
fn new(y: u8, fill_sky: bool) -> Section {
|
fn new(fill_sky: bool) -> Section {
|
||||||
let mut section = Section {
|
let mut section = Section {
|
||||||
cull_info: chunk_builder::CullInfo::all_vis(),
|
cull_info: chunk_builder::CullInfo::all_vis(),
|
||||||
render_buffer: render::ChunkBuffer::new(),
|
render_buffer: render::ChunkBuffer::new(),
|
||||||
y,
|
|
||||||
|
|
||||||
blocks: storage::BlockStorage::new(4096),
|
blocks: storage::BlockStorage::new(4096),
|
||||||
|
|
||||||
|
@ -1528,3 +1409,142 @@ impl Section {
|
||||||
self.sky_light.set(((y << 8) | (z << 4) | x) as usize, l);
|
self.sky_light.set(((y << 8) | (z << 4) | x) as usize, l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The kind of palette we are reading. This can affect how we interpret bits
|
||||||
|
/// per entry which is different between block states and biomes.
|
||||||
|
enum PaletteKind {
|
||||||
|
BlockStates,
|
||||||
|
Biomes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// These are the different formats the palette data can be interpreted as.
|
||||||
|
/// See https://wiki.vg/Chunk_Format#Palettes for more info.
|
||||||
|
enum PaletteFormat {
|
||||||
|
SingleValued(usize),
|
||||||
|
Indirect(Vec<usize>, u8),
|
||||||
|
Direct(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A struct to manage building a palette that can be used to resolve block
|
||||||
|
/// states or biomes inside a chunk section.
|
||||||
|
struct PaletteParser<'a> {
|
||||||
|
protocol_version: i32,
|
||||||
|
kind: PaletteKind,
|
||||||
|
data: &'a mut Cursor<Vec<u8>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaletteParser<'_> {
|
||||||
|
pub fn new(protocol_version: i32, kind: PaletteKind, data: &mut Cursor<Vec<u8>>) -> PaletteParser<'_> {
|
||||||
|
PaletteParser {
|
||||||
|
protocol_version,
|
||||||
|
kind,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_single_valued_palette(&mut self) -> Result<PaletteFormat, protocol::Error> {
|
||||||
|
Ok(PaletteFormat::SingleValued(VarInt::read_from(self.data)?.0 as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_indirect_palette(&mut self, bits_per_entry: u8) -> Result<PaletteFormat, protocol::Error> {
|
||||||
|
let count = VarInt::read_from(self.data)?.0 as usize;
|
||||||
|
let mut mapping = Vec::with_capacity(count);
|
||||||
|
|
||||||
|
for _i in 0..count {
|
||||||
|
mapping.push(VarInt::read_from(self.data)?.0 as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PaletteFormat::Indirect(mapping, bits_per_entry))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(mut self) -> Result<PaletteFormat, protocol::Error> {
|
||||||
|
let mut bits_per_entry = self.data.read_u8()?;
|
||||||
|
|
||||||
|
if self.protocol_version < 757 && bits_per_entry == 0 {
|
||||||
|
bits_per_entry = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out how we should interpret the palette based on bits_per_entry.
|
||||||
|
Ok(match self.kind {
|
||||||
|
PaletteKind::BlockStates => match bits_per_entry {
|
||||||
|
0 => self.parse_single_valued_palette()?,
|
||||||
|
n if (1..9).contains(&n) => self.parse_indirect_palette(n.max(4))?,
|
||||||
|
n if (9..17).contains(&n) => PaletteFormat::Direct(n),
|
||||||
|
// https://wiki.vg/Chunk_Format#Data_structure "This increase can go up to 16 bits per block"...
|
||||||
|
n => panic!("PaletteParser::parse: block state bits_per_entry={:?} > 16", n),
|
||||||
|
},
|
||||||
|
PaletteKind::Biomes => match bits_per_entry {
|
||||||
|
0 => self.parse_single_valued_palette()?,
|
||||||
|
n if (1..4).contains(&n) => self.parse_indirect_palette(n)?,
|
||||||
|
n if (4..17).contains(&n) => PaletteFormat::Direct(n),
|
||||||
|
n => panic!("PaletteParser::parse: biome bits_per_entry={:?} > 16", n),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A struct to manage building a chunk section struct from a palette and the
|
||||||
|
/// data received from the server.
|
||||||
|
struct SectionParser<'a> {
|
||||||
|
protocol_version: i32,
|
||||||
|
palette: PaletteFormat,
|
||||||
|
id_map: &'a block::VanillaIDMap,
|
||||||
|
modded_block_ids: &'a HashMap<usize, String>,
|
||||||
|
data: &'a mut Cursor<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SectionParser<'_> {
|
||||||
|
pub fn new<'a>(
|
||||||
|
protocol_version: i32,
|
||||||
|
palette: PaletteFormat,
|
||||||
|
id_map: &'a block::VanillaIDMap,
|
||||||
|
modded_block_ids: &'a HashMap<usize, String>,
|
||||||
|
data: &'a mut Cursor<Vec<u8>>
|
||||||
|
) -> SectionParser<'a> {
|
||||||
|
SectionParser {
|
||||||
|
protocol_version,
|
||||||
|
palette,
|
||||||
|
id_map,
|
||||||
|
modded_block_ids,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(self) -> Result<Section, protocol::Error> {
|
||||||
|
// TODO: Figure out skylight
|
||||||
|
// TODO: Figure out dirty
|
||||||
|
let mut section = Section::new(true);
|
||||||
|
section.dirty = true;
|
||||||
|
|
||||||
|
let bits = LenPrefixed::<VarInt, u64>::read_from(self.data)?.data;
|
||||||
|
let padded = self.protocol_version >= 736;
|
||||||
|
|
||||||
|
match self.palette {
|
||||||
|
PaletteFormat::SingleValued(id) => {
|
||||||
|
let block = self.id_map.by_vanilla_id(id, self.modded_block_ids);
|
||||||
|
for i in 0..4096 {
|
||||||
|
section.blocks.set(i, block);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PaletteFormat::Indirect(mapping, bits_per_entry) => {
|
||||||
|
let entries = bit::Map::from_raw(bits, bits_per_entry as usize, padded);
|
||||||
|
for i in 0..4096 {
|
||||||
|
let index = entries.get(i);
|
||||||
|
let id = *mapping.get(index).unwrap();
|
||||||
|
let block = self.id_map.by_vanilla_id(id, self.modded_block_ids);
|
||||||
|
section.blocks.set(i, block);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PaletteFormat::Direct(bits_per_entry) => {
|
||||||
|
let entries = bit::Map::from_raw(bits, bits_per_entry as usize, padded);
|
||||||
|
for i in 0..4096 {
|
||||||
|
let id = entries.get(i);
|
||||||
|
let block = self.id_map.by_vanilla_id(id, self.modded_block_ids);
|
||||||
|
section.blocks.set(i, block);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(section)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue