Make the storage system used for chunk sections reusable
This commit is contained in:
parent
7588b20bd0
commit
94bfc1493b
101
src/world/mod.rs
101
src/world/mod.rs
|
@ -30,6 +30,7 @@ use entity::block_entity;
|
||||||
use format;
|
use format;
|
||||||
|
|
||||||
pub mod biome;
|
pub mod biome;
|
||||||
|
mod storage;
|
||||||
|
|
||||||
pub struct World {
|
pub struct World {
|
||||||
chunks: HashMap<CPos, Chunk, BuildHasherDefault<FNVHash>>,
|
chunks: HashMap<CPos, Chunk, BuildHasherDefault<FNVHash>>,
|
||||||
|
@ -589,8 +590,7 @@ impl World {
|
||||||
section.dirty = true;
|
section.dirty = true;
|
||||||
|
|
||||||
let mut bit_size = try!(data.read_u8());
|
let mut bit_size = try!(data.read_u8());
|
||||||
section.block_map.clear();
|
section.blocks.clear();
|
||||||
section.rev_block_map.clear();
|
|
||||||
if bit_size == 0 {
|
if bit_size == 0 {
|
||||||
bit_size = 13;
|
bit_size = 13;
|
||||||
} else {
|
} else {
|
||||||
|
@ -598,35 +598,19 @@ impl World {
|
||||||
for i in 0 .. count {
|
for i in 0 .. count {
|
||||||
let id = try!(VarInt::read_from(&mut data)).0;
|
let id = try!(VarInt::read_from(&mut data)).0;
|
||||||
let bl = block::Block::by_vanilla_id(id as usize);
|
let bl = block::Block::by_vanilla_id(id as usize);
|
||||||
section.block_map.push((bl, 0));
|
section.blocks.force_mapping(i as usize, bl);
|
||||||
section.rev_block_map.insert(bl, i as usize);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bits = try!(LenPrefixed::<VarInt, u64>::read_from(&mut data)).data;
|
let bits = try!(LenPrefixed::<VarInt, u64>::read_from(&mut data)).data;
|
||||||
let m = bit::Map::from_raw(bits, bit_size as usize);
|
let m = bit::Map::from_raw(bits, bit_size as usize);
|
||||||
|
|
||||||
section.blocks = m;
|
section.blocks.use_raw(m);
|
||||||
|
|
||||||
|
// Spawn block entities
|
||||||
for bi in 0 .. 4096 {
|
for bi in 0 .. 4096 {
|
||||||
let bl_id = section.blocks.get(bi);
|
let b = section.blocks.get(bi);
|
||||||
if bit_size == 13 {
|
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
|
||||||
if section.block_map.get(bl_id)
|
|
||||||
.map(|v| v.1)
|
|
||||||
.unwrap_or(0) == 0 {
|
|
||||||
if bl_id >= section.block_map.len() {
|
|
||||||
section.block_map.resize(bl_id + 1, (block::Air{}, 0xFFFFF)); // Impossible to reach this value normally
|
|
||||||
}
|
|
||||||
let bl = block::Block::by_vanilla_id(bl_id as usize);
|
|
||||||
section.block_map[bl_id] = (bl, 0);
|
|
||||||
section.rev_block_map.insert(bl, bl_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let bmap = section.block_map.get_mut(bl_id).unwrap();
|
|
||||||
if bmap.1 == 0xFFFFF {
|
|
||||||
bmap.1 = 0;
|
|
||||||
}
|
|
||||||
bmap.1 += 1;
|
|
||||||
if block_entity::BlockEntityType::get_block_entity(bmap.0).is_some() {
|
|
||||||
let pos = Position::new(
|
let pos = Position::new(
|
||||||
(bi & 0xF) as i32,
|
(bi & 0xF) as i32,
|
||||||
(bi >> 8) as i32,
|
(bi >> 8) as i32,
|
||||||
|
@ -639,12 +623,6 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for entry in §ion.block_map {
|
|
||||||
if entry.1 == 0xFFFFF {
|
|
||||||
section.rev_block_map.remove(&entry.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try!(data.read_exact(&mut section.block_light.data));
|
try!(data.read_exact(&mut section.block_light.data));
|
||||||
try!(data.read_exact(&mut section.sky_light.data));
|
try!(data.read_exact(&mut section.sky_light.data));
|
||||||
}
|
}
|
||||||
|
@ -933,9 +911,7 @@ pub struct Section {
|
||||||
|
|
||||||
y: u8,
|
y: u8,
|
||||||
|
|
||||||
blocks: bit::Map,
|
blocks: storage::BlockStorage,
|
||||||
block_map: Vec<(block::Block, u32)>,
|
|
||||||
rev_block_map: HashMap<block::Block, usize, BuildHasherDefault<FNVHash>>,
|
|
||||||
|
|
||||||
block_light: nibble::Array,
|
block_light: nibble::Array,
|
||||||
sky_light: nibble::Array,
|
sky_light: nibble::Array,
|
||||||
|
@ -951,11 +927,7 @@ impl Section {
|
||||||
render_buffer: render::ChunkBuffer::new(),
|
render_buffer: render::ChunkBuffer::new(),
|
||||||
y: y,
|
y: y,
|
||||||
|
|
||||||
blocks: bit::Map::new(4096, 4),
|
blocks: storage::BlockStorage::new(4096),
|
||||||
block_map: vec![
|
|
||||||
(block::Air{}, 0xFFFFFFFF)
|
|
||||||
],
|
|
||||||
rev_block_map: HashMap::with_hasher(BuildHasherDefault::default()),
|
|
||||||
|
|
||||||
block_light: nibble::Array::new(16 * 16 * 16),
|
block_light: nibble::Array::new(16 * 16 * 16),
|
||||||
sky_light: nibble::Array::new(16 * 16 * 16),
|
sky_light: nibble::Array::new(16 * 16 * 16),
|
||||||
|
@ -968,61 +940,22 @@ impl Section {
|
||||||
section.sky_light.set(i, 0xF);
|
section.sky_light.set(i, 0xF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
section.rev_block_map.insert(block::Air{}, 0);
|
|
||||||
section
|
section
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block(&self, x: i32, y: i32, z: i32) -> block::Block {
|
fn get_block(&self, x: i32, y: i32, z: i32) -> block::Block {
|
||||||
let idx = self.blocks.get(((y << 8) | (z << 4) | x) as usize);
|
self.blocks.get(((y << 8) | (z << 4) | x) as usize)
|
||||||
self.block_map[idx].0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) -> bool {
|
fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) -> bool {
|
||||||
use std::collections::hash_map::Entry;
|
if self.blocks.set(((y << 8) | (z << 4) | x) as usize, b) {
|
||||||
let old = self.get_block(x, y, z);
|
|
||||||
if old == b {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Clean up old block
|
|
||||||
{
|
|
||||||
let idx = self.rev_block_map[&old];
|
|
||||||
let info = &mut self.block_map[idx];
|
|
||||||
info.1 -= 1;
|
|
||||||
if info.1 == 0 { // None left of this type
|
|
||||||
self.rev_block_map.remove(&old);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Entry::Vacant(entry) = self.rev_block_map.entry(b) {
|
|
||||||
let mut found = false;
|
|
||||||
let id = entry.insert(self.block_map.len());
|
|
||||||
for (i, ref mut info) in self.block_map.iter_mut().enumerate() {
|
|
||||||
if info.1 == 0 {
|
|
||||||
info.0 = b;
|
|
||||||
*id = i;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
if self.block_map.len() >= 1 << self.blocks.bit_size {
|
|
||||||
let new_size = self.blocks.bit_size + 1;
|
|
||||||
self.blocks = self.blocks.resize(new_size);
|
|
||||||
}
|
|
||||||
self.block_map.push((b, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let idx = self.rev_block_map[&b];
|
|
||||||
let info = &mut self.block_map[idx];
|
|
||||||
info.1 += 1;
|
|
||||||
self.blocks.set(((y << 8) | (z << 4) | x) as usize, idx);
|
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
self.set_sky_light(x, y, z, 0);
|
||||||
|
self.set_block_light(x, y, z, 0);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
self.set_sky_light(x, y, z, 0);
|
|
||||||
self.set_block_light(x, y, z, 0);
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_light(&self, x: i32, y: i32, z: i32) -> u8 {
|
fn get_block_light(&self, x: i32, y: i32, z: i32) -> u8 {
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
|
||||||
|
use types::bit;
|
||||||
|
use types::hash::FNVHash;
|
||||||
|
use world::block;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::BuildHasherDefault;
|
||||||
|
|
||||||
|
pub struct BlockStorage {
|
||||||
|
blocks: bit::Map,
|
||||||
|
block_map: Vec<(block::Block, u32)>,
|
||||||
|
rev_block_map: HashMap<block::Block, usize, BuildHasherDefault<FNVHash>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockStorage {
|
||||||
|
pub fn new(size: usize) -> BlockStorage {
|
||||||
|
let mut storage = BlockStorage {
|
||||||
|
blocks: bit::Map::new(size, 4),
|
||||||
|
block_map: vec![
|
||||||
|
(block::Air{}, 0xFFFFFFFF)
|
||||||
|
],
|
||||||
|
rev_block_map: HashMap::with_hasher(BuildHasherDefault::default()),
|
||||||
|
};
|
||||||
|
storage.rev_block_map.insert(block::Air{}, 0);
|
||||||
|
storage
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, idx: usize) -> block::Block {
|
||||||
|
let idx = self.blocks.get(idx);
|
||||||
|
self.block_map.get(idx).map_or(block::Missing{}, |v| v.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, idx: usize, b: block::Block) -> bool {
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
let old = self.get(idx);
|
||||||
|
if old == b {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Clean up old block
|
||||||
|
{
|
||||||
|
let idx = self.rev_block_map[&old];
|
||||||
|
let info = &mut self.block_map[idx];
|
||||||
|
info.1 -= 1;
|
||||||
|
if info.1 == 0 { // None left of this type
|
||||||
|
self.rev_block_map.remove(&old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Entry::Vacant(entry) = self.rev_block_map.entry(b) {
|
||||||
|
let mut found = false;
|
||||||
|
let id = entry.insert(self.block_map.len());
|
||||||
|
for (i, ref mut info) in self.block_map.iter_mut().enumerate() {
|
||||||
|
if info.1 == 0 {
|
||||||
|
info.0 = b;
|
||||||
|
*id = i;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
if self.block_map.len() >= 1 << self.blocks.bit_size {
|
||||||
|
let new_size = self.blocks.bit_size + 1;
|
||||||
|
self.blocks = self.blocks.resize(new_size);
|
||||||
|
}
|
||||||
|
self.block_map.push((b, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let b_idx = self.rev_block_map[&b];
|
||||||
|
let info = &mut self.block_map[b_idx];
|
||||||
|
info.1 += 1;
|
||||||
|
self.blocks.set(idx, b_idx);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick chunk loading support helpers
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.block_map.clear();
|
||||||
|
self.rev_block_map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn force_mapping(&mut self, idx: usize, b: block::Block) {
|
||||||
|
if self.block_map.len() < idx {
|
||||||
|
self.block_map[idx] = (b, 0);
|
||||||
|
} else if self.block_map.len() == idx {
|
||||||
|
self.block_map.push((b, 0));
|
||||||
|
} else {
|
||||||
|
panic!("Out of bounds force mapping")
|
||||||
|
}
|
||||||
|
self.rev_block_map.insert(b, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn use_raw(&mut self, data: bit::Map) {
|
||||||
|
self.blocks = data;
|
||||||
|
// Recount blocks
|
||||||
|
for bi in 0 .. 4096 {
|
||||||
|
let bl_id = self.blocks.get(bi);
|
||||||
|
if self.blocks.bit_size == 13 { // Global palette
|
||||||
|
if self.block_map.get(bl_id)
|
||||||
|
.map(|v| v.1)
|
||||||
|
.unwrap_or(0) == 0 {
|
||||||
|
if bl_id >= self.block_map.len() {
|
||||||
|
self.block_map.resize(bl_id + 1, (block::Air{}, 0xFFFFF)); // Impossible to reach this value normally
|
||||||
|
}
|
||||||
|
let bl = block::Block::by_vanilla_id(bl_id as usize);
|
||||||
|
self.block_map[bl_id] = (bl, 0);
|
||||||
|
self.rev_block_map.insert(bl, bl_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let bmap = self.block_map.get_mut(bl_id).unwrap();
|
||||||
|
if bmap.1 == 0xFFFFF {
|
||||||
|
bmap.1 = 0;
|
||||||
|
}
|
||||||
|
bmap.1 += 1;
|
||||||
|
}
|
||||||
|
self.gc_entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gc_entries(&mut self) {
|
||||||
|
for entry in &mut self.block_map {
|
||||||
|
if entry.1 == 0xFFFFF {
|
||||||
|
println!("GC'd block");
|
||||||
|
self.rev_block_map.remove(&entry.0);
|
||||||
|
entry.1 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue