Make the storage system used for chunk sections reusable

This commit is contained in:
Thinkofname 2016-04-20 19:25:51 +01:00
parent 7588b20bd0
commit 94bfc1493b
2 changed files with 148 additions and 84 deletions

View File

@ -30,6 +30,7 @@ use entity::block_entity;
use format;
pub mod biome;
mod storage;
pub struct World {
chunks: HashMap<CPos, Chunk, BuildHasherDefault<FNVHash>>,
@ -589,8 +590,7 @@ impl World {
section.dirty = true;
let mut bit_size = try!(data.read_u8());
section.block_map.clear();
section.rev_block_map.clear();
section.blocks.clear();
if bit_size == 0 {
bit_size = 13;
} else {
@ -598,35 +598,19 @@ impl World {
for i in 0 .. count {
let id = try!(VarInt::read_from(&mut data)).0;
let bl = block::Block::by_vanilla_id(id as usize);
section.block_map.push((bl, 0));
section.rev_block_map.insert(bl, i as usize);
section.blocks.force_mapping(i as usize, bl);
}
}
let bits = try!(LenPrefixed::<VarInt, u64>::read_from(&mut data)).data;
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 {
let bl_id = section.blocks.get(bi);
if bit_size == 13 {
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 b = section.blocks.get(bi);
if block_entity::BlockEntityType::get_block_entity(b).is_some() {
let pos = Position::new(
(bi & 0xF) as i32,
(bi >> 8) as i32,
@ -639,12 +623,6 @@ impl World {
}
}
for entry in &section.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.sky_light.data));
}
@ -933,9 +911,7 @@ pub struct Section {
y: u8,
blocks: bit::Map,
block_map: Vec<(block::Block, u32)>,
rev_block_map: HashMap<block::Block, usize, BuildHasherDefault<FNVHash>>,
blocks: storage::BlockStorage,
block_light: nibble::Array,
sky_light: nibble::Array,
@ -951,11 +927,7 @@ impl Section {
render_buffer: render::ChunkBuffer::new(),
y: y,
blocks: bit::Map::new(4096, 4),
block_map: vec![
(block::Air{}, 0xFFFFFFFF)
],
rev_block_map: HashMap::with_hasher(BuildHasherDefault::default()),
blocks: storage::BlockStorage::new(4096),
block_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.rev_block_map.insert(block::Air{}, 0);
section
}
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.block_map[idx].0
self.blocks.get(((y << 8) | (z << 4) | x) as usize)
}
fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) -> bool {
use std::collections::hash_map::Entry;
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);
if self.blocks.set(((y << 8) | (z << 4) | x) as usize, b) {
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 {

131
src/world/storage.rs Normal file
View File

@ -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;
}
}
}
}